diff --git a/apps/desktop/src/app/contrib/controller.tsx b/apps/desktop/src/app/contrib/controller.tsx index 92eb08b63d3..6994be8431c 100644 --- a/apps/desktop/src/app/contrib/controller.tsx +++ b/apps/desktop/src/app/contrib/controller.tsx @@ -27,6 +27,8 @@ import { revealTreePane, setPaneCollapsed, setTreePaneHidden, + setTreeSideCollapsed, + treeSideOfPane, watchContributedPanes } from '@/components/pane-shell/tree/store' import { SidebarProvider } from '@/components/ui/sidebar' @@ -533,7 +535,13 @@ bindTreeSideVisibility('right', $fileBrowserOpen, setFileBrowserOpen) // rode the rail's row and vanished with it), its zone stands on its own. const $hasWorkspace = computed($currentCwd, cwd => Boolean(cwd.trim())) -bindPaneVisibility('files', $hasWorkspace) +// The tree pane's own presence tracks ⌘J directly, not just the column's +// collapse — otherwise revealing a preview (which opens that shared column) +// would drag the tree along with it. See revealPreview. +bindPaneVisibility( + 'files', + computed([$hasWorkspace, $fileBrowserOpen], (workspace, open) => workspace && open) +) // ⌘G — the review sidebar appears/disappears (and comes to the front). bindPaneVisibility( 'review', @@ -596,6 +604,18 @@ registerPaneCloser('files', () => // the side, unhide, front — a NEW target while already visible still fronts. const revealPreview = () => { dockPaneBeside('preview', 'files') + + // The preview shares a collapsible column with the file tree, and + // revealTreePane un-collapses a column through its bound store — here ⌘J / + // $fileBrowserOpen, which IS the tree's toggle. Going through it would open + // the tree every time a preview opened. Un-collapse the column directly and + // leave the toggle alone, so a preview can appear on its own. + const side = treeSideOfPane('preview') + + if (side) { + setTreeSideCollapsed(side, false) + } + revealTreePane('preview') } diff --git a/apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts b/apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts index 90244a96c27..bb6153f50eb 100644 --- a/apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts +++ b/apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts @@ -127,6 +127,38 @@ describe('reactive pane unhide', () => { expect(tree.$collapsedTreeSides.get().has('right')).toBe(false) }) + // Opening a preview used to drag the file tree open with it: the preview + // shares ⌘J's column, and `revealTreePane` un-collapses a column through its + // bound store — which for the right side IS the file-browser toggle. The + // reveal now un-collapses the column directly and leaves the toggle alone. + it('revealing a preview opens its column without flipping the file-tree toggle', async () => { + const { tree, layout } = await setupWithFiles() + const { registry } = await import('@/contrib/registry') + + registry.register({ + id: 'preview', + area: 'panes', + data: { placement: 'right' }, + render: () => null, + title: 'preview' + }) + + layout.setFileBrowserOpen(false) + expect(tree.$collapsedTreeSides.get().has('right')).toBe(true) + + // The revealPreview sequence from controller.tsx. + const side = tree.treeSideOfPane('files') + + expect(side).toBe('right') + tree.setTreeSideCollapsed(side!, false) + tree.revealTreePane('preview') + + // The column is showing… + expect(tree.$collapsedTreeSides.get().has('right')).toBe(false) + // …but the tree's own toggle never moved, so the tree stays closed. + expect(layout.$fileBrowserOpen.get()).toBe(false) + }) + it('reactive unhide does not invoke the right side opener directly', async () => { const { tree, layout } = await setupWithFiles()