diff options
Diffstat (limited to 'packages/app/src')
| -rw-r--r-- | packages/app/src/components/file-tree.tsx | 94 |
1 files changed, 66 insertions, 28 deletions
diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 5552cc90b..758f5a83f 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -21,6 +21,8 @@ import { import { Dynamic } from "solid-js/web" import type { FileNode } from "@opencode-ai/sdk/v2" +const MAX_DEPTH = 128 + function pathToFileUrl(filepath: string): string { return `file://${encodeFilePath(filepath)}` } @@ -260,12 +262,20 @@ export default function FileTree(props: { _marks?: Set<string> _deeps?: Map<string, number> _kinds?: ReadonlyMap<string, Kind> + _chain?: readonly string[] }) { const file = useFile() const level = props.level ?? 0 const draggable = () => props.draggable ?? true const tooltip = () => props.tooltip ?? true + const key = (p: string) => + file + .normalize(p) + .replace(/[\\/]+$/, "") + .replaceAll("\\", "/") + const chain = props._chain ? [...props._chain, key(props.path)] : [key(props.path)] + const filter = createMemo(() => { if (props._filter) return props._filter @@ -307,23 +317,45 @@ export default function FileTree(props: { const out = new Map<string, number>() - const visit = (dir: string, lvl: number): number => { - const expanded = file.tree.state(dir)?.expanded ?? false - if (!expanded) return -1 + const root = props.path + if (!(file.tree.state(root)?.expanded ?? false)) return out + + const seen = new Set<string>() + const stack: { dir: string; lvl: number; i: number; kids: string[]; max: number }[] = [] + + const push = (dir: string, lvl: number) => { + const id = key(dir) + if (seen.has(id)) return + seen.add(id) - const nodes = file.tree.children(dir) - const max = nodes.reduce((max, node) => { - if (node.type !== "directory") return max - const open = file.tree.state(node.path)?.expanded ?? false - if (!open) return max - return Math.max(max, visit(node.path, lvl + 1)) - }, lvl) + const kids = file.tree + .children(dir) + .filter((node) => node.type === "directory" && (file.tree.state(node.path)?.expanded ?? false)) + .map((node) => node.path) - out.set(dir, max) - return max + stack.push({ dir, lvl, i: 0, kids, max: lvl }) + } + + push(root, level - 1) + + while (stack.length > 0) { + const top = stack[stack.length - 1]! + + if (top.i < top.kids.length) { + const next = top.kids[top.i]! + top.i++ + push(next, top.lvl + 1) + continue + } + + out.set(top.dir, top.max) + stack.pop() + + const parent = stack[stack.length - 1] + if (!parent) continue + parent.max = Math.max(parent.max, top.max) } - visit(props.path, level - 1) return out }) @@ -459,21 +491,27 @@ export default function FileTree(props: { }} style={`left: ${Math.max(0, 8 + level * 12 - 4) + 8}px`} /> - <FileTree - path={node.path} - level={level + 1} - allowed={props.allowed} - modified={props.modified} - kinds={props.kinds} - active={props.active} - draggable={props.draggable} - tooltip={props.tooltip} - onFileClick={props.onFileClick} - _filter={filter()} - _marks={marks()} - _deeps={deeps()} - _kinds={kinds()} - /> + <Show + when={level < MAX_DEPTH && !chain.includes(key(node.path))} + fallback={<div class="px-2 py-1 text-12-regular text-text-weak">...</div>} + > + <FileTree + path={node.path} + level={level + 1} + allowed={props.allowed} + modified={props.modified} + kinds={props.kinds} + active={props.active} + draggable={props.draggable} + tooltip={props.tooltip} + onFileClick={props.onFileClick} + _filter={filter()} + _marks={marks()} + _deeps={deeps()} + _kinds={kinds()} + _chain={chain} + /> + </Show> </Collapsible.Content> </Collapsible> </Match> |
