From af1b6be51f7ff6f33493f3136a3a7874f02736f5 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 29 Jul 2026 10:26:17 +0000 Subject: [PATCH] fix(explorer): apply directory toggles atomically --- .../app/src/components/file-explorer-pane.tsx | 18 ++++++++--------- packages/app/src/file-explorer/tree.test.ts | 17 ++++++++++++++++ packages/app/src/file-explorer/tree.ts | 20 +++++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index 3b5ee22ba..e2cc7759e 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -42,6 +42,7 @@ import { flattenExplorerTree, reconcileRestoredExpandedPaths, restoreExpandedDirectories, + setExpandedDirectoryPath, showHiddenFilesAndRestoreExpandedDirectories, type ExplorerTreeRow, } from "@/file-explorer/tree"; @@ -755,15 +756,14 @@ function toggleDirectory({ return; } const isExpanded = expandedPaths.has(entry.path); - if (isExpanded) { - setExpandedPathsForWorkspace( - workspaceStateKey, - Array.from(expandedPaths).filter((path) => path !== entry.path), - ); - return; - } - setExpandedPathsForWorkspace(workspaceStateKey, [...Array.from(expandedPaths), entry.path]); - if (!directories.has(entry.path)) { + setExpandedPathsForWorkspace(workspaceStateKey, (currentPaths) => + setExpandedDirectoryPath({ + currentExpandedPaths: currentPaths, + directoryPath: entry.path, + expanded: !isExpanded, + }), + ); + if (!isExpanded && !directories.has(entry.path)) { void requestDirectoryListing(entry.path, { recordHistory: false, setCurrentPath: false, diff --git a/packages/app/src/file-explorer/tree.test.ts b/packages/app/src/file-explorer/tree.test.ts index cb8a0b4b0..e3df026c0 100644 --- a/packages/app/src/file-explorer/tree.test.ts +++ b/packages/app/src/file-explorer/tree.test.ts @@ -5,6 +5,7 @@ import { flattenExplorerTree, reconcileRestoredExpandedPaths, restoreExpandedDirectories, + setExpandedDirectoryPath, showHiddenFilesAndRestoreExpandedDirectories, } from "./tree"; @@ -153,6 +154,22 @@ describe("file explorer tree", () => { expect(paths).toEqual([".", "manual"]); }); + it("applies a directory click to the latest restored expansion paths", () => { + const expanded = setExpandedDirectoryPath({ + currentExpandedPaths: [".", "restored"], + directoryPath: "manual", + expanded: true, + }); + const collapsed = setExpandedDirectoryPath({ + currentExpandedPaths: expanded, + directoryPath: "manual", + expanded: false, + }); + + expect(expanded).toEqual([".", "restored", "manual"]); + expect(collapsed).toEqual([".", "restored"]); + }); + it("shows hidden files before waiting for expanded directories to restore", async () => { const rootDirectory = { path: ".", diff --git a/packages/app/src/file-explorer/tree.ts b/packages/app/src/file-explorer/tree.ts index 8f39a52c6..472c3d707 100644 --- a/packages/app/src/file-explorer/tree.ts +++ b/packages/app/src/file-explorer/tree.ts @@ -36,6 +36,12 @@ interface ReconcileRestoredExpandedPathsInput { restoredExpandedPaths: string[]; } +interface SetExpandedDirectoryPathInput { + currentExpandedPaths: readonly string[]; + directoryPath: string; + expanded: boolean; +} + export function flattenExplorerTree({ directories, expandedPaths, @@ -153,6 +159,20 @@ export function reconcileRestoredExpandedPaths({ return Array.from(reconciledPaths); } +export function setExpandedDirectoryPath({ + currentExpandedPaths, + directoryPath, + expanded, +}: SetExpandedDirectoryPathInput): string[] { + const nextPaths = new Set(currentExpandedPaths); + if (expanded) { + nextPaths.add(directoryPath); + } else { + nextPaths.delete(directoryPath); + } + return Array.from(nextPaths); +} + function rowsForDirectory( directory: ExplorerDirectory, depth: number,