export type ViewerTheme = 'dark' | 'light' | undefined; export interface PageOptions { /** webview.cspSource */ cspSource: string; nonce: string; /** Forced diagram theme; undefined keeps the diagram's own choice. */ theme: ViewerTheme; /** Location hash to restore (Archify keeps focus/route state there). */ hash?: string; /** Opaque state handed to vscode.setState for panel restore. */ state?: unknown; /** Banner shown on top of the diagram. */ notice?: { text: string; kind: 'info' | 'warning' | 'error' }; } export function makeNonce(): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let nonce = ''; for (let i = 0; i < 32; i += 1) nonce += chars.charAt(Math.floor(Math.random() * chars.length)); return nonce; } export function escapeHtml(value: string): string { return value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } /** JSON that is safe to embed inside an inline \n` + `\n`; let page = html // Drop any CSP the file brings along; ours is the one that applies. .replace(/]*>/gi, '') // Allow the page's own inline scripts to run under the nonce policy. .replace(/
${escapeHtml(body)}
${detail ? `${escapeHtml(detail)}` : ''}