diff --git a/packages/app/src/components/dialog-select-directory-v2.tsx b/packages/app/src/components/dialog-select-directory-v2.tsx index 30d6012b0b0..4417e32001e 100644 --- a/packages/app/src/components/dialog-select-directory-v2.tsx +++ b/packages/app/src/components/dialog-select-directory-v2.tsx @@ -15,6 +15,7 @@ import { nextSuggestionIndex, nextTreeScrollTop, pickerFileSearchQuery, + pickerAbsoluteInput, pickerMode, preloadTreeDirectories, } from "./directory-tree" @@ -107,7 +108,7 @@ export function DialogSelectDirectoryV2(props: DialogSelectDirectoryV2Props) { } async function navigate(path: string) { - const value = cleanInput(path) + const value = policy.navigation(pickerAbsoluteInput(cleanInput(path), home())) if (!value) return const token = ++navigation setLoading(true) diff --git a/packages/app/src/components/directory-tree.test.ts b/packages/app/src/components/directory-tree.test.ts index c75819022c1..abc3e16e3ea 100644 --- a/packages/app/src/components/directory-tree.test.ts +++ b/packages/app/src/components/directory-tree.test.ts @@ -52,11 +52,14 @@ test("centralizes file and directory selection policy", () => { expect(file.selection("/repo/src", "index.ts")).toBe("src/index.ts") expect(file.selection("/repo", "src/")).toBeUndefined() expect(file.result("/repo", "src/index.ts")).toBe("src/index.ts") - expect(file.selection("/tmp", "example.txt")).toBe("/tmp/example.txt") + expect(file.selection("/tmp", "example.txt")).toBeUndefined() + expect(file.navigation("/repo/src")).toBe("/repo/src") + expect(file.navigation("/tmp")).toBeUndefined() const directory = pickerMode("directory") expect(directory.includeFiles).toBeFalse() expect(directory.selection("/repo", "src/")).toBe("/repo/src") + expect(directory.navigation("/tmp")).toBe("/tmp") expect(directory.result("/repo", "")).toBe("/repo") expect(directory.result("/repo", "", false)).toBeUndefined() }) diff --git a/packages/app/src/components/directory-tree.ts b/packages/app/src/components/directory-tree.ts index 140b65e7885..32387b497be 100644 --- a/packages/app/src/components/directory-tree.ts +++ b/packages/app/src/components/directory-tree.ts @@ -29,10 +29,14 @@ export function pickerMode(mode: "directory" | "file", base?: string) { entries(parent: string, nodes: ReadonlyArray<{ name: string; type: "file" | "directory" }>) { return treeEntries(parent, nodes) }, + navigation(path: string) { + return treePathWithin(base, path) ? path : undefined + }, result(root: string, selected: string) { return selected || undefined }, selection(root: string, path: string) { + if (!treePathWithin(base, root)) return return selectedTreePath(root, path, "file", base) }, } @@ -46,6 +50,9 @@ export function pickerMode(mode: "directory" | "file", base?: string) { nodes.filter((node) => node.type === "directory"), ) }, + navigation(path: string) { + return path + }, result(root: string, selected: string, valid = true) { if (!valid) return return selected || root || undefined @@ -64,6 +71,17 @@ export function pickerFileSearchQuery(root: string, input: string, home: string) return value } +export function pickerAbsoluteInput(input: string, home: string) { + return input.replace(/\\/g, "/").replace(/^~(?=\/|$)/, home).replace(/\/+$/, "") || "/" +} + +export function treePathWithin(base: string | undefined, path: string) { + if (!base) return false + const root = absoluteTreePath(base, "").toLowerCase() + const target = absoluteTreePath(path, "").toLowerCase() + return target === root || target.startsWith(root + "/") +} + export function preloadTreeDirectories( parent: string, nodes: ReadonlyArray<{ name: string; type: "file" | "directory" }>,