Skip to content

feat: embeddable web browser as a canvas tile (phases 0–2) #633

@srid

Description

@srid

Kolu currently has no webview / iframe capability. Adding one unlocks dev-server preview (the killer use case), doc/dashboard embeds while pairing, and — as a free side-effect — inclusion in tab-capture video recording (#632), since getDisplayMedia() already records the whole tab.

Architectural commitments

  • A browser is a first-class canvas tile, not a sub-panel of a terminal. Rationale: Kolu's direction is mode-less canvas workspace (commit 6318813); tying the browser's lifetime to a terminal's parentId would complect placement with terminal state and force preview to die when the terminal running pnpm dev restarts. Keep tile identity = URL, nothing more.
  • Tile identity is just a URL. No repo/branch/port metadata on the tile. Ports are globally unique on the host, so there's nothing to disambiguate.
  • Transparent localhost rewrite. User types http://localhost:5173 in the URL bar; Kolu detects the localhost host and routes through the Hono reverse proxy. Proxy is invisible infrastructure, not a separate mode.

Phase 0 — Browser canvas tile, any URL, no proxy

Drop a tile on the canvas, type any URL, renders in an iframe. Full canvas-tile citizenship: movable, resizable, multi-instance, captured by recording for free. Handle X-Frame-Options / frame-ancestors: none blocks explicitly — detect and show an "open externally" fallback instead of a silent blank tile. Useful standalone for docs, Figma, Grafana, the Kolu website. localhost:* silently fails at this phase (or shows a hint that Phase 1 is needed).

  • Browser tile component (iframe + minimal chrome: URL bar, reload, open-externally)
  • Canvas-tile integration (drag, resize, focus, persistence)
  • X-Frame-Options / CSP frame-ancestors block detection + fallback UI
  • Recording verification (open browser tile during recording → appears in captured .webm)

Phase 1 — Transparent localhost rewrite via Hono reverse proxy

Same URL bar; http://localhost:NNNN or 127.0.0.1:NNNN detected and rewritten through a Hono proxy route before loading. Choose subdomain-based proxy from the start (NNNN.preview.<kolu-host>) rather than path-prefix, so Vite HMR WebSockets and absolute asset paths just work. SSRF guard: allowlist ports that some terminal has announced recently (plus user-confirmed manual entries). Handle WebSocket upgrade in the proxy for HMR. Tile model doesn't change — proxy is a URL resolver inside the fetch path.

  • Hono proxy route (NNNN.preview.<host>127.0.0.1:NNNN)
  • WebSocket upgrade passthrough (Vite HMR)
  • Client-side URL rewrite in the tile fetch path (localhost / 127.0.0.1 → proxy subdomain)
  • SSRF guard: port allowlist from announced-ports + explicit user confirmation
  • Smoke test with Vite, Next, Astro, plain python -m http.server

Phase 2 — Port discovery from terminal stdout

Scrape terminal stdout for Local: http://localhost:NNNN patterns (Vite, Next, Astro, etc.). Ephemeral global suggestion list, labeled by announcing terminal ("5173 — from kolu:savage-touch"). Surface via: command palette entry ("Open preview…"), terminal context menu, or a small chrome-bar indicator. Pill-tree indicator per branch: derived at render time from "any terminal under this branch has a recently-announced URL" — no per-tile storage.

  • Stdout scanner (regex for common dev-server announcement patterns)
  • Ephemeral suggestion list (keyed by port; labeled by terminal)
  • Command palette entry: "Open preview…"
  • Terminal context-menu entry: "Open preview for this terminal"
  • Pill-tree branch indicator (derived, no storage)

Non-goals for this issue

Three-worktree stress test

Three dev servers on three unique ports → three tiles, each a URL. Canvas composites all three; getDisplayMedia() records all three in one frame. No per-tile repo/branch metadata needed because ports are globally unique.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions