import React from "react"; import ReactMarkdown from "react-markdown"; import type { Components } from "react-markdown"; import type { Element } from "hast"; function GoldDot() { return ( ); } function BlueDot() { return ( ); } function KeyBadge({ children }: { children: React.ReactNode }) { return ( {children} ); } // Extracts plain text from a HAST element's text children, // stripping a trailing colon (e.g. "Backend:" → "Backend"). function extractHastText(node: Element): string { return (node.children ?? []) .filter((c): c is { type: "text"; value: string } => c.type === "text") .map((c) => c.value) .join("") .replace(/:$/, "") .trim(); } const components: Components = { h1: ({ children }) => (

{children}

), h2: ({ children }) => (

{children}

), h3: ({ children }) => (

{children}

), p: ({ children }) => (

{children}

), ul: ({ children }) => ( ), li: ({ node, children }) => { // Inspect HAST to detect the `- **Key:** value` pattern. // HAST tagName is always 'strong' regardless of custom component overrides. const firstHast = node?.children?.[0]; const isKeyValue = firstHast?.type === "element" && firstHast.tagName === "strong"; if (isKeyValue) { const keyText = extractHastText(firstHast); // Skip index 0 (the rendered element) — works because react-markdown // emits strong as child[0] for tight lists with the `- **Key:** value` pattern. // Tight lists only: loose lists wrap content in

, changing the HAST structure. const rest = React.Children.toArray(children).slice(1); return (

  • {keyText} {rest}
  • ); } return (
  • {children}
  • ); }, a: ({ href, children }) => { const isExternal = !!href && /^https?:\/\//.test(href); return ( {children} ); }, strong: ({ children }) => ( {children} ), em: ({ children }) => ( {children} ), // `pre` reads code text directly from the HAST node so `code` renderer // is only ever called for inline code — no client-side context needed. pre: ({ node }) => { 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 (
            
              {codeText}
            
          
    ); }, // Always inline — block code is handled entirely by the `pre` renderer above. code: ({ children }) => ( {children} ), }; export default function MarkdownContent({ content }: { content: string }) { return {content}; }