refactor: convert MarkdownContent to server component, remove context

This commit is contained in:
2026-04-10 01:07:18 +02:00
parent 695ebf707f
commit ccde0af995

View File

@@ -1,15 +1,8 @@
"use client"; import React from "react";
import React, { createContext, useContext } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import type { Components } from "react-markdown"; import type { Components } from "react-markdown";
import type { Element } from "hast"; import type { Element } from "hast";
// Lets the `code` renderer know it's inside a `pre` block vs. inline.
// React renders parent-to-child, so the Provider in `pre` is active
// when `code` renders inside it.
const InsidePreContext = createContext(false);
function GoldDot() { function GoldDot() {
return ( return (
<span <span
@@ -72,40 +65,6 @@ function extractHastText(node: Element): string {
.trim(); .trim();
} }
function CodeRenderer({ children }: { children?: React.ReactNode }) {
const insidePre = useContext(InsidePreContext);
if (insidePre) {
return (
<code
style={{
fontFamily: "var(--font-mono, 'Geist Mono', monospace)",
fontSize: "0.9rem",
color: "#e2e8f0",
lineHeight: 1.65,
}}
>
{children}
</code>
);
}
return (
<code
style={{
background: "rgba(250, 204, 21, 0.1)",
border: "1px solid rgba(250, 204, 21, 0.3)",
borderRadius: 5,
padding: "1px 7px",
color: "#fde68a",
fontFamily: "var(--font-mono, 'Geist Mono', monospace)",
fontSize: "0.875em",
}}
>
{children}
</code>
);
}
const components: Components = { const components: Components = {
h1: ({ children }) => ( h1: ({ children }) => (
@@ -286,10 +245,15 @@ const components: Components = {
<em style={{ color: "#e2e8f0", fontStyle: "italic" }}>{children}</em> <em style={{ color: "#e2e8f0", fontStyle: "italic" }}>{children}</em>
), ),
// `pre` wraps `code` for fenced code blocks. The Provider lets the // `pre` reads code text directly from the HAST node so `code` renderer
// nested `code` renderer know it's in a block context (not inline). // is only ever called for inline code — no client-side context needed.
pre: ({ children }) => ( pre: ({ node }) => {
<InsidePreContext.Provider value={true}> const codeEl = node?.children?.[0] as Element | undefined;
const codeText = (codeEl?.children ?? [])
.filter((c): c is { type: "text"; value: string } => c.type === "text")
.map((c) => c.value)
.join("");
return (
<pre <pre
style={{ style={{
background: "rgba(255, 255, 255, 0.04)", background: "rgba(255, 255, 255, 0.04)",
@@ -301,12 +265,36 @@ const components: Components = {
margin: "0 0 1.25rem 0", margin: "0 0 1.25rem 0",
}} }}
> >
{children} <code
style={{
fontFamily: "var(--font-mono, 'Geist Mono', monospace)",
fontSize: "0.9rem",
color: "#e2e8f0",
lineHeight: 1.65,
}}
>
{codeText}
</code>
</pre> </pre>
</InsidePreContext.Provider> );
), },
code: CodeRenderer, // Always inline — block code is handled entirely by the `pre` renderer above.
code: ({ children }) => (
<code
style={{
background: "rgba(250, 204, 21, 0.1)",
border: "1px solid rgba(250, 204, 21, 0.3)",
borderRadius: 5,
padding: "1px 7px",
color: "#fde68a",
fontFamily: "var(--font-mono, 'Geist Mono', monospace)",
fontSize: "0.875em",
}}
>
{children}
</code>
),
}; };
export default function MarkdownContent({ content }: { content: string }) { export default function MarkdownContent({ content }: { content: string }) {