"use client" import Link from "next/link" import { ImageIcon } from "lucide-react" import ReactMarkdown, { defaultUrlTransform } from "react-markdown" import rehypeKatex from "rehype-katex" import remarkGfm from "remark-gfm" import remarkMath from "remark-math" import { cn } from "@/lib/utils" /** * Renders a pre-processed tutorial markdown string (see lib/tutorials.ts) with * the site's design tokens. Handles two source-specific concerns: * - relative `*.md` / `../about` links are rewritten to in-app routes; * - `placeholder:` image sources render as styled "screenshot coming" * callouts instead of broken images. */ export function TutorialMarkdown({ source }: { source: string }) { return (
url.startsWith("placeholder:") ? url : defaultUrlTransform(url) } components={{ a: ({ href, children }) => { const target = resolveHref(String(href ?? "")) if (target.external) { return ( {children} ) } return {children} }, img: ({ src, alt }) => { const value = String(src ?? "") if (value.startsWith("placeholder:")) { return } return (
{/* eslint-disable-next-line @next/next/no-img-element */} {alt {alt ?
{alt}
: null}
) }, }} > {source}
) } function resolveHref(href: string): { href: string; external: boolean } { if (/^https?:\/\//.test(href) || href.startsWith("mailto:")) { return { href, external: true } } if (href.startsWith("#")) { return { href, external: false } } // Relative links between guides, e.g. "quickstart.md" → "/help/quickstart". if (href.endsWith(".md")) { const base = href.replace(/^.*\//, "").replace(/\.md$/, "") return { href: `/help/${base}`, external: false } } // Relative escapes to the app root, e.g. "../about" → "/about". if (href.startsWith("../") || href.startsWith("./")) { const cleaned = href.replace(/^(\.\.\/|\.\/)+/, "") return { href: `/${cleaned}`, external: false } } return { href, external: false } } function ScreenshotPlaceholder({ file, note }: { file: string; note?: string }) { return (
Screenshot coming
{file} {note ?

{note}

: null}
) }