From 257efae3a3f79d325e1d446326b3872ba09dc4e9 Mon Sep 17 00:00:00 2001 From: mdatla Date: Thu, 30 Jul 2026 16:30:23 -0500 Subject: [PATCH] fix(app): show directories in web project picker on open The web directory picker always rendered "No folders found" on a fresh browser profile, making it impossible to add a first project: 1. An empty filter was sent to fuzzy find (/find/file), which returns no results for an empty query and additionally refuses to index $HOME or filesystem roots, where the picker bases itself on headless servers. 2. The browse call (file.list) omitted the required path query param, so the server answered HTTP 400 and the error was swallowed. Browse directories via file.list (with path: "") when the filter is empty, and keep fuzzy find for typed non-empty filters. Fixes #39434 Fixes #37961 Fixes #37611 --- .../directory-picker-domain.test.ts | 48 +++++++++++++++++++ .../src/components/directory-picker-domain.ts | 7 ++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/directory-picker-domain.test.ts b/packages/app/src/components/directory-picker-domain.test.ts index 1bc9af08334c..491825de7a84 100644 --- a/packages/app/src/components/directory-picker-domain.test.ts +++ b/packages/app/src/components/directory-picker-domain.test.ts @@ -175,6 +175,54 @@ test("searches from an absolute root without a default base", async () => { expect(directories).toEqual(["/"]) }) +test("browses directories instead of fuzzy finding when the filter is empty", async () => { + const calls = { list: [] as unknown[], find: [] as unknown[] } + const sdk = { + api: { + file: { + list: (input: { location?: { directory?: string }; path?: string }) => { + calls.list.push(input) + return Promise.resolve({ + data: [ + { path: "projects/", type: "directory" }, + { path: "notes.txt", type: "file" }, + ], + }) + }, + find: (input: unknown) => { + calls.find.push(input) + return Promise.resolve({ data: [] }) + }, + }, + }, + } as unknown as Parameters[0]["sdk"] + const search = createDirectorySearch({ sdk, home: () => "/home/luke", base: () => "/repo" }) + + expect(await search("")).toEqual(["/repo/projects"]) + expect(calls.find).toEqual([]) + expect(calls.list).toEqual([{ location: { directory: "/repo" }, path: "" }]) +}) + +test("uses fuzzy find for non-empty picker filters", async () => { + const calls = { list: [] as unknown[], find: [] as unknown[] } + const sdk = { + api: { + file: { + list: () => Promise.resolve({ data: [] }), + find: (input: { location?: { directory?: string }; query?: string }) => { + calls.find.push(input) + return Promise.resolve({ data: [{ path: "opencode" }] }) + }, + }, + }, + } as unknown as Parameters[0]["sdk"] + const search = createDirectorySearch({ sdk, home: () => "/home/luke", base: () => "/repo" }) + + expect(await search("opencode")).toEqual(["/repo/opencode"]) + expect(calls.find).toEqual([{ location: { directory: "/repo" }, query: "opencode", type: "directory", limit: 50 }]) + expect(calls.list).toEqual([]) +}) + test("identifies the next directory level to preload", () => { expect( preloadTreeDirectories("src/", [ diff --git a/packages/app/src/components/directory-picker-domain.ts b/packages/app/src/components/directory-picker-domain.ts index 9539ae1d01dc..9d309d887a55 100644 --- a/packages/app/src/components/directory-picker-domain.ts +++ b/packages/app/src/components/directory-picker-domain.ts @@ -343,7 +343,7 @@ export function createDirectorySearch(args: { sdk: ServerSDK; base: () => string const existing = cache.get(key) if (existing) return existing const request = args.sdk.api.file - .list({ location: { directory: key } }) + .list({ location: { directory: key }, path: "" }) .then((result) => result.data) .catch(() => []) .then((nodes) => @@ -374,6 +374,11 @@ export function createDirectorySearch(args: { sdk: ServerSDK; base: () => string const pathInput = raw.startsWith("~") || !!pickerRoot(raw) || raw.includes("/") const query = normalizePickerDrive(input.path) if (!pathInput) { + if (!query) { + const results = await match(input.directory, "", 50) + if (!active()) return [] + return results + } const results = await args.sdk.api.file .find({ location: { directory: input.directory }, query, type: "directory", limit: 50 }) .then((result) => result.data.map((entry) => entry.path))