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)
This commit is contained in:
+37
-8
@@ -440,15 +440,44 @@ export function downloadUrl(scope: SessionScope, path: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute URL of the archive route: one ZIP of every `paths` entry (files
|
||||
* and directories, recursive) served as an attachment. Same GET shape as
|
||||
* {@link downloadUrl}; the repeated `path` parameter is the only difference.
|
||||
* Start one archive build. The host fences and walks the selection, then
|
||||
* returns an id IMMEDIATELY — the zip itself is produced in the background and
|
||||
* polled through {@link api.archiveStatus}. `paths` are absolute paths in the
|
||||
* session's namespace (the same values `fsTree` / `downloadUrl` take).
|
||||
*/
|
||||
export function archiveUrl(scope: SessionScope, paths: readonly string[], name: string): string {
|
||||
const params = new URLSearchParams({ sessionId: scope.sessionId, name })
|
||||
if (scope.cwd !== undefined && scope.cwd !== '') params.set('cwd', scope.cwd)
|
||||
for (const path of paths) params.append('path', path)
|
||||
return `/sidebar/archive?${params.toString()}`
|
||||
export function archiveBuild(
|
||||
scope: SessionScope,
|
||||
paths: readonly string[],
|
||||
name: string,
|
||||
): Promise<{ id: string; entries: number }> {
|
||||
return call<{ id: string; entries: number }>('archive.build', scopePayload(scope, { paths: [...paths], name }))
|
||||
}
|
||||
|
||||
/** One archive build's progress (the shape `archive.status` returns). */
|
||||
export interface ArchiveBuildStatus {
|
||||
state: 'building' | 'ready' | 'error'
|
||||
/** Entries finished so far. */
|
||||
done: number
|
||||
/** Entries the archive will contain. */
|
||||
total: number
|
||||
/** Uncompressed bytes read so far. */
|
||||
bytes: number
|
||||
/** Present only when `state === 'error'`. */
|
||||
error?: string
|
||||
}
|
||||
|
||||
/** Poll one archive build (its id came from {@link archiveBuild}). */
|
||||
export function archiveStatus(id: string): Promise<ArchiveBuildStatus> {
|
||||
return call<ArchiveBuildStatus>('archive.status', { id })
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute URL of one FINISHED archive's bytes (the `archive.status` result
|
||||
* must be `ready` first): GET /sidebar/archive?id=…&sessionId=… The session
|
||||
* scope rides along because the host answers only the session that built it.
|
||||
*/
|
||||
export function archiveDownloadUrl(scope: SessionScope, id: string): string {
|
||||
return `/sidebar/archive?${new URLSearchParams({ sessionId: scope.sessionId, id }).toString()}`
|
||||
}
|
||||
|
||||
/** Shared URL builder for the /sidebar/file route (media vs download). */
|
||||
|
||||
Reference in New Issue
Block a user