"use client"; import type { CSSProperties } from "react"; import { ArrowRight, CheckCircle2, ShieldCheck, Sparkles } from "lucide-react"; import { cn } from "@/lib/utils"; import type { DetailUiDslNode, DetailUiDslRegion, DetailUiDslV2Document, } from "@/lib/detail-ui-dsl-v2"; function normalizedRegionToPixels(params: { region: DetailUiDslRegion; width: number; height: number; }) { return { x: params.region.x * params.width, y: params.region.y * params.height, width: params.region.width * params.width, height: params.region.height * params.height, }; } function iconFor(name?: string, size = 16) { const style = { width: size, height: size }; if (name === "shield") return ; if (name === "arrow") return ; if (name === "check") return ; return ; } function panelClass(variant?: string, tone?: string) { if (variant === "glass") { if (tone === "dark") { return "bg-slate-900/72 border border-white/10 text-white shadow-[0_18px_50px_rgba(15,23,42,0.26)] backdrop-blur-xl"; } if (tone === "accent") { return "bg-blue-600/88 border border-blue-300/30 text-white shadow-[0_20px_56px_rgba(37,99,235,0.28)] backdrop-blur-xl"; } return "bg-white/84 border border-white/50 text-slate-950 shadow-[0_20px_56px_rgba(15,23,42,0.18)] backdrop-blur-xl"; } if (variant === "ghost") { return "bg-transparent text-slate-900"; } return tone === "dark" ? "bg-slate-900 text-white" : tone === "accent" ? "bg-blue-600 text-white" : "bg-white text-slate-950"; } function renderNode(node: DetailUiDslNode): JSX.Element { if (node.kind === "panel") { return (
{node.children.map(renderNode)}
); } if (node.kind === "stack") { return (
{node.children.map(renderNode)}
); } if (node.kind === "row") { const columns = Math.max(1, node.columns || node.children.length || 1); return (
{node.children.map(renderNode)}
); } if (node.kind === "badge") { return (
{iconFor(node.icon, 14)} {node.text}
); } if (node.kind === "heading") { return (
{node.text}
); } if (node.kind === "body") { return (
{node.text}
); } if (node.kind === "feature-card") { return (
{node.tag ? (
{iconFor(node.icon, 12)} {node.tag}
) : null}
{node.title}
{node.body ? (
{node.body}
) : null}
); } if (node.kind === "spec-table") { return (
{node.rows.map((row, index) => (
0 && "border-t border-slate-100/90", )} >
{row.label}
{row.value}
))}
); } if (node.kind === "compare") { return (
{node.before.title}
{node.before.body}
{node.after.title}
{node.after.body}
); } if (node.kind === "proof") { return (
{iconFor(node.icon, 15)} {node.text}
); } if (node.kind === "cta") { return (
{node.headline ? (
{node.headline}
) : null}
{node.buttonText}
{node.supportText ? (
{node.supportText}
) : null}
); } return
; } export function DetailUiDomRenderer(params: { document: DetailUiDslV2Document | null; imageMetrics?: { offsetX: number; offsetY: number; renderedWidth: number; renderedHeight: number; } | null; className?: string; }) { if (!params.document) return null; const renderWidth = params.imageMetrics?.renderedWidth && params.imageMetrics.renderedWidth > 0 ? params.imageMetrics.renderedWidth : params.document.screenSize.width; const renderHeight = params.imageMetrics?.renderedHeight && params.imageMetrics.renderedHeight > 0 ? params.imageMetrics.renderedHeight : params.document.screenSize.height; const scale = Math.max( 0.55, Math.min( 2.4, Math.min( renderWidth / Math.max(1, params.document.screenSize.width), renderHeight / Math.max(1, params.document.screenSize.height), ), ), ); const containerStyle: CSSProperties = params.imageMetrics && params.imageMetrics.renderedWidth > 0 && params.imageMetrics.renderedHeight > 0 ? { position: "absolute", left: `${params.imageMetrics.offsetX}px`, top: `${params.imageMetrics.offsetY}px`, width: `${params.imageMetrics.renderedWidth}px`, height: `${params.imageMetrics.renderedHeight}px`, } : { position: "absolute", inset: "0px", }; const region = normalizedRegionToPixels({ region: params.document.region, width: renderWidth, height: renderHeight, }); return (
{params.document.nodes.map((node) => renderNode(node))}
); }