Skip to content

fix(workstation): keep hot tabs warm and restore rebuilt tab state - #1302

Merged
Harry19081 merged 1 commit into
developfrom
fix/workstation-tab-retention
Sep 5, 2026
Merged

fix(workstation): keep hot tabs warm and restore rebuilt tab state#1302
Harry19081 merged 1 commit into
developfrom
fix/workstation-tab-retention

Conversation

@Lando801

@Lando801 Lando801 commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Problem

Switching away from a WorkStation tab and back reset it. The code-editor host mounts only the active tab, so every switch unmounted the tab's subtree and rebuilt it on return. The Review (Source Control) tab was the worst case: its keep-alive overlay from issue #16 was removed in 52e778c (perf(source-control): lazy load diffs and release memory) without moving any of its view state into a store, so every return collapsed all files in All Changes, scrolled to the top, and re-suspended the Source Control sidebar into a loading placeholder. Focus-mode edits in the diff editor were silently discarded on switch while the tab kept showing its unsaved dot. Search mode/filters and the issue-detail sub-tab reset the same way. The sidebar kept flashing even once the pane was preserved because the worktree loader under it was gated on "Review is active", so leaving cleared the worktree list and returning refetched it, which flipped the scope identity and painted the full-pane loading overlay.

There was also no single place that said which tabs are preserved: the Project Manager list trio had its own keep-alive window, and the sidebar registry carried a separate monotonic keepAlive flag.

Solution

One declarative retention policy, with reconstruct-from-store as the fallback for everything it does not cover.

  • src/store/workstation/tabs/tabRetention.ts maps tab type to a bounded retention pool (grace window + warm cap). Review is in a single-slot pool; the Project Manager trio shares a two-slot pool. Everything else keeps the rebuild default. Widening or narrowing the preserved set is a table edit.
  • useRetainedTabPool turns a pool plus the pane's tabs into the ids that may stay mounted. The code editor computes it once and passes it to both the main pane (Review overlay plus a generic keyed hidden layer for any retained registry type) and SidebarSlot, so pane and sidebar hide/show the same instances in lockstep. Hidden layers use opacity/visibility, not display:none, so scroll offsets survive. The sidebar registry's keepAlive descriptor flag is removed; the Project Manager router reads the trio pool from the same table.
  • The worktree loader in useSourceControlSetup now follows "Review surface mounted" (active or retained) instead of "active", which removes the sidebar overlay flash.
  • For tabs that are rebuilt (or a retained tab past its grace): a session-only per-tab view-state store (tabViewState.ts, LRU-bounded, released from all four close mutations) with a useTabViewState hook. DiffSectionList snapshots expansion overrides, the Virtuoso scroll state, and the handled focus nonce, and rebuilds from them; AllChangesView persists that per tab. Search mode/filters and the issue sub-tab use the hook.
  • Diff-editor edits persist in gitDiffEditDrafts.ts, keyed by file path with a base-content guard so a stale draft never restores over content that changed on disk. Save, discard, and closing a git-diff tab drop the draft.
  • Two latent bugs fixed on the way: the list's collapse-signal effect cleared overrides on mount, and useAllChangesFiles started empty and filled from an effect, which flashed the "no changes" placeholder and pruned restored state before rows existed.

Potential risks

  • Behaviour change: Review (pane + sidebar) now stays mounted for up to 60 s after you leave it. That is one instance and its diff bodies are only loaded for expanded sections, so the memory cost is bounded, but it is a reversal of the July release-on-leave choice for this one tab.
  • A retained tab's renderer must gate side effects on isActive. Only Review and the PM trio are listed; four registry renderers publish a workstation header unconditionally and must not be added to the table until guarded (documented in the policy file).
  • Virtuoso scroll restore uses saved item sizes; if a diff body is still loading when the list rebuilds after the grace window, the position can settle slightly off.
  • The keepAlive field was removed from TabSidebarDescriptor; the only other user is the archived Benchmark sidebar under .archive/, which is tsconfig-excluded.
  • Draft persistence for the Focus view survives clearing the focus and re-focusing the same file, by design; closing a git-diff tab discards, matching prior behaviour.
  • Not verified in the running Tauri app; all evidence is tests and static checks.

Verification

Commit was built with a temporary index from a live checkout, so the pre-commit hook did not run and there is no Pre-commit hook ran. trailer. Run manually on the 34 changed files:

  • pnpm typecheck:fast (tsgo): clean for this change. It reports one pre-existing error in CanvasPreviewSurface.tsx from unrelated uncommitted work in the same checkout.
  • eslint --max-warnings 0 --report-unused-disable-directives <files>: clean.
  • oxlint -c .oxlintrc.json --max-warnings 0 <files>: clean.
  • prettier --check <files>: clean.
  • pnpm run check:test-placement: consistent across 499 directories.
  • pnpm run check:circular: no cycle introduced; one pre-existing cycle util/modelGrouping.ts <-> util/modelVariants.ts from fix(models): keep distinct codex models out of effort-variant families #1266 is unrelated.
  • vitest run over src/store/workstation, src/modules/WorkStation, src/hooks/tabHost, src/modules/ProjectManager/ProjectManagerLayout: 164 files, 1146 tests passed, including 7 new test files (retention policy, pool hook with fake timers, view-state store, hook remount in jsdom, close-mutation cleanup, diff drafts, list snapshot helpers).
  • Full pnpm test on the live checkout: 1512 of 1514 files passed, 11360 of 11363 tests. The 3 failures are in CanvasInlineCard/canvasBuilder.test.ts and staticHtmlCanvas.test.ts, whose sources carry unrelated uncommitted edits in the same checkout; none of those files are in this PR.
  • Not run: the Tauri app (no manual click-through), Rust tests (no Rust changes).

🤖 Generated with Claude Code

The code-editor host mounts only the active tab, so leaving a tab and
coming back rebuilt it from scratch. Review lost All Changes expansion
and scroll, dropped Focus-mode diff edits while keeping the unsaved dot,
and re-suspended its sidebar into a loading placeholder; search mode and
the issue sub-tab reset the same way.

Add a declarative tab retention policy (tabRetention.ts) with bounded
pools: Review and the Project Manager list trio stay mounted-but-hidden
for a grace window, driven through useRetainedTabPool by the editor host,
SidebarSlot, and the Project Manager router. The sidebar registry's
separate keepAlive flag is removed. Gate the Source Control worktree
loader on "surface mounted" rather than "active" so the sidebar no longer
paints a scope-loading overlay on return.

For rebuilt tabs (and retained tabs past their grace), add a session-only
per-tab view-state store (tabViewState.ts + useTabViewState), a
DiffSectionList snapshot (expansion, Virtuoso scroll state, handled focus
nonce), and a base-guarded diff-edit draft store (gitDiffEditDrafts.ts),
all released from the close mutations. Fix two latent bugs: the list's
collapse-signal effect cleared overrides on mount, and useAllChangesFiles
mounted empty and pruned restored state before rows existed.
@Harry19081
Harry19081 merged commit 507eeea into develop Sep 5, 2026
6 checks passed
@Harry19081 Harry19081 added bug Something isn't working workstation Workstation, editor, source control, LSP, or status bar sessions Sessions, history, replay, sidebar, workspace, or worktrees labels Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working sessions Sessions, history, replay, sidebar, workspace, or worktrees workstation Workstation, editor, source control, LSP, or status bar

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants