"use client"; import { cn } from "@mdxui/primitives/lib/utils"; import { Loader2 } from "lucide-react"; import { useTheme } from "next-themes"; import { useEffect, useRef, useState } from "react"; import type { CompileError, Viewport } from "../types"; import { PreviewError } from "./preview-error"; import { PreviewHeader } from "./preview-header"; interface PreviewPaneProps { content: string; onJumpToLine?: (line: number) => void; } const viewportWidths: Record = { mobile: 375, tablet: 768, desktop: "100%", }; export function PreviewPane({ content, onJumpToLine }: PreviewPaneProps) { const { resolvedTheme } = useTheme(); const [viewport, setViewport] = useState("desktop"); const [zoom, setZoom] = useState(100); const [compiledHtml, setCompiledHtml] = useState(""); const [isCompiling, setIsCompiling] = useState(false); const [compileError, setCompileError] = useState(null); const iframeRef = useRef(null); const isDark = resolvedTheme === "dark"; // For now, we'll just render the raw content in a simple HTML wrapper // Real MDX compilation would be done server-side or with @mdx-js/mdx useEffect(() => { const timer = setTimeout(() => { setIsCompiling(true); setCompileError(null); try { // CSS variables matching globals.css - synced with parent theme const cssVars = isDark ? { // Dark mode values from globals.css background: "oklch(0.145 0 0)", foreground: "oklch(0.985 0 0)", muted: "oklch(0.269 0 0)", mutedForeground: "oklch(0.708 0 0)", primary: "oklch(0.922 0 0)", border: "oklch(1 0 0 / 10%)", ring: "oklch(0.556 0 0)", } : { // Light mode values from globals.css background: "oklch(1 0 0)", foreground: "oklch(0.145 0 0)", muted: "oklch(0.97 0 0)", mutedForeground: "oklch(0.556 0 0)", primary: "oklch(0.205 0 0)", border: "oklch(0.922 0 0)", ring: "oklch(0.708 0 0)", }; // Simple markdown-like rendering for demo purposes // In production, this would use actual MDX compilation const html = `
${escapeHtml(content)}
`; setCompiledHtml(html); } catch (err) { setCompileError({ message: err instanceof Error ? err.message : "Unknown error", }); } finally { setIsCompiling(false); } }, 150); // 150ms debounce return () => clearTimeout(timer); }, [content, isDark]); // Listen for jump-to-line messages from iframe useEffect(() => { const handler = (e: MessageEvent) => { if (e.data.type === "jump-to-line" && onJumpToLine) { onJumpToLine(e.data.line); } }; window.addEventListener("message", handler); return () => window.removeEventListener("message", handler); }, [onJumpToLine]); const width = viewport === "desktop" ? "100%" : viewportWidths[viewport]; return (
{/* Compilation indicator */} {isCompiling && (
)} {/* Error state */} {compileError ? ( ) : ( /* Preview iframe */