dsh-structured 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.4 — 2026-10-24
4
+
5
+ - DSH 0.3.0-rc.1 兼容声明:`engines.dsh` 上限从 `<0.3.0-0` 放宽到 `<0.4.0-0`(否则 desktop 0.3.0-rc.1 的装配门禁会挡住本插件);`dsh.compatibility.dshReleases` 补 `0.2.1-rc.3`、`0.3.0-rc.1` 为 compatible。
6
+ - 无功能改动。0.2.1-rc.3 / 0.3.0-rc.1 为**声明性兼容**:插件依赖的 systemPrompt / commands / configForms / plugins.bundle.config 均为宿主稳定服务面,未见破坏性变更公告,未逐项实测;实测后如有差异再适配或收紧。
7
+
8
+ ## 0.9.3 — 2026-10-24
9
+
10
+ - 配置页主题分段:三个主题选项(DeepSeek / Codex / Anthropic)各带所代表主题的强调色——选项前 7px 圆点标识 + 选中态文字着色,明暗自适应(DeepSeek `#2563EB`/`#60A5FA`、Codex `#10A37F`/`#34D399`、Anthropic `#CC785C`/`#E8A087`,与视觉层三套 token 同源)。样式只作用于 `[data-dsh-structured-config]` 容器内,随配置页 slot 同生命周期注入/撤销。
11
+ - 截图:新增第 7 张 `assets/screenshots/11-config-page.png`(插件板块 → dsh-structured 详情页配置区实机截图,0.9.2 效果);`screenshots.json` 与 README Screenshots 节同步。
12
+
3
13
  ## 0.9.2 — 2026-10-24
4
14
 
5
15
  - 插件详情页配置区:client 注册 `plugins.bundle.config`(键 `dsh-structured`),`theme`/`visual`/`defaultEnabled`/`tailReminder`/`includeSubagents`/`genui` 六字段在 插件板块 → dsh-structured 详情页直接编辑;写入走 configForms 的 `set`(落到 active profile 的 Cordis patch),`writable=false` 时控件只读并提示。
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## 安装
19
19
 
20
- 要求:DSH 0.2.x(`@deepseek-ai/cordis` 4.x),Node ≥ 22。
20
+ 要求:DSH 0.2.x – 0.3.x(`@deepseek-ai/cordis` 4.x),Node ≥ 22。
21
21
 
22
22
  **默认目标就是 desktop profile。** 插件只硬依赖 `systemPrompt`(desktop 由 `@deepseek-ai/dsh-base` 提供),`commands` 是可选服务;不依赖 Electron/headless 专有 API,也没有 profile 分支。安装前先跑一次预检:
23
23
 
@@ -81,7 +81,7 @@ pnpm add $tgz
81
81
 
82
82
  ### 兼容性门禁
83
83
 
84
- 本包声明 `@deepseek-ai/schemastery` peer:`Config`(theme/visual/defaultEnabled/tailReminder/includeSubagents/genui)用它生成设置表单;这是 host 提供的服务,不是门禁。真正门禁是 `engines.dsh`(`>=0.1.7-rc.2 <0.3.0-0`),`dsh-app-boot` 在有 `engines.dsh` 时读取它;`dsh.compatibility.dshReleases` 是 advisory。实测 DSH CLI `0.1.7-rc.2` 与 desktop `0.2.1-rc.2`。
84
+ 本包声明 `@deepseek-ai/schemastery` peer:`Config`(theme/visual/defaultEnabled/tailReminder/includeSubagents/genui)用它生成设置表单;这是 host 提供的服务,不是门禁。真正门禁是 `engines.dsh`(`>=0.1.7-rc.2 <0.4.0-0`,0.9.4 起覆盖 0.3.x),`dsh-app-boot` 在有 `engines.dsh` 时读取它;`dsh.compatibility.dshReleases` 是 advisory。实测 DSH CLI `0.1.7-rc.2` 与 desktop `0.2.1-rc.2`;`0.2.1-rc.3` / `0.3.0-rc.1` 为声明性兼容(插件依赖的 systemPrompt / commands / configForms / plugins.bundle.config 均为宿主稳定服务面,未见破坏性变更公告;未逐项实测,实测后将更新标注)。
85
85
 
86
86
  ## 使用
87
87
 
