@changfenhuang/dsh-genui 0.11.1-preview.1 → 0.11.1-preview.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 +55 -0
- package/README.md +12 -6
- package/README.zh-CN.md +12 -6
- package/SKILL.md +26 -22
- package/lib/assets/echarts-full.js +26 -18
- package/lib/client.js +28 -22
- package/lib/index.js +1257 -88
- package/lib/types/client/TemplateDrawer.d.ts +2 -2
- package/lib/types/client/achievement-toast.d.ts +1 -1
- package/lib/types/client/achievements.d.ts +36 -21
- package/lib/types/client/asset-echarts.d.ts +1 -0
- package/lib/types/client/blocks/svg.d.ts +4 -0
- package/lib/types/client/dom-fence.d.ts +16 -0
- package/lib/types/client/fence-render.d.ts +46 -1
- package/lib/types/client/genui-runtime/schema.d.ts +5 -1
- package/lib/types/client/guard.d.ts +24 -0
- package/lib/types/client/i18n/en.d.ts +301 -0
- package/lib/types/client/i18n/index.d.ts +44 -0
- package/lib/types/client/i18n/runtime.d.ts +64 -0
- package/lib/types/client/i18n/zh.d.ts +8 -0
- package/lib/types/client/panel-command.d.ts +11 -6
- package/lib/types/client/primitive-labels.d.ts +15 -8
- package/lib/types/client/session-resolver.d.ts +8 -0
- package/lib/types/client/source-fence.d.ts +29 -0
- package/lib/types/client/spec.d.ts +41 -16
- package/lib/types/client/svg-fence.d.ts +2 -0
- package/lib/types/client/templates.d.ts +29 -10
- package/lib/types/plugin/fence-feedback.d.ts +109 -0
- package/lib/types/plugin/genui-diagnostic.d.ts +13 -0
- package/lib/types/plugin/index.d.ts +14 -2
- package/lib/types/shared/fence-resolve.d.ts +39 -0
- package/package.json +24 -20
- package/src/client/EChartNode.tsx +13 -0
- package/src/client/ErrorBoundary.tsx +2 -1
- package/src/client/GenuiBlock.module.css +4 -0
- package/src/client/GenuiBlock.tsx +21 -9
- package/src/client/TemplateDrawer.tsx +30 -20
- package/src/client/achievement-toast.tsx +9 -6
- package/src/client/achievements.ts +96 -47
- package/src/client/asset-echarts.ts +1 -0
- package/src/client/blocks/advanced.tsx +20 -12
- package/src/client/blocks/basic.tsx +11 -7
- package/src/client/blocks/charts.tsx +32 -19
- package/src/client/blocks/forms.tsx +10 -7
- package/src/client/blocks/image.tsx +4 -2
- package/src/client/blocks/render-node.tsx +5 -2
- package/src/client/blocks/svg.tsx +66 -0
- package/src/client/dom-fence.tsx +400 -62
- package/src/client/fence-render.tsx +60 -52
- package/src/client/gallery.ts +6 -0
- package/src/client/genui-runtime/diagnostics.ts +4 -2
- package/src/client/genui-runtime/normalize.ts +198 -3
- package/src/client/genui-runtime/schema.ts +46 -19
- package/src/client/guard.ts +183 -12
- package/src/client/i18n/en.ts +327 -0
- package/src/client/i18n/index.ts +125 -0
- package/src/client/i18n/runtime.ts +130 -0
- package/src/client/i18n/zh.ts +322 -0
- package/src/client/index.tsx +10 -2
- package/src/client/inline.ts +12 -3
- package/src/client/panel-command.ts +42 -33
- package/src/client/panel.tsx +20 -17
- package/src/client/parse-partial.ts +15 -1
- package/src/client/primitive-labels.ts +48 -30
- package/src/client/session-resolver.ts +33 -0
- package/src/client/source-fence.ts +70 -0
- package/src/client/spec.ts +68 -17
- package/src/client/svg-fence.tsx +39 -0
- package/src/client/templates.ts +293 -251
- package/src/plugin/fence-feedback.ts +320 -0
- package/src/plugin/genui-diagnostic.ts +112 -0
- package/src/plugin/index.ts +29 -12
- package/src/plugin/tool.ts +67 -33
- package/src/shared/fence-repair.ts +111 -0
- package/src/shared/fence-resolve.ts +80 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.11.1-preview.3] - 2026-09-23
|
|
6
|
+
|
|
7
|
+
### 兼容性
|
|
8
|
+
|
|
9
|
+
- 增加 DSH `^0.1.7-alpha.1` 支持;真实宿主验收覆盖 `dsh-v0.1.7-alpha.1` 与 `dsh-v0.1.7-alpha.2`。
|
|
10
|
+
- 保留 DSH `0.1.2-rc.1` 最低兼容基线检查。
|
|
11
|
+
- 增加新版 DSH API 编译检查,确保宿主公开类型变化能够在发布前被发现。
|
|
12
|
+
- DSH `0.1.7-alpha.2` 最终 DOM 不再暴露 `dsh-ui` fence language;改从公开 ChatSnapshot 原始 Markdown 恢复 source language,流式阶段即可识别,settle 后保持 GenUI,不回退为原始 JSON(#204)。
|
|
13
|
+
- 适配新版 `CommandClaim.name` 与 ui-primitives 箭头图标导出变化。
|
|
14
|
+
- 适配 `0.1.7-alpha.2` 的 `DiffBlockLabels` 工具栏文案。
|
|
15
|
+
|
|
16
|
+
### 新增
|
|
17
|
+
- **ECharts 词云**:`echart` 节点新增 `preset: "wordCloud"`,`data:[{label,value}]` 的 value 即权重,颜色按序循环 `palette`(缺省跟随主题调色板);`option` 模式同步注册 `echarts-wordcloud` 扩展,`series[].type: "wordCloud"` 直接可用。完整版引擎新增约 30 KB(#183)。
|
|
18
|
+
- **SVG 图形组件**:`{"type":"svg","code":"<svg …/>"}`(`title`/`height` 可选)以隔离图片模式渲染模型输出的独立 SVG——脚本注入、宿主样式污染在结构上被排除;解析失败或加载失败时保留源码并显示提示(#183)。
|
|
19
|
+
- **裸 `svg` 围栏自动预览**:模型直接输出 ` ```svg ` 代码围栏(不带 `dsh-ui` 包装)时,落定后自动显示为图形预览,带「预览/源码」切换,源码可复制;流式生成中保持原样,不闪错误;仅接管语言标签明确为 `svg` 的围栏,其他语言代码块不受影响。registry 与 DOM 两条渲染通道均已接入(#183)。
|
|
20
|
+
|
|
21
|
+
### 修复
|
|
22
|
+
- **堆叠柱状图使用分类总值计算尺度**:纵向堆叠柱的 Y 轴与横向堆叠柱的宽度统一按照最大分类合计值缩放,避免柱体越出绘图区并恢复分类间正确的总长度比例(#206)。
|
|
23
|
+
- **dsh-ui 最终围栏反馈默认开启**:回合结束时直接检查 assistant message 的最终围栏正文,并与浏览器 renderer 共用 JSON 修复、组件归一化和坏节点清理流程;无法渲染时在同一回合请求模型修正,`fenceFeedback: false` 可关闭(#200)。
|
|
24
|
+
- **Tetris 形 table columns 不再让围栏退化成代码块**:模型把表头数组提前闭合、又把行矩阵写成 `columns` 的**兄弟数组元素**(`"columns":["a","b"],["rows":[[…]]]` 或 `"columns":["a","b"],[["1","2"]]`)。这类正文两侧括号是**配平**的,所以 tier-2 的补括号扫描救不回来;现在 tier-2 先做一次形状重写(把该兄弟数组收编为 `"rows":[…]`、并丢弃错位留下的多余闭合符),整串 parse 通过才采纳。四个真实会话 130 条围栏的未渲染数从 1 归零(#192)。
|
|
25
|
+
- **兼容新版 DSH 会话快照**:DOM 通道在宿主移除 `sessions.list.current` 后,改用 `byId[*].retainedBy.mainView` 解析当前会话,恢复 action 回传、状态持久化和 `panel:true` 发布,并在无法解析会话时输出一次诊断(#196)。
|
|
26
|
+
- **降低 GenUI 模型反馈导致的回复语言漂移**:强化会话语言规则,将高诱导性的用户可见示例替换为 `<user-language ...>` 占位符,并将 validator / fence repair 的模型反馈改为语言中立的结构化协议(#165)。
|
|
27
|
+
- **离散交互不再被防抖合并**:`button` / `checkbox` / `radio` / `switch` / `select` / `input` / `textarea` / `submit` / `quiz` 等一次手势一次事件的交互**立即逐次回传**。此前 300ms 防抖以 action 名为 key,快速连点同 action 名的控件会静默丢弃前几次事件,模型收到残缺交互状态,与 SKILL.md 承诺的 checkbox「默认保持逐次 action 行为」矛盾(#178)。
|
|
28
|
+
- `slider` 拖拽保留防抖合并,且 key 从「action 名」改为「action 名 + `id`」:同一滑块的连续拖动仍合并成最后一次的值,多个共享 action 名的滑块互不挤占(#178)。
|
|
29
|
+
|
|
30
|
+
### 新增
|
|
31
|
+
- **文字字段支持换行**:JSON 字符串里的真实换行符(`\n`,含 `\r\n`)在 `text.content`、`callout` 正文、`list` 项、`keyvalue` 值等文字类字段里渲染为换行,不再被 HTML 空白规则折叠成空格;纯换行文本此前根本不进解析器(谓词不认 `\n`),现已修复。不引入 HTML 解析,`<br>` 仍字面显示;单行 chrome 类(nowrap)不含换行、不受影响。SKILL.md 行内标记表新增「该用什么」:多段文字写同一个字段用 `"\n"`,不要为换行拆成多个节点(#177)。
|
|
32
|
+
- **界面语言(i18n)**:浏览器侧全部用户可见文案改由 `en` / `zh` 两套词典驱动(`src/client/i18n/`),不再硬编码。语言解析顺序为「宿主语言偏好 → 浏览器 `navigator.languages` → 英文」;英文同时作为**逐键兜底**,因此词典缺键只会回退、绝不会把界面渲染成空白。
|
|
33
|
+
- **跟随 DSH 语言设置**:宿主提供 `@deepseek-ai/dsh-client-locale` 时,插件把两套词典注册到 `genui` 命名空间,并镜像宿主的当前语言(含后续切换)。该服务以**可选方式**读取、不写进 `inject`,所以不带该服务的宿主照常渲染(自动降级为浏览器探测)。
|
|
34
|
+
- 语言切换在已挂载的界面上**原地生效**:面板、模板中心、成就页与组件 chrome 通过 `useSyncExternalStore` 订阅语言修订号,无需刷新。
|
|
35
|
+
|
|
36
|
+
### 变更
|
|
37
|
+
- `DEFAULT_PANEL_SPEC` → `defaultPanelSpec()`,`GENUI_TEMPLATES` → `genuiTemplates()`:内容含可翻译文案,改为**按调用构建**,避免被模块加载时刻的语言固化。
|
|
38
|
+
- 模板分类改用稳定 id(`dashboard`/`data`/`flow`/`chart`/`interactive`/`quiz`/`advanced`),显示名走词典;成就仅持久化 id,名称与描述在读取时解析——切换语言不会丢失已解锁记录。
|
|
39
|
+
- 围栏诊断(`describeFenceFailure`,两条通道共用)同样走词典,跟随当前语言。
|
|
40
|
+
- 面向**模型**的文案(`[genui-action]` 提示词、注入系统提示的围栏词汇表)刻意不进词典:它属于插件与模型之间的协议,翻译它会改变模型行为而非界面语言。
|
|
41
|
+
|
|
42
|
+
## [0.11.1-preview.2] - 2026-09-16
|
|
43
|
+
|
|
44
|
+
### 新增
|
|
45
|
+
- DOM 通道对被拒绝的围栏给出**可见诊断**:settled 之后仍无法渲染的 dsh-ui 围栏,会在原始代码块上方显示红色诊断条(字段错误或 JSON 解析原因),原始内容保留;此前只有 `console.warn`,围栏的作者看不到失败原因(#158)。
|
|
46
|
+
- 可选的自修闭环 `fenceFeedback`(默认关闭,profile 配置 `fenceFeedback: true` 开启):回复中的 dsh-ui 围栏未能渲染时,通过宿主的 `agent/turn-stopping` 边界把一条插件来源的修正请求 steer 进**同一回合**,让模型重发修正版。边界:每回合最多一次、同一围栏内容最多一次、子代理会话不触发、精确匹配 `dsh-ui` 围栏、发送前记账、取消的回合不触发(#160)。
|
|
47
|
+
|
|
48
|
+
### 修复
|
|
49
|
+
- 根形状判定不再被重载的 `items` 骗过:根对象带白名单 `type` 时优先按**单组件根**处理(`{"type":"steps","items":[…]}` 此前被当成信封,步骤记录逐个被丢弃后整份围栏被拒);包装结果改为纯 spec 以免守卫二次包裹,根上的 `title` 在组件无该字段时上提为区块标题(#172)。
|
|
50
|
+
- `stat` 指标组 `{"type":"stat","items":[{label,value},…]}` 归一化为一行多个 `stat`(规范的多指标写法),不再丢弃该节点并连坐同围栏里合法的兄弟节点(#172)。
|
|
51
|
+
- `radio` / `select` / `quiz` 的 `items` 别名到 `options`,与 #175 的别名表取并集(#172)。
|
|
52
|
+
- 字段名错误不再让整条围栏降级为代码块:`callout.content`(模型常写 text/body)、`keyvalue.pairs`(常写 items/rows/entries)、`diff.diffs`(常写 items)、`image`/`audio`/`video.src`(常写 url)、`code`/`copy`、`quiz.question`/`options`(常写 title/choices)补进别名表,规范化后照常渲染并保留 warning(#163、#172)。
|
|
53
|
+
- `table` 只在给出行数据(`rows`/`data` 二维数组)而未给 `columns` 时,用首行作为表头推导列名,不再丢弃该节点;行列不齐时按 `列1…列N` 渲染,非二维体仍是契约错误(#172)。
|
|
54
|
+
- `list` 的单元格式写法(`[["文本"]]` 会渲染成空列表)与 `{title, description}`(正文丢失),以及 `keyvalue.pairs` 的 `[[key, value]]` 写法,统一在规范化层转成规范形状(#172)。
|
|
55
|
+
- 常驻提示词新增「字段名写错 = 整个围栏降级为代码块」的负例清单(callout 用 content、table 要 columns+rows、keyvalue 要 pairs),并压缩既有条目保持 #29 的 3200 字符预算。
|
|
56
|
+
- `validate_dsh_ui` 的丢弃诊断从「声明 N / 解析 M」升级为逐节点归因:每个被丢弃节点给出位置、类型、**已写字段**与**缺少的必填字段**(`- items[0](callout)缺少必填字段 content;已写字段 title`),原始错误列表保留(#163)。
|
|
57
|
+
|
|
3
58
|
## [0.11.1-preview.1] - 2026-09-15
|
|
4
59
|
|
|
5
60
|
- 更新 preview 预发布验收宿主至 DSH 0.1.6-alpha.1;发布任务验证同一安装包后发布至 npm preview。
|
package/README.md
CHANGED
|
@@ -75,9 +75,12 @@ The plugin ships **two rendering channels** and picks one automatically after th
|
|
|
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
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
|
+
- **DSH 0.1.7 generic code banner**: when the host's final DOM omits fenced-code language metadata, dsh-genui reads the current assistant's original Markdown from the public ChatSnapshot and only takes over `dsh-ui` fences. The DOM identifies the mount location. If source data is temporarily unavailable, a settled assistant CodeBlock can use the strict canonical GenUI validation as a final fallback.
|
|
78
79
|
|
|
79
80
|
Whichever channel is active, components, interactions, panels, and persistence behave identically.
|
|
80
81
|
|
|
82
|
+
CI's packed host smoke installs the generated npm tarball in a real DSH host and verifies host startup and rendering. Source-backed fence routing is covered by integration tests; the smoke does not claim to validate a real model response. Real-model E2E requires configured model credentials.
|
|
83
|
+
|
|
81
84
|
The repository ships both renderer channels, the host plugin, and the built browser bundle. The host still owns **client activation** and must provide the `slots` and `sessions` services. A downloaded `client.js` or a ModuleLoader cache entry proves only that bytes arrived — successful activation always prints `[genui] client active; fence-channel=registry|dom`. If that line is absent, fix package/profile identity or host activation first; DOM attributes such as `data-streaming` and `data-chat-anchor-key` are optional fallbacks, not installation prerequisites.
|
|
82
85
|
|
|
83
86
|
---
|
|
@@ -93,7 +96,7 @@ The repository ships both renderer channels, the host plugin, and the built brow
|
|
|
93
96
|
|
|
94
97
|
Prerequisites — all required:
|
|
95
98
|
|
|
96
|
-
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1
|
|
99
|
+
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1`** (verified host tags: `dsh-v0.1.2-rc.1`, `dsh-v0.1.7-alpha.1`, and `dsh-v0.1.7-alpha.2`; users on DSH `<=0.1.1-rc.x` should use dsh-genui `0.9.8`)
|
|
97
100
|
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
101
|
|
|
99
102
|
Install and activate in DSH (one command, all dependencies included):
|
|
@@ -166,11 +169,12 @@ The following is the detailed capability reference. Every behavior is constraine
|
|
|
166
169
|
- **Secrets ban**: GenUI must never ask for passwords, API keys, access tokens, recovery codes, or other secrets; even if a password input appears, it stays masked, is never persisted, and never enters form collection
|
|
167
170
|
- **Local-first principle**: state changes the UI can do itself (grading, quiz checking, resets, expand/collapse, selection) always happen locally and instantly; actions are reserved for things that genuinely need the model (generating new content, running tools, next-step suggestions)
|
|
168
171
|
- **Honest interactions**: interactive components must carry `action`; buttons without one render disabled (kills the "looks clickable, does nothing" fake button); buttons with `action` show instant "triggered" local feedback (proof the local event fired, not that the model received it)
|
|
169
|
-
- **Event loop**: buttons
|
|
172
|
+
- **Event loop**: buttons, checkboxes, radio buttons, switches, selects, inputs, textareas, submits, and quizzes send one event immediately per gesture; slider drags keep trailing-edge debounce, sending only the final value per slider, with different `id`s handled independently
|
|
170
173
|
- **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
174
|
- **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
|
-
- **
|
|
173
|
-
- **
|
|
175
|
+
- **Fence auto-repair**: enabled by default; set `fenceFeedback: false` in this plugin's config (under the plugin entry's `config:` in your profile's cordis.patch.yml) to disable it. When a reply's final dsh-ui fence fails to render, the plugin steers the SAME turn with the per-node diagnosis so the model can resend a fixed fence; at most one correction per turn and per fence, never in subagents, so it cannot loop.
|
|
176
|
+
- **Self-healing & limits**: every fence passes a spec guard — bad nodes are silently dropped (the surviving siblings keep rendering: one bad component no longer degrades the whole fence), numbers clamped, strings truncated; the whole tree is capped at 200 nodes / 8 nesting levels; pathological specs never crash the UI
|
|
177
|
+
- **Canonical component protocol**: native field aliases such as `card.label` → `title`, `table.data`/`table.items` → `rows`, `callout.kind`/`callout.desc` → `tone`/`content` (tone value `danger` → `error`), `steps.items` → `steps`, `keyvalue.items` → `pairs` (record `label` → `key`), and `file-tree.nodes` → `items` (record `label` → `name`, `type` defaults to `dir` when children exist) are normalized deterministically before validation and rendering. A root-level component array is adopted as `items`, and a double-encoded JSON string is decoded once. `validate_dsh_ui` reports these normalizations and warns about unknown native fields without blocking custom renderer nodes.
|
|
174
178
|
- **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
|
|
175
179
|
- **Accessibility**: tabs/accordions/switches/progress bars carry full ARIA and keyboard navigation (arrow keys switch tabs, Home/End jump)
|
|
176
180
|
- **Zero intrusion**: without the plugin, fences are just code blocks — no errors, no session pollution
|
|
@@ -215,7 +219,7 @@ The core render package stays light (≈110 KB min / 28 KB gzip); the mermaid, t
|
|
|
215
219
|
## ❓ FAQ
|
|
216
220
|
|
|
217
221
|
- **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.
|
|
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 || ^0.1.6-alpha.1`; users on DSH `<=0.1.1-rc.x` should use dsh-genui `0.9.8`.
|
|
222
|
+
- **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 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1`; users on DSH `<=0.1.1-rc.x` should use dsh-genui `0.9.8`.
|
|
219
223
|
- **`dsh: pnpm not found on PATH`?** Install pnpm, then **open a new terminal** and retry (`corepack enable` or `npm i -g pnpm`).
|
|
220
224
|
- **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.
|
|
221
225
|
- **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.
|
|
@@ -231,6 +235,8 @@ pnpm run check # type check + full tests + build
|
|
|
231
235
|
|
|
232
236
|
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
237
|
|
|
238
|
+
`pnpm run check:host-api dsh-v0.1.7-alpha.1` and `pnpm run check:host-api dsh-v0.1.7-alpha.2` install the corresponding published DSH packages in an isolated workspace and run TypeScript typecheck plus tsdown build. CI runs both checks before packed host smoke tests.
|
|
239
|
+
|
|
234
240
|
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
241
|
|
|
236
242
|
### Real-device e2e
|
|
@@ -261,7 +267,7 @@ Overridable: `--port 3098`, `--out <dir>`, `DSH_BIN` (set it to the `apps/cli/li
|
|
|
261
267
|
| Direction | Verdict | Rationale |
|
|
262
268
|
|---|---|---|
|
|
263
269
|
| Incremental patching (model sends diffs, not full specs) | Not doing | A fence costs 200–800 tokens; resending is nearly free; a patch protocol's teaching cost and error rate aren't worth it. Revisit if sub-second auto-refreshing panels ever appear |
|
|
264
|
-
| Action
|
|
270
|
+
| Action delivery | ✅ Discrete interactions send immediately; slider drags debounce by action and `id` | Discrete gestures remain individually observable while slider drags send their final value |
|
|
265
271
|
| 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 |
|
|
266
272
|
| 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 |
|
|
267
273
|
|
package/README.zh-CN.md
CHANGED
|
@@ -75,9 +75,12 @@ https://github.com/user-attachments/assets/f5db33ec-7471-4d4a-a85b-79c9962ab4ef
|
|
|
75
75
|
|
|
76
76
|
- **Registry 通道**:宿主提供 `fence-registry` 扩展点(新版 dsh 构建)时,围栏经宿主流式渲染管线注册,行为与宿主无缝;
|
|
77
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
|
+
- **DSH 0.1.7 通用代码块**:宿主最终 DOM 没有提供围栏 language metadata 时,dsh-genui 会从公开 ChatSnapshot 读取当前 assistant 的原始 Markdown,并且只接管 `dsh-ui` 围栏。DOM 负责确定挂载位置。source 数据暂时不可用时,assistant 已结束的通用 CodeBlock 才能通过严格的 canonical GenUI 规范校验进入最终兜底。
|
|
78
79
|
|
|
79
80
|
无论走哪条通道,组件、交互、面板、持久化行为完全一致。
|
|
80
81
|
|
|
82
|
+
CI 的 packed host smoke 会把实际生成的 npm tarball 安装到真实 DSH 宿主,并验证宿主启动与界面渲染。source-backed 围栏识别由 integration tests 覆盖;该 smoke 不代表真实模型回复验收。真实模型 E2E 需要配置模型凭据。
|
|
83
|
+
|
|
81
84
|
本仓库已经包含两条渲染通道、服务端插件和浏览器构建产物;宿主仍负责**激活客户端模块**,并提供 `slots` 与 `sessions` 服务。`client.js` 返回 200 或出现在 ModuleLoader 缓存里,只能证明文件下载成功;真正激活后一定会打印 `[genui] client active; fence-channel=registry|dom`。没有这行时应先核对包名/网页配置/宿主激活链,`data-streaming`、`data-chat-anchor-key` 等页面属性只是可选信息,不是安装前提。
|
|
82
85
|
|
|
83
86
|
---
|
|
@@ -93,7 +96,7 @@ https://github.com/user-attachments/assets/f5db33ec-7471-4d4a-a85b-79c9962ab4ef
|
|
|
93
96
|
|
|
94
97
|
前置条件,缺一不可:
|
|
95
98
|
|
|
96
|
-
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1
|
|
99
|
+
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1`**(验收宿主标签为 `dsh-v0.1.2-rc.1`、`dsh-v0.1.7-alpha.1` 与 `dsh-v0.1.7-alpha.2`;使用 DSH `<=0.1.1-rc.x` 的用户请使用 dsh-genui `0.9.8`)
|
|
97
100
|
2. **`pnpm` 在 PATH 上**:`dsh plugin` 命令依赖它。没有就 `corepack enable`(或 `npm i -g pnpm`),然后**新开一个终端**,确认 `pnpm -v` 有输出
|
|
98
101
|
|
|
99
102
|
安装并在 DSH 中激活(一行命令,自动带上全部依赖):
|
|
@@ -166,11 +169,12 @@ dsh plugin --profile web add link:$PWD
|
|
|
166
169
|
- **秘密禁令**:GenUI 不得索取密码、API Key、访问令牌、恢复码或其他秘密;密码输入即使出现也保持打码、不持久化、不进表单收集
|
|
167
170
|
- **本地优先原则**:UI 自己能完成的状态变化(判卷、判题、重置、展开、选中)一律本地即时完成;action 只用于必须模型参与的事(生成新内容、执行工具、下一步建议)
|
|
168
171
|
- **诚实交互**:交互组件必须带 `action`;不带 `action` 的按钮渲染为禁用态(消灭"看着能点、点了没反应"的假按钮);带 `action` 的按钮点击后立即显示「已触发」本地反馈(只证明本地事件已触发,不代表模型已收到)
|
|
169
|
-
-
|
|
172
|
+
- **事件循环**:按钮、复选框、单选、开关、下拉、输入、文本域、提交和测验等离散交互按一次手势一次事件立即回传模型;`slider` 连续拖动保留尾沿防抖,同一滑块只回传最终值,不同 `id` 的滑块互不合并。
|
|
170
173
|
- **工具通道**:`render_ui` 工具把同一份 spec 渲染成工具行卡片(交付物型 UI 走工具、回答型 UI 走围栏)
|
|
171
174
|
- **会话面板**:composer 上方常驻 dock,`render_ui` / `panel: true` 围栏原地更新同一块界面;`/panel` 命令客户端直开(`/panel <指令>` 转模型定制、`/panel clear` 清空);顶边框可拖拽调高;`append: true` 增量合并——同名标签页追加内容、新标签页新增;整面板默认最多 200 节点 / 200 条追加,达到上限后模型应发送 `replace` 重建
|
|
172
|
-
-
|
|
173
|
-
-
|
|
175
|
+
- **围栏自修**:默认开启;在 profile 的 cordis.patch.yml 中本插件条目的 `config:` 下设置 `fenceFeedback: false` 可以关闭。回答最终的 dsh-ui 围栏无法渲染时,插件借宿主的轮内转向(steer)把逐节点诊断送回**同一轮**,模型重发修好的围栏;每轮至多一次、每个围栏至多一次、子代理不触发,不会循环。
|
|
176
|
+
- **自愈与上限**:每个围栏过规格守卫——坏节点静默丢弃(同围栏其余组件照常渲染,单个坏组件不再拖垮整条围栏)、数值钳位、字符串截断,整树 ≤200 节点 / 8 层嵌套,病态 spec 不会拖垮界面
|
|
177
|
+
- **统一组件协议**:`card.label` → `title`、`table.data`/`table.items` → `rows`、`callout.kind`/`callout.desc` → `tone`/`content`(tone 值 `danger` → `error`)、`steps.items` → `steps`、`keyvalue.items` → `pairs`(记录内 `label` → `key`)、`file-tree.nodes` → `items`(记录内 `label` → `name`、有 children 时缺省 `dir`)等原生字段别名会在校验和渲染前确定性归一化;根级组件数组视为 `items`、双重编码的 JSON 字符串解一层;`validate_dsh_ui` 会报告归一化结果,并对原生组件未知字段给出警告,同时保持自定义 renderer 节点的透明兼容。
|
|
174
178
|
- **图错误自愈**:mermaid 渲染失败自动修复重试(剥反引号、引号化中文/空格标签、去 `<br/>`),仍失败才降级源码;错误图永不直接上屏
|
|
175
179
|
- **可访问性**:tabs/折叠/开关/进度条带完整 ARIA 与键盘导航(方向键切页、Home/End 跳转)
|
|
176
180
|
- **零打扰**:不装插件时围栏只是代码块,不报错、不污染会话
|
|
@@ -221,7 +225,7 @@ dsh plugin --profile web add link:$PWD
|
|
|
221
225
|
## ❓ 常见问题
|
|
222
226
|
|
|
223
227
|
- **显示成代码块?** 先在浏览器控制台找 `[genui] client active; fence-channel=registry|dom`。没有这行,即使 `client.js` 返回 200,也只是下载了文件、没有激活:请对齐网页配置依赖名、`package.json.name`、`cordis.patch.yml`、ModuleLoader id 和配置中的 bundle 名。出现这行后再查围栏标签/正文;宿主没有 registry 时会自动走 DOM 通道。
|
|
224
|
-
- **渲染 dsh-ui fence 时聊天界面白屏?** 此版 dsh-genui 要求 DSH `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1`;使用 DSH `<=0.1.1-rc.x` 的用户请使用 dsh-genui `0.9.8`。
|
|
228
|
+
- **渲染 dsh-ui fence 时聊天界面白屏?** 此版 dsh-genui 要求 DSH `^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1`;使用 DSH `<=0.1.1-rc.x` 的用户请使用 dsh-genui `0.9.8`。
|
|
225
229
|
- **`dsh: pnpm not found on PATH`?** 装 pnpm 后**新开终端**再试(`corepack enable` 或 `npm i -g pnpm`)。
|
|
226
230
|
- **npm 安装返回 404?** npm 包是公开的,无需登录。先执行 `npm view @changfenhuang/dsh-genui version` 核对包名与公共 registry;若新版本刚发布仍返回 404,稍后重试。
|
|
227
231
|
- **装了但 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 即可。
|
|
@@ -245,6 +249,8 @@ pnpm run check # 类型检查 + 全量测试 + 构建
|
|
|
245
249
|
|
|
246
250
|
安装锁定依赖后,检查脚本(`pnpm run check` 或 `npm run check`)使用固定的 DSH `0.1.2-rc.1` 发布包。
|
|
247
251
|
|
|
252
|
+
`pnpm run check:host-api dsh-v0.1.7-alpha.1` 与 `pnpm run check:host-api dsh-v0.1.7-alpha.2` 会在隔离目录安装对应的 DSH npm 发布包,运行 TypeScript 类型检查与 tsdown 构建。CI 对两个版本执行检查,然后运行安装包宿主 smoke 测试。
|
|
253
|
+
|
|
248
254
|
运行 `node scripts/verify-pack.mjs --keep` 可保留已验收的 tarball,便于检查或运行 e2e;默认的 `node scripts/verify-pack.mjs` 会在验收后清理临时目录。
|
|
249
255
|
|
|
250
256
|
### 真机 e2e
|
|
@@ -275,7 +281,7 @@ npx tsx scripts/e2e-visual.mts --keep # 保留 scratch DSH_HOME 便于排查
|
|
|
275
281
|
| 方向 | 结论 | 理由 |
|
|
276
282
|
|---|---|---|
|
|
277
283
|
| 增量 patch(模型只发 diff 不重发全量 spec) | 不做 | fence 一次 200–800 token,重发代价极小;patch 协议的教学成本与出错率不值得。若未来出现秒级自动刷新面板再议 |
|
|
278
|
-
| action
|
|
284
|
+
| action 回传 | ✅ 离散交互立即回传;slider 拖动按 action 和 `id` 防抖 | 离散操作逐次回传,slider 拖动只回传最终值 |
|
|
279
285
|
| 跨会话状态持久化(回放恢复 tabs/开关) | 不做 | 回放重置是更正确的默认行为(模型已用新 fence 更新过界面);流式期间状态天然保留 |
|
|
280
286
|
| MCP 适配器 / 独立画廊页 / i18n | 不做 | 无跨工具需求信号;画廊素材已被 `gallery.ts` + demo-prompts + README 截图覆盖;内置文案仅 6 处 |
|
|
281
287
|
|
package/SKILL.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: genui
|
|
3
|
-
description: "Render structured interactive UI inline
|
|
3
|
+
description: "Render structured interactive UI inline through the dsh-ui fence. Use for key points, emphasis, comparisons, flows, steps, status, data, demos, and interactions whenever structured presentation would be clearer than prose. Preserve conversation language for all user-visible text."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# GenUI — 生成式 UI 输出规范
|
|
7
7
|
|
|
8
|
-
**Language:** Match the user's requested language (otherwise the language of their message) in both surrounding prose and all user-visible UI text: titles, labels, content, options, and explanations. Chinese examples below illustrate the schema only; do not switch the conversation to Chinese after loading this skill. Keep JSON keys, component types, IDs, and actions unchanged. An English request gets English prose and UI text; a Chinese request gets Chinese prose and UI text.
|
|
8
|
+
**Language:** Match the user's requested language (otherwise the language of their message) in both surrounding prose and all user-visible UI text: titles, labels, content, options, and explanations. Chinese examples below illustrate the schema only; do not switch the conversation to Chinese after loading this skill. Keep JSON keys, component types, IDs, and actions unchanged. An English request gets English prose and UI text; a Chinese request gets Chinese prose and UI text. `<user-language ...>` tokens are meta-placeholders only. Replace every one with actual content in the conversation language; never emit these placeholders literally.
|
|
9
9
|
|
|
10
10
|
你可以**在回答正文中间**输出可交互 UI 组件:写一个 `dsh-ui` 围栏(fenced block with language tag `dsh-ui`),内含 JSON 规格,渲染器会把这一整块画成真实组件,文字照常穿插在前后。组件**就是回答的一部分**,不是工具调用。
|
|
11
11
|
|
|
12
12
|
```dsh-ui
|
|
13
|
-
{"title":"
|
|
13
|
+
{"title":"<user-language text>","gap":14,"items":[...]}
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
公式:`$...$` / `\(...\)` 为行内公式,`$$...$$` / `\[...\]` 为独立公式;支持矩阵、分段函数、多行对齐推导,以及加粗/高亮内的公式。正文、列表、键值、表头/单元格、卡片/步骤/时间线/标签页标题、题目与说明、按钮与表单标签、指标、媒体说明和图表外层标题共用此能力。JSON 字符串中的反斜杠必须双写,如 `"content": "\\(\\frac{a}{b}\\)"`。代码源码、输入值/占位符、原生下拉选项和图表引擎内部绘图文本仍遵循各自的原生格式,不解析富文本。数学语法以 KaTeX 为准,不执行 HTML、外部资源或脚本;不支持 LaTeX 文档编译、加载任意宏包。
|
|
@@ -26,7 +26,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
26
26
|
- text: `{"type":"text","size":"h1|h2|h3|body|muted|caption","content":"...","center":true?}`
|
|
27
27
|
- row / col: `{"type":"row"|"col","items":[...],"wrap":true?,"spacer":true?,"gap":n?}`
|
|
28
28
|
- grid: `{"type":"grid","cols":n,"items":[...]}`
|
|
29
|
-
- hero: `{"type":"hero","title":"
|
|
29
|
+
- hero: `{"type":"hero","title":"<user-language title>","subtitle":"<user-language text>","value":"99.96%","label":"<user-language metric>","delta":"+0.02%","spark":[...],"tone":"accent|success|warning|danger"}` — **封面块**:eyebrow + 超大数字(52px,带入场计数)+ 标题 + 副标题 + tone 渐变底色。**一条回答最多用一个**,放在最前面当视觉锚点
|
|
30
30
|
- span: 任意节点都可加 `"span":2`(grid 子节点占几列)——bento 排版的唯一原语:一张 `span:2` 宽卡配一张窄卡,比一列方块堆下去好看得多
|
|
31
31
|
- card: `{"type":"card","title":"...","items":[...]}`;`"accent":"#f59e0b"` 指定强调色(边框 + 标题 + 极淡底色)
|
|
32
32
|
- palette: `chart` / `echart` 都支持 `"palette":["#ff8800","#3ecf8e"]` 覆盖分类色板(默认跟随宿主主题)。**只有语义上需要指定颜色时才写**(成本=红、收益=绿),否则跟随主题更稳;`"tone":"info|success|warning|danger"` 给卡片底色(用于结论卡/风险卡)
|
|
@@ -37,15 +37,15 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
37
37
|
- badge: `{"type":"badge","label":"...","tone":"success|warn|danger|accent","icon":"emoji?"}`
|
|
38
38
|
- progress: `{"type":"progress","label":"...","value":0-100,"valueLabel":"70%"}`;`"variant":"ring"` 画环形进度,`"target":70` 在轨道上标出目标刻度
|
|
39
39
|
- avatar: `{"type":"avatar","name":"...","color":"#hex?"}`
|
|
40
|
-
- image: `{"type":"image","src":"/mmx-files/result.png","alt":"
|
|
41
|
-
- audio: `{"type":"audio","src":"/mmx-files/result.mp3","alt":"
|
|
42
|
-
- video: `{"type":"video","src":"/mmx-files/result.mp4","alt":"
|
|
40
|
+
- image: `{"type":"image","src":"/mmx-files/result.png","alt":"<user-language description>"}` — 展示浏览器可访问的 http(s) 或同源相对图片地址;懒加载;不支持 `file:`/`data:` 等本地或主动协议
|
|
41
|
+
- audio: `{"type":"audio","src":"/mmx-files/result.mp3","alt":"<user-language description>","loop":true?}` — 原生控制条;用户主动播放,不自动播放;仅 http(s) 或同源相对地址
|
|
42
|
+
- video: `{"type":"video","src":"/mmx-files/result.mp4","alt":"<user-language description>","poster":"/mmx-files/poster.jpg"?,"loop":true?,"muted":true?,"aspectRatio":"16:9|4:3|1:1|9:16"?}` — 原生播放/音量/全屏控制;不自动播放
|
|
43
43
|
- list: `{"type":"list","items":["..."] 或 [{"title":"...","desc":"..."}] 或嵌套节点(如 {"type":"badge","label":"TS"})}` — 行内可嵌节点(计入节点预算)
|
|
44
44
|
- table: `{"type":"table","columns":["..."],"rows":[["...","..."]],"types":["text|num|delta|bar|badge"]?,"details":[[...]]?,"total":true?}` — 表头点击本地排序(升/降/还原,零往返);数值感知:千分位(`1,234`)、`k/m/b`、`万/亿`、`%`、货币符号都能按真实数值比较,纯数值列自动右对齐;**带符号单元格自动着色**(`+12.4%` 绿、`-3` 红,无需额外字段);`types` 可按列指定 `bar`(0-100 内联进度条)、`ring`(0-100 小环)、`spark`(单元格写 `"3,5,4,8"` 画微趋势线)、`badge`(胶囊标签)、`delta`(强制涨跌色)、`num`(强制右对齐)、`index`(行号)、`group`(首列当分组标题:该行只有第一格有内容时渲染成跨列小标题);`"total":true` 追加合计行(数值列自动求和);**`"export":true`**:表格上方出现「复制 Markdown / 复制 CSV」两个小按钮(纯本地剪贴板,不发请求);**`"filter":"输入框id"`**:把表格和某个 input/select 绑定,读者输入即时过滤(`filterColumn` 可限定列)——数据多时**默认就该配一个**;**`"sortField":"下拉id"`** 用下拉的值(列名)排序;**`"details"` 与 rows 同序**,第 i 项是该行展开后的内容(可放任意组件,`null` = 该行不可展开)——首列出现 chevron,点开在整行下方展开明细,适合「主表 + 明细」
|
|
45
45
|
- keyvalue: `{"type":"keyvalue","pairs":[{"key":"...","value":"..."}]}`
|
|
46
46
|
- timeline: `{"type":"timeline","items":[{"title":"...","desc":"...","time":"..."}]}`
|
|
47
47
|
- file-tree: `{"type":"file-tree","items":[{"name":"...","type":"file|dir","children":[...]?}]}` — 目录行可点击折叠/展开(本地,零往返)
|
|
48
|
-
- breadcrumb: `{"type":"breadcrumb","items":["
|
|
48
|
+
- breadcrumb: `{"type":"breadcrumb","items":["<user-language text>","<user-language text>","<user-language text>"]}`
|
|
49
49
|
- diff: `{"type":"diff","diffs":[{"path":"...","oldText":"..."|null,"newText":"..."}]}`
|
|
50
50
|
- json: `{"type":"json","value":...}`(JSON 树查看器)
|
|
51
51
|
- code: `{"type":"code","lang":"ts","code":"..."}`
|
|
@@ -56,8 +56,8 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
56
56
|
- chart: `{"type":"chart","kind":"bars|line|donut","data":[{"label":"...","value":n,"color":"#hex?"}],"series":[{"label":"...","data":[...]}]?,"horizontal":true?}` — bars 默认;line 趋势;donut 占比;**series:bars 是分组柱,line 是多序列折线**;**`horizontal:true` 画横向柱**(排行/长标签首选);**`stacked:true` 把 series 堆叠**(构成/占比随时间);堆叠段够高时数值直接印在段内,鼠标悬停任意柱/段/点/扇区都会弹出即时 tooltip(堆叠显示该段数值 + 合计)。v3 渲染:宽度自适应、Y 轴 1/2/5 刻度、单序列负值在零线以下真实绘制、line 带面积渐变与抽稀 X 标签、donut 图例显示数值与百分比。**≤8 个点的快速对比用 chart;多序列、需要缩放/交互或数据量大时用 echart**
|
|
57
57
|
- plot: `{"type":"plot","series":[{"expr":"a*sin(b*x)","label":"...","color":"#hex?","params":[{"name":"a","value":1,"min":0,"max":5,"animateTo":3,"durationMs":4000,"loop":true},{"name":"b","value":1,"min":0.5,"max":5}]}],"xMin":-6.28,"xMax":6.28,"title":"..."}` — SVG 函数图;**series 可带 `"kind":"line|area|scatter"`**(缺省 line;area 填色到基线;scatter 散点);**params 渲染成实时滑块**(拖动即时重绘,**y 轴锁定**=只变曲线不变数轴);**animateTo 参数会显示播放按钮**(自动动画演示);SVG 可拖拽平移、滚轮缩放;表达式支持 sin/cos/tan/asin/acos/atan/sqrt/cbrt/exp/log/ln/abs/floor/ceil/round/min/max/pow,常量 pi/e/tau,变量 x(其他字母=参数)
|
|
58
58
|
- echart: `{"type":"echart","title":"...","height":300,"preset":"bar|line|area|pie|scatter","data":[{"label":"...","value":n}],"series":[...]?}` — **ECharts 全功能图表**,视觉效果远超 `chart`(渐变、tooltip、动画、图例交互);**preset 模式**:用和 `chart` 一样的 `data`/`series` 格式,自动构建主题化的 ECharts 配置(颜色跟随宿主主题);**preset 一览**(只写 preset + data/series/links,主题自动跟随):
|
|
59
|
-
`bar` · `line` · `area` · `pie` · `scatter` · **`radar`**(每 series 一个多边形,指标取第一条 series 的 label)· **`gauge`**(每个 datum 一个仪表,适合单 KPI)· **`funnel`**(漏斗/转化)· **`treemap`**(体积/层级占比)· **`sankey`**(流向,用 `links:[{from,to,value}]`)· **`graph`**(关系图,`links` 驱动,节点大小随连接数)· **`heatmap`**(`series` 当行、第一条 series 的 label 当列)· **`bigline`**(长序列 +
|
|
60
|
-
**full option 模式**:传 `"option":{...}` 直接写 ECharts 原生配置(支持
|
|
59
|
+
`bar` · `line` · `area` · `pie` · `scatter` · **`radar`**(每 series 一个多边形,指标取第一条 series 的 label)· **`gauge`**(每个 datum 一个仪表,适合单 KPI)· **`funnel`**(漏斗/转化)· **`treemap`**(体积/层级占比)· **`sankey`**(流向,用 `links:[{from,to,value}]`)· **`graph`**(关系图,`links` 驱动,节点大小随连接数)· **`heatmap`**(`series` 当行、第一条 series 的 label 当列)· **`bigline`**(长序列 + 内置缩放)· **`wordCloud`**(词云,`data:[{label,value}]` 的 value 表示权重;颜色跟随 `palette` 或主题调色板)
|
|
60
|
+
**full option 模式**:传 `"option":{...}` 直接写 ECharts 原生配置(支持 ECharts 内置图表及已注册的 `wordCloud` 词云扩展;其他第三方扩展不保证可用),option 中的函数会被过滤(只接受数据)。选择原则:**chart 轻量(无额外下载)适合 ≤8 点的快速对比;echart 视觉更丰富(渐变、tooltip、图例交互、dataZoom),但会按需下载约 1MB 引擎,多序列/大屏/交互场景优先**
|
|
61
61
|
|
|
62
62
|
### 交互
|
|
63
63
|
**本地优先(v2.6)**:UI 自己能做的状态变化——判卷、判题、重置、展开、选中——一律本地即时完成,**零模型往返**。action 只用于必须模型参与的事(生成新内容、执行工具、下一步建议)。**交互组件必须带 action:不带 action 的按钮渲染为禁用态,用户点不了;带 action 的按钮点击后有「已触发」本地反馈。**
|
|
@@ -65,24 +65,26 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
65
65
|
- **秘密禁令**:不得索取或生成密码、API Key、访问令牌、恢复码等秘密输入;遇到此类需求直接拒绝并解释
|
|
66
66
|
- 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`
|
|
67
67
|
- select: `{"type":"select","label":"...","options":["...","..."],"selected":下标?,"action":"pick"?,"id":"field-id"?}` — `selected` 预选某选项(缺省显示「请选择…」占位,不静默预选第一项);带 `id` 的选择跨刷新保留并进 submit 的 `fields`
|
|
68
|
-
- checkbox: `{"type":"checkbox","label":"
|
|
68
|
+
- checkbox: `{"type":"checkbox","label":"<user-language option>","checked":true?,"action":"toggle"?,"group":"group-id"?}` — 默认保持逐次 `action` 行为;**加 `group` 进入多选聚合模式**:同组 checkbox 可反复勾选/取消,变化只在本地记录、不发逐次 action,兄弟 `submit` 一次性把该组已选 label 作为字符串数组放进 `answers`(例如 `{"styles":["<user-language option>","<user-language option>"]}`)
|
|
69
69
|
- slider: `{"type":"slider","label":"...","min":0,"max":100,"step":1,"value":n?,"action":"name"?,"id":"field-id"?}` — 数值表单滑块:实时显示数值;带 `id` 跨刷新保留并进 submit 的 `fields`(拖拽经防抖合并成一次 action)
|
|
70
|
-
- radio: `{"type":"radio","label":"
|
|
70
|
+
- radio: `{"type":"radio","label":"<user-language label>","options":["<user-language option>","<user-language option>"],"selected":n?,"action":"pick"?}` — 单选;**加 `"group":"group-id"` 进入聚合模式**:选择只本地记录、不发往返;**加 `"answer":正确下标或标签` + `"explanation":"<user-language explanation>"` 后,交卷在本地判卷**
|
|
71
71
|
- link: `{"type":"link","label":"...","href":"https://..."?}` — 仅 http(s)/mailto 协议被接受;无 `href` 时渲染为纯文本样式(不会假装可点)
|
|
72
|
-
- submit: `{"type":"submit","label":"
|
|
72
|
+
- submit: `{"type":"submit","label":"<user-language action>","action":"grade","groups":["q1","styles"],"resetAction":"redo"?}` — 聚合按钮:纯 radio 且题目带 `answer` 时仍本地立即判卷(得分 + 每题 ✓/✗ + 解析,零往返);其余聚合场景一次发送 `[genui-action]`,payload 为 `{answers:{q1:"<user-language option>",styles:["<user-language option>","<user-language option>"]},fields:{id:"<user-language value>"},total,answered}`。`groups` 中每个 radio 必须已选择、每个 checkbox 组必须至少勾选一项才可提交
|
|
73
73
|
- switch: `{"type":"switch","label":"...","checked":true?,"action":"toggle"?}`
|
|
74
74
|
- textarea: `{"type":"textarea","label":"...","placeholder":"...","rows":n?,"value":"...","action":"save"?,"id":"field-id"?}` — action 在失焦和 **Ctrl/Cmd+Enter** 时触发;blur 仅值有变化才发送;带 `id` 的值刷新后保留
|
|
75
75
|
- tabs: `{"type":"tabs","tabs":[{"label":"...","items":[...]}]}`
|
|
76
76
|
- accordion: `{"type":"accordion","items":[{"title":"...","items":[...]}]}`
|
|
77
|
-
- copy: `{"type":"copy","label":"
|
|
77
|
+
- copy: `{"type":"copy","label":"<user-language action>","text":"<user-language text>"}`
|
|
78
78
|
|
|
79
79
|
**状态持久化(v2.7)**:radio 答案、checkbox 分组选择、交卷锁定、输入值按「会话 + 内容指纹」自动保存——用户刷新页面/重开会话,同一块 UI 的状态原样恢复;你重渲染**相同内容**会保留用户状态,渲染**新内容**(换题等)自动从头开始。
|
|
80
80
|
|
|
81
81
|
**卷子模式(多道选择题)**:每题一个 radio(带唯一 `group` + `answer` + `explanation`),最后放一个 submit(`groups` 列出全部题号)——用户全部选完点交卷,**分数和对错当场在 UI 里出现**,不用等你。只有换新题/进阶建议才发 action。不要每题单独发 action(会刷屏)。
|
|
82
82
|
|
|
83
83
|
### 高级
|
|
84
|
+
- svg: `{"type":"svg","title":"<user-language title>","height":300,"code":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 100\"><rect width=\"200\" height=\"100\" fill=\"#534ab7\"/></svg>"}` — 独立 SVG 图片预览;`title`/`height` 可省略,height 为 100–800,code 最多 12,000 字符。必须提供完整 SVG 文档(含 xmlns),建议带 viewBox。使用隔离图片模式,不支持脚本、宿主 CSS 或外部资源;图形无法加载时显示源码和提示。**放在 dsh-ui 围栏中;这不是 ECharts renderer 配置。**
|
|
85
|
+
- 也可以直接输出 ```svg 代码围栏(不带 dsh-ui 包装):会自动显示为「预览/源码」切换的图形预览,源码可复制;无法解析时保留源码并提示。
|
|
84
86
|
- mermaid: `{"type":"mermaid","code":"graph TD\\nA-->B"}` — flowchart/sequence/class/gantt/pie/er/state/journey;主题自动跟随宿主(暗/浅)
|
|
85
|
-
- diagram: `{"type":"diagram","kind":"architecture","title":"
|
|
87
|
+
- diagram: `{"type":"diagram","kind":"architecture","title":"<user-language title>","variant":"light|dark|editorial","nodes":[...],"edges":[...],"theme":{...}}` — **编辑级品牌图**(移植自 diagram-design 的 27 种视觉类型)。节点: `{"id":"a","label":"<user-language label>","type":"focal|backend|store|external|input|optional|security","x":40,"y":40,"w":128,"h":48,"sub":"<user-language text>","tag":"<user-language text>"}`;边: `{"from":"a","to":"b","label":"<user-language label>","kind":"solid|dashed|accent|link"}`。**规则由渲染器强制**: 正交连接器(r=8 弯折、禁止斜线)、4px 网格、语义 token(paper/ink/muted/accent)、焦点色 ≤2 个、复杂度预算(≤9 节点/≤12 边)、z-order(箭头在节点后)、边标签 6-10px 间隙。27 种 kind:architecture / it-state / flowchart / sequence / state / er / timeline / swimlane / quadrant / radar / loop / nested / tree / org-chart / layers / venn / pyramid / bar / line / gantt / scatter / high-level / process / medallion / data-flow / dp-integration / dp-security-matrix。**坐标类 kind**(architecture/it-state/high-level/process/medallion/data-flow/dp-integration)用 x/y/w/h 精确定位;**规则类 kind** 只给数据自动排版。架构/流程/层次结构优先用 diagram 而非 mermaid(自动布局用 mermaid,编辑级排版用 diagram)。
|
|
86
88
|
- scene3d: `{"type":"scene3d","title":"...","meshes":[{"shape":"box|sphere|cone|cylinder|torus","color":"#hex?","size":n|[w,h,d]?,"position":[x,y,z]?,"rotation":[rx,ry,rz]?,"scale":n?|[...]?}],"ambient":0-2?,"background":"#hex?"}` — 3D WebGL,可拖拽旋转、滚轮缩放;mesh 数量 1–5 个
|
|
87
89
|
- quiz: `{"type":"quiz","question":"...","options":[{"label":"...","correct":true?,"feedback":"..."?}],"explanation":"...","id":"..."?,"action":"answer"?}` — 教学问答:点选即判题、可重试;`id` 变化时重置;带 action 时另回传 `{type:'quiz',question,answer,correct}`
|
|
88
90
|
|
|
@@ -130,8 +132,9 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
130
132
|
| `**加粗**` | 强调(不换行、不成块) |
|
|
131
133
|
| `==高亮==` | 极淡底色标记 |
|
|
132
134
|
| `[文字](https://…)` | 行内链接(http/https/mailto;非法目标退化为纯文字) |
|
|
135
|
+
| JSON `"\n"`(真实换行符) | 换行——**多段文字写同一个字段**,不要为换行拆成多个节点 |
|
|
133
136
|
|
|
134
|
-
不嵌套、不解析 HTML(每个标记生成 React 元素,不走 innerHTML
|
|
137
|
+
不嵌套、不解析 HTML(每个标记生成 React 元素,不走 innerHTML;`<br>` 字面显示,换行用 `"\n"`);标记没闭合时原样显示。数值列 / badge / spark 单元格不解析(数字没什么可强调的)。
|
|
135
138
|
|
|
136
139
|
## 回答级版式:默认无卡,焦点唯一
|
|
137
140
|
|
|
@@ -177,7 +180,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
177
180
|
### 1. 状态汇报(多点 + 有构图)
|
|
178
181
|
|
|
179
182
|
```json dsh-ui
|
|
180
|
-
{"items":[{"type":"grid","cols":4,"items":[{"type":"stat","label":"
|
|
183
|
+
{"items":[{"type":"grid","cols":4,"items":[{"type":"stat","label":"<user-language metric>","value":"26","delta":"#123–#148"},{"type":"stat","label":"<user-language metric>","value":"0"},{"type":"stat","label":"<user-language metric>","value":"556","delta":"<user-language status>"},{"type":"stat","label":"<user-language metric>","value":"45"}]},{"type":"table","columns":["<user-language column>","<user-language column>","<user-language column>"],"types":["text","badge","text"],"rows":[["<user-language text>","<user-language status>","<user-language text>"],["<user-language text>","<user-language status>","<user-language text>"]]},{"type":"callout","tone":"info","title":"<user-language title>","content":"<user-language text>"}]}
|
|
181
184
|
```
|
|
182
185
|
|
|
183
186
|
不要这样:把同一句话既写进正文又放进卡片;也不要为每条信息配一张卡(4 个 stat 排一行就够)。
|
|
@@ -185,7 +188,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
185
188
|
### 2. 解释/教学(文字为主,一个点睛组件)
|
|
186
189
|
|
|
187
190
|
```json dsh-ui
|
|
188
|
-
{"items":[{"type":"text","size":"body","content":"
|
|
191
|
+
{"items":[{"type":"text","size":"body","content":"<user-language text>"},{"type":"list","items":[{"title":"<user-language title>","desc":"<user-language description>"},{"title":"<user-language title>","desc":"<user-language description>"}]},{"type":"callout","tone":"warning","title":"<user-language title>","content":"<user-language text>"}]}
|
|
189
192
|
```
|
|
190
193
|
|
|
191
194
|
不要这样:每段都配一个组件;把一句话拆成好几个 text 节点(用行内标记就够了)。
|
|
@@ -193,7 +196,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
193
196
|
### 3. 对比选型
|
|
194
197
|
|
|
195
198
|
```json dsh-ui
|
|
196
|
-
{"items":[{"type":"table","columns":["
|
|
199
|
+
{"items":[{"type":"table","columns":["<user-language column>","<user-language column>","<user-language column>"],"types":["text","text","badge"],"rows":[["<user-language text>","<user-language text>","<user-language status>"],["<user-language text>","<user-language text>","<user-language status>"]]},{"type":"callout","tone":"success","title":"<user-language title>","content":"<user-language text>"}]}
|
|
197
200
|
```
|
|
198
201
|
|
|
199
202
|
不要这样:表格里放同一批数据后又画一张图。
|
|
@@ -209,7 +212,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
209
212
|
### 4. 排查诊断(顺序即叙事)
|
|
210
213
|
|
|
211
214
|
```json dsh-ui
|
|
212
|
-
{"items":[{"type":"steps","current":1,"steps":[{"title":"
|
|
215
|
+
{"items":[{"type":"steps","current":1,"steps":[{"title":"<user-language step>","desc":"<user-language description>"},{"title":"<user-language step>","desc":"<user-language description>"},{"title":"<user-language step>","desc":"<user-language description>"}]},{"type":"diff","diffs":[{"path":"PlotBlock.tsx","oldText":"e.preventDefault()","newText":"if (!e.metaKey && !e.ctrlKey) return"}]},{"type":"callout","tone":"info","title":"<user-language title>","content":"<user-language text>"}]}
|
|
213
216
|
```
|
|
214
217
|
|
|
215
218
|
不要这样:把"复现/定位/修复"写成三个卡片并列(那是流程,用 steps)。
|
|
@@ -217,7 +220,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
217
220
|
### 5. 数据结论(一个主图 + 明细)
|
|
218
221
|
|
|
219
222
|
```json dsh-ui
|
|
220
|
-
{"items":[{"type":"chart","kind":"line","data":[],"series":[{"label":"
|
|
223
|
+
{"items":[{"type":"chart","kind":"line","data":[],"series":[{"label":"<user-language series>","data":[{"label":"<user-language point>","value":8},{"label":"<user-language point>","value":12},{"label":"<user-language point>","value":9}]}]},{"type":"table","columns":["<user-language column>","<user-language column>","<user-language column>"],"types":["text","num","delta"],"rows":[["<user-language text>","8","-4%"],["<user-language text>","12","+50%"]]}]}
|
|
221
224
|
```
|
|
222
225
|
|
|
223
226
|
不要这样:图与表用同一粒度表达同一批数据(图看趋势、表看明细才不重复)。
|
|
@@ -233,6 +236,7 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
233
236
|
1. **围栏放哪,组件就出现在哪** —— 文字在前后自然流动,不要用工具、不要解释"这是一个围栏"。**围栏一闭合就立即渲染**(不等整条回答结束),所以可以边写文字边出组件
|
|
234
237
|
2. **组合优先**:复杂界面用 `grid`+`card`+`stat`+`table` 拼,不要追求单一巨型组件
|
|
235
238
|
3. **JSON 必须严格合法,发出前完成 4 步自检**:插件**只**修标点级小错(字符串内半角引号、尾随逗号);**缺括号/错括号等结构错误一律不修**,直接红横幅退化成代码块——写错就重发,别指望兜底。**最容易犯的错:字符串值里用了半角引号 `"`**——中文引语一律写 `“”` 或 `「」`。发出围栏前自检 4 条:① 括号配对:`{` 与 `}`、`[` 与 `]` 数量相等,**收尾序列逐个核对**(长表格最易在最后几行错位:把 `]]}]}` 写成 `]}]}]}`)② 无尾随逗号 ③ 值内引号用中文引号 ④ 最后一个字符必须是 `}`。不要在 JSON 字符串里放 markdown;超长表格/列表拆成多个组件分开发,宁短勿长
|
|
239
|
+
3.5. **字段名逐个核对(写错一个 = 整条围栏降级为代码块)**:某个组件的必填字段写错 / 缺失 → 该组件被丢弃 → 整份 spec 判定不可渲染 → 用户只看到一段裸 JSON。高频误写:`callout` 正文是 `content`(不是 text/body);`table` 要 `columns` + `rows`(不是 data,只给二维 rows 时首行会当表头);`keyvalue` 要 `pairs:[{key,value}]`(不是 items);`diff` 是 `diffs`;图片/音视频是 `src`;`code` 是 `code`、`copy` 是 `text`。拿不准就调 `validate_dsh_ui`,不要凭直觉命名
|
|
236
240
|
4. **不要嵌套围栏**:dsh-ui 里不要再包 ``` 代码围栏
|
|
237
241
|
5. **深色主题友好**:配色选深底亮色;UI 主题跟随应用
|
|
238
242
|
6. **场景判断**:先查上面的映射表 —— 内容类型命中就上对应组件;只有纯文字问答、一句话能说清时才不用
|
|
@@ -240,4 +244,4 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
240
244
|
8. **规格要紧凑**:整棵组件树 ≤200 节点、≤8 层嵌套(超出部分会被渲染器裁掉),避免巨型 spec
|
|
241
245
|
9. **一个主题选一个主组件**:命中映射表后选**一种**组件承载,同一信息不要用两种组件重复表达(同一批数据又画 bars 又画 donut = 冗余)
|
|
242
246
|
10. **数量纪律**:一条回答 3–8 个组件为宜,宁缺毋滥。反例:该用 `table` 对比时写三段 `text`;一个 `stat` 能说清的事套 `card`+`grid`;与内容无关的 `scene3d` 炫技——3D 只在内容本身就是几何/空间时才用
|
|
243
|
-
11. **先验后发(复杂 UI)**:发出 ```dsh-ui 围栏前,若 spec ≥3 个组件或含 `table`(长表格最易括号错位),先调用 `validate_dsh_ui` 工具(参数 `spec` 传围栏内的 JSON 文本)验证;返回
|
|
247
|
+
11. **先验后发(复杂 UI)**:发出 ```dsh-ui 围栏前,若 spec ≥3 个组件或含 `table`(长表格最易括号错位),先调用 `validate_dsh_ui` 工具(参数 `spec` 传围栏内的 JSON 文本)验证;返回 `status=invalid` 就按诊断修正后重新验证,返回 `status=valid` 再发出;**若返回 `next=emit_repaired_fence`,直接照抄 `repaired_json` 发出,无需再次验证**;简单 UI(≤2 个组件)不必验证,渲染器会自动修复大部分标点/括号错误
|