Repository navigation
fix(app): scope the tab strip to Code after a side switch - #275
Merged
Merged
Conversation
Every sandbox that exists sits in server.retained, and useWorkspaceScope's sandbox() resolves to nothing both while a sandbox restarts and after the user switches to Code. The restart hold (36411be) only checked the second half, so switching Work -> Code kept the previous folder scope alive for as long as the sandbox existed: the strip went on showing Work's tabs over a Code session, every Code tab was filtered out, and the + button opened new drafts in the sandbox instead of the project on screen. The decision moves into the pure nextTabScope in side.ts (the folderEntry pattern -- Work cannot be driven from the web e2e harness), gated on the side still being Work before the retained hold applies, with regression tests either side of the gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
quickbeard
added a commit
that referenced
this pull request
Aug 19, 2026
The per-folder tab strips PR #239 gave the Work side now apply to Code: the strip is keyed by (server, folder) on both sides, where Code's folders are the selected server's projects. nextTabScope loses its work-only early return and takes a side-agnostic `selected` server; its restart hold is re-gated on the previous scope's server belonging to the side on screen (same fix as #275, now symmetric — a Work switch can no longer inherit a Code folder scope either). useWorkspaceScope resolves the selection on both sides, gated on platform.sandboxes so the web build, which has no Code/Work split, keeps upstream's single strip. Selecting a Code project in the sidebar becomes a move rather than a highlight, through the same folderEntry chain Work uses -- last session in that folder, else its open draft, an open tab, the server's most recent session there, else a new draft -- and the openSessions step is no longer gated to Work. The strip's fallback folder becomes per-side (defaultFolder): the sandbox's primary folder on Work, the last-used project then the first on Code; the side switch lands through the same helper so it agrees with the strip. Web keeps the selection toggle. Co-authored-by: Quickbeard <quickbeard@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Switching from Work back to Code left the tab strip stuck on the sandbox folder you had just left: Work's tabs stayed across the top of a Code session, Code's own tabs never appeared, and the + button opened new drafts in the sandbox rather than the project on screen (
openNewTab's folder-scope branch fires before every other fallback).Root cause
Two facts, individually correct, that compose into the bug:
useWorkspaceScope'ssandbox()memo resolves to nothing in two unrelated states: the selected sandbox's connection is momentarily gone (a restart applying a mount), and the side simply isn't Work anymore.server.retainedlists every sandbox that exists, running or not (retainedSandboxServersmaps allconfig.ids) — by design, so tabs survive a restart (fix(app): keep sandbox tabs through a restart, and stop the freeze it caused #247).The restart hold added in #256 — "keep the previous folder scope while the server it names is retained" — only distinguished the first state. After a Work → Code switch the memo therefore returned the stale Work folder scope on every recomputation, for as long as the sandbox existed. The strip filters
tabsStorethrough that scope, so it showed only the sandbox folder's tabs; every Code tab failed thetab.server !== scope.servertest. The sidebar looked fine becausehome-model.tsxalready gates on the side (sideForServerKey(selection().server) !== side()); the strip had no such gate.Fix
The scope decision moves into the pure
nextTabScopeinsrc/workspace/side.ts— thefolderEntrypattern from #239, since the Work side cannot be driven from the web e2e harness — and the retained hold is now gated on the side still being Work.useWorkspaceScopebecomes a thin reactive wrapper that resolves the sandbox's folders/directory and delegates.Behaviour after the fix:
Touched files
Fork-only files plus CHANGELOG — no
DIVERGENCES.mdchange needed. The existing row for tabs-outlive-a-restart ("workspace/tabs.tsxkeeps the tab strip's folder scope") stays accurate: it keeps it while on Work.Verification
test-browser/workspace-side.test.ts: the regression (Code side scope wins over a retained previous folder scope), the restart hold (returns the previous scope by reference), the removed-sandbox fall-through, the ordinary folder scope, and the no-folder fallback.bun typecheckclean inpackages/appandpackages/desktop;test:unit702 pass,test:browser106 pass.Not visually verified — I could not drive the Electron UI here. The repro to check by hand: open a sandbox session on Work, switch to Code, and confirm the strip shows Code's tabs (not the sandbox's), then mount a folder on Work and confirm the strip still holds steady through the restart.
🤖 Generated with Claude Code