import type {ModelEntry} from './types'; export interface ProjectTreeFile {path:string;size:number;} export interface ProjectTreeNode { name:string; path:string; kind:'directory'|'file'; size?:number; children?:ProjectTreeNode[]; } interface MutableDirectory { name:string; path:string; directories:Map; files:ProjectTreeNode[]; } function compareNodes(a:ProjectTreeNode,b:ProjectTreeNode):number { if(a.kind!==b.kind)return a.kind==='directory'?-1:1; return a.name.localeCompare(b.name,'zh-CN',{numeric:true,sensitivity:'base'}); } /** 将规范化后的工程路径转换为“目录优先、名称排序”的资源树。 */ // 同文件导出纯函数是为了让资源树的数据转换可独立测试。 // eslint-disable-next-line react-refresh/only-export-components export function buildProjectTree(files:ProjectTreeFile[]):ProjectTreeNode[] { const root:MutableDirectory={name:'',path:'',directories:new Map(),files:[]}; for(const file of files){ const parts=file.path.split('/').filter(Boolean); if(!parts.length)continue; let parent=root; for(const part of parts.slice(0,-1)){ const path=parent.path?`${parent.path}/${part}`:part; let directory=parent.directories.get(part); if(!directory){directory={name:part,path,directories:new Map(),files:[]};parent.directories.set(part,directory);} parent=directory; } parent.files.push({name:parts.at(-1)!,path:file.path,kind:'file',size:file.size}); } const finish=(directory:MutableDirectory):ProjectTreeNode[]=>[ ...Array.from(directory.directories.values(),child=>({name:child.name,path:child.path,kind:'directory' as const,children:finish(child)})), ...directory.files, ].sort(compareNodes); return finish(root); } function formatSize(bytes:number):string { if(bytes<1024)return `${bytes} B`; if(bytes<1024*1024)return `${(bytes/1024).toFixed(bytes<10*1024?1:0)} KB`; return `${(bytes/(1024*1024)).toFixed(1)} MB`; } function TreeNodes({nodes,entryFormats,selectedEntry}:{nodes:ProjectTreeNode[];entryFormats:Map;selectedEntry?:string}){ return ; } export function ProjectTree({files,entries,selectedEntry}:{files:ProjectTreeFile[];entries:ModelEntry[];selectedEntry?:string}){ const nodes=buildProjectTree(files); const entryFormats=new Map(entries.map(entry=>[entry.path,entry.format])); return ; }