Skip to content

feat(desktop): open projects in separate windows - #55

Merged
alexcstark merged 3 commits into
mainfrom
stark/desktop-multi-window
Oct 9, 2026
Merged

alexcstark merged 3 commits into
mainfrom
stark/desktop-multi-window

Conversation

@alexcstark

Copy link
Copy Markdown
Owner

Summary

The desktop app was limited to one window, so you couldn't keep each project open in its own window. Now the sidebar's project filter has a window icon next to each project that opens a new window showing only that project's threads. File → New Window (Cmd/Ctrl+Alt+Shift+N) opens an unfiltered window.

Background

The desktop shell kept one main window ref. A second launch, the dock, the CLI's t3 open, menu actions, and the preview browser all assumed that single window. The sidebar already had a per-project filter (sidebarProjectScopeKey), which is the natural way to define a "project window".

What changed

  • ElectronWindow: main is now the most recently focused app window, not a single ref. setMain puts a window first and clearMain drops it, so closing the focused window hands main to the previous one. Existing callers (dock activation, dialogs, CLI open, SSH prompts, snap shot) now target the window you're working in without further changes.
  • DesktopWindow: createWindow takes an optional project scope and passes it as ?projectScope= on the load URL. It focuses each window on focus, and new windows cascade 28px from the current window instead of stacking on the saved bounds. New openWindow service method. Additional windows don't restore the maximized state or open dev-mode DevTools.
  • PreviewManager: keeps a set of host windows instead of one (setMainWindow → addHostWindow). Webviews register from any app window. Closing one window releases picture-in-picture and recordings only for tabs whose guests it destroyed. The global teardown still runs when the last window closes.
  • DesktopAppActivation: tracks every ready renderer and sends CLI open requests to the main window's renderer, falling back to any ready one. Before this, closing the window that had registered left t3 open with no renderer until it timed out.
  • IPC/contracts: new optional desktopBridge.openWindow({ projectScopeKey? }), with a matching desktop:open-window handler.
  • Menu: File → New Window. Cmd+Shift+N and Cmd+Alt+N were already bound to new-thread commands, so it uses Cmd/Ctrl+Alt+Shift+N.
  • Web: uiStateStore applies ?projectScope= over the saved filter at boot and then removes it from the URL. The sidebar project picker shows the window button only on desktop builds that expose openWindow.
  • Docs: a short "Work in separate windows" section in docs/user/thread-sidebar.md.

Scope / surfaces

  • Desktop: implemented.
  • Web: the browser already supports this with tabs or windows, so the button is hidden there.
  • Mobile: not applicable.
  • Providers / server: unchanged. Each window is just another client connection to the same server.

Verification

  • tsc --noEmit passes for apps/desktop, apps/web, and packages/contracts.
  • vp test run src in apps/desktop: 1354 passed, 34 skipped. apps/web uiStateStore.test.ts: 19 passed.
  • New tests:
    • main follows the most recently focused window, and closing it falls back to the previous one;
    • a preview tab in the first window registers after a second window opens, and recording still works after that second window closes;
    • cascade bounds stay on screen.
  • Negative control: I reverted the clearMain and host-window logic and confirmed the new tests went red, then restored the code.
  • vp lint on changed files: no errors.
  • Not yet verified: a manual pass in the real Electron app (opening, focusing, and closing several windows; t3 open while two windows are open).

Known limitations / follow-ups

  • All windows share one localStorage, so the saved sidebar filter comes from whichever window wrote last. The next launch may restore with that window's project filter.
  • Opening the same thread's preview in two windows at once still shares one preview tab state.
  • Each window is a full renderer with its own WebSocket connection, so memory grows with each open window.

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB +24 B (+0.5%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB +24 B (+2.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB +41 B (+0.2%) 29.3 KiB ✅
Claude Live turn messages 1 2 +1 (+100.0%) 8 ✅

Baseline: f825f5b · PR result: 38cc37d · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@alexcstark
alexcstark merged commit 314c8fa into main Oct 9, 2026
25 checks passed
@alexcstark
alexcstark deleted the stark/desktop-multi-window branch October 9, 2026 14:31
alexcstark added a commit that referenced this pull request Oct 11, 2026
* feat(desktop): open projects in separate windows

* refactor(desktop): make renderer activation registration synchronous

* docs: describe opening projects in separate desktop windows
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant