Add delegate recursion and reinscription UI
This commit is contained in:
1 parent
9f27897967
commit
5d1a1ae866
10 files changed
+327
-31
No files matched your search
+17
-2
@@ -1,8 +1,14 @@
|
||||
import { recursivePayload, connectRecursion } from './recursion.js';
|
||||
// Inert parsing only; inscription markup is never attached to the parent DOM.
|
||||
// The deliberately small allowlist also removes navigation (including meta refresh).
|
||||
const tags = new Set('p div span h1 h2 h3 h4 h5 h6 br hr pre code b strong i em u s ul ol li table thead tbody tr td th blockquote svg g path rect circle ellipse line polyline polygon text tspan defs lineargradient radialgradient stop'.split(' '));
|
||||
const attrs = new Set('style title width height viewbox fill stroke stroke-width d x y x1 y1 x2 y2 cx cy r rx ry points transform opacity offset stop-color font-size text-anchor'.split(' '));
|
||||
export function preview(item) {
|
||||
export function preview(item, api = null) {
|
||||
if (api && item.id) {
|
||||
const response = api.request(`/content/${item.id}`);
|
||||
if (response.status !== 200) { const error = document.createElement('pre'); error.textContent = `${response.status}: ${response.body.error}`; return error; }
|
||||
item = { ...item, bytes: response.body, contentType: response.contentType };
|
||||
}
|
||||
const bytes = new Uint8Array(item.bytes);
|
||||
const type = item.contentType.split(';')[0];
|
||||
const text = new TextDecoder().decode(bytes);
|
||||
@@ -13,6 +19,7 @@ export function preview(item) {
|
||||
if (type === 'text/html' || type === 'image/svg+xml') {
|
||||
const inert = document.implementation.createHTMLDocument('');
|
||||
const template = inert.createElement('template'); template.innerHTML = text;
|
||||
const scripts = [...template.content.querySelectorAll('script')].filter(s => !s.src).map(s => s.textContent);
|
||||
const clean = parent => {
|
||||
for (const child of [...parent.children]) {
|
||||
if (!tags.has(child.localName.toLowerCase())) { child.remove(); continue; }
|
||||
@@ -21,6 +28,14 @@ export function preview(item) {
|
||||
}
|
||||
};
|
||||
clean(template.content);
|
||||
if (api && item.id && scripts.length) {
|
||||
node = document.createElement('iframe'); node.setAttribute('sandbox', 'allow-scripts');
|
||||
node.title = `${type} recursive preview`; node.referrerPolicy = 'no-referrer';
|
||||
const payload = recursivePayload(template.innerHTML, scripts, item.id);
|
||||
node.onload = () => { node.cleanup?.(); node.cleanup = connectRecursion(node, api, payload); };
|
||||
node.src = '/recursive-preview.html'; node.dataset.previewUrl = ''; node.className = 'inscription-preview';
|
||||
return node;
|
||||
}
|
||||
const csp = "default-src 'none'; style-src 'unsafe-inline'; script-src 'none'; form-action 'none'; base-uri 'none'";
|
||||
blob = new Blob([`<!doctype html><meta http-equiv="Content-Security-Policy" content="${csp}">`, template.innerHTML], { type: 'text/html' });
|
||||
node = document.createElement('iframe'); node.setAttribute('sandbox', ''); node.title = `${type} safe preview`;
|
||||
@@ -32,5 +47,5 @@ export function preview(item) {
|
||||
node.className = 'inscription-preview'; return node;
|
||||
}
|
||||
export function clearPreviews(root) {
|
||||
root.querySelectorAll('[data-preview-url]').forEach(node => URL.revokeObjectURL(node.dataset.previewUrl));
|
||||
root.querySelectorAll('[data-preview-url]').forEach(node => { node.cleanup?.(); URL.revokeObjectURL(node.dataset.previewUrl); });
|
||||
}
|
||||
Reference in new issue
Block a user