# feat\(preview\): in-app browser preview panel · gitcafe/t3code

[View on GitCafe](https://git.cafe/gitcafe/t3code/commit/52c77c1ec4c1c2c2c27b917baaec91d050d0a737)

Repository: [gitcafe/t3code](https://git.cafe/gitcafe/t3code)

Visibility: public

Requested revision: 52c77c1ec4c1c2c2c27b917baaec91d050d0a737

Requested commit: 52c77c1ec4c1c2c2c27b917baaec91d050d0a737

Commit: 52c77c1ec4c1c2c2c27b917baaec91d050d0a737

Tree: e00bf772b3631077978c118c0dc39d6091741791

Author: Aiden Bai

Committer: Theo Browne

## Message

```
feat(preview): in-app browser preview panel

Adds a desktop-only browser preview that lives in the right panel slot
alongside plan/diff. Lets the user point an Electron <webview> at any
URL — typed into a chrome-style URL bar, clicked from the empty-state
list of detected localhost dev servers, or auto-opened by a project
script with `previewUrl` set. Single-tab per thread.

Server (Effect/Layers):
- PreviewManager: per-(thread, tab) session metadata via SynchronizedRef
  + PubSub<PreviewEvent>; survives WS reconnect via `list`/replay.
- PreviewPortScanner: lsof on macOS/Linux, TCP probe fallback on
  Windows; reference-counted polling so we only scan when subscribed.
- WS RPC + streams (`preview.open|navigate|refresh|close|list|reportStatus`,
  `subscribePreviewEvents`, `subscribeDiscoveredLocalServers`).

Desktop:
- PreviewViewManager owns Chromium WebContents per tab, mediates
  navigation/zoom/devtools/clear-storage. registerWebview gates by
  webContents.getType() === "webview" and host-window match.
- IPC channels for create/close/register/navigate/back/forward/refresh/
  zoom/hardReload/openDevTools/clearCookies/clearCache/getBrowserPartition.
- Forwards app-level shortcuts (mod+shift+J, mod+K, mod+,, mod+W) from
  the webview back to the main window.
- Persisted browser session partition (cookies, cache).

Web:
- PreviewPanel/PreviewView/PreviewWebview render the surface; chrome row
  with back/forward/refresh + URL input + Open-in-browser + 3-dot menu
  (Hard reload, DevTools, Zoom −/+/reset, Clear cookies/cache).
- usePreviewSession subscribes to server events; usePreviewBridge
  mirrors desktop state into the store and forwards Loading→Success/
  LoadFailed back to the server.
- previewStateStore: per-thread snapshot + desktopOverlay + recently-
  seen URLs (Zustand).
- rightPanelStore arbitrates plan vs. preview vs. diff; ChatView's
  toggles strip the `?diff=1` URL hint when switching to preview and
  vice versa so the panels are mutually exclusive.
- Top-nav Globe toggle in ChatHeader (desktop builds only) and a
  `mod+shift+J` keybinding routed via a typed previewActionBus.
- PreviewEmptyState lists detected localhost servers (scanner +
  configured project URLs + recently-seen) with live "listening" pulse.
- PreviewUnreachable: theme-aware port of Chromium's "site can't be
  reached" page.
- Resizable inline panel (RightPanelResizeHandle + useResizableWidth);
  width persists to localStorage on drag-end.
- Terminal link "Open in preview" context-menu integration for loopback
  URLs.

Contracts:
- preview.ts schemas (PreviewSessionSnapshot, PreviewNavStatus,
  PreviewEvent, RPC inputs/results, DiscoveredLocalServer).
- ProjectScript schema gains optional `previewUrl` + `autoOpenPreview`.
- New keybinding commands: preview.toggle/refresh/focusUrl/zoomIn/Out/
  resetZoom; new `when:` contexts `previewFocus` / `previewOpen`.

Shared:
- @t3tools/shared/preview: normalizePreviewUrl, isPreviewableUrl,
  isLoopbackHost, newPreviewTabId, LSOF_LOCAL_HOST_TOKENS.

Tests:
- contracts: schema decode tests for all preview events/snapshots/inputs.
- shared: URL normalization coverage.
- server: PreviewManager (open/navigate/reportStatus/refresh/close,
  multi-subscriber isolation, idempotency); PortScanner (lsof parsing
  including IPv6, TCP probe, reference-counted polling).
- web: previewStateStore (per-tab event application, dedupe,
  reconnect recovery); rightPanelStore arbitration.

```

## Parents

- [d6eb386e3d3a1cb114f7e00ab42c5b8e6b63c181](https://git.cafe/gitcafe/t3code/commit/d6eb386e3d3a1cb114f7e00ab42c5b8e6b63c181?format=markdown)

[Source at this commit](https://git.cafe/gitcafe/t3code/tree/52c77c1ec4c1c2c2c27b917baaec91d050d0a737?format=markdown)
