fix(sidebar): 二轮独立验证的 9 项缺陷 + 目录级暂存

verifier 二轮报告(F1–F9)全部修复,每条都有「临时回退即变红」的判别性验证:

- F1【高】中文等非 latin1 下载名 → 真实宿主 500:content-disposition 改用
  RFC 5987(ASCII 回退 + filename*=UTF-8'');补真 node:http 用例与根因对照断言
- F2【中】多选同名文件归档重名(解压互相覆盖):冲突项逐级上溯父目录消歧
- F3【中低】maxBytes 在读完之后才判定;budget<=0 的 0 字节 FIFO 会永久阻塞:
  改为先 stat、读取前拒绝(FIFO 探针验证)
- F4【中低】SSH 模式 + 宿主不可用时 reveal 整条消失:保留 kind==='reveal' 目标
- F5【低】图钉嵌套交互语义非法且键盘不可达:改为诚实的鼠标热区,键盘入口归设置面板
- F6【低】变更树空闲 tick 全量重渲染:行 memo + 恒定 props + 内容键派生 +
  共享 store 的 sameStatus 保住快照身份
- F7【低】排序随运行时 locale 漂移:显式 'en' collation + 码点兜底
- F8【低】POSIX 文件名的反斜杠被当成层级:不再替换
- F9【低】打包中重复点击静默、无反馈:busy 行(role=status)+ in-flight 守卫
- 新增:目录行「暂存/取消暂存该目录」(git pathspec 语义,不触发折叠)

验证:typecheck / lint / test(134 文件 1441 通过)/ build 全绿;
DSH_CMD='npx -y --package @deepseek-ai/dsh@0.1.7-rc.1 dsh' pnpm test:mount 7/7 通过
This commit is contained in:
Menghuan1918
2026-09-28 18:20:08 +08:00
parent b011be1e98
commit 13d4bae321
17 changed files with 846 additions and 127 deletions
+25
View File
@@ -250,6 +250,31 @@ describe('change tree (the Git lens reads its groups through it)', () => {
])
})
it('pins the collation, so the order cannot move with the runtime locale', () => {
// Mixed case + accents + non-letters: the comparator runs an explicit
// 'en' base collation and breaks ties by code point. A bare
// `localeCompare(other)` would follow the machine's ICU default and could
// reorder the same change list on another machine.
const names = ['b.ts', 'A.ts', 'ä.ts', 'Z.ts', 'a.ts', 'Ä.ts', '_x.ts', '1.ts']
const order = (list: readonly string[]): string[] =>
buildChangeTree(list.map(path => entry(path))).map(node => node.name)
expect(order(names)).toEqual(['_x.ts', '1.ts', 'A.ts', 'a.ts', 'Ä.ts', 'ä.ts', 'b.ts', 'Z.ts'])
// …and it is a TOTAL order: reversing the input cannot move a row.
expect(order([...names].reverse())).toEqual(order(names))
const dirs = ['Zdir/f.ts', 'adir/f.ts', 'Ädir/f.ts', '_dir/f.ts']
const dirOrder = (list: readonly string[]): string[] =>
buildChangeTree(list.map(path => entry(path))).map(node => node.name)
expect(dirOrder(dirs)).toEqual(['_dir', 'adir', 'Ädir', 'Zdir'])
expect(dirOrder([...dirs].reverse())).toEqual(dirOrder(dirs))
// Why the pin is load-bearing: under Swedish collation 'ä' sorts AFTER
// 'z', so an unpinned comparator would order this very list differently on
// a machine whose runtime locale happens to be Swedish.
expect(['z.ts', 'ä.ts'].sort((left, right) => left.localeCompare(right, 'sv', { sensitivity: 'base' })))
.toEqual(['z.ts', 'ä.ts'])
})
it('returns no nodes for an empty or all-clean list', () => {
expect(buildChangeTree([])).toEqual([])
expect(buildChangeTree([entry('a.ts', ' '), entry('b.ts', '!!')])).toEqual([])