Skip to content

feat(desktop): workspace views live in main; one copy of the pane layout - #9245

Draft
saddlepaddle wants to merge 7 commits into
mainfrom
superset/workspace-view-state
Draft

saddlepaddle wants to merge 7 commits into
mainfrom
superset/workspace-view-state

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

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 ./core entry, and connectWorkspaceStore. Store actions write through to a source; changes made elsewhere replace the store's layout.
  • Shared code: feat(cli): let agents drive the right sidebar with superset panes #9204's applyPaneLayoutOp / describePaneLayout and the pane kind constants move to src/shared/pane-layout, so main can run them.
  • Main:
    • a workspace_views table (migration generated with drizzle-kit);
    • a WorkspaceViews service that is its only writer and broadcasts every change;
    • an electronTrpc workspaceViews router (list, onChange, put, patch, remove).
  • CLI path: the bridge's /pane-layout endpoint applies ops in main. The relay to a renderer window (pane-layout-requests, the paneLayout router, GlobalPaneLayoutRequests) is deleted.
  • Renderer collection: v2WorkspaceViews is 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, useRightPaneMode and the old sync.
  • Right pane state: open and full screen are rightPane.visibility (closed / open / fullScreen), with the transitions in shared/workspace-views, used by both main and the renderer.
  • Readers and writers moved to views:

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 v2WorkspaceLocalState are 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 file paths 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/panes tests (147 pass), bun run lint, and typecheck in desktop, panes and local-db.
  • Build: a full electron-vite build succeeds, the main bundle contains the new service, and 0060_add_workspace_views.sql is in dist/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 lint and bun run typecheck pass (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-layout contract and host-service are unchanged, so released CLIs work as before.

Written for commit cc4f569. Summary will update on new commits.

View guided diff Turn on auto-fix

@superset/panes/core exports the store, layout utils and transfers
without React, so the Electron main process can run pane reducers.

connectWorkspaceStore makes a WorkspaceStateSource the only writable
copy of a store's layout: actions write through to the source, and
changes made elsewhere replace the store's layout.
applyPaneLayoutOp, describePaneLayout, preserveLocalPaneSelection and the
pane kind constants move to src/shared/pane-layout, so the main process
can apply pane layout ops without importing renderer modules. Pane data
types stay in the renderer and are imported as types only.
…ocess

A workspace_views table holds each v2 workspace's main area and right
pane: tabs, the active tab and whether the right pane is open or full
screen. The WorkspaceViews service in main is its only writer. It
broadcasts every change, and applies pane layout ops itself.

The bridge's /pane-layout endpoint now applies ops through that service
instead of handing them to one renderer window, so a CLI op no longer
needs a window open and does not time out waiting for one. The
paneLayout relay (pane-layout-requests, its router and the renderer's
GlobalPaneLayoutRequests) is gone. electronTrpc gains workspaceViews:
list, onChange, put and remove.
v2WorkspaceViews is a TanStack DB collection fed by the workspaceViews
router: it loads main's views, applies every change main broadcasts, and
sends local writes to main. Changes that arrive during the first load
are applied after it, so an older list cannot overwrite them. A kept
terminal reported by main is marked for the background in this window.

It is not per organization, so it lives outside the per-org collection
cache and is never evicted with it.
… layout

Each workspace's pane stores now mirror its view in v2WorkspaceViews
through connectWorkspaceStore: store actions write through to the view,
and changes from main (the CLI, another window) replace the store's
layout. The two-way sync with v2WorkspaceLocalState, its snapshot
comparisons, the per-window selection memory (v2-pane-selection) and the
mounted-store registry (v2-pane-layout-stores) are gone. The active tab
is part of the view, so windows showing the same workspace show the
same tab.

The right pane's open and full screen state is the view's
rightPane.visibility, with the transitions in shared/workspace-views;
stores/right-pane-full-screen and useRightPaneMode are gone.

Views are created lazily from a workspace's old localStorage layout the
first time anything needs them, and only after the collection has loaded
from main. Writers and readers move to views: workspace creation, the
pending cloud chat, agent browser opens and their saved state, hiding
and removing a workspace, notifications, browser lifecycle and the
resource snapshot. The old layout fields stay in localStorage, unwritten,
for older builds.
Once the views load from main, every visible workspace with a saved
localStorage layout gets a view, so CLI pane ops keep working on
workspaces that have not been opened since the upgrade. A write that
arrives before the views have loaded does nothing instead of throwing.
From review of this branch:
- A failed first load from main is retried, and the views are not
  ready until a load succeeds. Before, a failed load marked an empty
  collection ready, and the migration and the first opens then wrote
  stale or empty views over the stored ones.
- A renderer write sends only the areas it changed, and main merges
  them, so a CLI change to the right pane is not undone by a concurrent
  edit of the main area.
- A CLI op on a workspace main has no view for creates one, as the
  saved layout used to, instead of refusing.
- A browser's saved state never creates a view, so it cannot bring back
  the view of a hidden workspace.
- Hiding or removing a workspace before the views load deletes its view
  once they have.
@coderabbitai

coderabbitai Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) ✅ View Branch
Vercel API (Vercel) ✅ Open Preview
Vercel Web (Vercel) ✅ Open Preview
Vercel Marketing (Vercel) ✅ Open Preview
Vercel Docs (Vercel) ✅ Open Preview

Preview updates automatically with new commits

This branch was previously deployed

1 inactive deployment
preview — cc4f569a Deployed Nov 1, 2026 by saddlepaddle via Deploy API #13536
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