docs+fix: exports 加 ./client/api 别名;catch-all+detect 纯嗅探规则;设计文档偏差记录

- package.json exports 新增 './client/api' 别名指向 service.d.ts(设计文档
  §7 的公共导入路径,api.ts 已被 fetch 助手占用故不换文件名)
- matchFileViewer 补充 sniff-only 规则:exts: [] + detect 的 descriptor 在
  无 head 时不得盲认领(AGENTS.md 的 magic-sniffer 示例原实现会吞掉
  图片/PDF 等真实 viewer 的文件);有 head 时只认领 detect 命中的。
  新增测试断言三种情形(无 head / magic 命中 / magic miss)
- docs/plans/2026-08-11-service-registry-design.md 追加 §17 实施偏差记录:
  dedupeKey+single 语法糖、available 三参、单趟匹配+嗅探规则、EditorHost/
  TextEditor 拆分、fs.read head 字段、导出别名、+ 菜单 disabled 行、
  保留的 store 级操作、内置清单、测试
- AGENTS.md:available 三参、single 语法糖、viewerId、单趟匹配+head 语义、
  8 viewer 内置清单、openTab seed.title 可选、参考实现指向 builtins/ 目录
- README 服务化章节:6 viewer → 8 viewer,标注全注册表驱动
This commit is contained in:
Menghuan1918
2026-08-11 11:25:37 +08:00
parent 1b24bc8ff2
commit 39455ff18e
6 changed files with 127 additions and 24 deletions
+14 -2
View File
@@ -222,8 +222,20 @@ export function createBetterSidebarService(store: SidebarStore): BetterSidebarSe
for (const v of Array.from(viewers.values()).sort(
(a, b) => (b.priority ?? 0) - (a.priority ?? 0),
)) {
if (head !== undefined && v.detect !== undefined && v.detect(path, head)) return v
if (v.exts.length === 0 || v.exts.includes(ext)) return v
// Content sniff first (only when head bytes are available).
if (head !== undefined && v.detect !== undefined) {
if (v.detect(path, head)) return v
// A catch-all with detect is SNIFF-ONLY: it must not blind-claim
// paths it never sniffed (a magic-number viewer must not swallow
// every file before the real viewers get their turn).
if (v.exts.length === 0) continue
} else if (v.exts.length === 0) {
// Blind catch-all (no detect) claims anything; a sniff-only
// catch-all (detect defined, no head yet) yields this round.
if (v.detect === undefined) return v
continue
}
if (v.exts.includes(ext)) return v
}
return undefined
}