dsh-structured 0.9.2 → 0.9.3
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 +5 -0
- package/README.md +4 -2
- package/assets/screenshots/11-config-page.png +0 -0
- package/lib/client.js +57 -4
- package/package.json +1 -1
- package/screenshots.json +2 -1
- package/src/client.js +57 -4
- package/test/client-config-page.test.mjs +62 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.3 — 2026-10-24
|
|
4
|
+
|
|
5
|
+
- 配置页主题分段:三个主题选项(DeepSeek / Codex / Anthropic)各带所代表主题的强调色——选项前 7px 圆点标识 + 选中态文字着色,明暗自适应(DeepSeek `#2563EB`/`#60A5FA`、Codex `#10A37F`/`#34D399`、Anthropic `#CC785C`/`#E8A087`,与视觉层三套 token 同源)。样式只作用于 `[data-dsh-structured-config]` 容器内,随配置页 slot 同生命周期注入/撤销。
|
|
6
|
+
- 截图:新增第 7 张 `assets/screenshots/11-config-page.png`(插件板块 → dsh-structured 详情页配置区实机截图,0.9.2 效果);`screenshots.json` 与 README Screenshots 节同步。
|
|
7
|
+
|
|
3
8
|
## 0.9.2 — 2026-10-24
|
|
4
9
|
|
|
5
10
|
- 插件详情页配置区: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
|
@@ -129,7 +129,9 @@ Phase 2 已落地 2.1–2.4:卡片类型(结论/判断/指标/风险/下一
|
|
|
129
129
|
|
|
130
130
|

|
|
131
131
|
|
|
132
|
-
|
|
132
|
+

|
|
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
|
|
|
Binary file
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.9.3",
|
|
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",
|
package/screenshots.json
CHANGED
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
|
-
|
|
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: {
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
})
|