From cb3ece4f99a624edf82c7bef8f13770a6faa1df0 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Thu, 18 Jun 2026 02:39:45 +0200 Subject: [PATCH] ssr --- src/app/music/_components/Page.tsx | 66 ++++++++++++ src/app/music/page.tsx | 78 +++------------ src/app/projects/_components/Page.tsx | 111 ++++++++++++++++++++ src/app/projects/page.tsx | 139 ++++++-------------------- 4 files changed, 221 insertions(+), 173 deletions(-) create mode 100644 src/app/music/_components/Page.tsx create mode 100644 src/app/projects/_components/Page.tsx diff --git a/src/app/music/_components/Page.tsx b/src/app/music/_components/Page.tsx new file mode 100644 index 0000000..f2aca17 --- /dev/null +++ b/src/app/music/_components/Page.tsx @@ -0,0 +1,66 @@ +'use client' + +import * as Card from "~/components/ui/card"; +import { useTimeLine } from "../../_providers/GsapProvicer"; +import AnimatedPageTitle from "../../_components/Animated/AnimatedPageTitle"; +import AnimateTextIn from "../../_components/Animated/AnimateIn"; +import { ScrollArea } from "~/components/ui/scroll-area"; +import AnimatePopUp from "../../_components/Animated/AnimatePopUp"; +import AudioPlayer from "./AudioPlayer"; +import type { RouterOutputs } from "~/server/routers/_app"; + +export default function MusicPage(props: { + tracks: RouterOutputs['music']['list'], +}) { + const { tracks } = props; + useTimeLine(tracks) + return ( + + Just Some Music I Made +
+ +

All works on this page are licensed under:

+
CC BY-NC-SA 4.0
+
+ +
+ + + + +
+
+
+
+ {tracks && tracks.map((track, i) => ( +
+ + + + {track.title} + + + + {track.description && ( +

{track.description}

+ )} + + + +
+
+
+
+ ))} + {!tracks?.length && +
+ No music yet. +
} + + ); +} diff --git a/src/app/music/page.tsx b/src/app/music/page.tsx index 78de68e..7539b01 100644 --- a/src/app/music/page.tsx +++ b/src/app/music/page.tsx @@ -1,67 +1,13 @@ -'use client' -import { trpc } from "~/app/_trpc/Client"; -import * as Card from "~/components/ui/card"; -import { useTimeLine } from "../_providers/GsapProvicer"; -import AnimatedPageTitle from "../_components/Animated/AnimatedPageTitle"; -import { Spinner } from "~/components/ui/spinner"; -import AnimateTextIn from "../_components/Animated/AnimateIn"; -import { ScrollArea } from "~/components/ui/scroll-area"; -import AnimatePopUp from "../_components/Animated/AnimatePopUp"; -import AudioPlayer from "./_components/AudioPlayer"; -export default function MusicPage() { - const { data: tracks, isLoading } = trpc.music.list.useQuery(); - useTimeLine(tracks) - return ( - - Just Some Music I Made -
- -

All works on this page are licensed under:

- -
- -
- - - - -
-
-
-
- {tracks && tracks.map((track, i) => ( -
- - - - {track.title} - - - - {track.description && ( -

{track.description}

- )} - - - -
-
-
-
- ))} - {!isLoading && !tracks?.length && -
- No music yet. -
- } - {isLoading &&
- Loading Tracks -
} - - ); +import { Suspense } from "react"; +import { servTrpc as trpc } from "../_trpc/ServerClient"; +import Page from "./_components/Page"; + +export default async function MusicPage() { + const tracks = await trpc.music.list(); + + return ( + + + + ); } diff --git a/src/app/projects/_components/Page.tsx b/src/app/projects/_components/Page.tsx new file mode 100644 index 0000000..291adbb --- /dev/null +++ b/src/app/projects/_components/Page.tsx @@ -0,0 +1,111 @@ +'use client' + +import type { ReactNode } from "react"; +import * as Card from "~/components/ui/card"; +import { Badge } from "~/components/ui/badge"; +import { StackBadge } from "~/components/StackBadge"; +import { ScrollArea } from "~/components/ui/scroll-area"; +import AnimatedPageTitle from "../../_components/Animated/AnimatedPageTitle"; +import AnimateTextIn from "../../_components/Animated/AnimateIn"; +import { useTimeLine } from "../../_providers/GsapProvicer"; +import AnimatePopUp from "../../_components/Animated/AnimatePopUp"; +import { Button } from "~/components/ui/button"; +import type { RouterOutputs } from "~/server/routers/_app"; + +export default function ProjectsPage(props: { + projects: RouterOutputs['projectv2']['listWithStack'], + descriptions: Record, +}) { + const { projects, descriptions } = props; + useTimeLine(projects) + + if (!projects?.length) { + return ( +
+ No projects yet. +
+ ); + } + + return ( + + Projects I've Been Working on +
+ {projects.map((project, i) => ( +
+ + +
+ {project.title} +
+ {project.sourceType && ( + + + {project.sourceType === "open" ? "Open Source" : "Closed Source"} + + + )} + {project.releaseStatus && ( + + {project.releaseStatus === "released" ? "Released" : "Unreleased"} + + )} +
+
+
+ {(project.description || project.sourceLink || project.releaseLink || project.techStack?.stackItems?.length) && ( + + {project.description && ( +
+ + {descriptions[project.id] ?? project.description} + +
+ )} +
+ {project.techStack?.stackItems && project.techStack.stackItems.length > 0 && ( +
+ {project.techStack.stackItems.map((item, k) => ( + + ))} +
+ )} + {(project.sourceLink || project.releaseLink) && ( +
+ {project.sourceLink && + + } + {project.releaseLink && + + + } +
+ )} +
+
+ )} +
+
+
+ ))} + + ); +} diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index eca31da..8ea2bf4 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -1,115 +1,40 @@ -'use client' +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"; -import { trpc } from "~/app/_trpc/Client"; -import * as Card from "~/components/ui/card"; -import { Badge } from "~/components/ui/badge"; -import { StackBadge } from "~/components/StackBadge"; -import { ScrollArea } from "~/components/ui/scroll-area"; -import AnimatedPageTitle from "../_components/Animated/AnimatedPageTitle"; -import AnimateTextIn from "../_components/Animated/AnimateIn"; -import { useTimeLine } from "../_providers/GsapProvicer"; -import AnimatePopUp from "../_components/Animated/AnimatePopUp"; -import { Button } from "~/components/ui/button"; -import { ClientMdx } from "~/components/ClientMdx"; +export default async function ProjectsPage() { + const projects = await trpc.projectv2.listWithStack(); -export default function ProjectsPage() { - const { data: projects, isLoading } = trpc.projectv2.listWithStack.useQuery(); - useTimeLine(projects) - if (isLoading) { - return ( -
- Loading... -
- ); - } - - if (!projects?.length) { - return ( -
- No projects yet. -
+ // 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 = {}; + for (const project of projects ?? []) { + if (!project.description?.trim()) continue; + descriptions[project.id] = ( + ); } return ( - - Projects I've Been Working on -
- {projects.map((project, i) => ( -
- - -
- {project.title} -
- {project.sourceType && ( - - - {project.sourceType === "open" ? "Open Source" : "Closed Source"} - - - )} - {project.releaseStatus && ( - - {project.releaseStatus === "released" ? "Released" : "Unreleased"} - - )} -
-
-
- {(project.description || project.sourceLink || project.releaseLink || project.techStack?.stackItems?.length) && ( - - {project.description && ( -
- - - -
- )} -
- {project.techStack?.stackItems && project.techStack.stackItems.length > 0 && ( -
- {project.techStack.stackItems.map((item, k) => ( - - ))} -
- )} - {(project.sourceLink || project.releaseLink) && ( -
- {project.sourceLink && - - } - {project.releaseLink && - - - } -
- )} -
-
- )} -
-
-
- ))} - + + + ); }