/** * Shared vocabulary of the sidebar's HTML preview surfaces (the editor's * html viewer and the changes tab's op render preview): the sandbox tokens * both preview iframes load with, plus the document-base rewrite the srcdoc * delivery path needs. */ /** * The sandbox tokens of an HTML preview iframe. NO allow-same-origin (the * preview must stay in an opaque origin — with the route's own origin it * could read session data) and NO allow-top-navigation (a previewed page * must not hijack the GUI). The editor viewer can disable the sandbox * per-feature in the side card settings (warned); the changes tab's op * render preview always stays sandboxed. */ export const HTML_IFRAME_SANDBOX = 'allow-scripts allow-popups allow-downloads allow-modals' /** Whether a document already declares its own base URL. */ const HAS_BASE = /]/i /** Index just past the element a `` must precede to land in . */ function insertionPoint(html: string): number { const head = /]*>/i.exec(html) if (head !== null) return head.index + head[0].length const htmlTag = /]*>/i.exec(html) if (htmlTag !== null) return htmlTag.index + htmlTag[0].length return 0 } /** * The directory part of a preview route URL, with its trailing slash. * @param url - the `/sidebar/html//` route URL. * @returns the URL up to and including the last `/`. */ export function previewBaseOf(url: string): string { const cut = url.lastIndexOf('/') return cut < 0 ? url : url.slice(0, cut + 1) } /** * Point a preview document's relative references back at its own route. * * A `srcdoc` document has no URL of its own — the browser resolves its * relative references against the EMBEDDING page's URL, so `./style.css` in a * previewed file would resolve to the GUI's origin and hit whatever route * happens to match. Injecting `` * keeps relative resolution inside `/sidebar/html/…`, where the file's own * siblings live. A document that declares its own `` is left alone: the * author said what the base is. * * @param html - the previewed document's markup. * @param url - the route URL the document was fetched from. * @returns the markup, with a base element injected when it had none. */ export function withPreviewBase(html: string, url: string): string { if (HAS_BASE.test(html)) return html const href = previewBaseOf(url).replace(/"/g, '"') const at = insertionPoint(html) return `${html.slice(0, at)}${html.slice(at)}` }