From 26169102a028ba099edd191407fc91f6f2ccd2ac Mon Sep 17 00:00:00 2001 From: Linghong Date: Fri, 11 Sep 2026 05:16:39 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=AD=96=E5=88=92=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E5=8C=BA=E6=96=87=E4=BB=B6=E6=A0=91=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改用可展开的层级目录树显示文件 保留相对路径读取并修复目录内产物不可见问题 --- .../DesignWorkspacePanel.tsx | 226 ++++++++++++++---- apps/ai-game-creator-shell/src/styles.css | 6 + 2 files changed, 190 insertions(+), 42 deletions(-) diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx index 55c470eb9..9f4c6b770 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx @@ -1,4 +1,12 @@ -import { ArrowLeft, Check, File, Folder, RefreshCw } from 'lucide-react'; +import { + ArrowLeft, + Check, + ChevronDown, + ChevronRight, + File, + Folder, + RefreshCw, +} from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { resolveTauriInvoke } from '../../app/tauri'; @@ -21,8 +29,143 @@ function phaseLabel(phase: string) { return PHASES.find(([id]) => id === phase)?.[1] ?? phase; } -function pathDepth(path: string) { - return path.split(/[\\/]/u).filter(Boolean).length - 1; +type DesignTreeNode = { + name: string; + path: string; + kind: 'directory' | 'file'; + children: DesignTreeNode[]; +}; + +function buildDesignTree(entries: DesignWorkspaceEntry[]) { + const root: DesignTreeNode = { + name: '工作区', + path: '', + kind: 'directory', + children: [], + }; + const nodes = new Map([['', root]]); + + for (const entry of entries) { + const parts = entry.path.split(/[\\/]/u).filter(Boolean); + let parent = root; + let currentPath = ''; + parts.forEach((part, index) => { + currentPath = currentPath ? `${currentPath}/${part}` : part; + let node = nodes.get(currentPath); + if (!node) { + node = { + name: part, + path: currentPath, + kind: + index === parts.length - 1 && entry.kind !== 'directory' + ? 'file' + : 'directory', + children: [], + }; + nodes.set(currentPath, node); + parent.children.push(node); + } + if (index === parts.length - 1) { + node.kind = entry.kind === 'directory' ? 'directory' : 'file'; + } + parent = node; + }); + } + + function sort(nodesToSort: DesignTreeNode[]) { + nodesToSort.sort((left, right) => { + if (left.kind !== right.kind) { + return left.kind === 'directory' ? -1 : 1; + } + return left.name.localeCompare(right.name, 'zh-CN'); + }); + nodesToSort.forEach((node) => sort(node.children)); + } + sort(root.children); + return root; +} + +function DesignTreeBranch({ + node, + depth, + expandedPaths, + selectedPath, + onToggle, + onOpenFile, +}: { + node: DesignTreeNode; + depth: number; + expandedPaths: Set; + selectedPath: string | null; + onToggle: (path: string) => void; + onOpenFile: (path: string) => void; +}) { + const isDirectory = node.kind === 'directory'; + const expanded = isDirectory && expandedPaths.has(node.path); + const paddingLeft = `${12 + depth * 14}px`; + const label = ( + <> + {isDirectory ? ( + expanded ? ( +