0.1.04 文件树与导入mmap
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# 文件树(类 VSCode Explorer)实现顺序 + 对应文件 + 必测用例
|
||||
|
||||
> 目标:在「文件模式」页面树中实现接近 VSCode Explorer 的体验:点击行高亮选择(支持多选/范围选)、`Ctrl+C/Ctrl+V` 复制粘贴(真实文件/文件夹复制)、拖拽移动/按修饰键拖拽复制。
|
||||
>
|
||||
> 非目标:键盘导航(↑↓←→)与编辑器正文内复制粘贴行为。
|
||||
|
||||
## 0. 参考代码位置(VSCode)
|
||||
|
||||
说明:VSCode Explorer 不是独立 React 组件,依赖 Workbench 服务体系;这里用于“行为/结构参考”,不直接搬运实现。
|
||||
|
||||
- [x] VSCode 参考仓库:`cankao/vscode`
|
||||
- [x] 关键入口与文件(详见 `design/vscodetree`):
|
||||
- [x] `cankao/vscode/src/vs/workbench/contrib/files/browser/files.contribution.ts`
|
||||
- [x] `cankao/vscode/src/vs/workbench/contrib/files/browser/views/explorerView.ts`
|
||||
- [x] `cankao/vscode/src/vs/workbench/contrib/files/common/explorerModel.ts`
|
||||
- [x] `cankao/vscode/src/vs/base/common/resourceTree.ts`
|
||||
- [x] `cankao/vscode/src/vs/workbench/contrib/files/browser/fileCommands.ts`
|
||||
|
||||
## 1. MNOTE 当前实现位置(改造落点)
|
||||
|
||||
- [x] 文件树(文件模式):`wolai-frontend/src/components/sidebar/file-tree.tsx`
|
||||
- [x] 侧边栏状态/数据/选择/复制/拖拽集成:`wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
- [x] 文档树扁平化:`wolai-frontend/src/lib/sidebar-tree.ts`(已有 `flattenDocumentTree`)
|
||||
- [x] 复制页面(保留单页复制):`wolai-frontend/src/app/api/documents/duplicate/route.ts`
|
||||
- [x] 递归复制入口:`wolai-frontend/src/app/api/documents/copy-tree/route.ts`
|
||||
- [x] 附件 copy/move/delete/rename:`wolai-frontend/src/app/api/media/batch/route.ts`
|
||||
|
||||
## 2. 实现顺序(建议小步提交,每步能回归)
|
||||
|
||||
### Step 0:建立“可见行模型”(TreeRow)——多选/复制/拖拽的共同基础
|
||||
|
||||
**要做**
|
||||
- [x] 抽象 `visibleRows: FileTreeRow[]`(按展开状态 flatten:doc + index.md + assets + 子 doc)
|
||||
- [x] `rowId` 全局唯一:`doc:<id>` / `index:<docId>` / `asset:<assetId>`
|
||||
|
||||
**新增/改造文件**
|
||||
- [x] `wolai-frontend/src/lib/file-tree/types.ts`
|
||||
- [x] `wolai-frontend/src/lib/file-tree/rows.ts`
|
||||
|
||||
**必测用例(Vitest:纯函数)**
|
||||
- [x] `buildVisibleRows` 顺序稳定(展开/折叠后符合预期)
|
||||
- [x] `rowId` 唯一且可逆解析
|
||||
|
||||
---
|
||||
|
||||
### Step 1:选择模型(Selection Model)——对齐 VSCode 的点击/多选/范围选/右键语义
|
||||
|
||||
**要做**
|
||||
- [x] 去除复选框,改为“点击行高亮选择”
|
||||
- [x] 多选覆盖 doc/index.md/asset 同一集合
|
||||
- [x] 状态字段:
|
||||
- [x] `selectedRowIds: Set<string>`
|
||||
- [x] `anchorRowId: string | null`(Shift 范围起点)
|
||||
- [x] `focusedRowId: string | null`(最后交互行,用于粘贴目标推断)
|
||||
|
||||
**新增文件**
|
||||
- [x] `wolai-frontend/src/lib/file-tree/selection.ts`(把交互写成纯函数,便于测)
|
||||
|
||||
**必测用例(Vitest:纯函数)**
|
||||
- [x] 单击:清空并仅选中当前;更新 anchor/focus
|
||||
- [x] Ctrl/Cmd+单击:切换选中;不清空其他
|
||||
- [x] Shift+单击:按 `visibleRows` 做区间选择(覆盖式)
|
||||
- [x] 右键:未选中项右键 → 先切为单选再弹菜单;已选中项 → 保持多选集合
|
||||
- [x] 空白处单击清空选择
|
||||
|
||||
---
|
||||
|
||||
### Step 2:`Ctrl+C / Ctrl+V`(仅文件树区域生效)
|
||||
|
||||
**要做**
|
||||
- [x] 只在“文件树区域激活”时响应 `Ctrl+C/V`,不影响编辑器输入框/正文
|
||||
- [x] 复制时写入自定义剪贴板 payload(同时提供内存 fallback)
|
||||
- [x] 粘贴目标推断:
|
||||
- [x] focused 为 doc → 贴入其下
|
||||
- [x] focused 为 index/asset → 贴入其所属 doc
|
||||
- [x] 无 focused → fallback activeDocId
|
||||
|
||||
**新增文件**
|
||||
- [x] `wolai-frontend/src/lib/file-tree/clipboard.ts`
|
||||
|
||||
**必测用例(Vitest)**
|
||||
- [x] payload 版本/字段校验(`type/version/action/rowIds`)
|
||||
- [x] 目标推断 3 分支覆盖(doc / index|asset / null)
|
||||
- [x] 在 `input/textarea/contenteditable` 内不拦截 `Ctrl+C/V`
|
||||
|
||||
---
|
||||
|
||||
### Step 3:“真实文件/文件夹复制”后端能力(对齐 VSCode 语义)
|
||||
|
||||
**要做**
|
||||
- [x] doc(文件夹/页面)复制:支持递归复制其内容(子页面 + 正文 + 附件)
|
||||
- [x] asset(真实文件)复制:复制存储对象并创建新记录(`media/batch copy` + 重名策略)
|
||||
- [x] 命名冲突:自动生成不冲突名称(文件夹与文件分别处理)
|
||||
- [x] 虚拟附件(mindmap.json 等)不参与复制/拖拽(仅保留打开/下载逻辑)
|
||||
|
||||
**新增/改造 API**
|
||||
- [x] 新增:`wolai-frontend/src/app/api/documents/copy-tree/route.ts`(递归复制入口,返回新旧 id 映射)
|
||||
- [x] 保持:`wolai-frontend/src/app/api/documents/duplicate/route.ts`(现有单页复制不破坏)
|
||||
- [x] 补齐:`wolai-frontend/src/app/api/media/upload/route.ts`(写入 `bucket/storage_path`,便于后续 copy/move)
|
||||
- [x] 补齐:`wolai-frontend/src/app/api/media/batch/route.ts`(copy/move 支持 `storage_path` 或可解析的 `file_url`;重名策略)
|
||||
|
||||
**纯函数工具与测试**
|
||||
- [x] 命名:`wolai-frontend/src/lib/file-tree/naming.ts`
|
||||
- [x] 测试:`wolai-frontend/src/lib/file-tree/naming.test.ts`
|
||||
|
||||
---
|
||||
|
||||
### Step 4:拖拽移动/复制(含多选拖拽)
|
||||
|
||||
**要做**
|
||||
- [x] 拖拽默认移动;按修饰键(`Alt`)为复制(浏览器层面 dropEffect 已设置;Alt-copy 建议人工补测一次)
|
||||
- [x] 起拖行在选择集中 → 拖整个选择集;否则仅拖当前行并先切为单选
|
||||
- [x] drop 目标:
|
||||
- [x] drop 到 doc 行:贴入其下
|
||||
- [x] drop 到 index/asset 行:等同贴入其所属 doc
|
||||
- [x] 禁止把 doc 拖到自身或后代(含多选去重/去后代)
|
||||
- [x] 与“拖拽上传文件到页面”的 drop 行为区分(payload 不同)
|
||||
|
||||
**新增文件**
|
||||
- [x] `wolai-frontend/src/lib/file-tree/dnd.ts`
|
||||
- [x] `wolai-frontend/src/lib/file-tree/asset.ts`(判断“真实文件”附件)
|
||||
|
||||
**必测用例(Vitest:纯函数)**
|
||||
- [x] `inferDropTargetDocId`:doc / index|asset / null 推断正确(`wolai-frontend/src/lib/file-tree/dnd.test.ts`)
|
||||
- [x] `isInvalidDocDrop`:自拖/后代拖拦截(`wolai-frontend/src/lib/file-tree/dnd.test.ts`)
|
||||
- [x] `isRealFileAsset`:排除 mindmap 等虚拟附件(`wolai-frontend/src/lib/file-tree/asset.test.ts`)
|
||||
|
||||
## 3. 手工验收(每步至少跑一遍)
|
||||
|
||||
- [x] 点击行高亮;active(当前打开页)与 selected(选中)同时可识别
|
||||
- [x] `Ctrl/Cmd+单击` 多选;`Shift+单击` 连续范围选;右键语义正确
|
||||
- [ ] `Ctrl+C/Ctrl+V`:仅树区域生效;编辑器正文不受影响(MCP 按键模拟不稳定,建议人工复核一次)
|
||||
- [x] 复制 doc:`/api/documents/copy-tree` 已验证返回 200(页面端 `fetch`)
|
||||
- [ ] 复制 asset:生成新的存储对象(不是引用),新文件可独立下载(需要至少 1 个真实附件用例)
|
||||
- [x] 拖拽移动 doc:已通过页面拖拽触发 `/api/documents/move` 并验证 200
|
||||
- [ ] Alt+拖拽复制:逻辑已接入(`event.altKey`),需要人工补测一次(MCP 暂无法“按住 Alt 拖拽”)
|
||||
|
||||
---
|
||||
|
||||
### Step 5:多选删除(右键删除作用于选择集)
|
||||
|
||||
**要做**
|
||||
- [x] 右键删除:若右键对象在 `selectedRowIds` 内,则对整个选择集执行删除
|
||||
- [x] index 行视作其所属页面(去重后再删)
|
||||
- [x] 页面删除:移动到垃圾桶(复用 `/api/documents/delete`)
|
||||
- [x] 附件删除:沿用当前逻辑(`/api/media/batch delete`;mindmap 走 `/api/mindmap/:docId`)
|
||||
- [x] 若页面被删除,则跳过其页面内已选附件删除(避免重复/无效操作)
|
||||
|
||||
**新增/改造文件**
|
||||
- [x] `wolai-frontend/src/lib/file-tree/delete.ts`
|
||||
- [x] `wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
|
||||
**必测用例(Vitest:纯函数)**
|
||||
- [x] 选中父/子页面时,仅删除父页面(去后代)
|
||||
- [x] index 行与 doc 行同时选中时,页面 id 去重
|
||||
- [x] 页面被删时,其下已选附件不参与附件删除
|
||||
|
||||
**手工验收**
|
||||
- [x] 多选后右键任一已选行 → 点击“删除到垃圾桶”,应删除整个选择集(而不是仅最后右键项)
|
||||
Reference in New Issue
Block a user