/**
* 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)}`
}