@changfenhuang/dsh-genui 0.9.9 → 0.10.0
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 +29 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/SKILL.md +128 -6
- package/lib/assets/echarts-core.js +43 -0
- package/lib/assets/{echarts.js → echarts-full.js} +1 -1
- package/lib/assets/mermaid.js +1 -1
- package/lib/client.js +5 -3
- package/lib/index.js +230 -69
- package/lib/types/client/EChartNode.d.ts +9 -0
- package/lib/types/client/GenuiBlock.d.ts +3 -4
- package/lib/types/client/asset-echarts-core.d.ts +1 -0
- package/lib/types/client/asset-echarts.d.ts +3 -2
- package/lib/types/client/asset-loader.d.ts +2 -2
- package/lib/types/client/blocks/advanced.d.ts +0 -3
- package/lib/types/client/blocks/charts.d.ts +23 -12
- package/lib/types/client/blocks/state.d.ts +5 -3
- package/lib/types/client/echarts-lazy.d.ts +4 -1
- package/lib/types/client/genui-runtime/schema.d.ts +11 -1
- package/lib/types/client/inline.d.ts +29 -0
- package/lib/types/client/spec.d.ts +76 -6
- package/lib/types/plugin/index.d.ts +1 -1
- package/package.json +4 -3
- package/src/client/EChartNode.tsx +233 -19
- package/src/client/GenuiBlock.module.css +895 -145
- package/src/client/GenuiBlock.tsx +31 -23
- package/src/client/PlotBlock.module.css +17 -1
- package/src/client/PlotBlock.tsx +33 -1
- package/src/client/asset-echarts-core.ts +39 -0
- package/src/client/asset-echarts.ts +4 -3
- package/src/client/asset-loader.ts +5 -3
- package/src/client/blocks/advanced.tsx +60 -16
- package/src/client/blocks/charts.tsx +761 -144
- package/src/client/blocks/render-node.tsx +252 -11
- package/src/client/blocks/state.ts +5 -3
- package/src/client/dom-fence.tsx +60 -6
- package/src/client/echarts-lazy.ts +11 -2
- package/src/client/fence-render.tsx +6 -5
- package/src/client/gallery.ts +78 -2
- package/src/client/genui-runtime/schema.ts +45 -11
- package/src/client/guard.ts +161 -23
- package/src/client/index.tsx +14 -3
- package/src/client/inline.ts +73 -0
- package/src/client/mermaid-core.ts +42 -5
- package/src/client/spec.ts +79 -6
- package/src/plugin/index.ts +33 -45
- package/src/plugin/tool.ts +5 -1
- package/tsdown.config.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.10.0] - 2026-09-11
|
|
6
|
+
|
|
7
|
+
- CI 与发布包验收固定到 DSH 0.1.5-rc.2;保留 0.1.2-rc.1 支持下限验证。
|
|
8
|
+
- 入场动画完成后不再随宿主重新插入节点重放;恢复已有交互状态的组件直接展示,保留新组件的首次入场动画(#101)。
|
|
9
|
+
- 补充五个可渲染场景范例与短回答反例;新增行内富文本、图表按需加载、表格明细,以及图表缩放与筛选交互修复。
|
|
10
|
+
|
|
11
|
+
### 新增
|
|
12
|
+
- **表格列类型**:`table.types` 支持 `index` / `spark` / `ring` / `bar` / `badge` / `delta` / `num` / `group`;`table.total` 追加合计行(数值列求和);`group` 首列渲染为**可折叠的分组标题**(chevron + 组内行数),组内行缩进并带引导线,分组内排序不会打乱结构。
|
|
13
|
+
- **图表形态**:`chart` 新增 `horizontal`(横向柱)与 `stacked`(series 堆叠);`line` 支持 `series` 多序列;横向/堆叠/折线/环形图全部支持**即时自绘 tooltip**(堆叠段显示该段数值 + 合计)。
|
|
14
|
+
- **指标增强**:`stat.spark` 微趋势线(面积 + 末点,贴卡片下沿);`stat.size:"hero"` 超大数字;数值与单位自动拆分(单位半字号弱化)。
|
|
15
|
+
- **进度组件**:`progress.variant:"ring"` 环形进度;`progress.target` 目标刻度。
|
|
16
|
+
- **卡片语气**:`card.tone`(info / success / warning / danger)。
|
|
17
|
+
- **流式骨架**:围栏正文仍在流式且尚无完整组件时显示骨架,首个组件解析后换成真组件,settle 仍不可解析则归还原始代码块。
|
|
18
|
+
- **采纳度审计**:`scripts/genui-usage-audit.mjs`,扫本地会话日志量化 `dsh-ui` 围栏采纳率与组件分布。
|
|
19
|
+
|
|
20
|
+
### 变更
|
|
21
|
+
- 设计系统 v3–v10:字号下限 12.5px、标题层级拉开、CJK 行高与字距、表格数据化(斑马纹 / 大写表头 / 首列加粗 / 带符号自动着色);指标卡以数字为主体,单位半字号。
|
|
22
|
+
- 常驻系统提示加入硬触发规则与关键组件字段速查,SKILL.md 同步;`echart` 引导改为场景化(≤8 点用 `chart`,多序列/交互/大数据用 `echart`)。
|
|
23
|
+
- 移除早期版本引入的强调色竖条/色块,层级改由字号、留白与结构表达。
|
|
24
|
+
|
|
25
|
+
### 修复
|
|
26
|
+
- **分组柱状图无法渲染**:`data: []` + `series` 的合法写法被图表契约整份拒绝,导致含此类图的围栏完全不渲染。
|
|
27
|
+
- **图表契约**:`line` + `series` 由「拒绝」改为「支持」;`donut` 仍为单序列。
|
|
28
|
+
- **可视化回归脚本**:`scripts/e2e-visual.mts` 在 rc7 之后失效(宿主机路径、缺 `--profile`/`--no-open`、401 token、漏 `await`、空 profile 遮罩拦截指针事件),并新增流式骨架与 tooltip 的真实浏览器断言。
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### 修复
|
|
32
|
+
- **插件热重载生命周期(issue #121)**:assets 路由与模型工具改由可选服务的依赖 fiber 管理;插件或服务卸载时自动注销,服务重新提供时自动注册,避免热重载残留路由导致新 fiber 启动失败。
|
|
33
|
+
|
|
5
34
|
## [0.9.9] - 2026-09-09
|
|
6
35
|
|
|
7
36
|
- 预览宿主验证更新到 DSH 0.1.5-alpha.1,正式宿主仍为 0.1.2-rc.1;安装包验证覆盖 Diff/Code/JSON 渲染及真实复制。
|
package/README.md
CHANGED
|
@@ -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 `^0.1.2-rc.1 || ^0.1.5-alpha.1`** (dsh-genui 0.
|
|
96
|
+
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1`** (dsh-genui 0.10.0 is verified on DSH 0.1.5-rc.2 and the 0.1.2-rc.1 minimum; 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):
|
package/README.zh-CN.md
CHANGED
|
@@ -93,7 +93,7 @@ https://github.com/user-attachments/assets/f5db33ec-7471-4d4a-a85b-79c9962ab4ef
|
|
|
93
93
|
|
|
94
94
|
前置条件,缺一不可:
|
|
95
95
|
|
|
96
|
-
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1`**(dsh-genui 0.
|
|
96
|
+
1. **dsh `^0.1.2-rc.1 || ^0.1.5-alpha.1`**(dsh-genui 0.10.0 已验证 DSH 0.1.5-rc.2,并保留 0.1.2-rc.1 下限验证;使用 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 中激活(一行命令,自动带上全部依赖):
|
package/SKILL.md
CHANGED
|
@@ -22,19 +22,22 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
22
22
|
- text: `{"type":"text","size":"h1|h2|h3|body|muted|caption","content":"...","center":true?}`
|
|
23
23
|
- row / col: `{"type":"row"|"col","items":[...],"wrap":true?,"spacer":true?,"gap":n?}`
|
|
24
24
|
- grid: `{"type":"grid","cols":n,"items":[...]}`
|
|
25
|
-
-
|
|
25
|
+
- hero: `{"type":"hero","title":"...","subtitle":"...","value":"99.96%","label":"可用率","delta":"+0.02%","spark":[...],"tone":"accent|success|warning|danger"}` — **封面块**:eyebrow + 超大数字(52px,带入场计数)+ 标题 + 副标题 + tone 渐变底色。**一条回答最多用一个**,放在最前面当视觉锚点
|
|
26
|
+
- span: 任意节点都可加 `"span":2`(grid 子节点占几列)——bento 排版的唯一原语:一张 `span:2` 宽卡配一张窄卡,比一列方块堆下去好看得多
|
|
27
|
+
- card: `{"type":"card","title":"...","items":[...]}`;`"accent":"#f59e0b"` 指定强调色(边框 + 标题 + 极淡底色)
|
|
28
|
+
- palette: `chart` / `echart` 都支持 `"palette":["#ff8800","#3ecf8e"]` 覆盖分类色板(默认跟随宿主主题)。**只有语义上需要指定颜色时才写**(成本=红、收益=绿),否则跟随主题更稳;`"tone":"info|success|warning|danger"` 给卡片底色(用于结论卡/风险卡)
|
|
26
29
|
- divider: `{"type":"divider"}`; spacer: `{"type":"spacer"}`
|
|
27
30
|
|
|
28
31
|
### 展示
|
|
29
|
-
- stat: `{"type":"stat","label":"...","value":"...","delta":"+12.4%|-3%"}`(`-` 开头自动红、`+`
|
|
32
|
+
- stat: `{"type":"stat","label":"...","value":"...","delta":"+12.4%|-3%"}`(`-` 开头自动红、`+` 绿);可选 `"spark":[3,5,4,8,6]` 画一条微趋势线(2–60 个有限数值);`"size":"hero"` 渲染超大数字(一条回答最多用一次,作为视觉锚点)
|
|
30
33
|
- badge: `{"type":"badge","label":"...","tone":"success|warn|danger|accent","icon":"emoji?"}`
|
|
31
|
-
- progress: `{"type":"progress","label":"...","value":0-100,"valueLabel":"70%"}`
|
|
34
|
+
- progress: `{"type":"progress","label":"...","value":0-100,"valueLabel":"70%"}`;`"variant":"ring"` 画环形进度,`"target":70` 在轨道上标出目标刻度
|
|
32
35
|
- avatar: `{"type":"avatar","name":"...","color":"#hex?"}`
|
|
33
36
|
- image: `{"type":"image","src":"/mmx-files/result.png","alt":"结果图片"}` — 展示浏览器可访问的 http(s) 或同源相对图片地址;懒加载;不支持 `file:`/`data:` 等本地或主动协议
|
|
34
37
|
- audio: `{"type":"audio","src":"/mmx-files/result.mp3","alt":"语音结果","loop":true?}` — 原生控制条;用户主动播放,不自动播放;仅 http(s) 或同源相对地址
|
|
35
38
|
- video: `{"type":"video","src":"/mmx-files/result.mp4","alt":"视频结果","poster":"/mmx-files/poster.jpg"?,"loop":true?,"muted":true?,"aspectRatio":"16:9|4:3|1:1|9:16"?}` — 原生播放/音量/全屏控制;不自动播放
|
|
36
39
|
- list: `{"type":"list","items":["..."] 或 [{"title":"...","desc":"..."}] 或嵌套节点(如 {"type":"badge","label":"TS"})}` — 行内可嵌节点(计入节点预算)
|
|
37
|
-
- table: `{"type":"table","columns":["..."],"rows":[["...","..."]]}` — 表头点击本地排序(升/降/还原,零往返);数值感知:千分位(`1,234`)、`k/m/b
|
|
40
|
+
- 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,点开在整行下方展开明细,适合「主表 + 明细」
|
|
38
41
|
- keyvalue: `{"type":"keyvalue","pairs":[{"key":"...","value":"..."}]}`
|
|
39
42
|
- timeline: `{"type":"timeline","items":[{"title":"...","desc":"...","time":"..."}]}`
|
|
40
43
|
- file-tree: `{"type":"file-tree","items":[{"name":"...","type":"file|dir","children":[...]?}]}` — 目录行可点击折叠/展开(本地,零往返)
|
|
@@ -46,9 +49,11 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
46
49
|
- steps: `{"type":"steps","current":n,"steps":[{"title":"...","desc":"..."}]}`
|
|
47
50
|
|
|
48
51
|
### 图表
|
|
49
|
-
- chart: `{"type":"chart","kind":"bars|line|donut","data":[{"label":"...","value":n,"color":"#hex?"}],"series":[...]?}` — bars 默认;line 趋势;donut
|
|
52
|
+
- 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**
|
|
50
53
|
- 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(其他字母=参数)
|
|
51
|
-
- 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 配置(颜色跟随宿主主题);**
|
|
54
|
+
- 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,主题自动跟随):
|
|
55
|
+
`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`**(长序列 + 内置缩放)
|
|
56
|
+
**full option 模式**:传 `"option":{...}` 直接写 ECharts 原生配置(支持 dataZoom/visualMap/radar/gauge/heatmap 等所有图表类型),option 中的函数会被过滤(只接受数据)。选择原则:**chart 轻量(无额外下载)适合 ≤8 点的快速对比;echart 视觉更丰富(渐变、tooltip、图例交互、dataZoom),但会按需下载约 1MB 引擎,多序列/大屏/交互场景优先**
|
|
52
57
|
|
|
53
58
|
### 交互
|
|
54
59
|
**本地优先(v2.6)**:UI 自己能做的状态变化——判卷、判题、重置、展开、选中——一律本地即时完成,**零模型往返**。action 只用于必须模型参与的事(生成新内容、执行工具、下一步建议)。**交互组件必须带 action:不带 action 的按钮渲染为禁用态,用户点不了;带 action 的按钮点击后有「已触发」本地反馈。**
|
|
@@ -81,12 +86,21 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
81
86
|
|
|
82
87
|
**判断口诀**:这段内容换成结构化组件,会不会比纯文字更好扫、更好懂、更好操作?会 → 就用,**不需要等用户开口要 UI**。
|
|
83
88
|
|
|
89
|
+
**硬触发(出现就至少出一个围栏,不要退回纯文字段落)**:
|
|
90
|
+
- ≥3 条并列要点 → `list`;≥2 组数字对比 → `table`;指标/进度/状态 → `stat`/`progress`/`badge`
|
|
91
|
+
- 步骤/时间线 → `steps`/`timeline`/`mermaid`;架构/流程 → `diagram`/`mermaid`;风险/结论 → `callout`;代码/改动 → `code`/`diff`/`json`
|
|
92
|
+
- 趋势/占比 → `chart`(≤8 点)或 `echart`(多序列/要交互/数据量大)
|
|
93
|
+
- 收尾自检:回答超过约 10 行时确认至少有一个围栏;同一份信息不要既写文字又重复出组件;纯问答不套 UI。
|
|
94
|
+
|
|
84
95
|
| 你要呈现的内容 | 用这些组件 |
|
|
85
96
|
|---|---|
|
|
86
97
|
| 关键结论 / 要点罗列(≥2 条) | `list`、`keyvalue`、`callout` |
|
|
87
98
|
| 重点强调 / 警告 / 注意事项 | `callout`(info/success/warning/error)、`badge`、`stat` |
|
|
88
99
|
| 数据对比 / 趋势 / 占比 | `chart`(bars/line/donut)、`echart`(ECharts 全功能)、`table` |
|
|
89
100
|
| 关键指标数字 / 进度状态 | `stat`、`progress`、`badge` |
|
|
101
|
+
| 回答的视觉锚点(第一个组件) | `hero`(封面块,一条回答最多一个) |
|
|
102
|
+
| 想排版不呆板 | `grid` + 子节点 `span`(bento:宽窄混排) |
|
|
103
|
+
| 数据多、需要读者自己找 | `input`(id)+ `table`/`chart`/`list` 的 `filter` 绑定 |
|
|
90
104
|
| 流程 / 步骤 / 阶段 / 时间线 | `steps`、`timeline`、`mermaid`(flowchart/sequence/gantt) |
|
|
91
105
|
| 架构 / 系统拓扑 / 数据流 / 品牌图 | `diagram`(编辑级,27 种类型;自动布局需求才用 `mermaid`) |
|
|
92
106
|
| 目录 / 文件结构 / 层级关系 | `file-tree`、`mermaid`、`accordion` |
|
|
@@ -102,6 +116,114 @@ description: "Render structured interactive UI inline in your reply via the dsh-
|
|
|
102
116
|
|
|
103
117
|
**别用的情况**:一句话能说清的事、纯闲聊、用户明确说不要 UI、以及"为了炫技硬塞"——组件服务内容,不是内容服务组件。
|
|
104
118
|
|
|
119
|
+
## 行内富文本(文字类回答的底座)
|
|
120
|
+
|
|
121
|
+
`text.content`、`list` 项、`table` 文本列、`keyvalue` 值、`callout` 标题与正文里可以直接写四种行内标记——**重点留在句子里,不必为一个词单起一个组件**:
|
|
122
|
+
|
|
123
|
+
| 写法 | 渲染 |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `` `code` `` | 行内代码胶囊 |
|
|
126
|
+
| `**加粗**` | 强调(不换行、不成块) |
|
|
127
|
+
| `==高亮==` | 极淡底色标记 |
|
|
128
|
+
| `[文字](https://…)` | 行内链接(http/https/mailto;非法目标退化为纯文字) |
|
|
129
|
+
|
|
130
|
+
不嵌套、不解析 HTML(每个标记生成 React 元素,不走 innerHTML);标记没闭合时原样显示。数值列 / badge / spark 单元格不解析(数字没什么可强调的)。
|
|
131
|
+
|
|
132
|
+
## 回答级版式:默认无卡,焦点唯一
|
|
133
|
+
|
|
134
|
+
**规则来自设计规范,不是口味**(`design` skill 的 `references/design-reference.md`):
|
|
135
|
+
「通用圆角矩形卡片当默认容器 = 模板思维,默认无卡;只有内容类型确实需要时才加卡片处理」,
|
|
136
|
+
「如果替换内容不需要改布局,那就是模板,重做」。
|
|
137
|
+
|
|
138
|
+
### 三条判据(不设组件数量上限)
|
|
139
|
+
|
|
140
|
+
1. **必要性**:这个组件承载的信息,用文字表达会明显更差吗?数字对比、趋势、空间关系、代码/数据原文才算过关,否则删掉。
|
|
141
|
+
2. **焦点唯一**:一条回答只有一个视觉焦点(最大那张图或那组数字);其余组件的视觉权重必须明显更低,靠尺寸/位置/色彩强度拉开,**不是靠数组件个数**。
|
|
142
|
+
3. **不重复**:同一批数据不做两种表达(表格与图表二选一)。
|
|
143
|
+
|
|
144
|
+
### 卡片(`card`)只在两种场合用
|
|
145
|
+
|
|
146
|
+
- 需要**并排**的 `grid` 子项(没有边界就分不清内容归属);
|
|
147
|
+
- 承载**数据对象**:表格、图、keyvalue、指标组。
|
|
148
|
+
|
|
149
|
+
单段文字、单个列表、已经自带边界的表格/图表,**不要包卡**。用 `h3` 标题 + 正文 + 间距代替。
|
|
150
|
+
|
|
151
|
+
### 层级靠字,不靠框
|
|
152
|
+
|
|
153
|
+
- 卡片标题 16px / 字重 650 / 句首大小写(**不是** 12.5px 全大写小标签);
|
|
154
|
+
- 12.5px 全大写只用于 eyebrow(如 hero 上方那一行);
|
|
155
|
+
- 大字号带负字距(≥32px 约 -0.022em,20–28px 约 -0.012em),数字列 `tabular-nums`;
|
|
156
|
+
- CJK 正文行高 1.7,长段落行宽 ≤68ch;
|
|
157
|
+
- 表面对比遵循规范:浅色相邻表面明度差 ≥4%,或阴影 ≥ `0 1px 3px rgba(0,0,0,0.10)`;深色靠半透明白叠加(卡片约 4%,抬升面 8%),投影在深色几乎无效。
|
|
158
|
+
|
|
159
|
+
### 不要
|
|
160
|
+
|
|
161
|
+
- 装饰性编号(①②③):该分点用 `list`,该分节用标题;
|
|
162
|
+
- 同一套骨架每条回答复用(标题栏 → 卡片网格 → 表格 → callout);
|
|
163
|
+
- 为了"显得丰富"堆组件:读者找不到重点就是失败。
|
|
164
|
+
|
|
165
|
+
### 怎么验证没模板化
|
|
166
|
+
|
|
167
|
+
`node scripts/genui-usage-audit.mjs` 输出「版式多样性」:不同版式签名数、最常见签名占比、归一化熵(越接近 1 越多样)、每回答 card 数分布。熵明显下降或 card 数不降反升,就说明规则没生效。
|
|
168
|
+
|
|
169
|
+
## 范例:示范「判断」,不要照抄组件序列
|
|
170
|
+
|
|
171
|
+
每个范例后面都跟着**什么时候不要这样**。组件多是好事——只要每个都在承载不同信息、并且有焦点和层次;真正的毛病是重复表达、以及把纯文字段落包进卡片。
|
|
172
|
+
|
|
173
|
+
### 1. 状态汇报(多点 + 有构图)
|
|
174
|
+
|
|
175
|
+
```json dsh-ui
|
|
176
|
+
{"items":[{"type":"grid","cols":4,"items":[{"type":"stat","label":"已合并","value":"26","delta":"#123–#148"},{"type":"stat","label":"未合并","value":"0"},{"type":"stat","label":"测试","value":"556","delta":"全绿"},{"type":"stat","label":"组件","value":"45"}]},{"type":"table","columns":["层","状态","生效方式"],"types":["text","badge","text"],"rows":[["组件与样式","已生效","每次从磁盘读"],["系统提示","待重启","Node 半只在启动时加载"]]},{"type":"callout","tone":"info","title":"结论","content":"改动都上了,但 **效果还没证据**。"}]}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
不要这样:把同一句话既写进正文又放进卡片;也不要为每条信息配一张卡(4 个 stat 排一行就够)。
|
|
180
|
+
|
|
181
|
+
### 2. 解释/教学(文字为主,一个点睛组件)
|
|
182
|
+
|
|
183
|
+
```json dsh-ui
|
|
184
|
+
{"items":[{"type":"text","size":"body","content":"根因不是记性,是规则自相矛盾:一条说「≥3 条并列 → 出 list」,另一条说「组件只在 ==文字表达会更差== 时出现」。"},{"type":"list","items":[{"title":"先修规则","desc":"把闸门限定为「不要包卡片」,而不是「少用组件」"},{"title":"再看数据","desc":"如果漏发率不降,才考虑兜底手段"}]},{"type":"callout","tone":"warning","title":"别急着加监控","content":"事后提醒来得太晚,还会逼人在不需要组件的地方硬塞。"}]}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
不要这样:每段都配一个组件;把一句话拆成好几个 text 节点(用行内标记就够了)。
|
|
188
|
+
|
|
189
|
+
### 3. 对比选型
|
|
190
|
+
|
|
191
|
+
```json dsh-ui
|
|
192
|
+
{"items":[{"type":"table","columns":["方案","代价","判断"],"types":["text","text","badge"],"rows":[["改规则","改一行字","推荐"],["加看门狗","事后才提醒,会变噪音","不推荐"]]},{"type":"callout","tone":"success","title":"选前者","content":"成本一行,且解决根因。"}]}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
不要这样:表格里放同一批数据后又画一张图。
|
|
196
|
+
|
|
197
|
+
反例(这条会被围栏校验直接拒绝,所以别抄):
|
|
198
|
+
|
|
199
|
+
```json dsh-ui-bad
|
|
200
|
+
{"items":[{"type":"chart","kind":"donut","data":[{"label":"A","value":1}],"series":[{"label":"B","data":[{"label":"B","value":2}]}]}]}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
为什么拒:`series` 只对 `bars`/`line` 有效;环形图给了 `series` 属于契约冲突,围栏会**静默降级为代码块**。
|
|
204
|
+
|
|
205
|
+
### 4. 排查诊断(顺序即叙事)
|
|
206
|
+
|
|
207
|
+
```json dsh-ui
|
|
208
|
+
{"items":[{"type":"steps","current":1,"steps":[{"title":"复现","desc":"滚动页面时光标压在图上"},{"title":"定位","desc":"onWheel 无条件 preventDefault"},{"title":"修复","desc":"改为仅 ⌘/Ctrl + 滚轮缩放"}]},{"type":"diff","diffs":[{"path":"PlotBlock.tsx","oldText":"e.preventDefault()","newText":"if (!e.metaKey && !e.ctrlKey) return"}]},{"type":"callout","tone":"info","title":"另外补了退路","content":"视图偏离时显示当前区间并提供 ==回到初始区间==。"}]}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
不要这样:把"复现/定位/修复"写成三个卡片并列(那是流程,用 steps)。
|
|
212
|
+
|
|
213
|
+
### 5. 数据结论(一个主图 + 明细)
|
|
214
|
+
|
|
215
|
+
```json dsh-ui
|
|
216
|
+
{"items":[{"type":"chart","kind":"line","data":[],"series":[{"label":"本周","data":[{"label":"一","value":8},{"label":"二","value":12},{"label":"三","value":9}]}]},{"type":"table","columns":["时段","量","环比"],"types":["text","num","delta"],"rows":[["周一","8","-4%"],["周二","12","+50%"]]}]}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
不要这样:图与表用同一粒度表达同一批数据(图看趋势、表看明细才不重复)。
|
|
220
|
+
|
|
221
|
+
### 6. 短回答:正确地不套组件
|
|
222
|
+
|
|
223
|
+
> 这个改动我先不做了——它要动宿主的输入组件,而你说了不碰宿主。
|
|
224
|
+
|
|
225
|
+
不要这样:一句话的结论配 stat + 表格 + callout。**没有内容就不发组件,这是对的,不算漏发。**
|
|
226
|
+
|
|
105
227
|
## 使用规则
|
|
106
228
|
|
|
107
229
|
1. **围栏放哪,组件就出现在哪** —— 文字在前后自然流动,不要用工具、不要解释"这是一个围栏"。**围栏一闭合就立即渲染**(不等整条回答结束),所以可以边写文字边出组件
|