/** * F7 preview webview client (sealed sandbox, INV-21). Receives annotated HTML * from the extension host and swaps it in; runs mermaid over `.mermaid` blocks * (mermaid needs a DOM, so it runs here, not in the host). Bundled by esbuild as * a standalone IIFE → out/media/preview.js, so mermaid never enters the host * bundle. No network, no LLM. */ import mermaid from "mermaid"; // Imported so esbuild emits the sibling out/media/preview.css (the controller // links it into the sealed shell via asWebviewUri). import "./preview.css"; interface RenderMessage { type: "render"; html: string; epoch: string; summary: { added: number; removed: number; changed: number }; } const body = document.getElementById("cw-body")!; const header = document.getElementById("cw-epoch")!; const summary = document.getElementById("cw-summary")!; function themeFor(): "dark" | "default" { return document.body.classList.contains("vscode-dark") || document.body.classList.contains("vscode-high-contrast") ? "dark" : "default"; } async function renderMermaid(): Promise { const nodes = Array.from(body.querySelectorAll("pre.mermaid")); if (nodes.length === 0) return; mermaid.initialize({ startOnLoad: false, theme: themeFor(), securityLevel: "strict" }); try { await mermaid.run({ nodes }); } catch { // mermaid.run already marks failed nodes; ensure a visible chip per failure. for (const n of nodes) { if (!n.querySelector("svg")) n.setAttribute("data-cw-error", "true"); } } } window.addEventListener("message", (event: MessageEvent) => { const msg = event.data; if (msg?.type !== "render") return; body.innerHTML = msg.html; header.textContent = `Track changes since ${msg.epoch}`; summary.innerHTML = `+${msg.summary.added + msg.summary.changed} ` + `−${msg.summary.removed + msg.summary.changed}`; void renderMermaid(); });