fix(annotate): render embedded local HTML documents instead of the editor app (#1561)
* fix(annotate): anchor raw-HTML documents at their own asset directory A srcdoc document has no URL of its own, so every relative URL in it resolves against the PARENT page — the Plannotator server — and an embedded sibling (<iframe src="prototype.html">) hit the catch-all and rendered the editor app inside the embed. A serve-time attribute rewrite cannot fix that: the src is often assigned by script at runtime from data-src, and carries a query string. rewriteHtmlAssetReferences gains a baseHref option that installs a <base href> first in <head>, so RESOLUTION lands in the document's own token'd directory whatever writes the URL and whenever. resolveHtmlAssetRoute is the one place both runtimes decide what an /api/html-assets request means: .html/.htm now serve as real documents under the 2MB annotate cap with a CSP sandbox that grants scripting but never allow-same-origin, framed and document requests get a small HTML error page naming the missing file, and the traversal guard is unchanged. The share/inline path takes inertBase instead, so a portable export's embeds render empty rather than resolving onto the host's own catch-all. * fix(annotate): serve embedded local documents from the Bun asset route rewriteHtml installs the root-relative <base href> (a srcdoc resolves its own base against the parent's URL, so the port need not be known), the asset route serves .html siblings with the sandbox CSP + nosniff, and the annotate catch-all refuses a Sec-Fetch-Dest of iframe/frame/embed/object with a 404 document instead of handing a nested frame the editor app. * fix(annotate): mirror embedded local documents in the Pi server Same shared decision (resolveHtmlAssetRoute), Node transport: the asset handler now takes the request so it can read Sec-Fetch-Dest, HTML responses carry the sandbox CSP and nosniff, and the SPA fallback answers a framed request with the 404 document. * feat(annotate): pin an embedded document as one element while armed The bridge is never injected into a nested frame, so a click inside an embed lands in another document. While pinpoint is armed (srcdoc sessions only, never live-app) frames become pointer-transparent, so the click pins the <iframe> itself; Interact hands the embed back for native use. * fix(annotate): an armed click inside an embed pins the frame, not its wrapper Pointer-transparent frames make hit-testing pass THROUGH the embed to the container painted behind it, so the pin would name the wrapper div. A point inside a frame's own rect now resolves to that frame, bounded to the frames inside the element already resolved. * docs: embedded local documents in raw-HTML annotate sessions
M
Michael Ramos committed
1954a19a5b7d350ad3f156478ff4f6af6d53cc17
Parent: 2a51b26
Committed by GitHub <noreply@github.com>
on 9/17/2026, 8:29:47 PM