Commit Graph
2 Commits
Author SHA1 Message Date
Menghuan1918 5c95002641 perf(sidebar): fs.tree 22.8ms→3.8ms + 删除工作区路径检测(放开权限)+ 树展开状态按会话
用户实测反馈:

1) `/sidebar/api/fs.tree` 性能极差 —— 先测后改(10k 条目目录、热缓存、中位数):
   - 冷调用 cap=1000:22.76ms → **3.77ms**;TTL 内重复:≈23ms → **0.000ms**
   - 改法:opendir for-await → readdir(withFileTypes) 一次取回;只为 cap 条建行
     (slice + 定长数组);路径用字符串拼接而非 path.join;排序弃 localeCompare
     (14.6ms/10k → 0.35ms)改小写比较 + 原名码点兜底;新增 1.5s 目录级缓存
     (键 绝对路径\0cap,上限 512),由 fs.write/rename/remove/mkdir/upload 与
     fs-watch 的变更通知显式失效
   - 契约逐字不变;新增 tests/fs-tree.spec.ts 6 例 + fs-watch 失效用例(判别性验证)

2) 删除工作区路径检测(默认不检测、放开权限):
   - path-security 变纯词法解析(realpath/包含性判定删除)、fs-operations 去 fence
     分支、所有 fs 路由不再读 fence、403 分支消失、config 删 workspaceFence 字段
     (schemastery 容忍未知键,旧 profile 不会加载失败)、settings 开关删除
   - ⚠️ 安全影响:插件 fs 路由(tree/read/write/rename/remove/mkdir/媒体/HTML/
     upload/archive)现在可读写宿主用户可访问的任意路径,只受 OS 权限约束;
     指南 §8.1 已加醒目声明
   - 死代码清理:FenceErrorNotice 组件 + EditorHost 分支 + 4 个词条(settingsFence*、
     fenceErrorReason、fenceDisableAction)+ isOutsideWorkspace* 助手 + TreePanel 的
     store prop + 相关 CSS 块

3) 批量列举路由 `fs.trees`(单层失败只在该层带 error)+ 客户端批量加载:
   挂载/刷新时的 N+1 请求变为 1 个(8 个展开目录:9 → 1,探针断言)
   refreshTick 不再先清缓存(旧行保留到新答案;失败保留旧行 + warn 提示)

4) 修复「关闭文件后返回文件树,展开状态被重置」:
   原生承载面的展开集合原来存在每个 tab 的记录里(drop 即丢),改为**每会话状态**
   的投影(ensure 从会话态读、toggleExpanded 写回会话态、registerNativeSurface 用
   attachStore 订阅);同一会话多 tab 共享、不同会话隔离;revealed 保持 per-tab

验证:typecheck / lint / test(136 文件 / 1484 通过 / 9 跳过)/ build 全绿;
DSH_CMD='npx -y --package @deepseek-ai/dsh@0.1.7-rc.1 dsh' pnpm test:mount 7/7 通过;
版本镜像同步(dsh.plugin.json + SIDEBAR_SERVICE_VERSION)
2026-09-28 20:37:56 +08:00
Menghuan1918 ee1fe05067 feat(sidebar): 菜单层级/性能 + 选中条吸底 + 打包进度 + 修复「在侧边打开」
用户实测反馈 5 条:

1) 文件列表性能 / 右键菜单不再刷屏(先测量后改,探针 tests/file-tree-menu-perf.spec.tsx)
   - 宿主应用列表按路径缓存:N 次打开菜单只请求 1 次(原每次 1 次 + Loading 帧)
   - 行上的 git 数据改为原始值(tone/letter):共享 store 每次轮询重建对象不再触达行
     (探针:开窗 folderIcons 4 → 0;root 行也 memo 化)
   - 菜单开关不触发目录重列(fsTree=0);实测「菜单打开期间暂停轮询」会让变更行瞬时
     掉色并多两次重渲染,故按测量结果放弃该做法(数字与理由写进探针注释)

2) 「已选择 N 项」条移到底部:滚动容器最后一个内容子节点 + sticky bottom(不遮最后
   一行),窄栏 flex-wrap 换行不出横向滚动条

3) 菜单层级重构:一级只留默认动作(宿主默认应用 / 打开方式 ▸ / 在文件管理器中显示 /
   新 Tab / 在侧边打开 / 下载 / 压缩并下载 / 复制路径 / 重命名 / 删除;目录行另有
   新建文件夹、上传到此处),应用列表整体收进「打开方式」子菜单;宿主探测到本机关联
   应用时默认不显示插件自研 open-with 项目,新增设置开关 openWithPluginTargets
   (editor descriptor 的 pluginToggles)可强制并存;宿主不可用时插件项目照旧

4) 压缩并下载显示进度:宿主改为可观测任务(archive.build / archive.status /
   /sidebar/archive?id=,4 并发 + 5 分钟 TTL + sessionId 绑定,buildZip 逐条目
   onProgress),客户端轮询 250ms 显示「正在打包 done/total · N%」,ready 后 blob 下载,
   失败进 zipFailed

5) 修复「在侧边打开没有效果」:根因是它只写插件底部工作台(原生右侧栏的 tab 不在
   bottomSplits 里),现按承载面分流 —— 原生 tab 走 service.openTab(target:'side') →
   宿主 openResource(preferNewPane, revealIfOpened:false) 真正分栏;底部工作台行为不变
   (接入 API 变更已同步 docs/external-plugin-guide.md)

验证:typecheck / lint / test(135 文件 1461 通过)/ build 全绿;
DSH_CMD='npx -y --package @deepseek-ai/dsh@0.1.7-rc.1 dsh' pnpm test:mount 7/7 通过;
新增/更新测试:menu-perf(3)、archive(10)、open-in-app(18)、open-with(10)、multiselect(9)、
zip(37)、service(64)、editor-host(11)、builtins(23)、locales(20)
2026-09-28 20:11:46 +08:00