feat(annotate): annotate live localhost design previews#1049
Open
notxcain wants to merge 1 commit into
Open
Conversation
Add `plannotator annotate http://localhost:PORT/path`, which renders a live frontend dev-server page (as an HTML artifact) for annotation instead of converting it to markdown. - Per-session reverse proxy forwards to the dev server, proxies its HMR websocket, and injects the annotation bridge into HTML responses. Loopback only (isLoopbackUrl guard), bound to 127.0.0.1. Bun impl in packages/server/preview-proxy.ts; node:http mirror in apps/pi-extension/server/previewProxy.ts. - The editor renders an <iframe src> at the proxy origin (additive optional `src` prop on HtmlViewer) so the real ES-module graph loads with no null-origin CORS problem; annotation works via the existing postMessage bridge. The srcDoc path is unchanged when `src` is absent. - The live-preview iframe renders like a real browser tab: an opaque white canvas with color-scheme:normal, so an unstyled/transparent page shows black-on-white instead of bleeding through to the host surface. - Thread livePreviewUrl through the annotate /api/plan and CLI dispatch across all three runtimes (Claude Code, OpenCode, Pi), carrying the target path onto the iframe URL. - Live-preview sessions disable URL sharing, version history, and host-theme token injection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
plannotator annotate http://localhost:PORT/path— renders a live frontend dev-server page (as an HTML artifact) for annotation, instead of converting it to markdown.Partially addresses #642 (annotate a live website/URL in a frame, no extension, works over SSH) — scoped here to localhost/loopback dev previews; arbitrary public URLs still convert to markdown.
isLoopbackUrlguard), bound to127.0.0.1. Bun impl (packages/server/preview-proxy.ts) +node:httpmirror for Pi (apps/pi-extension/server/previewProxy.ts).<iframe src>at the proxy origin (additive optionalsrcprop onHtmlViewer) so the real ES-module graph loads with no null-origin CORS problem; annotation works via the existingpostMessagebridge. ThesrcDocpath is byte-for-byte unchanged whensrcis absent (@plannotator/uiseam rule).livePreviewUrlthrough the annotate/api/plan(Bun, OpenCode, Pi) and CLI dispatch, carrying the target path onto the iframe URL.Why a proxy (not
<base>/ srcdoc)A
srcDociframe is a null origin; a modern Vite dev server returns noAccess-Control-Allow-Originto a null origin, so its module scripts are CORS-blocked → blank page. And its URLs are root-absolute (/@vite/client,/src/main.tsx), which a<base>can't redirect. A proxy that owns the origin root solves both.Caveat for reviewers — Pi runtime not fully verified
The Pi runtime's live-preview path (
apps/pi-extension/server/previewProxy.ts, anode:httpmirror of the Bun proxy) is not end-to-end verified. HTTP forwarding + bridge injection were smoke-tested against a real dev server, but the HMR-websocket passthrough was never exercised against a live HMR client — no Pi runtime was available this session. It is a dependency-free raw-socket implementation mirroring the Bun version. One known edge case: the WS passthrough hooks the socketconnectevent, which is correct for plain TCP but wrong for TLS — an https-loopback dev server with HMR would needsecureConnect(plain-http loopback, the common case, is unaffected). The Claude Code path is verified end-to-end in a browser; OpenCode shares the same server/UI code but was only build-checked.