@changfenhuang/dsh-genui 0.9.9-preview.1 → 0.9.9

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +13 -8
  3. package/README.zh-CN.md +13 -8
  4. package/SKILL.md +5 -5
  5. package/lib/client.js +3 -3
  6. package/lib/index.js +1273 -338
  7. package/lib/types/client/blocks/advanced.d.ts +1 -1
  8. package/lib/types/client/blocks/checkbox.d.ts +17 -0
  9. package/lib/types/client/blocks/forms.d.ts +3 -8
  10. package/lib/types/client/blocks/state.d.ts +18 -14
  11. package/lib/types/client/dom-fence.d.ts +1 -1
  12. package/lib/types/client/genui-runtime/diagnostics.d.ts +18 -0
  13. package/lib/types/client/genui-runtime/index.d.ts +1 -0
  14. package/lib/types/client/genui-runtime/limits.d.ts +54 -0
  15. package/lib/types/client/genui-runtime/normalize.d.ts +15 -0
  16. package/lib/types/client/genui-runtime/schema.d.ts +73 -0
  17. package/lib/types/client/genui-runtime/value-utils.d.ts +18 -0
  18. package/lib/types/client/guard.d.ts +60 -71
  19. package/lib/types/client/interaction-store.d.ts +7 -2
  20. package/lib/types/client/panel-command.d.ts +1 -1
  21. package/lib/types/client/primitive-labels.d.ts +11 -0
  22. package/lib/types/client/spec.d.ts +17 -17
  23. package/lib/types/plugin/chart-contract.d.ts +6 -4
  24. package/lib/types/plugin/index.d.ts +1 -4
  25. package/package.json +74 -13
  26. package/src/client/GenuiBlock.tsx +27 -11
  27. package/src/client/blocks/advanced.tsx +6 -5
  28. package/src/client/blocks/charts.tsx +1 -1
  29. package/src/client/blocks/checkbox.tsx +60 -0
  30. package/src/client/blocks/diagram/index.tsx +1 -1
  31. package/src/client/blocks/forms.tsx +51 -28
  32. package/src/client/blocks/render-node.tsx +3 -16
  33. package/src/client/blocks/state.ts +18 -14
  34. package/src/client/dom-fence.tsx +2 -1
  35. package/src/client/fence-render.tsx +26 -15
  36. package/src/client/genui-runtime/diagnostics.ts +143 -0
  37. package/src/client/genui-runtime/index.ts +1 -0
  38. package/src/client/genui-runtime/limits.ts +54 -0
  39. package/src/client/genui-runtime/normalize.ts +115 -0
  40. package/src/client/genui-runtime/schema.ts +291 -0
  41. package/src/client/genui-runtime/value-utils.ts +67 -0
  42. package/src/client/guard.ts +314 -169
  43. package/src/client/index.tsx +3 -2
  44. package/src/client/interaction-store.ts +7 -2
  45. package/src/client/panel-command.ts +2 -1
  46. package/src/client/panel.tsx +1 -1
  47. package/src/client/primitive-labels.ts +37 -0
  48. package/src/client/spec.ts +17 -17
  49. package/src/client/toolview.tsx +2 -1
  50. package/src/index.ts +3 -6
  51. package/src/plugin/chart-contract.ts +7 -97
  52. package/src/plugin/index.ts +2 -6
  53. package/src/plugin/tool.ts +60 -40