@@ -129,7 +129,9 @@ Phase 2 已落地 2.1–2.4:卡片类型(结论/判断/指标/风险/下一
129
129
 
130
130
  ![三套主题·暗色](assets/screenshots/10-theme-dark.png)
131
131
 
132
- `screenshots.json` 列出全部 6 张:前 4 张是实机裁图,覆盖标题卡、表格 + 表格结论、状态列、代码块与 800px 窄窗;后 2 张是按 0.9.1 实际 token 渲染的主题对照图,用于看 accent/surface/status,**不是桌面实机截图**。GenUI 截图因避免暴露工作区未收录。
132
+ ![插件设置页](assets/screenshots/11-config-page.png)
133
+
134
+ `screenshots.json` 列出全部 7 张:前 4 张是实机裁图,覆盖标题卡、表格 + 表格结论、状态列、代码块与 800px 窄窗;09/10 两张是按 0.9.1 实际 token 渲染的主题对照图(不是桌面实机截图);第 7 张是 0.9.2 起插件板块 → dsh-structured 详情页的配置区实机截图(六字段 + 主题分段按钮带各主题强调色)。GenUI 截图因避免暴露工作区未收录。
133
135
 
134
136
  ### 配置
135
137
 
@@ -144,7 +146,7 @@ Phase 2 已落地 2.1–2.4:卡片类型(结论/判断/指标/风险/下一
144
146
  GenUI 联动 auto | on | off
145
147
  ```
146
148
 
147
- 宿主 describe 未服务本插件、或部署缺少前端 primitives 时,配置区自动隐藏(此时用下面的 patch 方式)。会话级开关不在这里:用 `/structured on | off`。
149
+ 宿主 describe 未服务本插件、或部署缺少前端 primitives 时,配置区自动隐藏(此时用下面的 patch 方式)。会话级开关不在这里:用 `/structured on | off`。主题分段的三个选项各带所代表主题的强调色圆点,选中态文字同色(0.9.3 起)。
148
150
 
149
151
  底层方式:在 profile 的 `cordis.patch.yml` 里覆盖同一行(注意:config 是整份替换,不是深合并):
150
152
 
package/lib/client.js CHANGED
@@ -44,6 +44,49 @@ window.__ModuleLoader__.load({
44
44
  { field: 'genui', kind: 'segmented', fallback: 'auto', label: 'GenUI 联动 / GenUI routing', hint: 'auto:检测到 dsh-genui 才追加 dsh-ui 路由规则', options: [['auto', 'auto'], ['on', 'on'], ['off', 'off']] },
45
45
  ]
46
46
 
47
+ /**
48
+ * 配置页专用样式:主题分段选择器的三个选项各带自己所代表主题的强调色
49
+ * (圆点标识 + 选中态文字着色,明暗自适应)。色值与视觉层三套 token 同源,
50
+ * 但作用域是设置面板(不在 _markdown_ 容器内),所以单独一份而不是复用 --ds-*。
51
+ */
52
+ const CONFIG_STYLE_ID = 'dsh-structured-config-style'
53
+ const CONFIG_CSS = `
54
+ [data-dsh-structured-config] button[id^="dsh-structured-theme-"]{position:relative}
55
+ [data-dsh-structured-config] button[id^="dsh-structured-theme-"]::before{
56
+ content:"";display:inline-block;width:7px;height:7px;border-radius:50%;
57
+ margin-right:7px;vertical-align:baseline;background:var(--dsw-alias-label-tertiary,#9CA3AF);
58
+ }
59
+ [data-dsh-structured-config] button#dsh-structured-theme-deepseek::before{background:#2563EB}
60
+ [data-dsh-structured-config] button#dsh-structured-theme-codex::before{background:#10A37F}
61
+ [data-dsh-structured-config] button#dsh-structured-theme-anthropic::before{background:#CC785C}
62
+ [data-dsh-structured-config] button#dsh-structured-theme-deepseek[aria-selected="true"]{color:#2563EB;font-weight:600}
63
+ [data-dsh-structured-config] button#dsh-structured-theme-codex[aria-selected="true"]{color:#0B6B4F;font-weight:600}
64
+ [data-dsh-structured-config] button#dsh-structured-theme-anthropic[aria-selected="true"]{color:#A14A2E;font-weight:600}
65
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-deepseek::before{background:#60A5FA}
66
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-codex::before{background:#34D399}
67
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-anthropic::before{background:#E8A087}
68
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-deepseek[aria-selected="true"]{color:#60A5FA}
69
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-codex[aria-selected="true"]{color:#34D399}
70
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-anthropic[aria-selected="true"]{color:#E8A087}
71
+ `
72
+
73
+ /** 插入配置页样式;返回移除函数。document 不可用时是空操作。 */
74
+ const ensureConfigStyle = () => {
75
+ if (typeof document === 'undefined' || document.head === null) return () => {}
76
+ document.getElementById(CONFIG_STYLE_ID)?.remove()
77
+ const style = document.createElement('style')
78
+ style.id = CONFIG_STYLE_ID
79
+ style.textContent = CONFIG_CSS
80
+ document.head.appendChild(style)
81
+ return () => {
82
+ try {
83
+ style.remove()
84
+ } catch {
85
+ // 移除失败不影响宿主。
86
+ }
87
+ }
88
+ }
89
+
47
90
  const CARD_TYPES = new Map([
48
91
  ['结论', 'conclusion'], ['总结', 'conclusion'], ['答案', 'conclusion'],
49
92
  ['判断', 'decision'], ['决策', 'decision'],
@@ -555,8 +598,10 @@ body[data-ds-dark-theme][data-dsh-structured-theme="anthropic"] [class*="_markdo
555
598
 
556
599
  child.effect?.(
557
600
  () =>
558
- child.configForms.whileServed([CONFIG_NS], () =>
559
- child.slots.inject(BUNDLE_CONFIG_SLOT, () =>
601
+ child.configForms.whileServed([CONFIG_NS], () => {
602
+ // 样式与 slot 注册同生命周期:命名空间不被服务时一并撤下。
603
+ const removeStyle = ensureConfigStyle()
604
+ const offSlot = child.slots.inject(BUNDLE_CONFIG_SLOT, () =>
560
605
  child.slots.register({
561
606
  name: BUNDLE_CONFIG_SLOT,
562
607
  key: 'dsh-structured',
@@ -571,8 +616,16 @@ body[data-ds-dark-theme][data-dsh-structured-theme="anthropic"] [class*="_markdo
571
616
  },
572
617
  }),
573
618
  }, StructuredConfigCard),
574
- ),
575
- ),
619
+ )
620
+ return () => {
621
+ try {
622
+ offSlot?.()
623
+ } catch {
624
+ // slot 撤销失败交由宿主清理。
625
+ }
626
+ removeStyle()
627
+ }
628
+ }),
576
629
  'dsh-structured: bundle config page',
577
630
  )
578
631
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-structured",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Structured output for DSH: no-label title card first, then tables/lists/steps; per-session switch and style.md override. 让 DSH 的每条实质回复默认结构化。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -49,7 +49,9 @@
49
49
  "0.1.7-rc.2": "compatible",
50
50
  "0.2.0-rc.2": "compatible",
51
51
  "0.2.1-rc.1": "compatible",
52
- "0.2.1-rc.2": "compatible"
52
+ "0.2.1-rc.2": "compatible",
53
+ "0.2.1-rc.3": "compatible",
54
+ "0.3.0-rc.1": "compatible"
53
55
  }
54
56
  }
55
57
  },
@@ -85,7 +87,7 @@
85
87
  },
86
88
  "engines": {
87
89
  "node": ">=22",
88
- "dsh": ">=0.1.7-rc.2 <0.3.0-0"
90
+ "dsh": ">=0.1.7-rc.2 <0.4.0-0"
89
91
  },
90
92
  "peerDependencies": {
91
93
  "@deepseek-ai/schemastery": "^3.18.0"
package/screenshots.json CHANGED
@@ -4,5 +4,6 @@
4
4
  "assets/screenshots/07-code-block.png",
5
5
  "assets/screenshots/08-narrow.png",
6
6
  "assets/screenshots/09-theme-light.png",
7
- "assets/screenshots/10-theme-dark.png"
7
+ "assets/screenshots/10-theme-dark.png",
8
+ "assets/screenshots/11-config-page.png"
8
9
  ]
package/src/client.js CHANGED
@@ -42,6 +42,49 @@ window.__ModuleLoader__.load({
42
42
  { field: 'genui', kind: 'segmented', fallback: 'auto', label: 'GenUI 联动 / GenUI routing', hint: 'auto:检测到 dsh-genui 才追加 dsh-ui 路由规则', options: [['auto', 'auto'], ['on', 'on'], ['off', 'off']] },
43
43
  ]
44
44
 
45
+ /**
46
+ * 配置页专用样式:主题分段选择器的三个选项各带自己所代表主题的强调色
47
+ * (圆点标识 + 选中态文字着色,明暗自适应)。色值与视觉层三套 token 同源,
48
+ * 但作用域是设置面板(不在 _markdown_ 容器内),所以单独一份而不是复用 --ds-*。
49
+ */
50
+ const CONFIG_STYLE_ID = 'dsh-structured-config-style'
51
+ const CONFIG_CSS = `
52
+ [data-dsh-structured-config] button[id^="dsh-structured-theme-"]{position:relative}
53
+ [data-dsh-structured-config] button[id^="dsh-structured-theme-"]::before{
54
+ content:"";display:inline-block;width:7px;height:7px;border-radius:50%;
55
+ margin-right:7px;vertical-align:baseline;background:var(--dsw-alias-label-tertiary,#9CA3AF);
56
+ }
57
+ [data-dsh-structured-config] button#dsh-structured-theme-deepseek::before{background:#2563EB}
58
+ [data-dsh-structured-config] button#dsh-structured-theme-codex::before{background:#10A37F}
59
+ [data-dsh-structured-config] button#dsh-structured-theme-anthropic::before{background:#CC785C}
60
+ [data-dsh-structured-config] button#dsh-structured-theme-deepseek[aria-selected="true"]{color:#2563EB;font-weight:600}
61
+ [data-dsh-structured-config] button#dsh-structured-theme-codex[aria-selected="true"]{color:#0B6B4F;font-weight:600}
62
+ [data-dsh-structured-config] button#dsh-structured-theme-anthropic[aria-selected="true"]{color:#A14A2E;font-weight:600}
63
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-deepseek::before{background:#60A5FA}
64
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-codex::before{background:#34D399}
65
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-anthropic::before{background:#E8A087}
66
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-deepseek[aria-selected="true"]{color:#60A5FA}
67
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-codex[aria-selected="true"]{color:#34D399}
68
+ body[data-ds-dark-theme] [data-dsh-structured-config] button#dsh-structured-theme-anthropic[aria-selected="true"]{color:#E8A087}
69
+ `
70
+
71
+ /** 插入配置页样式;返回移除函数。document 不可用时是空操作。 */
72
+ const ensureConfigStyle = () => {
73
+ if (typeof document === 'undefined' || document.head === null) return () => {}
74
+ document.getElementById(CONFIG_STYLE_ID)?.remove()
75
+ const style = document.createElement('style')
76
+ style.id = CONFIG_STYLE_ID
77
+ style.textContent = CONFIG_CSS
78
+ document.head.appendChild(style)
79
+ return () => {
80
+ try {
81
+ style.remove()
82
+ } catch {
83
+ // 移除失败不影响宿主。
84
+ }
85
+ }
86
+ }
87
+
45
88
  const CARD_TYPES = new Map([
46
89
  ['结论', 'conclusion'], ['总结', 'conclusion'], ['答案', 'conclusion'],
47
90
  ['判断', 'decision'], ['决策', 'decision'],
@@ -553,8 +596,10 @@ body[data-ds-dark-theme][data-dsh-structured-theme="anthropic"] [class*="_markdo
553
596
 
554
597
  child.effect?.(
555
598
  () =>
556
- child.configForms.whileServed([CONFIG_NS], () =>
557
- child.slots.inject(BUNDLE_CONFIG_SLOT, () =>
599
+ child.configForms.whileServed([CONFIG_NS], () => {
600
+ // 样式与 slot 注册同生命周期:命名空间不被服务时一并撤下。
601
+ const removeStyle = ensureConfigStyle()
602
+ const offSlot = child.slots.inject(BUNDLE_CONFIG_SLOT, () =>
558
603
  child.slots.register({
559
604
  name: BUNDLE_CONFIG_SLOT,
560
605
  key: 'dsh-structured',
@@ -569,8 +614,16 @@ body[data-ds-dark-theme][data-dsh-structured-theme="anthropic"] [class*="_markdo
569
614
  },
570
615
  }),
571
616
  }, StructuredConfigCard),
572
- ),
573
- ),
617
+ )
618
+ return () => {
619
+ try {
620
+ offSlot?.()
621
+ } catch {
622
+ // slot 撤销失败交由宿主清理。
623
+ }
624
+ removeStyle()
625
+ }
626
+ }),
574
627
  'dsh-structured: bundle config page',
575
628
  )
576
629
  }
@@ -21,12 +21,30 @@ function makeDom() {
21
21
  return null
22
22
  },
23
23
  })
24
+ // 可观察的 style 表:createElement('style') 产物按 id 登记,remove 时注销。
25
+ const styles = new Map()
26
+ const styleElement = () => {
27
+ const style = element()
28
+ style.id = ''
29
+ style.textContent = ''
30
+ style.remove = () => {
31
+ if (style.id) styles.delete(style.id)
32
+ }
33
+ return style
34
+ }
24
35
  return {
36
+ styles,
25
37
  body: element(),
26
38
  documentElement: { setAttribute() {}, removeAttribute() {} },
27
- head: { children: [], appendChild() {} },
28
- createElement: () => element(),
29
- getElementById: () => null,
39
+ head: {
40
+ children: [],
41
+ appendChild(style) {
42
+ this.children.push(style)
43
+ if (style.id) styles.set(style.id, style)
44
+ },
45
+ },
46
+ createElement: (tag) => (tag === 'style' ? styleElement() : element()),
47
+ getElementById: (id) => styles.get(id) ?? null,
30
48
  querySelectorAll: () => [],
31
49
  }
32
50
  }
@@ -82,13 +100,14 @@ function loadClient(dom, fakeRequire) {
82
100
  }
83
101
 
84
102
  function makeChild(form, slots) {
85
- return {
103
+ const child = {
86
104
  configForms: {
87
105
  get(id) {
88
106
  return id === 'dsh-structured' ? form : undefined
89
107
  },
90
108
  whileServed(namespaces, register) {
91
- if (served) register()
109
+ // 把 register 返回的组合 disposer 暴露给测试,模拟命名空间停服。
110
+ if (served) child.__disposeRegistration = register()
92
111
  return () => {}
93
112
  },
94
113
  },
@@ -97,6 +116,7 @@ function makeChild(form, slots) {
97
116
  return fn()
98
117
  },
99
118
  }
119
+ return child
100
120
  }
101
121
 
102
122
  let served = true
@@ -106,7 +126,7 @@ function collectRegistration() {
106
126
  const slots = {
107
127
  inject(name, factory) {
108
128
  assert.equal(name, 'plugins.bundle.config')
109
- factory()
129
+ return factory()
110
130
  },
111
131
  register(options, component) {
112
132
  registered = { options, component }
@@ -271,3 +291,39 @@ test('config page:插件未就绪与只读态的降级渲染', () => {
271
291
  assert.equal(switches.length, 4)
272
292
  for (const element of switches) assert.equal(element.props.disabled, true)
273
293
  })
294
+
295
+ test('config page:主题分段按钮带各主题强调色,随注册注入/撤销移除', () => {
296
+ served = true
297
+ const { require: fakeRequire } = fakeRuntime()
298
+ const dom = makeDom()
299
+ const module = loadClient(dom, fakeRequire)
300
+ const form = {
301
+ set() {
302
+ return Promise.resolve(true)
303
+ },
304
+ getSnapshot() {
305
+ return { status: 'ready', writable: true, value: {} }
306
+ },
307
+ subscribe() {
308
+ return () => {}
309
+ },
310
+ }
311
+ const { slots } = collectRegistration()
312
+ const child = makeChild(form, slots)
313
+ module.__test.setupConfigPage(child, form)
314
+
315
+ const style = dom.getElementById('dsh-structured-config-style')
316
+ assert.ok(style, 'config style should be injected with the slot registration')
317
+ // 三套主题的强调色选择器齐全(明暗两态),与视觉层 token 同源。
318
+ for (const token of ['#2563EB', '#10A37F', '#CC785C', '#60A5FA', '#34D399', '#E8A087']) {
319
+ assert.ok(style.textContent.includes(token), 'missing theme token ' + token)
320
+ }
321
+ assert.match(style.textContent, /#dsh-structured-theme-deepseek\[aria-selected="true"\]/)
322
+ assert.match(style.textContent, /#dsh-structured-theme-codex\[aria-selected="true"\]/)
323
+ assert.match(style.textContent, /#dsh-structured-theme-anthropic\[aria-selected="true"\]/)
324
+ assert.match(style.textContent, /body\[data-ds-dark-theme\]/)
325
+
326
+ // 命名空间停服:slot 与样式一并撤下。
327
+ child.__disposeRegistration?.()
328
+ assert.equal(dom.getElementById('dsh-structured-config-style'), null)
329
+ })
@@ -573,7 +573,10 @@ test('仓库边界:不 append 消息、不碰 pre-step / 工具服务、files
573
573
  assert.equal(pkg.dsh.client.platform, 'web')
574
574
  assert.equal(pkg.exports['./client'], './lib/client.js')
575
575
  assert.match(pkg.engines.dsh, /0\.1\.7-rc\.2/)
576
+ assert.equal(pkg.engines.dsh, '>=0.1.7-rc.2 <0.4.0-0')
576
577
  assert.equal(pkg.dsh.compatibility.dshReleases['0.2.1-rc.2'], 'compatible')
578
+ assert.equal(pkg.dsh.compatibility.dshReleases['0.2.1-rc.3'], 'compatible')
579
+ assert.equal(pkg.dsh.compatibility.dshReleases['0.3.0-rc.1'], 'compatible')
577
580
  assert.ok(existsSync('cordis.patch.yml'))
578
581
  assert.ok(existsSync('lib/client.js'))
579
582
  })