43 lines
1.4 KiB
TypeScript
43 lines
1.4 KiB
TypeScript
import { Suspense, type ReactNode } from "react";
|
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
|
import rehypeHighlight from "rehype-highlight";
|
|
import remarkGfm from "remark-gfm";
|
|
import { servTrpc as trpc } from "../_trpc/ServerClient";
|
|
import { mdxComponents } from "~/components/mdx-components";
|
|
import Page from "./_components/Page";
|
|
|
|
export default async function CvPage() {
|
|
const cv = await trpc.categoryv2.listAllWithEntries();
|
|
|
|
// Render the MDX descriptions on the server so they exist at first paint.
|
|
// The client tree (which runs the GSAP entrance via useTimeLine) only places
|
|
// these already-rendered nodes — it never invokes the MDX renderer itself, so
|
|
// the 'use client' boundary stays intact and the animations no longer play
|
|
// against an un-rendered fallback.
|
|
const descriptions: Record<string, ReactNode> = {};
|
|
for (const category of cv ?? []) {
|
|
for (const entry of category.cvEntry) {
|
|
if (!entry.description?.trim()) continue;
|
|
descriptions[entry.id] = (
|
|
<MDXRemote
|
|
source={entry.description}
|
|
components={mdxComponents}
|
|
options={{
|
|
mdxOptions: {
|
|
format: "mdx",
|
|
remarkPlugins: [remarkGfm],
|
|
rehypePlugins: [rehypeHighlight],
|
|
},
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Suspense>
|
|
<Page cv={cv} descriptions={descriptions} />
|
|
</Suspense>
|
|
)
|
|
}
|