feat(desktop): workspace views live in main; one copy of the pane layout
What & why
Moves each v2 workspace's view (main area tabs, right pane tabs, the active tab, and whether the right pane is open or full screen) out of renderer localStorage and into local.db, owned by the Electron main process. There is now one writable copy of the layout, and every write goes through one place, whether it comes from a click, an agent or the CLI.
Before, the tabs lived twice: in v2WorkspaceLocalState and in a zustand store per area. A two-way sync kept them in step (snapshot comparisons, preserveLocalPaneSelection, per-window selection memory). Writers had to choose between the live store and the row, and anything outside the open window (CLI ops, background agent browsers) took its own path. CLI pane ops were relayed to one renderer window: they needed a window open, and timed out after 10 s.
What changes
@superset/panes: a React-free./coreentry, andconnectWorkspaceStore. Store actions write through to a source; changes made elsewhere replace the store's layout.- Shared code: #9204's
applyPaneLayoutOp/describePaneLayoutand the pane kind constants move tosrc/shared/pane-layout, so main can run them. - Main:
- a
workspace_viewstable (migration generated with drizzle-kit); - a
WorkspaceViewsservice that is its only writer and broadcasts every change; - an electronTrpc
workspaceViewsrouter (list,onChange,put,patch,remove).
- a
- CLI path: the bridge's
/pane-layoutendpoint applies ops in main. The relay to a renderer window (pane-layout-requests, thepaneLayoutrouter,GlobalPaneLayoutRequests) is deleted. - Renderer collection:
v2WorkspaceViewsis a TanStack DB collection synced from main. Writes apply at once, and only the changed areas are sent to main, which merges them. - One copy: each pane store mirrors one area of its view.
- Deleted:
v2-pane-selection,v2-pane-layout-stores,stores/right-pane-full-screen,useRightPaneModeand the old sync. - Right pane state: open and full screen are
rightPane.visibility(closed/open/fullScreen), with the transitions inshared/workspace-views, used by both main and the renderer. - Readers and writers moved to views:
- workspace creation;
- the pending cloud chat;
- agent browser opens and their saved state (this supersedes #9047);
- hiding and removing a workspace;
- notifications, browser lifecycle and the resource snapshot.
Migration of saved data
- At startup, once the views load from main, every visible workspace with a saved localStorage layout gets a view. Any workspace that still has none gets one the first time it is used.
- Nothing creates a view before the collection has loaded from main, and a failed load is retried, not marked ready.
- The old fields in
v2WorkspaceLocalStateare left in place, unwritten, so an older build opens with the layout as it was at upgrade time.
Behaviour changes
- The active tab is part of the view. Two windows showing the same workspace show the same tab. Before, each window kept its own.
- CLI pane ops no longer need a desktop window open. They need only the app running, and they apply even to a workspace that isn't on screen.
Kept on purpose: the CLI still reaches the desktop through host-service. Its panes router does host-side work that has to stay there:
- kills a terminal's session on
close; - checks that a terminal belongs to the workspace;
- resolves
open filepaths against the worktree; - adds terminal titles and agent state.
Only the step after it changed.
Compatibility: host-service and the bridge's /pane-layout contract are unchanged, so released CLIs work as before. The new electronTrpc procedures are internal: main and renderer ship together.
How I tested it
- Unit tests for:
connectWorkspaceStore;- the main service: ops, visibility, a workspace main has not seen, patches,
keepTerminal; - the views collection: load ordering, a failed load retried, patches, removes, kept terminals;
- the visibility transitions;
- migration;
- the rewritten agent browser open, pending cloud chat, workspace creation and sidebar tests.
- Full checks: desktop
bun test(9,062 pass, 0 fail),@superset/panestests (147 pass),bun run lint, and typecheck in desktop, panes and local-db. - Build: a full
electron-vite buildsucceeds, the main bundle contains the new service, and0060_add_workspace_views.sqlis indist/resources/migrations. - Review: an independent review of the diff found five real issues. All are fixed in the last commit.
- Not verified in the running app. I have not driven it over CDP: opening workspaces after the upgrade, CLI ops with no window, two windows, full screen. That's why this is a draft.
Checklist
- PR title follows conventional commits (
type(scope): subject) -
bun run lintandbun run typecheckpass (CI fails on lint warnings too) - "Allow edits from maintainers" is checked on fork PRs
- I have signed the CLA (the bot asks on your first PR)
Summary by cubic
Moves each v2 workspace's pane layout out of renderer localStorage into local.db, owned by the Electron main process. There is now one writable copy of each workspace's view, and every write goes through main whether it comes from a click, an agent, or the CLI.
Behavior changes
- The active tab is part of the view, so two windows showing the same workspace show the same tab.
- CLI pane ops no longer need a desktop window open; they need only the app running and apply even to workspaces not on screen.
- The two-way sync is gone: per-window selection memory, the mounted-store registry, and the right-pane full-screen store are deleted, and each pane store mirrors the view through
connectWorkspaceStore.
Migration
- At startup, every visible workspace with a saved localStorage layout gets a view; workspaces without one get one the first time they are used.
- Writes that arrive before the views load do nothing rather than throw, and a failed load is retried.
- The old localStorage fields stay in place, unwritten, so an older build opens with the layout as it was at upgrade time.
- The bridge's
/pane-layoutcontract and host-service are unchanged, so released CLIs work as before.