diff --git a/notice.md b/notice.md
deleted file mode 100644
index e69de29..0000000
diff --git a/packages/viewer/package.json b/packages/viewer/package.json
index 7fe1af1..b3d1816 100644
--- a/packages/viewer/package.json
+++ b/packages/viewer/package.json
@@ -20,6 +20,9 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-infinite-scroll-component": "^6.1.0",
+ "react-markdown": "^9.0.0",
+ "rehype-katex": "^7.0.0",
+ "remark-math": "^6.0.0",
"socket.io-client": "^4.7.2",
"swr": "^2.2.2"
},
diff --git a/packages/viewer/src/app/[id]/[page]/page.tsx b/packages/viewer/src/app/[id]/[page]/page.tsx
index fb4e778..513b534 100644
--- a/packages/viewer/src/app/[id]/[page]/page.tsx
+++ b/packages/viewer/src/app/[id]/[page]/page.tsx
@@ -2,8 +2,10 @@ import { notFound } from "next/navigation";
import prisma from "@/lib/prisma";
import paginate from "@/lib/pagination";
import PageButtons from "@/components/replies/PageButtons";
-import serializeReply from "@/lib/serialize-reply";
+// import serializeReply from "@/lib/serialize-reply";
import Reply from "@/components/replies/Reply";
+import { selectReply } from "@/lib/reply";
+import { selectPost } from "@/lib/post";
const REPLIES_PER_PAGE = parseInt(process.env.REPLIES_PER_PAGE ?? "10", 10);
@@ -16,35 +18,19 @@ export default async function Page({
const page = parseInt(params.page, 10);
if (Number.isNaN(page)) notFound();
const {
- snapshots: [{ authorId }],
+ snapshots,
replies,
_count: { replies: numReplies },
} = await prisma.post
.findUnique({
where: { id },
select: {
- snapshots: {
- select: { authorId: true },
- orderBy: { time: "desc" },
- take: 1,
- },
+ ...selectPost.withLatestSnapshotMeta,
replies: {
select: {
- id: true,
- author: true,
- time: true,
- content: true,
- takedown: {
- select: {
- submitter: {
- select: {
- id: true,
- username: true,
- },
- },
- reason: true,
- },
- },
+ ...selectReply.withBasic,
+ ...selectReply.withTakedown,
+ ...selectReply.withLatestContent,
},
orderBy: { id: "asc" },
skip: (page - 1) * REPLIES_PER_PAGE,
@@ -53,16 +39,7 @@ export default async function Page({
_count: { select: { replies: true } },
},
})
- .then((discussion) => discussion ?? notFound())
- .then(async (discussion) => ({
- ...discussion,
- replies: await Promise.all(
- discussion.replies.map(async (reply) => ({
- ...reply,
- ...(await serializeReply(id, reply)),
- })),
- ),
- }));
+ .then((post) => post ?? notFound())
const numPages = Math.ceil(numReplies / REPLIES_PER_PAGE);
const { pagesLocalAttachedFront, pagesLocalAttachedBack, pagesLocal } =
paginate(numPages, page);
@@ -70,7 +47,7 @@ export default async function Page({
return (
<>
{replies.map((reply) => (
-
+
))}
{numPages > 1 && (
diff --git a/packages/viewer/src/app/[id]/context/[user]/route.ts b/packages/viewer/src/app/[id]/context/[user]/route.ts
index afed980..be508c9 100644
--- a/packages/viewer/src/app/[id]/context/[user]/route.ts
+++ b/packages/viewer/src/app/[id]/context/[user]/route.ts
@@ -1,12 +1,13 @@
import { NextRequest, NextResponse } from "next/server";
+import { selectReply } from "@/lib/reply";
import prisma from "@/lib/prisma";
-import serializeReply from "@/lib/serialize-reply";
+// import serializeReply from "@/lib/serialize-reply";
export async function GET(
request: NextRequest,
- { params }: { params: { id: string; user: string } },
+ { params }: { params: { id: string; user: string } }
) {
- const discussionId = parseInt(params.id, 10);
+ const postId = parseInt(params.id, 10);
const authorId = parseInt(params.user, 10);
/* eslint-disable @typescript-eslint/no-non-null-assertion */
const replyId = parseInt(request.nextUrl.searchParams.get("reply")!, 10);
@@ -15,54 +16,36 @@ export async function GET(
const reply = await (offset <= 0
? prisma.reply.findFirst({
select: {
- id: true,
- author: true,
- time: true,
- content: true,
- discussionId: true,
- takedown: {
- select: {
- submitter: {
- select: {
- id: true,
- username: true,
- },
- },
- reason: true,
- },
+ ...selectReply.withBasic,
+ ...selectReply.withTakedown,
+ ...selectReply.withLatestContent,
+ },
+ where: {
+ postId,
+ snapshots: {
+ some: { authorId },
},
+ id: { lt: replyId },
},
- where: { discussionId, authorId, id: { lt: replyId } },
orderBy: { id: "desc" },
skip: -offset,
})
: prisma.reply.findFirst({
select: {
- id: true,
- author: true,
- time: true,
- content: true,
- discussionId: true,
- takedown: {
- select: {
- submitter: {
- select: {
- id: true,
- username: true,
- },
- },
- reason: true,
- },
+ ...selectReply.withBasic,
+ ...selectReply.withTakedown,
+ ...selectReply.withLatestContent,
+ },
+ where: {
+ postId,
+ snapshots: {
+ some: { authorId },
},
+ id: { gt: replyId },
},
- where: { discussionId, authorId, id: { gt: replyId } },
orderBy: { id: "asc" },
skip: offset - 1,
}));
- if (reply)
- return NextResponse.json({
- ...reply,
- ...(await serializeReply(discussionId, reply)),
- });
+ if (reply) return NextResponse.json({ reply });
return NextResponse.json({});
}
diff --git a/packages/viewer/src/app/[id]/layout.tsx b/packages/viewer/src/app/[id]/layout.tsx
index 21e2610..5013c79 100644
--- a/packages/viewer/src/app/[id]/layout.tsx
+++ b/packages/viewer/src/app/[id]/layout.tsx
@@ -1,13 +1,13 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import prisma from "@/lib/prisma";
-import { getDiscussionUrl, getForumName, getForumUrl } from "@/lib/luogu";
+import { getPostUrl, getForumUrl } from "@/lib/luogu";
import stringifyTime from "@/lib/time";
import UserInfo from "@/components/UserInfo";
import "@/components/markdown.css";
import UpdateButton from "@/components/UpdateButton";
-import serializeReply from "@/lib/serialize-reply";
import Reply from "@/components/replies/Reply";
+import { selectPost } from "@/lib/post";
export async function generateMetadata({
params,
@@ -16,16 +16,14 @@ export async function generateMetadata({
}): Promise
{
const id = parseInt(params.id, 10);
if (Number.isNaN(id)) notFound();
- const snapshot = await prisma.snapshot.findFirst({
- select: { title: true, discussion: { select: { takedown: true } } },
+ const snapshot = await prisma.postSnapshot.findFirst({
+ select: { title: true, post: { select: { takedown: true } } },
orderBy: { time: "desc" },
- where: { discussionId: id },
+ where: { postId: id },
});
return {
title: `${
- snapshot && !snapshot.discussion.takedown
- ? `「${snapshot.title}」`
- : "404"
+ snapshot && !snapshot.post.takedown ? `「${snapshot.title}」` : "404"
} - 洛谷帖子保存站`,
};
}
@@ -42,23 +40,12 @@ export default async function Page({
snapshots: [{ title, forum, author, content, until: updatedAt }],
_count: { replies },
takedown,
- } = (await prisma.discussion.findUnique({
+ } = (await prisma.post.findUnique({
where: { id },
select: {
- time: true,
- replyCount: true,
- snapshots: {
- select: {
- until: true,
- title: true,
- forum: true,
- author: true,
- content: true,
- },
- orderBy: { time: "desc" },
- take: 1,
- },
- takedown: { select: { reason: true, submitter: true } },
+ ...selectPost.withBasic,
+ ...selectPost.withLatestContent,
+ ...selectPost.withTakedown,
_count: { select: { replies: true } },
},
})) ?? notFound();
@@ -87,7 +74,7 @@ export default async function Page({
target="_blank"
rel="noopener noreferrer"
>
- {getForumName(forum)}
+ {forum.name}
@@ -118,7 +105,7 @@ export default async function Page({
{children}
diff --git a/packages/viewer/src/app/[id]/replies/route.ts b/packages/viewer/src/app/[id]/replies/route.ts
index 37672bf..8202a6b 100644
--- a/packages/viewer/src/app/[id]/replies/route.ts
+++ b/packages/viewer/src/app/[id]/replies/route.ts
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma";
-import serializeReply from "@/lib/serialize-reply";
+// import serializeReply from "@/lib/serialize-reply";
import { selectReply } from "@/lib/reply";
@@ -24,12 +24,7 @@ export async function GET(
take: parseInt(limit ?? "10", 10),
});
return NextResponse.json({
- data: await Promise.all(
- replies.map(async (reply) => ({
- ...reply,
- ...(await serializeReply(id, reply)),
- })),
- ),
+ replies,
nextCursor: replies.length ? replies[replies.length - 1].id : null,
});
}
diff --git a/packages/viewer/src/app/r/[rid]/page.tsx b/packages/viewer/src/app/r/[rid]/page.tsx
index e473c86..06c91ef 100644
--- a/packages/viewer/src/app/r/[rid]/page.tsx
+++ b/packages/viewer/src/app/r/[rid]/page.tsx
@@ -1,3 +1,5 @@
+// eslint-disable
+// Not impl yet
import { notFound } from "next/navigation";
import Link from "next/link";
import prisma from "@/lib/prisma";
@@ -13,6 +15,8 @@ export const metadata = { title: "金玉良言 - 洛谷帖子保存站" };
const REPLIES_PER_PAGE = parseInt(process.env.REPLIES_PER_PAGE ?? "10", 10);
export default async function Page({ params }: { params: { rid: string } }) {
+ return <>抱歉,本功能暂未完成 TAT>;
+
const id = parseInt(params.rid, 10);
if (Number.isNaN(id)) notFound();
const replyRaw = await getReplyRaw(id);
diff --git a/packages/viewer/src/app/user/[uid]/page.tsx b/packages/viewer/src/app/user/[uid]/page.tsx
index 99467db..b5ba744 100644
--- a/packages/viewer/src/app/user/[uid]/page.tsx
+++ b/packages/viewer/src/app/user/[uid]/page.tsx
@@ -1,11 +1,13 @@
import { notFound } from "next/navigation";
import prisma from "@/lib/prisma";
import UserParticipated from "./participated/UserParticipated";
+import { selectUser } from "@/lib/user";
export default async function Page({ params }: { params: { uid: string } }) {
const user =
(await prisma.user.findUnique({
where: { id: parseInt(params.uid, 10) },
+ select: selectUser.withLatest,
})) ?? notFound();
return ;
}
diff --git a/packages/viewer/src/app/user/[uid]/participated/UserParticipated.tsx b/packages/viewer/src/app/user/[uid]/participated/UserParticipated.tsx
index 391521c..43c4eae 100644
--- a/packages/viewer/src/app/user/[uid]/participated/UserParticipated.tsx
+++ b/packages/viewer/src/app/user/[uid]/participated/UserParticipated.tsx
@@ -3,7 +3,6 @@
import Link from "next/link";
import useSWRInfinite from "swr/infinite";
import InfiniteScroll from "react-infinite-scroll-component";
-import type { User } from "@prisma/client";
import type { PostWithLatestContent } from "@/lib/post";
import type { UserMetioned } from "@/lib/serialize-reply";
import UserAvatar from "@/components/UserAvatar";
@@ -12,6 +11,7 @@ import Content from "@/components/replies/Content";
import fetcher from "@/lib/fetcher";
import Spinner from "@/components/Spinner";
import { NUM_MAX_REPLIES_SHOWED_DEFAULT } from "../constants";
+import { LatestUser } from "@/lib/user";
interface PageData {
data: (PostWithLatestContent & {
@@ -36,7 +36,7 @@ export default function UserParticipated({
user,
}: {
uid: string;
- user: User;
+ user: LatestUser;
}) {
const { data, size, setSize, isValidating } = useSWRInfinite(
(pageIndex: number, previousPageData: PageData) =>
@@ -137,7 +137,7 @@ export default function UserParticipated({
) : undefined}
@@ -235,7 +235,7 @@ export default function UserParticipated({
discussion.snapshots[0].author.id
}
content={reply.content}
- usersMetioned={reply.usersMetioned}
+ // usersMetioned={reply.usersMetioned}
/>
{href === undefined ? (
- {user.username}
+ {snapshot.name}
) : (
- {user.username}
+ {snapshot.name}
)}
- {user.checkmark && (
+ {snapshot.ccfLevel >= 3 && (
)}
- {user.badge && (
+ {snapshot.badge && (
- {user.badge}
+ {snapshot.badge}
)}
diff --git a/packages/viewer/src/components/replies/Content.tsx b/packages/viewer/src/components/replies/Content.tsx
index 43edd6c..d236ee9 100644
--- a/packages/viewer/src/components/replies/Content.tsx
+++ b/packages/viewer/src/components/replies/Content.tsx
@@ -1,94 +1,96 @@
+// eslint-disable
+// Not impl: Context
"use client";
-import "katex/dist/katex.css";
+import "katex/dist/katex.min.css";
import "highlight.js/styles/tokyo-night-dark.css";
import { useEffect, useRef } from "react";
-import renderMathInElement from "katex/contrib/auto-render";
-import { computePosition, shift } from "@floating-ui/dom";
-import type { UserMetioned } from "@/lib/serialize-reply";
-import UserInfo from "@/components/UserInfo";
-import UserAvatar from "@/components/UserAvatar";
+// import { computePosition, shift } from "@floating-ui/dom";
+// import type { UserMetioned } from "@/lib/serialize-reply";
+// import UserInfo from "@/components/UserInfo";
+// import UserAvatar from "@/components/UserAvatar";
+import Markdown from "react-markdown";
+import rehypeKatex from "rehype-katex";
+import remarkMath from "remark-math";
export default function Content({
+ // Markdown
content,
discussionAuthor,
- usersMetioned,
+ // usersMetioned,
userIdState,
}: {
content: string;
discussionAuthor: number;
- usersMetioned: UserMetioned[];
+ // usersMetioned: UserMetioned[];
// eslint-disable-next-line react/require-default-props
userIdState?: [number | null, (userId: number | null) => void];
}) {
const contentRef = useRef(null);
- const userRefs = useRef>({});
+ // const userRefs = useRef>({});
- useEffect(() => {
- renderMathInElement(contentRef.current!, {
- delimiters: [
- { left: "$$", right: "$$", display: true },
- { left: "$", right: "$", display: false },
- ],
- });
+ // useEffect(() => {
+ // contentRef.current?.querySelectorAll("a[data-uid]").forEach((element) => {
+ // const uid = parseInt(element.getAttribute("data-uid")!, 10);
+ // const tooltip = userRefs.current[uid]!;
+ // if (!tooltip) return;
- contentRef.current?.querySelectorAll("a[data-uid]").forEach((element) => {
- const uid = parseInt(element.getAttribute("data-uid")!, 10);
- const tooltip = userRefs.current[uid]!;
- if (!tooltip) return;
+ // function update() {
+ // // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ // computePosition(element as HTMLElement, tooltip, {
+ // placement: "top",
+ // middleware: [shift()],
+ // }).then(({ x, y }) =>
+ // Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px` }),
+ // );
+ // }
+ // function showTooltip() {
+ // update();
+ // tooltip.style.display = "block";
+ // }
+ // function hideTooltip() {
+ // tooltip.style.display = "none";
+ // }
- function update() {
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- computePosition(element as HTMLElement, tooltip, {
- placement: "top",
- middleware: [shift()],
- }).then(({ x, y }) =>
- Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px` }),
- );
- }
- function showTooltip() {
- update();
- tooltip.style.display = "block";
- }
- function hideTooltip() {
- tooltip.style.display = "none";
- }
-
- (
- [
- ["mouseenter", showTooltip],
- ["mouseleave", hideTooltip],
- ["focus", showTooltip],
- ["blur", hideTooltip],
- ...(userIdState
- ? [
- [
- "click",
- (event: Event) => {
- event.preventDefault();
- hideTooltip();
- if (userIdState[0] !== uid) userIdState[1](uid);
- else userIdState[1](null);
- },
- ],
- ]
- : []),
- ] as [string, () => void][]
- ).forEach(([event, listener]) =>
- element.addEventListener(event, listener),
- );
- });
- });
+ // (
+ // [
+ // ["mouseenter", showTooltip],
+ // ["mouseleave", hideTooltip],
+ // ["focus", showTooltip],
+ // ["blur", hideTooltip],
+ // ...(userIdState
+ // ? [
+ // [
+ // "click",
+ // (event: Event) => {
+ // event.preventDefault();
+ // hideTooltip();
+ // if (userIdState[0] !== uid) userIdState[1](uid);
+ // else userIdState[1](null);
+ // },
+ // ],
+ // ]
+ // : []),
+ // ] as [string, () => void][]
+ // ).forEach(([event, listener]) =>
+ // element.addEventListener(event, listener),
+ // );
+ // });
+ // });
return (
<>
- {usersMetioned.map((user) => (
+
+ {/* {usersMetioned.map((user) => (
{
userRefs.current[user.id] = el;
@@ -134,7 +136,7 @@ export default function Content({
- ))}
+ ))} */}
>
);
}
diff --git a/packages/viewer/src/components/replies/ContextViewer.tsx b/packages/viewer/src/components/replies/ContextViewer.tsx
index 6d9eecb..30e5d61 100644
--- a/packages/viewer/src/components/replies/ContextViewer.tsx
+++ b/packages/viewer/src/components/replies/ContextViewer.tsx
@@ -2,7 +2,6 @@
import { useState } from "react";
import useSWR from "swr";
-import type { User } from "@prisma/client";
import fetcher from "@/lib/fetcher";
import UserInfo from "@/components/UserInfo";
import type { UserMetioned } from "@/lib/serialize-reply";
@@ -22,122 +21,126 @@ export default function ContextViewer({
discussionId,
userId,
replyId,
- userMetioned,
+ // userMetioned,
}: {
discussionAuthor: number;
discussionId: number;
userId: number;
replyId: number;
- userMetioned: User;
+ // userMetioned: User;
}) {
- const [pageIndex, setPageIndex] = useState(0);
- const { data, isLoading } = useSWR(
- `/${discussionId}/context/${userId}?reply=${replyId}&offset=${pageIndex}`,
- fetcher,
- );
- return (
-
-
-
- {/* eslint-disable-next-line no-nested-ternary */}
- {isLoading ? (
-
- ) : Object.keys(data ?? {}).length ? (
-
-
-
-
- {/* eslint-disable-next-line no-nested-ternary */}
- {pageIndex === 0 ? (
-
- 推测的上文,发布于 {data!.time}
-
- ) : pageIndex < 0 ? (
-
- 可能的上文,发布于 {data!.time}
-
- ) : (
-
- 本层后发布,发布于 {data!.time}
-
- )}
-
-
- ) : (
-
-
- 空空如也,真好奇 {" "}
- 到底说过些什么呢?
-
-
- )}
-
-
-
-
-
-
-
- );
+
+ // TODO: TBD here
+ return <>>;
+
+ // const [pageIndex, setPageIndex] = useState(0);
+ // const { data, isLoading } = useSWR(
+ // `/${discussionId}/context/${userId}?reply=${replyId}&offset=${pageIndex}`,
+ // fetcher,
+ // );
+ // return (
+ //
+ //
+ //
+ // {/* eslint-disable-next-line no-nested-ternary */}
+ // {isLoading ? (
+ //
+ // ) : Object.keys(data ?? {}).length ? (
+ //
+ //
+ //
+ //
+ // {/* eslint-disable-next-line no-nested-ternary */}
+ // {pageIndex === 0 ? (
+ //
+ // 推测的上文,发布于 {data!.time}
+ //
+ // ) : pageIndex < 0 ? (
+ //
+ // 可能的上文,发布于 {data!.time}
+ //
+ // ) : (
+ //
+ // 本层后发布,发布于 {data!.time}
+ //
+ // )}
+ //
+ //
+ // ) : (
+ //
+ //
+ // 空空如也,真好奇 {" "}
+ // 到底说过些什么呢?
+ //
+ //
+ // )}
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // );
}
diff --git a/packages/viewer/src/components/replies/InfiniteScrollReplies.tsx b/packages/viewer/src/components/replies/InfiniteScrollReplies.tsx
index e2ad0fa..f861b64 100644
--- a/packages/viewer/src/components/replies/InfiniteScrollReplies.tsx
+++ b/packages/viewer/src/components/replies/InfiniteScrollReplies.tsx
@@ -9,15 +9,10 @@ import fetcher from "@/lib/fetcher";
import Spinner from "@/components/Spinner";
import PageButtons from "./PageButtons";
import Reply from "./Reply";
+import { ReplyWithLatestContent } from "@/lib/reply";
interface PageData {
- data: {
- id: number;
- time: string;
- author: User;
- content: string;
- usersMetioned: UserMetioned[];
- }[];
+ replies: ReplyWithLatestContent[];
nextCursor: number;
}
@@ -26,7 +21,7 @@ const REPLIES_PER_PAGE = parseInt(process.env.REPLIES_PER_PAGE ?? "10", 10);
export const getKey =
(id: number) => (pageIndex: number, previousPageData: PageData) => {
// 已经到最后一页
- if (previousPageData && !previousPageData.data.length) return null;
+ if (previousPageData && !previousPageData.replies.length) return null;
// 在首页时,没有 `previousPageData`
if (pageIndex === 0) return `/${id}/replies?limit=${REPLIES_PER_PAGE}`;
// 将游标添加到 API
@@ -54,17 +49,17 @@ export default function InfiniteScrollReplies({
return (
<>
c + a.data.length, 0) ?? 0}
+ dataLength={data?.reduce((c, a) => c + a.replies.length, 0) ?? 0}
next={() => showPageButtons || setSize(size + 1)}
- hasMore={(data?.[data.length - 1].data.length ?? 0) >= REPLIES_PER_PAGE}
+ hasMore={(data?.[data.length - 1].replies.length ?? 0) >= REPLIES_PER_PAGE}
loader=""
style={{ overflow: "inherit" }}
scrollThreshold="1024px"
>
{data?.map(
- (replies) =>
- replies.data?.map((reply) => (
-
+ (data) =>
+ data.replies?.map((reply) => (
+
)),
)}
diff --git a/packages/viewer/src/components/replies/MarkdownContent.tsx b/packages/viewer/src/components/replies/MarkdownContent.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/packages/viewer/src/components/replies/Reply.tsx b/packages/viewer/src/components/replies/Reply.tsx
index 0bab0a3..74b825b 100644
--- a/packages/viewer/src/components/replies/Reply.tsx
+++ b/packages/viewer/src/components/replies/Reply.tsx
@@ -1,51 +1,50 @@
"use client";
import { useState } from "react";
-import type { User } from "@prisma/client";
+// import type { User } from "@prisma/client";
import Link from "next/link";
-import type { UserMetioned } from "@/lib/serialize-reply";
+// import type { UserMetioned } from "@/lib/serialize-reply";
import UserAvatar from "@/components/UserAvatar";
import UserInfo from "@/components/UserInfo";
import Content from "./Content";
import ContextViewer from "./ContextViewer";
+import type { ReplyWithLatestContent } from "@/lib/reply";
+import stringifyTime from "@/lib/time";
+
export default function Reply({
- discussion,
+ post,
reply,
children,
}: React.PropsWithChildren<{
- discussion: { id: number; authorId: number };
- reply: {
- id?: number;
- time: string;
- author: User;
- content: string;
- usersMetioned: UserMetioned[];
- };
+ post: { id: number; authorId: number };
+ reply: ReplyWithLatestContent;
}>) {
const [userId, setUserId] = useState(null);
+ const snapshot = reply.snapshots[0]
+
return (
- {reply.id && userId && (
+ {reply.id !== -1 && userId && (
user.id === userId)!}
+ // userMetioned={reply.usersMetioned.find((user) => user.id === userId)!}
key={userId}
/>
)}
-
+
-
- {reply.author.id === discussion.authorId ? (
+
+ {snapshot.author.id === post.authorId ? (
- {reply.time}
- {reply.id !== undefined ? (
+ {stringifyTime(reply.time)}
+ {reply.id !== -1 ? (
{children}
- {reply.time}
+ {stringifyTime(reply.time)}
diff --git a/packages/viewer/src/lib/luogu.ts b/packages/viewer/src/lib/luogu.ts
index 197d4a1..d29efff 100644
--- a/packages/viewer/src/lib/luogu.ts
+++ b/packages/viewer/src/lib/luogu.ts
@@ -1,4 +1,6 @@
-export const getDiscussionUrl = (discussion: number, page?: number) =>
+import { Color, Forum } from "@prisma/client";
+
+export const getPostUrl = (discussion: number, page?: number) =>
`https://www.luogu.com.cn/discuss/${discussion}${
page !== undefined ? `?page=${page}` : ""
}`;
@@ -11,18 +13,10 @@ export const getUserRealUrl = (user: number) =>
export const getUserAvatarUrl = (user: number) =>
`https://cdn.luogu.com.cn/upload/usericon/${user}.png`;
-export const getForumUrl = (forum: string) =>
- `https://www.luogu.com.cn/discuss/lists?forumname=${forum}`;
+export const getForumUrl = (forum: Forum) =>
+ `https://www.luogu.com.cn/discuss/lists?forumname=${forum.slug}`;
-export const getForumName = (forum: string) =>
- ({
- siteaffairs: "站务版",
- problem: "题目总版",
- academics: "学术版",
- relevantaffairs: "灌水区",
- service: "反馈、申请、工单专版",
- miaomiaowu: "小黑屋",
- })[forum] ?? forum;
+export const getForumName = (forum: Forum) => forum.name;
export const judgementUrl = "https://www.luogu.com.cn/judgement";
@@ -46,7 +40,7 @@ export function getUserIdFromUrl(target: URL) {
(target.pathname.startsWith("/user/") && target.pathname.split("/")[2]) ||
((target.pathname === "/space/show" &&
target.searchParams.get("uid")) as string),
- 10,
+ 10
);
return Number.isNaN(uid) ? null : uid;
}
@@ -58,7 +52,7 @@ export function getDiscussionIdFromUrl(target: URL) {
target.pathname.split("/")[2]) ||
((target.pathname === "/discuss/show" &&
target.searchParams.get("postid")) as string),
- 10,
+ 10
);
return Number.isNaN(discussionId) ? null : discussionId;
}
@@ -80,6 +74,31 @@ export function getDiscussionId(s: string) {
(url.pathname === "/discuss/show" && url.searchParams.get("postid")) ||
((url.pathname.startsWith("/discuss/") &&
url.pathname.split("/")[2]) as string),
- 10,
+ 10
);
}
+
+export function getCheckmarkColor(ccfLevel: number) {
+ if (ccfLevel <= 5) return "#52c41a";
+ if (ccfLevel <= 7) return "#3498db";
+ return "#ffc116";
+}
+
+export function getNameClassByColor(color: Color) {
+ switch (color) {
+ case "Blue":
+ return "bluelight";
+ case "Green":
+ return "green";
+ case "Gray":
+ return "gray";
+ case "Cheater":
+ return "brown";
+ case "Orange":
+ return "orange";
+ case "Purple":
+ return "purple";
+ case "Red":
+ return "red";
+ }
+}
diff --git a/packages/viewer/src/lib/post.ts b/packages/viewer/src/lib/post.ts
index 6e10513..815ee1d 100644
--- a/packages/viewer/src/lib/post.ts
+++ b/packages/viewer/src/lib/post.ts
@@ -1,4 +1,6 @@
import { Prisma } from "@prisma/client";
+import { getReply } from "./reply";
+import { selectUser } from "./user";
export const selectPost = {
withBasic: Prisma.validator
()({
@@ -8,6 +10,8 @@ export const selectPost = {
replyCount: true,
},
}).select,
+
+ // Not compatible to use `withLatestContent` here, because the `content` field
withLatestSnapshotMeta: Prisma.validator()({
select: {
snapshots: {
@@ -15,7 +19,10 @@ export const selectPost = {
time: true,
title: true,
forum: true,
- author: true,
+ author: {
+ select: selectUser.withLatest,
+ },
+ until: true,
},
orderBy: { time: "desc" },
take: 1,
@@ -27,11 +34,13 @@ export const selectPost = {
takedown: {
select: {
reason: true,
- submitter: true,
+ submitter: { select: selectUser.withLatest },
},
},
},
}).select,
+
+ // Not compatible to use `withLatestSnapshotMeta` here, because the `content` field
withLatestContent: Prisma.validator()({
select: {
snapshots: {
@@ -39,8 +48,11 @@ export const selectPost = {
time: true,
title: true,
forum: true,
- author: true,
+ author: {
+ select: selectUser.withLatest,
+ },
content: true,
+ until: true,
},
orderBy: { time: "desc" },
take: 1,
@@ -70,6 +82,16 @@ export const selectPostWithLatestContent =
select: getPost.latestWithContent,
});
+export const selectPostWithLatestReplies =
+ Prisma.validator()({
+ select: {
+ ...selectPost.withLatestContent,
+ replies: {
+ select: getReply.latestWithContent,
+ },
+ },
+ });
+
export type PostWithLatestSnapshotMeta = Prisma.PostGetPayload<
typeof selectPostWithLatestSnapshotMeta
>;
@@ -77,3 +99,7 @@ export type PostWithLatestSnapshotMeta = Prisma.PostGetPayload<
export type PostWithLatestContent = Prisma.PostGetPayload<
typeof selectPostWithLatestContent
>;
+
+export type PostWithLatestReplies = Prisma.PostGetPayload<
+ typeof selectPostWithLatestReplies
+>;
diff --git a/packages/viewer/src/lib/reply.ts b/packages/viewer/src/lib/reply.ts
index 331216e..67106fb 100644
--- a/packages/viewer/src/lib/reply.ts
+++ b/packages/viewer/src/lib/reply.ts
@@ -1,4 +1,5 @@
import { Prisma } from "@prisma/client";
+import { selectUser } from "./user";
export const selectReply = {
withBasic: Prisma.validator()({
@@ -13,7 +14,7 @@ export const selectReply = {
snapshots: {
select: {
time: true,
- author: true,
+ author: { select: selectUser.withLatest },
},
orderBy: { time: "desc" },
take: 1,
@@ -25,11 +26,7 @@ export const selectReply = {
takedown: {
select: {
reason: true,
- submitter: {
- select: {
- userSnapshots: true,
- },
- },
+ submitter: { select: selectUser.withLatest },
},
},
},
@@ -39,7 +36,7 @@ export const selectReply = {
snapshots: {
select: {
time: true,
- author: true,
+ author: { select: selectUser.withLatest },
content: true,
},
orderBy: { time: "desc" },
diff --git a/packages/viewer/src/lib/user.ts b/packages/viewer/src/lib/user.ts
new file mode 100644
index 0000000..ab33ffb
--- /dev/null
+++ b/packages/viewer/src/lib/user.ts
@@ -0,0 +1,24 @@
+import { Prisma } from "@prisma/client";
+
+export const selectUser = {
+ withIdOnly: Prisma.validator()({
+ select: {
+ id: true,
+ },
+ }).select,
+ withLatest: Prisma.validator()({
+ select: {
+ id: true,
+ userSnapshots: {
+ orderBy: { time: "desc" },
+ take: 1,
+ },
+ },
+ }).select,
+};
+
+export const selectUserWithLatest = Prisma.validator()({
+ select: selectUser.withLatest,
+});
+
+export type LatestUser = Prisma.UserGetPayload;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b57d350..96a5a43 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -131,6 +131,15 @@ importers:
react-infinite-scroll-component:
specifier: ^6.1.0
version: 6.1.0(react@18.2.0)
+ react-markdown:
+ specifier: ^9.0.0
+ version: 9.0.0(@types/react@18.2.22)(react@18.2.0)
+ rehype-katex:
+ specifier: ^7.0.0
+ version: 7.0.0
+ remark-math:
+ specifier: ^6.0.0
+ version: 6.0.0
socket.io-client:
specifier: ^4.7.2
version: 4.7.2
@@ -656,6 +665,12 @@ packages:
'@types/node': 20.6.3
dev: false
+ /@types/debug@4.1.9:
+ resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==}
+ dependencies:
+ '@types/ms': 0.7.32
+ dev: false
+
/@types/eslint-scope@3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies:
@@ -674,6 +689,12 @@ packages:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
dev: true
+ /@types/hast@3.0.1:
+ resolution: {integrity: sha512-hs/iBJx2aydugBQx5ETV3ZgeSS0oIreQrFJ4bjBl0XvM4wAmDjFEALY7p0rTSLt2eL+ibjRAAs9dTPiCLtmbqQ==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
/@types/jsdom@21.1.3:
resolution: {integrity: sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA==}
dependencies:
@@ -692,14 +713,22 @@ packages:
/@types/katex@0.16.3:
resolution: {integrity: sha512-CeVMX9EhVUW8MWnei05eIRks4D5Wscw/W9Byz1s3PA+yJvcdvq9SaDjiUKvRvEgjpdTyJMjQA43ae4KTwsvOPg==}
- dev: true
+
+ /@types/mdast@4.0.1:
+ resolution: {integrity: sha512-IlKct1rUTJ1T81d8OHzyop15kGv9A/ff7Gz7IJgrk6jDb4Udw77pCJ+vq8oxZf4Ghpm+616+i1s/LNg/Vh7d+g==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
+ /@types/ms@0.7.32:
+ resolution: {integrity: sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==}
+ dev: false
/@types/node@20.6.3:
resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==}
/@types/prop-types@15.7.6:
resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==}
- dev: true
/@types/react-dom@18.2.7:
resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==}
@@ -713,11 +742,9 @@ packages:
'@types/prop-types': 15.7.6
'@types/scheduler': 0.16.3
csstype: 3.1.2
- dev: true
/@types/scheduler@0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
- dev: true
/@types/semver@7.5.2:
resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==}
@@ -731,6 +758,10 @@ packages:
resolution: {integrity: sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg==}
dev: true
+ /@types/unist@3.0.0:
+ resolution: {integrity: sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==}
+ dev: false
+
/@types/yauzl@2.10.0:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
@@ -869,6 +900,10 @@ packages:
eslint-visitor-keys: 3.4.3
dev: true
+ /@ungap/structured-clone@1.2.0:
+ resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ dev: false
+
/@webassemblyjs/ast@1.11.6:
resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
dependencies:
@@ -1271,6 +1306,10 @@ packages:
/b4a@1.6.4:
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
+ /bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+ dev: false
+
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -1424,6 +1463,10 @@ packages:
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: true
+ /character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+ dev: false
+
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -1532,6 +1575,10 @@ packages:
delayed-stream: 1.0.0
dev: false
+ /comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+ dev: false
+
/commander@1.1.1:
resolution: {integrity: sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==}
engines: {node: '>= 0.6.x'}
@@ -1631,7 +1678,6 @@ packages:
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
- dev: true
/damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -1676,6 +1722,12 @@ packages:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
dev: false
+ /decode-named-character-reference@1.0.2:
+ resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+ dependencies:
+ character-entities: 2.0.2
+ dev: false
+
/decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
@@ -1753,7 +1805,6 @@ packages:
/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
- dev: true
/detect-libc@2.0.2:
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
@@ -1762,6 +1813,12 @@ packages:
dev: false
optional: true
+ /devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ dependencies:
+ dequal: 2.0.3
+ dev: false
+
/devtools-protocol@0.0.1179426:
resolution: {integrity: sha512-KKC7IGwdOr7u9kTGgjUvGTov/z1s2H7oHi3zKCdR9eSDyCPia5CBi4aRhtp7d8uR7l0GS5UTDw3TjKGu5CqINg==}
@@ -2429,6 +2486,10 @@ packages:
dev: false
optional: true
+ /extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ dev: false
+
/extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
@@ -2839,6 +2900,98 @@ packages:
function-bind: 1.1.1
dev: true
+ /hast-util-from-dom@5.0.0:
+ resolution: {integrity: sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==}
+ dependencies:
+ '@types/hast': 3.0.1
+ hastscript: 8.0.0
+ web-namespaces: 2.0.1
+ dev: false
+
+ /hast-util-from-html-isomorphic@2.0.0:
+ resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==}
+ dependencies:
+ '@types/hast': 3.0.1
+ hast-util-from-dom: 5.0.0
+ hast-util-from-html: 2.0.1
+ unist-util-remove-position: 5.0.0
+ dev: false
+
+ /hast-util-from-html@2.0.1:
+ resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==}
+ dependencies:
+ '@types/hast': 3.0.1
+ devlop: 1.1.0
+ hast-util-from-parse5: 8.0.1
+ parse5: 7.1.2
+ vfile: 6.0.1
+ vfile-message: 4.0.2
+ dev: false
+
+ /hast-util-from-parse5@8.0.1:
+ resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/unist': 3.0.0
+ devlop: 1.1.0
+ hastscript: 8.0.0
+ property-information: 6.3.0
+ vfile: 6.0.1
+ vfile-location: 5.0.2
+ web-namespaces: 2.0.1
+ dev: false
+
+ /hast-util-is-element@3.0.0:
+ resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+ dependencies:
+ '@types/hast': 3.0.1
+ dev: false
+
+ /hast-util-parse-selector@4.0.0:
+ resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
+ dependencies:
+ '@types/hast': 3.0.1
+ dev: false
+
+ /hast-util-to-jsx-runtime@2.2.0:
+ resolution: {integrity: sha512-wSlp23N45CMjDg/BPW8zvhEi3R+8eRE1qFbjEyAUzMCzu2l1Wzwakq+Tlia9nkCtEl5mDxa7nKHsvYJ6Gfn21A==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/unist': 3.0.0
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ property-information: 6.3.0
+ space-separated-tokens: 2.0.2
+ style-to-object: 0.4.2
+ unist-util-position: 5.0.0
+ vfile-message: 4.0.2
+ dev: false
+
+ /hast-util-to-text@4.0.0:
+ resolution: {integrity: sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/unist': 3.0.0
+ hast-util-is-element: 3.0.0
+ unist-util-find-after: 5.0.0
+ dev: false
+
+ /hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+ dependencies:
+ '@types/hast': 3.0.1
+ dev: false
+
+ /hastscript@8.0.0:
+ resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
+ dependencies:
+ '@types/hast': 3.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-parse-selector: 4.0.0
+ property-information: 6.3.0
+ space-separated-tokens: 2.0.2
+ dev: false
+
/highlight.js@11.8.0:
resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
engines: {node: '>=12.0.0'}
@@ -2851,6 +3004,10 @@ packages:
whatwg-encoding: 2.0.0
dev: false
+ /html-url-attributes@3.0.0:
+ resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==}
+ dev: false
+
/http-proxy-agent@5.0.0:
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
engines: {node: '>= 6'}
@@ -2952,6 +3109,10 @@ packages:
dev: false
optional: true
+ /inline-style-parser@0.1.1:
+ resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
+ dev: false
+
/internal-slot@1.0.5:
resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
@@ -3111,6 +3272,11 @@ packages:
engines: {node: '>=8'}
dev: true
+ /is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+ dev: false
+
/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: false
@@ -3429,6 +3595,10 @@ packages:
wrap-ansi: 8.1.0
dev: true
+ /longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+ dev: false
+
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
@@ -3445,6 +3615,78 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
+ /mdast-util-from-markdown@2.0.0:
+ resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ '@types/unist': 3.0.0
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.0
+ micromark-util-decode-numeric-character-reference: 2.0.0
+ micromark-util-decode-string: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-math@3.0.0:
+ resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/mdast': 4.0.1
+ devlop: 1.1.0
+ longest-streak: 3.1.0
+ mdast-util-from-markdown: 2.0.0
+ mdast-util-to-markdown: 2.1.0
+ unist-util-remove-position: 5.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-phrasing@4.0.0:
+ resolution: {integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ unist-util-is: 6.0.0
+ dev: false
+
+ /mdast-util-to-hast@13.0.2:
+ resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/mdast': 4.0.1
+ '@ungap/structured-clone': 1.2.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.0
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ dev: false
+
+ /mdast-util-to-markdown@2.1.0:
+ resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ '@types/unist': 3.0.0
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.0.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-decode-string: 2.0.0
+ unist-util-visit: 5.0.0
+ zwitch: 2.0.4
+ dev: false
+
+ /mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ dev: false
+
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
@@ -3454,6 +3696,193 @@ packages:
engines: {node: '>= 8'}
dev: true
+ /micromark-core-commonmark@2.0.0:
+ resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==}
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.0
+ micromark-factory-label: 2.0.0
+ micromark-factory-space: 2.0.0
+ micromark-factory-title: 2.0.0
+ micromark-factory-whitespace: 2.0.0
+ micromark-util-character: 2.0.1
+ micromark-util-chunked: 2.0.0
+ micromark-util-classify-character: 2.0.0
+ micromark-util-html-tag-name: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-resolve-all: 2.0.0
+ micromark-util-subtokenize: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-extension-math@3.0.0:
+ resolution: {integrity: sha512-iJ2Q28vBoEovLN5o3GO12CpqorQRYDPT+p4zW50tGwTfJB+iv/VnB6Ini+gqa24K97DwptMBBIvVX6Bjk49oyQ==}
+ dependencies:
+ '@types/katex': 0.16.3
+ devlop: 1.1.0
+ katex: 0.16.8
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-factory-destination@2.0.0:
+ resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
+ dependencies:
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-factory-label@2.0.0:
+ resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-factory-space@2.0.0:
+ resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
+ dependencies:
+ micromark-util-character: 2.0.1
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-factory-title@2.0.0:
+ resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
+ dependencies:
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-factory-whitespace@2.0.0:
+ resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
+ dependencies:
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-character@2.0.1:
+ resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-chunked@2.0.0:
+ resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-classify-character@2.0.0:
+ resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
+ dependencies:
+ micromark-util-character: 2.0.1
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-combine-extensions@2.0.0:
+ resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
+ dependencies:
+ micromark-util-chunked: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-decode-numeric-character-reference@2.0.0:
+ resolution: {integrity: sha512-pIgcsGxpHEtTG/rPJRz/HOLSqp5VTuIIjXlPI+6JSDlK2oljApusG6KzpS8AF0ENUMCHlC/IBb5B9xdFiVlm5Q==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-decode-string@2.0.0:
+ resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ micromark-util-character: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.0
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-encode@2.0.0:
+ resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+ dev: false
+
+ /micromark-util-html-tag-name@2.0.0:
+ resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
+ dev: false
+
+ /micromark-util-normalize-identifier@2.0.0:
+ resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-resolve-all@2.0.0:
+ resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
+ dependencies:
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-sanitize-uri@2.0.0:
+ resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+ dependencies:
+ micromark-util-character: 2.0.1
+ micromark-util-encode: 2.0.0
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-subtokenize@2.0.0:
+ resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==}
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-symbol@2.0.0:
+ resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+ dev: false
+
+ /micromark-util-types@2.0.0:
+ resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+ dev: false
+
+ /micromark@4.0.0:
+ resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
+ dependencies:
+ '@types/debug': 4.1.9
+ debug: 4.3.4
+ decode-named-character-reference: 1.0.2
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.0
+ micromark-factory-space: 2.0.0
+ micromark-util-character: 2.0.1
+ micromark-util-chunked: 2.0.0
+ micromark-util-combine-extensions: 2.0.0
+ micromark-util-decode-numeric-character-reference: 2.0.0
+ micromark-util-encode: 2.0.0
+ micromark-util-normalize-identifier: 2.0.0
+ micromark-util-resolve-all: 2.0.0
+ micromark-util-sanitize-uri: 2.0.0
+ micromark-util-subtokenize: 2.0.0
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'}
@@ -3975,6 +4404,10 @@ packages:
react-is: 16.13.1
dev: true
+ /property-information@6.3.0:
+ resolution: {integrity: sha512-gVNZ74nqhRMiIUYWGQdosYetaKc83x8oT41a0LlV3AAFCAZwCpg4vmGkq8t34+cUhp3cnM4XDiU/7xlgK7HGrg==}
+ dev: false
+
/proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@@ -4102,6 +4535,29 @@ packages:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: true
+ /react-markdown@9.0.0(@types/react@18.2.22)(react@18.2.0):
+ resolution: {integrity: sha512-v6yNf3AB8GfJ8lCpUvzxAXKxgsHpdmWPlcVRQ6Nocsezp255E/IDrF31kLQsPJeB/cKto/geUwjU36wH784FCA==}
+ peerDependencies:
+ '@types/react': '>=18'
+ react: '>=18'
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/react': 18.2.22
+ devlop: 1.1.0
+ hast-util-to-jsx-runtime: 2.2.0
+ html-url-attributes: 3.0.0
+ mdast-util-to-hast: 13.0.2
+ micromark-util-sanitize-uri: 2.0.0
+ react: 18.2.0
+ remark-parse: 11.0.0
+ remark-rehype: 11.0.0
+ unified: 11.0.3
+ unist-util-visit: 5.0.0
+ vfile: 6.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -4167,6 +4623,50 @@ packages:
set-function-name: 2.0.1
dev: true
+ /rehype-katex@7.0.0:
+ resolution: {integrity: sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/katex': 0.16.3
+ hast-util-from-html-isomorphic: 2.0.0
+ hast-util-to-text: 4.0.0
+ katex: 0.16.8
+ unist-util-visit-parents: 6.0.1
+ vfile: 6.0.1
+ dev: false
+
+ /remark-math@6.0.0:
+ resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ mdast-util-math: 3.0.0
+ micromark-extension-math: 3.0.0
+ unified: 11.0.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /remark-parse@11.0.0:
+ resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+ dependencies:
+ '@types/mdast': 4.0.1
+ mdast-util-from-markdown: 2.0.0
+ micromark-util-types: 2.0.0
+ unified: 11.0.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /remark-rehype@11.0.0:
+ resolution: {integrity: sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==}
+ dependencies:
+ '@types/hast': 3.0.1
+ '@types/mdast': 4.0.1
+ mdast-util-to-hast: 13.0.2
+ unified: 11.0.3
+ vfile: 6.0.1
+ dev: false
+
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -4530,6 +5030,10 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
+ /space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+ dev: false
+
/split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
@@ -4657,6 +5161,12 @@ packages:
engines: {node: '>=8'}
dev: true
+ /style-to-object@0.4.2:
+ resolution: {integrity: sha512-1JGpfPB3lo42ZX8cuPrheZbfQ6kqPPnPHlKMyeRYtfKD+0jG+QsXgXN57O/dvJlzlB2elI6dGmrPnl5VPQFPaA==}
+ dependencies:
+ inline-style-parser: 0.1.1
+ dev: false
+
/styled-jsx@5.1.1(react@18.2.0):
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
engines: {node: '>= 12.0.0'}
@@ -4858,6 +5368,14 @@ packages:
punycode: 2.3.0
dev: false
+ /trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+ dev: false
+
+ /trough@2.1.0:
+ resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
+ dev: false
+
/ts-api-utils@1.0.3(typescript@5.2.2):
resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
engines: {node: '>=16.13.0'}
@@ -4962,6 +5480,65 @@ packages:
buffer: 5.7.1
through: 2.3.8
+ /unified@11.0.3:
+ resolution: {integrity: sha512-jlCV402P+YDcFcB2VcN/n8JasOddqIiaxv118wNBoZXEhOn+lYG7BR4Bfg2BwxvlK58dwbuH2w7GX2esAjL6Mg==}
+ dependencies:
+ '@types/unist': 3.0.0
+ bail: 2.0.2
+ devlop: 1.1.0
+ extend: 3.0.2
+ is-plain-obj: 4.1.0
+ trough: 2.1.0
+ vfile: 6.0.1
+ dev: false
+
+ /unist-util-find-after@5.0.0:
+ resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-is: 6.0.0
+ dev: false
+
+ /unist-util-is@6.0.0:
+ resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
+ /unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
+ /unist-util-remove-position@5.0.0:
+ resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-visit: 5.0.0
+ dev: false
+
+ /unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
+ /unist-util-visit-parents@6.0.1:
+ resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-is: 6.0.0
+ dev: false
+
+ /unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+ dev: false
+
/universalify@0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
@@ -5029,6 +5606,28 @@ packages:
engines: {node: '>= 0.8'}
dev: false
+ /vfile-location@5.0.2:
+ resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
+ dependencies:
+ '@types/unist': 3.0.0
+ vfile: 6.0.1
+ dev: false
+
+ /vfile-message@4.0.2:
+ resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-stringify-position: 4.0.0
+ dev: false
+
+ /vfile@6.0.1:
+ resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.2
+ dev: false
+
/w3c-xmlserializer@4.0.0:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
engines: {node: '>=14'}
@@ -5043,6 +5642,10 @@ packages:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
+ /web-namespaces@2.0.1:
+ resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+ dev: false
+
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -5330,3 +5933,7 @@ packages:
/zod@3.21.4:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
dev: false
+
+ /zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+ dev: false
diff --git a/prisma/migrations/20230928144633_init/migration.sql b/prisma/migrations/20230928144633_init/migration.sql
new file mode 100644
index 0000000..ce0ac75
--- /dev/null
+++ b/prisma/migrations/20230928144633_init/migration.sql
@@ -0,0 +1,221 @@
+-- CreateEnum
+CREATE TYPE "Color" AS ENUM ('Cheater', 'Gray', 'Blue', 'Green', 'Orange', 'Red', 'Purple');
+
+-- CreateTable
+CREATE TABLE "User" (
+ "id" INTEGER NOT NULL,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Forum" (
+ "slug" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+
+ CONSTRAINT "Forum_pkey" PRIMARY KEY ("slug")
+);
+
+-- CreateTable
+CREATE TABLE "Post" (
+ "id" INTEGER NOT NULL,
+ "time" TIMESTAMP(0) NOT NULL,
+ "replyCount" INTEGER NOT NULL,
+
+ CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Reply" (
+ "id" INTEGER NOT NULL,
+ "postId" INTEGER NOT NULL,
+ "time" TIMESTAMP(0) NOT NULL,
+
+ CONSTRAINT "Reply_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Activity" (
+ "id" INTEGER NOT NULL,
+ "type" INTEGER NOT NULL,
+ "time" TIMESTAMP(0) NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "content" TEXT NOT NULL,
+
+ CONSTRAINT "Activity_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Paste" (
+ "id" CHAR(8) NOT NULL,
+ "time" TIMESTAMP(0) NOT NULL,
+ "userId" INTEGER NOT NULL,
+
+ CONSTRAINT "Paste_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Judgement" (
+ "time" TIMESTAMP(0) NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "content" TEXT NOT NULL,
+
+ CONSTRAINT "Judgement_pkey" PRIMARY KEY ("time","userId")
+);
+
+-- CreateTable
+CREATE TABLE "UserSnapshot" (
+ "userId" INTEGER NOT NULL,
+ "name" TEXT NOT NULL,
+ "badge" TEXT,
+ "isAdmin" BOOLEAN NOT NULL,
+ "isBanned" BOOLEAN NOT NULL,
+ "isRoot" BOOLEAN,
+ "color" "Color" NOT NULL,
+ "ccfLevel" INTEGER NOT NULL,
+ "until" TIMESTAMP(3) NOT NULL,
+ "time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT "UserSnapshot_pkey" PRIMARY KEY ("userId","time")
+);
+
+-- CreateTable
+CREATE TABLE "PostSnapshot" (
+ "postId" INTEGER NOT NULL,
+ "authorId" INTEGER NOT NULL,
+ "time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "until" TIMESTAMP(3) NOT NULL,
+ "title" TEXT NOT NULL,
+ "forumSlug" TEXT NOT NULL,
+ "content" TEXT NOT NULL,
+
+ CONSTRAINT "PostSnapshot_pkey" PRIMARY KEY ("postId","time")
+);
+
+-- CreateTable
+CREATE TABLE "ReplySnapshot" (
+ "replyId" INTEGER NOT NULL,
+ "authorId" INTEGER NOT NULL,
+ "time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "until" TIMESTAMP(3) NOT NULL,
+ "content" TEXT NOT NULL,
+
+ CONSTRAINT "ReplySnapshot_pkey" PRIMARY KEY ("replyId","time")
+);
+
+-- CreateTable
+CREATE TABLE "PasteSnapshot" (
+ "pasteId" CHAR(8) NOT NULL,
+ "time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "until" TIMESTAMP(3) NOT NULL,
+ "public" BOOLEAN NOT NULL,
+ "data" TEXT,
+
+ CONSTRAINT "PasteSnapshot_pkey" PRIMARY KEY ("pasteId","time")
+);
+
+-- CreateTable
+CREATE TABLE "PostTakedown" (
+ "postId" INTEGER NOT NULL,
+ "submitterId" INTEGER NOT NULL,
+ "reason" TEXT NOT NULL,
+
+ CONSTRAINT "PostTakedown_pkey" PRIMARY KEY ("postId")
+);
+
+-- CreateTable
+CREATE TABLE "ReplyTakedown" (
+ "replyId" INTEGER NOT NULL,
+ "submitterId" INTEGER NOT NULL,
+ "reason" TEXT NOT NULL,
+
+ CONSTRAINT "ReplyTakedown_pkey" PRIMARY KEY ("replyId")
+);
+
+-- CreateTable
+CREATE TABLE "ActivityTakedown" (
+ "activityId" INTEGER NOT NULL,
+ "submitterId" INTEGER NOT NULL,
+ "reason" TEXT NOT NULL,
+
+ CONSTRAINT "ActivityTakedown_pkey" PRIMARY KEY ("activityId")
+);
+
+-- CreateIndex
+CREATE INDEX "Reply_postId_idx" ON "Reply"("postId");
+
+-- CreateIndex
+CREATE INDEX "Paste_userId_idx" ON "Paste"("userId");
+
+-- CreateIndex
+CREATE INDEX "Judgement_time_idx" ON "Judgement"("time" DESC);
+
+-- CreateIndex
+CREATE INDEX "Judgement_userId_idx" ON "Judgement"("userId");
+
+-- CreateIndex
+CREATE INDEX "UserSnapshot_userId_idx" ON "UserSnapshot"("userId");
+
+-- CreateIndex
+CREATE INDEX "PostSnapshot_postId_idx" ON "PostSnapshot"("postId");
+
+-- CreateIndex
+CREATE INDEX "PostSnapshot_authorId_idx" ON "PostSnapshot"("authorId");
+
+-- CreateIndex
+CREATE INDEX "ReplySnapshot_replyId_idx" ON "ReplySnapshot"("replyId");
+
+-- CreateIndex
+CREATE INDEX "ReplySnapshot_authorId_idx" ON "ReplySnapshot"("authorId");
+
+-- AddForeignKey
+ALTER TABLE "Reply" ADD CONSTRAINT "Reply_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Activity" ADD CONSTRAINT "Activity_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Paste" ADD CONSTRAINT "Paste_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Judgement" ADD CONSTRAINT "Judgement_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "UserSnapshot" ADD CONSTRAINT "UserSnapshot_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PostSnapshot" ADD CONSTRAINT "PostSnapshot_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PostSnapshot" ADD CONSTRAINT "PostSnapshot_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PostSnapshot" ADD CONSTRAINT "PostSnapshot_forumSlug_fkey" FOREIGN KEY ("forumSlug") REFERENCES "Forum"("slug") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ReplySnapshot" ADD CONSTRAINT "ReplySnapshot_replyId_fkey" FOREIGN KEY ("replyId") REFERENCES "Reply"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ReplySnapshot" ADD CONSTRAINT "ReplySnapshot_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PasteSnapshot" ADD CONSTRAINT "PasteSnapshot_pasteId_fkey" FOREIGN KEY ("pasteId") REFERENCES "Paste"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PostTakedown" ADD CONSTRAINT "PostTakedown_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PostTakedown" ADD CONSTRAINT "PostTakedown_submitterId_fkey" FOREIGN KEY ("submitterId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ReplyTakedown" ADD CONSTRAINT "ReplyTakedown_replyId_fkey" FOREIGN KEY ("replyId") REFERENCES "Reply"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ReplyTakedown" ADD CONSTRAINT "ReplyTakedown_submitterId_fkey" FOREIGN KEY ("submitterId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ActivityTakedown" ADD CONSTRAINT "ActivityTakedown_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "Activity"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ActivityTakedown" ADD CONSTRAINT "ActivityTakedown_submitterId_fkey" FOREIGN KEY ("submitterId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml
new file mode 100644
index 0000000..fbffa92
--- /dev/null
+++ b/prisma/migrations/migration_lock.toml
@@ -0,0 +1,3 @@
+# Please do not edit this file manually
+# It should be added in your version-control system (i.e. Git)
+provider = "postgresql"
\ No newline at end of file