opencode/packages/app/src/components/file-tree-v2-model.ts
2026-07-06 16:26:03 +10:00

77 lines
2.2 KiB
TypeScript

import type { FileNode } from "@opencode-ai/sdk/v2"
export type FileTreeV2Model = {
children: ReadonlyMap<string, readonly FileTreeV2Node[]>
total: number
}
export type FileTreeV2Node = FileNode & { originalPath: string }
export type FileTreeV2Row = {
node: FileTreeV2Node
level: number
}
export function normalizeFileTreeV2Path(value: string) {
return value
.replaceAll("\\", "/")
.replace(/^\/+|\/+$/g, "")
.replace(/\/{2,}/g, "/")
}
export function buildFileTreeV2Model(paths: readonly string[]): FileTreeV2Model {
const nodes = new Map<string, FileTreeV2Node>()
paths.forEach((value) => {
const file = normalizeFileTreeV2Path(value)
if (!file) return
const parts = file.split("/")
parts.forEach((name, index) => {
const path = parts.slice(0, index + 1).join("/")
if (nodes.has(path)) return
nodes.set(path, {
name,
path,
absolute: path,
type: index === parts.length - 1 ? "file" : "directory",
ignored: false,
originalPath: index === parts.length - 1 ? value : path,
})
})
})
const children = new Map<string, FileTreeV2Node[]>()
nodes.forEach((node) => {
const index = node.path.lastIndexOf("/")
const parent = index === -1 ? "" : node.path.slice(0, index)
const list = children.get(parent)
if (list) list.push(node)
else children.set(parent, [node])
})
children.forEach((nodes) =>
nodes.sort((a, b) => {
if (a.type !== b.type) return a.type === "directory" ? -1 : 1
return a.name.localeCompare(b.name)
}),
)
return { children, total: nodes.size }
}
export function flattenFileTreeV2(model: FileTreeV2Model, expanded: (path: string) => boolean) {
const rows: FileTreeV2Row[] = []
const stack = (model.children.get("") ?? []).toReversed().map((node) => ({ node, level: 0 }))
while (stack.length > 0) {
const row = stack.pop()!
rows.push(row)
if (row.node.type !== "directory" || !expanded(row.node.path)) continue
const children = model.children.get(row.node.path) ?? []
for (let index = children.length - 1; index >= 0; index--) {
stack.push({ node: children[index]!, level: row.level + 1 })
}
}
return rows
}