You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This keeps the compact right file explorer owned by the app shell while it is open, instead of letting an individual workspace screen own the visual shell.
The left sidebar already used that app-level ownership shape. This applies the same compact/mobile invariant to the right side: when the compact panel state says the file explorer is open, the shell is mounted from app chrome and derives its content from the active workspace. Desktop right sidebar behavior stays in WorkspaceScreen.
How did you verify it
npm run format
npx vitest run packages/app/src/components/compact-explorer-sidebar-host-state.test.ts --bail=1
npm run typecheck
npm run lint
Pre-commit hook reran lint, format check, and typecheck successfully.
Risk surface before merge: this touches compact Expo UI behavior and gesture ownership, so a mobile smoke test should open/close the right explorer from the header and right-edge gesture while a workspace is busy/loading. Desktop right sidebar should still resize and close normally.
Checklist
One focused change. Unrelated cleanups split out.
npm run typecheck passes
npm run lint passes
npm run format ran (Biome)
UI changes include screenshots or video for every affected platform
This PR lifts compact (mobile/Expo) right-side file explorer ownership from individual workspace screens to the app shell (_layout.tsx), applying the same app-level ownership pattern the left sidebar already used. The ExplorerSidebarAnimationProvider and gesture surface now live at the app-chrome layer instead of inside each WorkspaceScreenContent mount.
New CompactExplorerSidebarHost (compact-explorer-sidebar-host.tsx + compact-explorer-sidebar-host-state.ts) owns the compact sidebar at the app shell level, retaining the last-known workspace model while the explorer is open so it stays visible during workspace reloads.
explorer-sidebar.tsx is split into a CompactExplorerSidebar (mobile overlay, always-true close gesture) and the existing ExplorerSidebar (desktop fixed-width with resize handle), sharing state through the new useExplorerSidebarSharedState helper.
diff-pane.tsx / surface.tsx thread a textMetricsStyle prop through all diff row and gutter components so font size, line height, and font family are applied consistently; the E2E test for diff row alignment is hardened with expect.poll and a localStorage guard to eliminate the flakiness those changes previously exposed.
Confidence Score: 5/5
Safe to merge pending a mobile smoke test of open/close from the header and right-edge gesture across workspace states (loading, busy, idle).
The ownership transfer is a clean lift-and-shift following the exact pattern used by the left sidebar. The new CompactExplorerSidebarHost correctly addresses the previous double-hook-call concern with a single call site. The retainedModelRef pattern for workspace-reload stability is correct and well-tested by the four pure-function unit tests. The explorer-sidebar.tsx split removes all isMobile conditionals from the combined component without leaving dead branches. Desktop resize, close, and animation paths are unchanged.
No files require special attention, but the compact explorer gesture ownership change warrants a manual mobile smoke test before shipping.
New component hosting the compact sidebar at app-shell level; single hook call, correct retainedModelRef pattern for workspace-reload stability, gesture surface correctly gated on model?.workspaceRoot.
Pure resolver for CompactExplorerSidebarHostModel; correctly retains previous root/isGit during workspace reload, switches ownership on workspace change, returns null with no selection.
packages/app/src/app/_layout.tsx
App shell now wraps compact children with ExplorerSidebarAnimationProvider + CompactExplorerSidebarHost; desktop path is unchanged.
packages/app/src/components/explorer-sidebar.tsx
Cleanly split into CompactExplorerSidebar (mobile overlay, always-enabled close gesture) and ExplorerSidebar (desktop resize handle). Shared tab state extracted into useExplorerSidebarSharedState.
Mobile gesture surface, openExplorerForWorkspace, ExplorerSidebarAnimationProvider, and the isMobile branch in handleOpenFileFromExplorer all removed cleanly. shouldShowWorkspaceExplorerSidebar correctly gates on !isMobile.
packages/app/src/git/diff-pane.tsx
textMetricsStyle threaded through all diff row and gutter leaf components; useDiffRowMetricsStyle helper correctly memoizes minHeight from lineHeight.
packages/app/e2e/diff-row-alignment.spec.ts
expectDiffCodeFontSize migrated to expect.poll(); useCodeFont guarded with localStorage presence check; expectStoredCodeFontSize added as a synchronisation barrier.
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
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.
Linked issue
N/A
Type of change
What does this PR do
This keeps the compact right file explorer owned by the app shell while it is open, instead of letting an individual workspace screen own the visual shell.
The left sidebar already used that app-level ownership shape. This applies the same compact/mobile invariant to the right side: when the compact panel state says the file explorer is open, the shell is mounted from app chrome and derives its content from the active workspace. Desktop right sidebar behavior stays in
WorkspaceScreen.How did you verify it
npm run formatnpx vitest run packages/app/src/components/compact-explorer-sidebar-host-state.test.ts --bail=1npm run typechecknpm run lintRisk surface before merge: this touches compact Expo UI behavior and gesture ownership, so a mobile smoke test should open/close the right explorer from the header and right-edge gesture while a workspace is busy/loading. Desktop right sidebar should still resize and close normally.
Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatran (Biome)