Repository navigation
fix(desktop): prevent blank file viewer on workspace switch - #752
Conversation
📝 WalkthroughWalkthroughThis change introduces workspace-scoped active tab resolution to prevent blank file viewer panes when switching workspaces. It adds a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✏️ Tip: You can disable this entire section by setting 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. Comment |
|
Thanks for grabbing this! This definitely needs to be fixed so i'm fine with merging to make sure the bug is gone, but if you have a moment it may be time to organize the workspace state under a workspace instead of it being global and having things like all the tabs in all workspaces at the top-level.i.e.: If you have the time it'd be nice to solve the root issue, otherwise ye we can merge this in 👍🏼 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@apps/desktop/plans/20260114-2050-workspace-scoped-tabs-state.md`:
- Around line 79-103: Handle tabs with missing/invalid workspaceId during the
migration by adding logic in the normalizer that checks tab.workspaceId and
either (a) places such tabs into a special "orphaned" workspace bucket under
byWorkspace["__orphaned__"] or (b) drops them while emitting a warning log;
update the normalizer acceptance criteria in Milestone 2 to specify the chosen
behavior (orphan bucket vs drop+warn) and implement a unit test in
tabs-state-normalize.test.ts that covers tab entries with
null/undefined/non-existent workspaceId to assert they are routed/removed and
that a warning is emitted when appropriate.
- Around line 260-271: Add a backward-compatibility test and document the chosen
downgrade behavior: update Milestone 7 validation to include a test that
simulates a version rollback by feeding the legacy tab-state shape and asserting
behavior for your chosen approach (either accept both shapes via the DL-1 union
type, require a new schemaVersion flag and assert old clients detect
incompatibility, or mark as breaking and assert state is lost); update the
Milestone 7 text to state which approach was selected and where the test lives,
and ensure the test exercises the migration code/path that handles tab state so
regressions are caught.
- Around line 106-125: Add a workspace-deletion invariant: ensure that when a
workspace is deleted its entire byWorkspace[workspaceId] slice is removed
atomically and UI code tolerates a missing active workspace. Update the
invariants list to include this rule and then update store actions that mutate
workspaces (e.g., the reducer/action that calls removeWorkspace or
deleteWorkspace, and any functions that read activeWorkspaceId/activeTabId,
focusedPaneIds, tabHistoryStack) to: (1) perform an atomic removal of
byWorkspace[workspaceId] (the whole slice) in the workspace deletion action, (2)
clear or re-evaluate activeWorkspaceId and any workspace-scoped pointers
(activeTabId, focusedPaneIds[tabId], tabHistoryStack) so they don’t point to
removed tabs/panes, and (3) make UI selectors that use byWorkspace[workspaceId]
resilient to missing slices (guard reads of byWorkspace[activeWorkspaceId],
activeTabIds, focusedPaneIds, tabHistoryStack) so the app renders gracefully
when the active workspace no longer exists; add tests that delete a workspace
and assert the slice is gone and selectors return safe defaults.
- Around line 139-144: Update normalizeTabsState to document and implement a
deterministic sanitization order: first, if input is legacy shape, group tabs by
tab.workspaceId into byWorkspace and create workspace slices; second, remove
orphan panes whose pane.tabId no longer exists in those slices; third, sanitize
focusedPaneIds per workspace (only referencing panes that remain); and finally,
sanitize activeTabId and tabHistoryStack using the Milestone 1 invariants (only
referencing tabs that remain). Ensure these steps are executed in that order
inside normalizeTabsState and add a brief comment describing the sequence to
prevent cascading failures.
🧹 Nitpick comments (2)
apps/desktop/plans/20260114-2050-workspace-scoped-tabs-state.md (2)
29-30: Consider keeping defensive selectors even after migration.While the new workspace-scoped state shape makes cross-workspace references structurally harder, keeping
resolveActiveTabIdForWorkspaceas a defensive fallback could protect against:
- Race conditions during workspace deletion
- Corrupted state from third-party tools or manual edits to
~/.superset/app-state.json- Future bugs in normalization logic
If kept, document it explicitly as "corruption recovery" rather than a primary selection path, and add a warning log when it triggers to aid debugging.
273-287: Consider automatic backup before migration.To provide a safety net if migration corrupts state, the normalizer (called during app startup) could automatically create a timestamped backup before performing the first migration:
~/.superset/app-state.json.pre-migration-20260114Implementation:
- Check if backup already exists before creating (to avoid duplicates on restart)
- Store backup creation timestamp in the migrated state to skip backup on subsequent runs
- Document the backup location in logs and user-facing docs
This gives users a manual recovery path without requiring them to understand lowdb internals.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/desktop/plans/20260114-2050-workspace-scoped-tabs-state.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
bc9a360 to
4056895
Compare
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
Links
apps/desktop/plans/done/20260114-1739-fix-file-pane-cross-workspace.mdapps/desktop/plans/20260114-2050-workspace-scoped-tabs-state.mdDemo of bug and fix: https://www.loom.com/share/26e180759f1143219a47950ffb99b654
Summary
worktreePathfromtab.workspaceIdand sanitize persisted tab pointers on hydration.Why / Context
Switching workspaces while a file viewer pane was open could render an empty/blank file pane in the target workspace. The root cause was an invariant violation: the route
workspaceIdand the rendered tab’sworkspaceIdcould drift (staleactiveTabIds/ history), so the UI rendered a tab from another workspace.How It Works
resolveActiveTabIdForWorkspace(active → MRU → first tab fallback).activeTabIds,tabHistoryStacks,focusedPaneIds) at hydration.TabViewfetch the workspace bytab.workspaceIdfor file viewer panes.movePaneToTabguard).Manual QA Checklist
Testing
bun run typecheck --filter=@superset/desktopbun run lintbun test apps/desktop/src/renderer/stores/tabs/utils.test.tsbun test(repo-wide) currently fails due toapps/cli/src/lib/storage/lowdb-adapter.test.ts(unrelated).Risks / Rollback
570a819fandb800faf4.Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.