feat(settings): 侧边栏设置声明式化 — 注册表驱动的开关清单 + 内置支持插头化

- SidebarPrefs 新增 tabsEnabled / viewersEnabled 开放 map(缺省 = 启用),
  PrefsSchema 用 z.dict 校验,旧文档零迁移
- TabDescriptor/FileViewerDescriptor 新增声明式设置面:viewer 补 title/icon,
  两者补 settings.toggles(子代理声明 autoOpenSubagent、终端声明 agentTerminalTools)
- 服务新增 isTabEnabled/isViewerEnabled;openTab 拒绝禁用类型、matchFileViewer
  跳过禁用 viewer
- SideCardSection 重写为注册表驱动清单:侧边栏内容(图标+类型id+开关+嵌套开关)
  + 文件预览(图标+标题+扩展名+开关),开关持久化 + 乐观提交回滚
- 消费点 gating:+ 菜单过滤、子代理自动展开、agent 终端 reconcile、
  产出文件拦截(editor 关闭时回落默认行)、新会话 explorer 预置
- 内置 viewer 补 6 个手绘 outline 图标;store 快照携带 prefs 使开关变更即时生效
- 测试:service gating / builtins 声明 / parsePrefs map 校验 / schema 断言 /
  新增 SideCardSection renderToString 规格
- 文档:README 设置小节、AGENTS.md 描述符与服务方法、新增设计文档;版本 0.5.0
This commit is contained in:
Menghuan1918
2026-08-11 14:08:55 +08:00
parent 89d2c65707
commit 89ddf8a9e7
25 changed files with 938 additions and 99 deletions
+27
View File
@@ -39,6 +39,25 @@ describe('built-in tab registrations', () => {
expect(service.getTab(id)?.single).toBe(true)
}
})
it('the subagent tab declares its auto-open related setting', () => {
const { service } = setup()
const toggles = service.getTab('subagent')?.settings?.toggles ?? []
expect(toggles.map(t => t.key)).toEqual(['autoOpenSubagent'])
})
it('the terminal tab declares the model terminal-tools related setting', () => {
const { service } = setup()
const toggles = service.getTab('terminal')?.settings?.toggles ?? []
expect(toggles.map(t => t.key)).toEqual(['agentTerminalTools'])
})
it('every built-in tab carries the settings-surface icon', () => {
const { service } = setup()
for (const tab of service.getTabs()) {
expect(tab.icon, tab.id).toBeDefined()
}
})
})
describe('built-in file viewer registrations', () => {
@@ -88,6 +107,14 @@ describe('built-in file viewer registrations', () => {
// claims it before binary-download (-50) is consulted.
expect(service.matchFileViewer('book.docx', new Uint8Array([0x50, 0x4b, 0x03, 0x04]))?.id).toBe('docx')
})
it('every built-in viewer carries the declarative settings surface (title + icon)', () => {
const { service } = setup()
for (const viewer of service.getFileViewers()) {
expect(viewer.title, viewer.id).toBeDefined()
expect(viewer.icon, viewer.id).toBeDefined()
}
})
})
describe('built-in disposer', () => {