package/CHANGELOG.md CHANGED
@@ -2,8 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [0.9.9-preview.1] - 2026-09-05
6
- - 面向 DSH 0.1.3-alpha.1 的预览版本,包含 0.9.8 的适配改动;发布到 npm `preview`,不覆盖 `latest`。
5
+ ## [0.9.9] - 2026-09-09
6
+
7
+ - 预览宿主验证更新到 DSH 0.1.5-alpha.1,正式宿主仍为 0.1.2-rc.1;安装包验证覆盖 Diff/Code/JSON 渲染及真实复制。
8
+
9
+ - 测试环境初始化不再预加载生产模块;围栏宿主由需要它的测试显式加载,图表测试可直接使用普通模块 mock(#115)。
10
+
11
+ ### 兼容性
12
+ - dsh-genui 0.9.9 的 DSH 支持范围为 `^0.1.2-rc.1 || ^0.1.5-alpha.1`。使用 DSH `<=0.1.1-rc.x` 的用户请固定使用 dsh-genui `0.9.8`。
13
+
14
+ ### 修复
15
+ - **Diff 组件适配 rc.1(issue #113)**:为 dsh `DiffBlock`、`CodeBlock` 与 `JsonTree` 补齐调用方负责的中文 labels,并覆盖复制交互,避免 rc.1 必填文案遗漏导致类型检查失败或界面出现 `undefined`。
16
+ - **统一 GenUI 组件协议(issue #102)**:新增 runtime schema registry,统一原生字段 alias 归一化、canonical 校验、repair 和 unknown-field 诊断;`validate_dsh_ui`、`render_ui` 与 dsh-ui fence renderer 共用同一处理流水线。`card` / `table` / `callout` / `steps` 的高频字段别名会给出 warning 后归一化,自定义 renderer 类型保持 opaque,真正被 repair 丢弃的原生节点仍会明确报错。
7
17
 
8
18
  ## [0.9.8] - 2026-09-05
9
19
  ### 兼容性
package/README.md CHANGED
@@ -74,7 +74,7 @@ The walkthrough moves from an answer-embedded panel through forms, plotting, Mer
74
74
  The plugin ships **two rendering channels** and picks one automatically after the host activates its browser module:
75
75
 
76
76
  - **Registry channel**: when the host exposes the `fence-registry` extension point (newer dsh builds), fences register through the host's streaming render pipeline and behave seamlessly with the host;
77
- - **DOM channel**: when the host lacks that extension point (including stock DSH and older builds), the plugin observes the session DOM and mounts its own render tree. Since 0.7.2 it **supports streaming rendering**: components appear as the model writes them — the first finished component shows up immediately, no need to wait for the whole reply. Since 0.8.3 fence discovery is **multi-surface**: it matches the stock `md-code-block` surface, the deepsuite-style `.code-block` / `.code-block-small` surfaces some host builds render instead, and — as a structural backstop — any element whose banner labels it `dsh-ui` and contains a `<pre>` body. If your dsh build renders fences with a different class name, they still render (and a one-time console warning tells you the host DOM drifted).
77
+ - **DOM channel**: when the host lacks that extension point (including supported stock DSH builds), the plugin observes the session DOM and mounts its own render tree. Since 0.7.2 it **supports streaming rendering**: components appear as the model writes them — the first finished component shows up immediately, no need to wait for the whole reply. Since 0.8.3 fence discovery is **multi-surface**: it matches the stock `md-code-block` surface, the deepsuite-style `.code-block` / `.code-block-small` surfaces some host builds render instead, and — as a structural backstop — any element whose banner labels it `dsh-ui` and contains a `<pre>` body. If your dsh build renders fences with a different class name, they still render (and a one-time console warning tells you the host DOM drifted).
78
78
 
79
79
  Whichever channel is active, components, interactions, panels, and persistence behave identically.
80
80
 
@@ -93,7 +93,7 @@ The repository ships both renderer channels, the host plugin, and the built brow
93
93
 
94
94
  Prerequisites — all required:
95
95
 
96
- 1. **dsh installed** (any open-source build works — the plugin picks its rendering channel at startup, see "dual-channel rendering" above)
96
+ 1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1`** (dsh-genui 0.9.9 requires this DSH release line; users on DSH `<=0.1.1-rc.x` should use dsh-genui `0.9.8`)
97
97
  2. **`pnpm` on your PATH**: the `dsh plugin` command depends on it. If missing: `corepack enable` (or `npm i -g pnpm`), then **open a new terminal** and confirm `pnpm -v` prints a version
98
98
 
99
99
  Install and activate in DSH (one command, all dependencies included):
@@ -170,6 +170,7 @@ The following is the detailed capability reference. Every behavior is constraine
170
170
  - **Tool channel**: the `render_ui` tool renders the same spec as a card in the tool row (deliverable-style UI goes through the tool, answer-style UI through the fence)
171
171
  - **Session panel**: a persistent dock above the composer; `render_ui` / `panel: true` fences update the same surface in place; `/panel` opens it from the client (`/panel <instruction>` customizes via the model, `/panel clear` clears); the top border is draggable to resize; `append: true` merges incrementally — same-named tabs append content, new tabs get added; the whole panel caps at 200 nodes / 200 appends, after which the model should send `replace` to rebuild
172
172
  - **Self-healing & limits**: every fence passes a spec guard — bad nodes are silently dropped, numbers clamped, strings truncated; the whole tree is capped at 200 nodes / 8 nesting levels; pathological specs never crash the UI
173
+ - **Canonical component protocol**: native field aliases such as `card.label` → `title`, `table.data` → `rows`, `callout.kind` → `tone`, and `steps.items` → `steps` are normalized deterministically before validation and rendering. `validate_dsh_ui` reports these normalizations and warns about unknown native fields without blocking custom renderer nodes.
173
174
  - **Chart error self-healing**: mermaid failures auto-retry with repairs (strip backticks, quote Chinese/space labels, remove `<br/>`) before degrading to source; a broken chart never hits the screen
174
175
  - **Accessibility**: tabs/accordions/switches/progress bars carry full ARIA and keyboard navigation (arrow keys switch tabs, Home/End jump)
175
176
  - **Zero intrusion**: without the plugin, fences are just code blocks — no errors, no session pollution
@@ -214,7 +215,7 @@ The core render package stays light (≈110 KB min / 28 KB gzip); the mermaid, t
214
215
  ## ❓ FAQ
215
216
 
216
217
  - **Rendering as a code block?** First check the browser console for `[genui] client active; fence-channel=registry|dom`. If absent, the client bundle was not activated even if its URL returns 200 — align the profile dependency, `package.json.name`, `cordis.patch.yml`, ModuleLoader id, and configured bundle name. If present, inspect the fence label/body; registry-less hosts automatically use the DOM channel.
217
- - **Chat UI goes blank when rendering a dsh-ui fence?** Your dsh is too old — update dsh first, then reinstall the plugin.
218
+ - **Chat UI goes blank when rendering a dsh-ui fence?** This dsh-genui release requires DSH `^0.1.2-rc.1 || ^0.1.5-alpha.1`; users on DSH `<=0.1.1-rc.x` should use dsh-genui `0.9.8`.
218
219
  - **`dsh: pnpm not found on PATH`?** Install pnpm, then **open a new terminal** and retry (`corepack enable` or `npm i -g pnpm`).
219
220
  - **npm install returns 404?** The npm package is public and requires no login. Run `npm view @changfenhuang/dsh-genui version` to verify the package name and public registry; if a newly published version still returns 404, retry shortly.
220
221
  - **Installed but scene3d/mermaid/echarts don't render?** The engines (mermaid / three / echarts) are no longer inlined in client.js — they load on demand the first time they're used (`/plugins/@changfenhuang/dsh-genui/assets/*.js`, hosted by the plugin's own HTTP routes). First restart dsh web + hard refresh (Cmd+Shift+R); still broken, remove and reinstall (`dsh plugin --profile web remove @changfenhuang/dsh-genui`, then add again). Hosts without the asset routes degrade to source/load-error hints — update dsh.
@@ -228,26 +229,32 @@ pnpm install
228
229
  pnpm run check # type check + full tests + build
229
230
  ```
230
231
 
232
+ With the locked dependencies installed, the check script (`pnpm run check` or `npm run check`) uses the pinned DSH `0.1.2-rc.1` release packages.
233
+
234
+ Run `node scripts/verify-pack.mjs --keep` to retain the verified tarball for inspection or e2e use. The default `node scripts/verify-pack.mjs` removes its temporary directory after verification.
235
+
231
236
  ### Real-device e2e
232
237
 
233
238
  The real chain end to end: start a temporary dsh web → install the plugin → send a message in a browser so the model outputs a `dsh-ui` fence → assert the rendering → click an action button → assert the model responds (event-loop closure):
234
239
 
235
240
  ```sh
241
+ export DSH_ROOT=/path/to/deepseek-harness-0.1.2-rc.1
242
+ export DSH_BIN="$DSH_ROOT/apps/cli/lib/bin.js"
236
243
  DEEPSEEK_API_KEY=sk-... node scripts/e2e.mjs # link-installs the current workspace
237
244
  ```
238
245
 
239
- Prereqs: `dsh`/`pnpm` on PATH, `DEEPSEEK_API_KEY`, and the main repo's web build output (playwright resolves it from the main repo). On PASS it saves an `e2e-final.png` screenshot.
246
+ Build the DSH `0.1.2-rc.1` checkout first. Set `DSH_ROOT` to that checkout and `DSH_BIN` to its `apps/cli/lib/bin.js`, as shown above; also provide `pnpm`, `DEEPSEEK_API_KEY`, and the main repo's web build output. On PASS it saves an `e2e-final.png` screenshot.
240
247
 
241
248
  ### Visual e2e (no model key)
242
249
 
243
- For style/component iterations, a visual smoke that needs no API key: boots a real dsh web with the plugin link-installed, injects the component gallery fence through the DOM channel, renders it in headless Chrome, screenshots the full page, and exercises local interactions (table sort, quiz judging, tree collapse, numeric alignment) with hard assertions:
250
+ For style/component iterations, a visual smoke that needs no API key: boots a real DSH `0.1.2-rc.1` checkout with the plugin link-installed, injects the component gallery fence through the DOM channel, renders it in headless Chrome, screenshots the full page, and exercises local interactions (table sort, quiz judging, tree collapse, numeric alignment) with hard assertions. Set `DSH_ROOT` and `DSH_BIN` to that checkout as shown above before running it.
244
251
 
245
252
  ```sh
246
253
  npx tsx scripts/e2e-visual.mts # → .e2e-artifacts/gallery.png + interactions.png
247
254
  npx tsx scripts/e2e-visual.mts --keep # keep the scratch DSH_HOME for debugging
248
255
  ```
249
256
 
250
- Overridable: `--port 3098`, `--out <dir>`, `DSH_BIN` (defaults to the npm-mode `~/node_modules/.bin/dsh`), `PLAYWRIGHT_PATH` (defaults to the global playwright-core).
257
+ Overridable: `--port 3098`, `--out <dir>`, `DSH_BIN` (set it to the `apps/cli/lib/bin.js` inside the DSH `0.1.2-rc.1` checkout), `PLAYWRIGHT_PATH` (defaults to the global playwright-core).
251
258
 
252
259
  ## 🗺️ Roadmap (evaluated)
253
260
 
@@ -258,8 +265,6 @@ Overridable: `--port 3098`, `--out <dir>`, `DSH_BIN` (defaults to the npm-mode `
258
265
  | Cross-session state persistence (replay restores tabs/switches) | Not doing | Replay-reset is the more correct default (the model has already updated the UI with a new fence); state survives naturally during streaming |
259
266
  | MCP adapter / standalone gallery page / i18n | Not doing | No cross-tool demand signal; gallery material is covered by `gallery.ts` + demo-prompts + README screenshots; only 6 built-in strings |
260
267
 
261
- Unit tests and builds use the locked published dsh rc.8 packages. `DSH_ROOT` is only needed by source-level or end-to-end checks.
262
-
263
268
  ## 🔗 Friendly links
264
269
 
265
270
  - [Linux.do](https://linux.do)
package/README.zh-CN.md CHANGED
@@ -74,7 +74,7 @@ https://github.com/user-attachments/assets/f5db33ec-7471-4d4a-a85b-79c9962ab4ef
74
74
  本插件自带**两套渲染通道**;宿主激活浏览器模块后,插件会自动选择:
75
75
 
76
76
  - **Registry 通道**:宿主提供 `fence-registry` 扩展点(新版 dsh 构建)时,围栏经宿主流式渲染管线注册,行为与宿主无缝;
77
- - **DOM 通道**:宿主没有该扩展点(包括原版 DSH 与旧版构建)时,插件观察会话 DOM 自行挂载渲染树。自 0.7.2 起**支持流式渲染**:模型写到哪渲染到哪,首个完成的组件立即出现,不用等整段回复写完。自 0.8.3 起围栏发现**多表面兼容**:同时匹配标准 `md-code-block` 表面、部分宿主构建使用的 deepsuite 风格 `.code-block` / `.code-block-small` 表面,并以「label+`<pre>`」结构兜底——任何 banner 标注 `dsh-ui` 且含 `<pre>` 正文的元素都能被识别。即使你的 dsh 构建用了别的类名,围栏照常渲染(控制台会有一条一次性提示说明宿主 DOM 发生漂移)。
77
+ - **DOM 通道**:宿主没有该扩展点(包括支持范围内的原版 DSH 构建)时,插件观察会话 DOM 自行挂载渲染树。自 0.7.2 起**支持流式渲染**:模型写到哪渲染到哪,首个完成的组件立即出现,不用等整段回复写完。自 0.8.3 起围栏发现**多表面兼容**:同时匹配标准 `md-code-block` 表面、部分宿主构建使用的 deepsuite 风格 `.code-block` / `.code-block-small` 表面,并以「label+`<pre>`」结构兜底——任何 banner 标注 `dsh-ui` 且含 `<pre>` 正文的元素都能被识别。即使你的 dsh 构建用了别的类名,围栏照常渲染(控制台会有一条一次性提示说明宿主 DOM 发生漂移)。
78
78
 
79
79
  无论走哪条通道,组件、交互、面板、持久化行为完全一致。
80
80
 
@@ -93,7 +93,7 @@ https://github.com/user-attachments/assets/f5db33ec-7471-4d4a-a85b-79c9962ab4ef
93
93
 
94
94
  前置条件,缺一不可:
95
95
 
96
- 1. **dsh 已安装**(开源版任意构建均可——插件启动时自动选择渲染通道,见上文「双通道渲染」)
96
+ 1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1`**(dsh-genui 0.9.9 要求这一 DSH 发布线;使用 DSH `<=0.1.1-rc.x` 的用户请使用 dsh-genui `0.9.8`)
97
97
  2. **`pnpm` 在 PATH 上**:`dsh plugin` 命令依赖它。没有就 `corepack enable`(或 `npm i -g pnpm`),然后**新开一个终端**,确认 `pnpm -v` 有输出
98
98
 
99
99
  安装并在 DSH 中激活(一行命令,自动带上全部依赖):
@@ -170,6 +170,7 @@ dsh plugin --profile web add link:$PWD
170
170
  - **工具通道**:`render_ui` 工具把同一份 spec 渲染成工具行卡片(交付物型 UI 走工具、回答型 UI 走围栏)
171
171
  - **会话面板**:composer 上方常驻 dock,`render_ui` / `panel: true` 围栏原地更新同一块界面;`/panel` 命令客户端直开(`/panel <指令>` 转模型定制、`/panel clear` 清空);顶边框可拖拽调高;`append: true` 增量合并——同名标签页追加内容、新标签页新增;整面板默认最多 200 节点 / 200 条追加,达到上限后模型应发送 `replace` 重建
172
172
  - **自愈与上限**:每个围栏过规格守卫——坏节点静默丢弃、数值钳位、字符串截断,整树 ≤200 节点 / 8 层嵌套,病态 spec 不会拖垮界面
173
+ - **统一组件协议**:`card.label` → `title`、`table.data` → `rows`、`callout.kind` → `tone`、`steps.items` → `steps` 等原生字段别名会在校验和渲染前确定性归一化;`validate_dsh_ui` 会报告归一化结果,并对原生组件未知字段给出警告,同时保持自定义 renderer 节点的透明兼容。
173
174
  - **图错误自愈**:mermaid 渲染失败自动修复重试(剥反引号、引号化中文/空格标签、去 `<br/>`),仍失败才降级源码;错误图永不直接上屏
174
175
  - **可访问性**:tabs/折叠/开关/进度条带完整 ARIA 与键盘导航(方向键切页、Home/End 跳转)
175
176
  - **零打扰**:不装插件时围栏只是代码块,不报错、不污染会话
@@ -220,7 +221,7 @@ dsh plugin --profile web add link:$PWD
220
221
  ## ❓ 常见问题
221
222
 
222
223
  - **显示成代码块?** 先在浏览器控制台找 `[genui] client active; fence-channel=registry|dom`。没有这行,即使 `client.js` 返回 200,也只是下载了文件、没有激活:请对齐网页配置依赖名、`package.json.name`、`cordis.patch.yml`、ModuleLoader id 和配置中的 bundle 名。出现这行后再查围栏标签/正文;宿主没有 registry 时会自动走 DOM 通道。
223
- - **渲染 dsh-ui fence 时聊天界面白屏?** dsh 版本太旧——先更新 dsh 再重装插件。
224
+ - **渲染 dsh-ui fence 时聊天界面白屏?** 此版 dsh-genui 要求 DSH `^0.1.2-rc.1 || ^0.1.5-alpha.1`;使用 DSH `<=0.1.1-rc.x` 的用户请使用 dsh-genui `0.9.8`。
224
225
  - **`dsh: pnpm not found on PATH`?** 装 pnpm 后**新开终端**再试(`corepack enable` 或 `npm i -g pnpm`)。
225
226
  - **npm 安装返回 404?** npm 包是公开的,无需登录。先执行 `npm view @changfenhuang/dsh-genui version` 核对包名与公共 registry;若新版本刚发布仍返回 404,稍后重试。
226
227
  - **装了但 scene3d/mermaid/echarts 不渲染?** 引擎(mermaid / three / echarts)不再内联进 client.js——它们在首次用到时按需加载(`/plugins/@changfenhuang/dsh-genui/assets/*.js`,插件自带 HTTP 路由托管)。先重启 dsh web + 硬刷新(Cmd+Shift+R);仍不渲染就卸掉重装(`dsh plugin --profile web remove @changfenhuang/dsh-genui` 后再 add)。旧版宿主缺少资产路由时会降级显示源码/加载失败提示,更新 dsh 即可。
@@ -234,26 +235,32 @@ pnpm install
234
235
  pnpm run check # 类型检查 + 全量测试 + 构建
235
236
  ```
236
237
 
238
+ 安装锁定依赖后,检查脚本(`pnpm run check` 或 `npm run check`)使用固定的 DSH `0.1.2-rc.1` 发布包。
239
+
240
+ 运行 `node scripts/verify-pack.mjs --keep` 可保留已验收的 tarball,便于检查或运行 e2e;默认的 `node scripts/verify-pack.mjs` 会在验收后清理临时目录。
241
+
237
242
  ### 真机 e2e
238
243
 
239
244
  真实链路验证:起一个临时 dsh web → 装上插件 → 浏览器里发消息让模型输出 `dsh-ui` fence → 断言渲染 → 点击 action 按钮 → 断言模型响应(事件循环闭环):
240
245
 
241
246
  ```sh
247
+ export DSH_ROOT=/path/to/deepseek-harness-0.1.2-rc.1
248
+ export DSH_BIN="$DSH_ROOT/apps/cli/lib/bin.js"
242
249
  DEEPSEEK_API_KEY=sk-... node scripts/e2e.mjs # link 安装当前工作区
243
250
  ```
244
251
 
245
- 前置:`dsh`/`pnpm` 在 PATH、`DEEPSEEK_API_KEY`、主仓 web 构建产物(playwright 从主仓解析)。PASS 时保存 `e2e-final.png` 截图。
252
+ 先构建 DSH `0.1.2-rc.1` checkout。按上面的示例将 `DSH_ROOT` 指向该 checkout,将 `DSH_BIN` 指向其中的 `apps/cli/lib/bin.js`;另需准备 `pnpm`、`DEEPSEEK_API_KEY` 和主仓 web 构建产物。PASS 时保存 `e2e-final.png` 截图。
246
253
 
247
254
  ### 视觉 e2e(无需模型 key)
248
255
 
249
- 样式/组件迭代用:起真实 dsh web + link 安装插件 → 通过 DOM 通道注入组件画廊围栏 → headless Chrome 全页截图 + 本地交互(表格排序 / 判题 / 目录折叠 / 数值对齐)硬断言,不需要任何模型额度:
256
+ 样式/组件迭代用:使用 DSH `0.1.2-rc.1` checkout 起真实 dsh web + link 安装插件 → 通过 DOM 通道注入组件画廊围栏 → headless Chrome 全页截图 + 本地交互(表格排序 / 判题 / 目录折叠 / 数值对齐)硬断言,不需要任何模型额度。运行前按上面的示例将 `DSH_ROOT` 和 `DSH_BIN` 指向该 checkout:
250
257
 
251
258
  ```sh
252
259
  npx tsx scripts/e2e-visual.mts # → .e2e-artifacts/gallery.png + interactions.png
253
260
  npx tsx scripts/e2e-visual.mts --keep # 保留 scratch DSH_HOME 便于排查
254
261
  ```
255
262
 
256
- 可覆盖:`--port 3098`、`--out <dir>`、`DSH_BIN`(默认 npm 生产模式 `~/node_modules/.bin/dsh`)、`PLAYWRIGHT_PATH`(默认全局 playwright-core)。
263
+ 可覆盖:`--port 3098`、`--out <dir>`、`DSH_BIN`(设为 DSH `0.1.2-rc.1` checkout 内的 `apps/cli/lib/bin.js`)、`PLAYWRIGHT_PATH`(默认全局 playwright-core)。
257
264
 
258
265
  ## 🗺️ Roadmap(已评估项)
259
266
 
@@ -264,8 +271,6 @@ npx tsx scripts/e2e-visual.mts --keep # 保留 scratch DSH_HOME 便于排查
264
271
  | 跨会话状态持久化(回放恢复 tabs/开关) | 不做 | 回放重置是更正确的默认行为(模型已用新 fence 更新过界面);流式期间状态天然保留 |
265
272
  | MCP 适配器 / 独立画廊页 / i18n | 不做 | 无跨工具需求信号;画廊素材已被 `gallery.ts` + demo-prompts + README 截图覆盖;内置文案仅 6 处 |
266
273
 
267
- 单元测试和构建直接使用锁定的 dsh rc.8 发布包;只有源码级或端到端检查才需要设置 `DSH_ROOT`。
268
-
269
274
  ## 🔗 友情链接
270
275
 
271
276
  - [Linux.do](https://linux.do)
package/SKILL.md CHANGED
@@ -54,20 +54,20 @@ description: "Render structured interactive UI inline in your reply via the dsh-
54
54
  **本地优先(v2.6)**:UI 自己能做的状态变化——判卷、判题、重置、展开、选中——一律本地即时完成,**零模型往返**。action 只用于必须模型参与的事(生成新内容、执行工具、下一步建议)。**交互组件必须带 action:不带 action 的按钮渲染为禁用态,用户点不了;带 action 的按钮点击后有「已触发」本地反馈。**
55
55
  - button: `{"type":"button","label":"...","tone":"primary|danger|success|ghost","full":true?,"small":true?,"icon":"emoji?","action":"refresh"?}`
56
56
  - **秘密禁令**:不得索取或生成密码、API Key、访问令牌、恢复码等秘密输入;遇到此类需求直接拒绝并解释
57
- - input: `{"type":"input","label":"...","placeholder":"...","inputType":"text|email","value":"...","action":"name"?,"id":"field-id"?}` — action 在失焦**和回车**时触发(回车带 `submit:true`);**blur 仅值有变化才发送**(聚焦又离开不产生空往返);payload 带 `id` 帮模型定位字段;带 `id` 的值刷新后保留、并被 submit 收集进 `fields`
57
+ - input: `{"type":"input","label":"...","placeholder":"...","inputType":"text|email|color","value":"...","action":"name"?,"id":"field-id"?}` — `color` 使用浏览器原生取色器,值使用 `#RRGGBB`;action 在失焦**和回车**时触发(回车带 `submit:true`);**blur 仅值有变化才发送**(聚焦又离开不产生空往返);payload 带 `id` 帮模型定位字段;带 `id` 的值刷新后保留、并被 submit 收集进 `fields`
58
58
  - select: `{"type":"select","label":"...","options":["...","..."],"selected":下标?,"action":"pick"?,"id":"field-id"?}` — `selected` 预选某选项(缺省显示「请选择…」占位,不静默预选第一项);带 `id` 的选择跨刷新保留并进 submit 的 `fields`
59
- - checkbox: `{"type":"checkbox","label":"...","checked":true?,"action":"toggle"?}`
59
+ - checkbox: `{"type":"checkbox","label":"...","checked":true?,"action":"toggle"?,"group":"组名"?}` — 默认保持逐次 `action` 行为;**加 `group` 进入多选聚合模式**:同组 checkbox 可反复勾选/取消,变化只在本地记录、不发逐次 action,兄弟 `submit` 一次性把该组已选 label 作为字符串数组放进 `answers`(例如 `{"styles":["极简","线稿"]}`)
60
60
  - slider: `{"type":"slider","label":"...","min":0,"max":100,"step":1,"value":n?,"action":"name"?,"id":"field-id"?}` — 数值表单滑块:实时显示数值;带 `id` 跨刷新保留并进 submit 的 `fields`(拖拽经防抖合并成一次 action)
61
61
  - radio: `{"type":"radio","label":"...","options":["...","..."],"selected":n?,"action":"pick"?}` — 单选;**加 `"group":"题目名"` 进入聚合模式**:选择只本地记录、不发往返;**加 `"answer":正确下标或标签` + `"explanation":"解析"` 后,交卷在本地判卷**
62
62
  - link: `{"type":"link","label":"...","href":"https://..."?}` — 仅 http(s)/mailto 协议被接受;无 `href` 时渲染为纯文本样式(不会假装可点)
63
- - submit: `{"type":"submit","label":"交卷","action":"grade","groups":["q1","q2","q3"],"resetAction":"redo"?}` — 交卷按钮:**题目带 answer 时点击本地立即判卷**(得分 + 每题 ✓/✗ + 解析,零往返),并锁定题目,点「重新作答」本地重置(`resetAction` 可选通知你);**只有题目都没带 answer 时才**汇总成一次 `[genui-action]`(payload: `{answers:{q1:选项A,...},fields:{id:值},total,answered}`,`fields` 收集所有带 `id` 的输入);`groups` 列出的题全部答完才可点
63
+ - submit: `{"type":"submit","label":"交卷","action":"grade","groups":["q1","styles"],"resetAction":"redo"?}` — 聚合按钮:纯 radio 且题目带 `answer` 时仍本地立即判卷(得分 + 每题 ✓/✗ + 解析,零往返);其余聚合场景一次发送 `[genui-action]`,payload 为 `{answers:{q1:选项A,styles:[选项1,选项2]},fields:{id:值},total,answered}`。`groups` 中每个 radio 必须已选择、每个 checkbox 组必须至少勾选一项才可提交
64
64
  - switch: `{"type":"switch","label":"...","checked":true?,"action":"toggle"?}`
65
65
  - textarea: `{"type":"textarea","label":"...","placeholder":"...","rows":n?,"value":"...","action":"save"?,"id":"field-id"?}` — action 在失焦和 **Ctrl/Cmd+Enter** 时触发;blur 仅值有变化才发送;带 `id` 的值刷新后保留
66
66
  - tabs: `{"type":"tabs","tabs":[{"label":"...","items":[...]}]}`
67
67
  - accordion: `{"type":"accordion","items":[{"title":"...","items":[...]}]}`
68
68
  - copy: `{"type":"copy","label":"复制","text":"..."}`
69
69
 
70
- **状态持久化(v2.7)**:答案、交卷锁定、输入值按「会话 + 内容指纹」自动保存——用户刷新页面/重开会话,同一块 UI 的状态原样恢复;你重渲染**相同内容**会保留用户状态,渲染**新内容**(换题等)自动从头开始。
70
+ **状态持久化(v2.7)**:radio 答案、checkbox 分组选择、交卷锁定、输入值按「会话 + 内容指纹」自动保存——用户刷新页面/重开会话,同一块 UI 的状态原样恢复;你重渲染**相同内容**会保留用户状态,渲染**新内容**(换题等)自动从头开始。
71
71
 
72
72
  **卷子模式(多道选择题)**:每题一个 radio(带唯一 `group` + `answer` + `explanation`),最后放一个 submit(`groups` 列出全部题号)——用户全部选完点交卷,**分数和对错当场在 UI 里出现**,不用等你。只有换新题/进阶建议才发 action。不要每题单独发 action(会刷屏)。
73
73
 
@@ -97,7 +97,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
97
97
  | 两个方案 / 选项对比 | `table`、`tabs`、`diff` |
98
98
  | 教学 / 自测 / 判断题 | `quiz` |
99
99
  | 数学函数 / 曲线关系 | `plot`(可带参数滑块、动画) |
100
- | 需要用户操作 / 筛选 / 反馈 | `button`、`input`、`select`、`radio`、`switch`、`tabs` |
100
+ | 需要用户操作 / 筛选 / 反馈 | `button`、`input`、`select`、`checkbox`、`radio`、`switch`、`tabs` |
101
101
  | 3D 物体 / 空间布局 | `scene3d` |
102
102
 
103
103
  **别用的情况**:一句话能说清的事、纯闲聊、用户明确说不要 UI、以及"为了炫技硬塞"——组件服务内容,不是内容服务组件。