Skip to content

feat(web): Serve realtime runs canvas in the browser - #63

Merged
4ndreello merged 6 commits into
mainfrom
feat/web-realtime-canvas
Sep 8, 2026
Merged

feat(web): Serve realtime runs canvas in the browser#63
4ndreello merged 6 commits into
mainfrom
feat/web-realtime-canvas

Conversation

@4ndreello

Copy link
Copy Markdown
Owner

Serve the runs canvas over HTTP with live daemon data instead of mock files.

codedeck web starts a localhost-only bridge (default :3100, --open launches the browser) that serves a single self-contained page: sessions grouped by runId with the orchestrator at the center, status in plain language, and one event stream per active session translated into motion on the canvas (text deltas travel as particles, tool starts pulse, permission requests raise the amber alert).

The page makes zero external requests and the server binds 127.0.0.1 with no host flag on purpose; realtime send stays out of scope for V2 since session.send only admits idle sessions and open sessions refuse it.

Careful-review notes: the canvas page is a large template string in src/web/canvas-page.ts promoted byte-identical from the validated spike (asserted in tests: no backticks, no interpolation, no external URLs); the stream route pre-flights session.get before the 200 so a dead daemon yields JSON 404 instead of a hanging EventSource.

Add a localhost-only codedeck web command that serves a self-contained
canvas page: session nodes grouped by run with the orchestrator at the
center, live status, and per-session event streams translated into motion
(particles, pulses, activity feed). Includes port parsing, SSE framing,
and route helpers with unit tests.
A non-loopback dummy base trips the S5332 cleartext-protocol rule even
though only the path and query are read. 127.0.0.1 keeps the parse
identical and stays inside the rule's loopback exception.
The motion button toggled the class on the inner SVG instead of the
button, so its pressed state never followed the click. Recenter forced
zoom 1, which lands in the void between runs on a spread fleet; it now
refits the most active run like the initial framing. Also adds a hide
toggle for finished sessions with a hidden count in the summary.
@4ndreello
4ndreello marked this pull request as ready for review September 8, 2026 01:08
…yout

Clicking a node opens a drawer with model, repo, pid, event counts and
a humanized history tail fetched from session.get. Hide/motion state
lives in the query string so F5 keeps the filters. Offscreen active
sessions get clickable edge pills that jump the camera to them. Ring
spacing tightened (verified numerically) and tool activity shows a
snippet of the real input instead of a generic label.
Draw a subtle dashed container with the repo name around each run so
the project is visible without opening the detail drawer. Recompute
the layout over the visible set when hiding completed, center cards on
their true outer width, fit small sets entirely, pin dragged nodes,
and show model effort plus a dead label in the detail view.
Share the ps liveness rule with the session.list and session.get read
boundary so the web canvas and the terminal agree: an active session
whose recorded process is gone serves as dead instead of stale
working. The stored row is never mutated, only the served view.
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

@4ndreello
4ndreello merged commit d9ecd01 into main Sep 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant