Skip to content

fix(canvas): follow live on design revisions; execute react artifacts in a sandboxed frame - #814

Merged
Neonforge98 merged 3 commits into
developfrom
fix/canvas-simulator-refresh-react-runner
Aug 14, 2026
Merged

fix(canvas): follow live on design revisions; execute react artifacts in a sandboxed frame#814
Neonforge98 merged 3 commits into
developfrom
fix/canvas-simulator-refresh-react-runner

Conversation

@Neonforge98

Copy link
Copy Markdown
Collaborator

Problem

Two follow-ups from the Canvas audit (PRs #709/#767/#768):

  1. Stale Simulator after a design revision. A design revision acts on the live Canvas, but when the replay cursor had been parked by jump-from-chat (navigateToEvent), the up-to-cursor simulator event window excluded the incoming revision events. The Canvas looked stale until the session was reopened — verified on a real machine: the revision completed, the activity row showed the steps, yet the visible canvas kept the pre-revision content.

  2. React canvases could not execute on desktop. The packaged CSP (script-src 'self' 'wasm-unsafe-eval') blocks react-live's new Function, and srcdoc iframes inherit the parent policy, so an inline-script srcdoc runtime cannot run either. After the audit fix the desktop showed a visible notice card instead of a silent blank — but the artifact still never ran.

Solution

  1. CanvasDesignSurface snaps back to follow mode (goLiveAtom) when a design revision is submitted, so the revision streams and materializes in view.

  2. Serve compiled react artifacts over a dedicated canvas-artifact:// URI scheme instead of host-document eval:

    • reactArtifactDocument.ts ports the cloud viewer's sandbox document (sucrase compile, React 18 UMD runtimes, error overlay, strict CSP with connect-src 'none'), so desktop and public share links run identical artifact semantics.
    • Rust CanvasArtifactStore (16 entries / 8 MiB, oldest evicts) behind a canvas_artifact_publish command; the protocol handler responds with its own CSP + nosniff + no-store headers, 404 for unknown ids.
    • ReactArtifactRunner publishes once per source hash and renders <iframe sandbox="allow-scripts" src="…canvas-artifact.localhost/<id>">; compile/publish failures surface in a visible alert. react-live removed.
    • App CSP only gains the scheme in frame-src; unsafe-eval stays out.

Security

The artifact frame is a separate, sandboxed, opaque origin. WebView2 does inject __TAURI_INTERNALS__ into subframes (wry injects init scripts for all frames), but IPC is unreachable through three independent layers, each verified: no capability grants any remote context; the IPC protocol rejects the opaque frame's Origin: null; and the artifact document's connect-src 'none' blocks the invoke transport outright. Live test from inside a real artifact frame: invoke() never completes (blocked at transport). sandbox must never gain allow-same-origin — pinned by tests.

Verification

  • tsc --noEmit clean; canvas + CanvasInlineCard suites 19 files / 159 tests green (includes new reactArtifactDocument.test.ts, rewritten runner tests, and a regression test that a parked replay cursor snaps to follow on submit).
  • Rust: cargo check -p org2 clean; store/protocol unit tests compile (the test exe cannot load on the dev machine due to a pre-existing comctl32 manifest issue; they should run on CI).
  • Real machine (Windows, dev-build exe, CDP-driven, real Opus agent turn): a react canvas rendered live in the chat card through http://canvas-artifact.localhost/ra-… with sandbox="allow-scripts"; the button inside was clicked and cycled state (Rose → Amber) proving interactivity; the revised html canvas from the previous session renders its materialized state after restart.

Notes

  • With >16 live artifacts the backend evicts oldest; a still-mounted evicted frame 404s (blank) until remount — accepted under the 16-entry budget.
  • sucrase (~200 KB) + two React 18 UMD texts replace react-live in the bundle.

A design revision acts on the live Canvas, but a replay cursor parked by
jump-from-chat kept the up-to-cursor simulator window from ever including
the incoming revision events - the Canvas looked stale until the session
was reopened. Snap back to follow mode on submit so the revision streams
and materializes in view.

Pre-commit hook ran. Total eslint: 18, total circular: 0
…frame

React canvases could not run under the packaged CSP (no unsafe-eval, and
srcdoc frames inherit the parent policy), so the desktop showed a notice
card instead of the artifact. Serve the compiled artifact document over a
dedicated canvas-artifact:// URI scheme instead: its response carries its
own CSP, the iframe is a separate origin sandboxed with allow-scripts
only, and the app CSP merely allows the scheme in frame-src.

- reactArtifactDocument.ts ports the cloud viewer's sandbox document
  (sucrase compile, React 18 UMD runtimes, error overlay, strict CSP) so
  desktop and public share links run identical artifact semantics
- Rust CanvasArtifactStore (16 entries / 8 MiB, oldest evicts) behind a
  canvas_artifact_publish command and the protocol handler (CSP + nosniff
  + no-store headers, 404 for unknown ids)
- ReactArtifactRunner publishes once per source hash and renders the
  frame; compile and publish failures surface in a visible alert
- react-live removed; IPC unreachable from artifacts (no remote
  capability grants, opaque-origin rejection, connect-src 'none')

Pre-commit hook ran. Total eslint: 18, total circular: 0
Pre-commit hook ran. Total eslint: 18, total circular: 0
@Neonforge98
Neonforge98 merged commit 2e95f8c into develop Aug 14, 2026
3 checks passed
@Harry19081 Harry19081 added bug Something isn't working chat Chat, composer, markdown, canvas, browser, or terminal surfaces frontend-ui Frontend UI, design system, accessibility, layout, or theming labels Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working chat Chat, composer, markdown, canvas, browser, or terminal surfaces frontend-ui Frontend UI, design system, accessibility, layout, or theming

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants