diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 8ba1ce618..83baa7a5e 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -24,13 +24,16 @@ type Entry = { path?: string } -export function DialogSelectFile() { +type DialogSelectFileMode = "all" | "files" + +export function DialogSelectFile(props: { mode?: DialogSelectFileMode }) { const command = useCommand() const language = useLanguage() const layout = useLayout() const file = useFile() const dialog = useDialog() const params = useParams() + const filesOnly = () => props.mode === "files" const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey)) const view = createMemo(() => layout.view(sessionKey)) @@ -46,11 +49,12 @@ export function DialogSelectFile() { ] const limit = 5 - const allowed = createMemo(() => - command.options.filter( + const allowed = createMemo(() => { + if (filesOnly()) return [] + return command.options.filter( (option) => !option.disabled && !option.id.startsWith("suggested.") && option.id !== "file.open", - ), - ) + ) + }) const commandItem = (option: CommandOption): Entry => ({ id: "command:" + option.id, @@ -99,10 +103,50 @@ export function DialogSelectFile() { return items.slice(0, limit) }) - const items = async (filter: string) => { - const query = filter.trim() + const root = createMemo(() => { + const nodes = file.tree.children("") + const paths = nodes + .filter((node) => node.type === "file") + .map((node) => node.path) + .sort((a, b) => a.localeCompare(b)) + return paths.slice(0, limit).map(fileItem) + }) + + const unique = (items: Entry[]) => { + const seen = new Set() + const out: Entry[] = [] + for (const item of items) { + if (seen.has(item.id)) continue + seen.add(item.id) + out.push(item) + } + return out + } + + const items = async (text: string) => { + const query = text.trim() setGrouped(query.length > 0) + + if (!query && filesOnly()) { + const loaded = file.tree.state("")?.loaded + const pending = loaded ? Promise.resolve() : file.tree.list("") + const next = unique([...recent(), ...root()]) + + if (loaded || next.length > 0) { + void pending + return next + } + + await pending + return unique([...recent(), ...root()]) + } + if (!query) return [...picks(), ...recent()] + + if (filesOnly()) { + const files = await file.searchFiles(query) + return files.map(fileItem) + } const files = await file.searchFiles(query) const entries = files.map(fileItem) return [...list(), ...entries] @@ -146,7 +190,9 @@ export function DialogSelectFile() { dialog.show(() => )} + onClick={() => dialog.show(() => )} aria-label={language.t("command.file.open")} />