dsh-mosaic 0.2.1 → 1.1.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 CHANGED
@@ -1,7 +1,62 @@
1
1
  # Changelog
2
2
 
3
- > 版本规则:插件版本与**最近一次验证通过的 DeepSeek Harness 版本**保持一致。当前对齐 `0.2.1`(实测 rc.3 与 rc.5)。
4
- > 同一 Harness 版本下的插件自身修复按补丁号递增(`0.2.1` → `0.2.2` ……);一旦换到新的 Harness 版本验证通过,版本号立即改成该 Harness 版本。
3
+ > 版本规则:插件版本按 npm 语义独立递增,**不再与 DeepSeek Harness 版本号对齐**。
4
+ > 兼容性分两处表达:`package.json` 的 `engines.dsh` 给机器可读的范围,README「安装」一节给「插件版本 ↔ 实测 Harness 版本」对照表;每次在真实 Harness 上验证后更新这两处。
5
+ > `0.2.1` 及各 `0.2.1-<标签>` 是改规则前的历史版本,保留用于追溯。
6
+
7
+ ## 1.1.0
8
+
9
+ ### 示意图
10
+
11
+ - `diagram` 从 4 种扩到 13 种。新增 `quadrant`、`tree`、`funnel`、`venn`、`swimlane` 由插件自己排布(与 `timeline`、`layers` 同一条原生 SVG 路线,不依赖图引擎);新增 `state`、`er`、`gantt`、`mindmap` 走 Mermaid 语法。
12
+ - `legend: [{label, tone}]`:给颜色加说明,所有图类型通用,渲染在卡片下方。
13
+ - 节点按标签文字估宽,不再固定宽度后截断成省略号;`tree` 的父节点按子树居中,`swimlane` 的列宽随步数自适应,`quadrant` 的标签相撞时自动下移。
14
+ - 修复同页多张图共用 `#mosaic-arrow` / `#mosaic-seq-arrow` 的问题:marker id 现在按组件实例生成。
15
+
16
+ ### 导出
17
+
18
+ - 每个 diagram 卡片新增 `svg` 与 `png` 导出。导出前用 `getComputedStyle` 把主题令牌解析成实际颜色并内联,去掉仍带 `var(--m-*)` 的表现属性,因此文件脱离页面也能正常显示;PNG 以 2 倍尺寸光栅化,并先铺卡片背景。
19
+
20
+ ### 视觉修正
21
+
22
+ - 列表圆点和链接箭头改用 `1lh` 定高,跟随实际行高居中;此前用写死的像素偏移,宿主行高不同就会一个偏高、一个偏低。
23
+ - 柱状图柱宽封顶 28px 并在分类槽里居中,多序列组内留 4px 间距;此前柱宽按分类宽度等比放大,7 个分类时每根接近 60px。
24
+ - Mermaid 输出的 SVG 带 `width="100%"`,插件此前用 `max-width:none` 去掉上限,导致整图被拉到卡片宽度、文字跟着放大。现在按 `viewBox` 钉回自然尺寸,并套用与原生示意图相同的高度上限。
25
+ - ER 与 sequence 在 Mermaid 内部写死 14px / 16px,不认 `themeVariables.fontSize`;统一归一到 12px,作用域限定在该 SVG 自己的 id 上。
26
+ - `row` 的子节点原本套着一层 `.mosaic-grid-item`,`col` 的百分比宽度会按这层收缩到内容宽度的包装计算(40% 只剩 30px)。改用 `display:contents` 让 `col` 直接作为 flex 子项,两栏按比例分配并吸收间距。
27
+ - README 的效果图按修复后的渲染重新截图。
28
+
29
+ ### 校验与建议
30
+
31
+ - `validate_view` 与 `render_view` 新增 `warnings=`:视图合法但超出可读规模时给出具体改法(表格超过 8 行、列表超过 7 项、图表点数为 1、`accent` 用在超过 2 个元素、卡片嵌套超过 2 层、某类图超过自己的规模、`widget` 引用外部资源)。这些只是建议,不影响渲染。
32
+ - 模型侧提示词与 `SKILL.md` 增加反模式清单、焦点规则和每类图的可读规模;`SKILL.md` 补全 13 种图的字段说明与选型表。
33
+
34
+ ## 1.0.0
35
+
36
+ 首个稳定版,也是 npm 上第一次发布的完整版本(`0.2.1` 之后的所有改动都在这里)。
37
+
38
+ - 版本规则变更:不再与 DeepSeek Harness 版本号对齐。此前用 `0.2.1-<标签>` 表示「对齐 0.2.1 的本地改动」,但这类版本在 semver 里是**预发布**(`0.2.1-harden < 0.2.1`),既不会被 `npm i` 选中,发布时还必须显式 `--tag`。兼容范围改由 `engines.dsh` 与 README 的对照表表达。
39
+ - 发布自动化:新增 tag 触发的发布工作流(`.github/workflows/release.yml`),使用 npm trusted publishing(OIDC,无需长期 token),并在发布前校验 tag 与 `package.json` 版本一致、该版本尚未发布。
40
+
41
+ ### 功能(此前未发布)
42
+
43
+ - 示意图与图表支持缩放/平移:`−`/`+`/`⟲`/`⤢`、拖动平移、`Ctrl/⌘ + 滚轮`、双击复位、键盘 `+`/`-`/`0`;示意图按原始尺寸渲染,超出部分裁切可拖动。
44
+
45
+ ### 安全与状态修正
46
+
47
+ - Widget 沙箱重建:整份文档(`doctype`/`html`/`head`/`body`)由宿主统一生成,模型内容只投放到宿主给出的 head/body 槽位,CSP 因此永远排在模型脚本之前。此前完整 HTML 文档走 `<\/head>` 前插入,模型写在 `<head>` 里的 `<script>` 会在 CSP 生效前执行;模型自带的 CSP `<meta>` 与 `<base>` 一并剥离。
48
+ - Widget 动作校验补上父窗口一侧:nonce 写在 Widget 自己的文档里,模型代码能读出来直接 `postMessage` 伪造,所以除 iframe 内的判断之外,父页面独立校验自己文档的 `navigator.userActivation`;iframe 内的判断同时改为 fail-closed。额度从「每个 Widget 一份」改为**整个会话共享**,payload 上限按 UTF-8 **字节**(16 KB)而不是字符数计算。
49
+ - 自有路由增加 Host 权威校验:`/dsh-mosaic/prompt` 与 `/dsh-mosaic/log` 只接受 loopback、监听地址或 `config.trustedHosts` 声明的授权。仅比较 Origin 与 Host 挡不住 DNS rebinding——被 rebinding 的请求两者都等于攻击者域名。
50
+ - Prompt 状态:`reset` 不再被迟到的 `hydrate` 覆盖(用独立标志区分「还没读过」和「用户刚清空」);`GET` 返回 `revision`,`POST` 可带 `expectedRevision`,对不上返回 409 并附当前内容,跨浏览器编辑不再静默覆盖。
51
+ - 日志路由在 `flush` 之后才回 204,204 变成真正的落盘回执;测试改为等待具体事件而不是「目录里有任意 jsonl」,修掉那条在 CI 上必然失败的竞态。
52
+ - 缩放/平移:触屏改为 `touch-action: pan-y`,手指上下划可以继续滚动聊天页面;ResizeObserver 里不再在 `setZoom` 的 updater 内调用 `setOverflowing`,两个更新都在回调顶层发出。
53
+ - 测试:新增 `test/client.test.mjs`(Widget 文档构造:CSP 顺序、外壳唯一性、剥离模型 CSP/base、bridge 策略),以及 Host 权威、reset/hydrate 竞态、revision 冲突的回归用例。`npm test` 现为 78 项。
54
+
55
+ ## 0.2.1-zoom
56
+
57
+ - 示意图与图表可以缩放和拖动:右上角 `−`/`+`/`⟲`/`⤢`,视图内按住拖动平移,`Ctrl/⌘ + 滚轮`缩放,双击或 `⟲` 复位,键盘 `+`/`-`/`0` 同样可用;`⤢` 在遮罩层里打开更大的画布。
58
+ - 示意图保留原始尺寸(不再被压缩到卡片宽度),超出部分在视口里裁切、可拖动查看。
59
+ - 修复外观切换后 Mermaid 重渲染报 `Unsupported color format: "currentColor"`、随后掉到内置布局的问题:主题变量的兜底改用具体颜色。
5
60
 
6
61
  ## 0.2.1
7
62
 
package/README.md CHANGED
@@ -6,8 +6,9 @@ Mosaic 是一个轻量的 DeepSeek Harness 可视化回答插件。它把模型
6
6
 
7
7
  它刻意不追求“什么都能画”:
8
8
 
9
- - 主包没有 ECharts / Three.js;flowchart / sequence 用官方 Mermaid 发行包,作为懒加载资产,只在出现 diagram 时下载(原始 5.5 MB,gzip 后约 1.3 MB,带版本号可长期缓存)。
10
- - 每个 diagram 卡片右上角有 `drawio` 按钮,可导出标准 mxGraph XML,在 draw.io 里继续编辑。
9
+ - 主包没有 ECharts / Three.js;`flowchart`、`sequence`、`state`、`er`、`gantt`、`mindmap` 用官方 Mermaid 发行包,作为懒加载资产,只在出现这几种图时下载(原始 5.5 MB,gzip 后约 1.3 MB,带版本号可长期缓存)。
10
+ - `timeline`、`layers`、`quadrant`、`tree`、`funnel`、`venn`、`swimlane` 由插件自己排布,不依赖图引擎;节点宽度按标签文字估算,不截断成省略号。
11
+ - 每个 diagram 卡片右上角有 `svg` / `png` / `drawio` 三个导出按钮:前两个直接下载图片(颜色、字号按当前主题解析后内联,脱离页面也能看),`drawio` 导出标准 mxGraph XML,在 draw.io 里继续编辑。
11
12
  - 组件目录保持精简,字段、校验、提示词来自同一份定义。
12
13
  - 普通文件/Office 只给链接,不在聊天里硬解。
13
14
  - 交互默认在本地完成;只有声明了 `action` 的组件才回模型。
@@ -39,7 +40,7 @@ Mosaic 是一个轻量的 DeepSeek Harness 可视化回答插件。它把模型
39
40
 
40
41
  插件向模型贡献一段说明可视化能力的 Prompt,默认是包内文本。
41
42
 
42
- **Settings → Mosaic** 就是这段文本的编辑页:打开即显示当前生效的内容(默认就是内置文案),可以直接改、点「保存」,随时点「恢复默认」回到插件自带文案。保存后从下一轮模型请求开始生效,所有浏览器看到的是同一份,重启也在。
43
+ **Settings → Mosaic** 就是这段文本的编辑页:打开即显示当前生效的内容(默认就是内置文案),可以直接改、点「保存」,随时点「恢复默认」回到插件自带文案。保存后从下一轮模型请求开始生效,所有浏览器看到的是同一份,重启也在。两个浏览器同时编辑时,后保存的那个会收到「已在别处被修改」,页面载入最新内容而不覆盖别人的修改。
43
44
 
44
45
  生效顺序是「你保存的覆盖 > 配置文件里的 `config.prompt` > 内置文本」。想在 profile 里批量固定一份基线,仍然可以在 `cordis.patch.yml` 给 mosaic 行加 `config.prompt`;留空、类型不对或超过 128 KB 会回落。
45
46
 
@@ -57,6 +58,26 @@ Mosaic 是一个轻量的 DeepSeek Harness 可视化回答插件。它把模型
57
58
 
58
59
  `diff` 带行号和 `-`/`+` gutter,长行自动换行。
59
60
 
61
+ `diagram` 支持 13 种图,按“读者要回答的问题”选:
62
+
63
+ | 读者要看懂 | kind | 建议规模 |
64
+ | --- | --- | --- |
65
+ | 分支、重试、判断 | `flowchart` | 9 节点 / 12 边 |
66
+ | 谁在什么时候说了什么 | `sequence` | 5 条生命线 / 12 条消息 |
67
+ | 时间线上的事件 | `timeline` | 7 项 |
68
+ | 分层结构 | `layers` | 6 层 |
69
+ | 两轴定位(优先级、取舍) | `quadrant` | 12 项 |
70
+ | 父子层级、组织关系 | `tree` | 12 节点 / 4 层 |
71
+ | 逐级流失的转化 | `funnel` | 6 级 |
72
+ | 集合重叠 | `venn` | 3 个集合 |
73
+ | 跨角色的交接 | `swimlane` | 6 步 / 5 条泳道 |
74
+ | 状态与迁移 | `state` | 9 个状态 |
75
+ | 表结构与关系 | `er` | 8 个实体 |
76
+ | 排期 | `gantt` | 12 个任务 |
77
+ | 主题发散 | `mindmap` | 12 个分支 |
78
+
79
+ `legend: [{label, tone}]` 给颜色加说明:`tone` 表达含义时写,纯装饰时省略。
80
+
60
81
  完整字段见 [`SKILL.md`](./SKILL.md)。
61
82
 
62
83
  ## 日志
@@ -71,7 +92,7 @@ $DSH_HOME/logs/dsh-mosaic/YYYY-MM-DD.jsonl # 未设置 DSH_HOME 时是 ~/.dsh/
71
92
  - 记录什么:激活/停用、Prompt 的读取与保存(来源、字符数、是否落盘)、工具执行(名称、结果状态、耗时)、路由请求(方法、状态码、耗时)、技能被读取、以及浏览器侧上报的失败(`client.` 前缀:围栏解析失败、Mermaid 失败、Prompt 读写失败、Widget 动作被限流/超限丢弃)。
72
93
  - 不记录什么:Prompt 正文、组件 JSON、任何文本字段的内容。字段名为 `prompt`/`text`/`code`/`content`/`secret`/`token`/`password`/`apiKey` 时只写字符数;其它字符串超过 300 字符会被截断。
73
94
  - 体积:单文件超过 2 MB 滚动为 `<日期>.1.jsonl`;超过 7 天的文件在下次启动时删除;文件权限 `0600`。写不进去时不会影响插件,只在宿主日志里告警一次。
74
- - 浏览器侧的失败通过同源 `POST /dsh-mosaic/log` 上报(请求体上限 4 KB,跨站 403,客户端每分钟最多 20 条)。
95
+ - 浏览器侧的失败通过同源 `POST /dsh-mosaic/log` 上报(请求体上限 4 KB,非本机权威或跨站一律 403,客户端每分钟最多 20 条);接口在日志落盘之后才回 204,看到成功就是真的写进去了。
75
96
  - 默认级别 `info`;想要更细可以给 mosaic 行加 `config.logLevel: debug`(或 `warn` 减少噪音)。
76
97
 
77
98
  排查示例:
@@ -87,15 +108,17 @@ grep -c '"event":"tool.failed"' "$DSH_HOME"/logs/dsh-mosaic/*.jsonl
87
108
 
88
109
  ## 已知边界
89
110
 
90
- - 版本号跟随「最近一次验证通过的 DeepSeek Harness 版本」:当前是 `0.2.1`(实测 rc.3、rc.5)。同一 Harness 版本下的插件修复递增补丁号;换到新 Harness 版本验证后再把版本号对齐过去。
111
+ - 本插件的 npm 版本独立递增(当前 `1.0.0`),不再与 Harness 版本号对齐;兼容范围由 `package.json` 的 `engines.dsh` 声明,最近一次实测的 Harness 版本见「安装」一节。
91
112
  - 围栏渲染靠扫描页面里的代码块实现:0.2.1 全系列(实测 rc.3 与 rc.5,master 同样)都没有给插件开放「渲染 fenced code block」的扩展点。工具通道 `render_view` + `tool.call.toolview` 是原生做法,围栏是它的补充。
92
113
  - 从 0.2.1 起代码块由 Shiki 渲染,`<code>` 上**没有** `language-*` 类;识别靠「代码块位于 assistant 行内 + 正文能解析成合法视图 spec」这条路径,用户消息、工具结果、推理块里的代码块都被排除。
93
- - `widget` 里调用 `window.mosaic.action()` 只有**真实用户手势**之后才会把动作发回模型(用浏览器的 `navigator.userActivation` 判断,Widget 自己伪造不了);确实需要自行提交的 Widget 要在节点上写 `actions: "programmatic"`,并且仍受「每分钟 10 次、单次 16 KB」的限制。
114
+ - `diagram` 与 `chart` 自带缩放/平移:卡片右上角有 `−`/`+`/`⟲`/`⤢`,放大后按住图面拖动即可平移,`Ctrl/⌘ + 滚轮`缩放,双击复位;示意图按原始尺寸渲染,不会被压缩到卡片宽度。触屏上图表区域只拦横向拖动(`touch-action: pan-y`),手指上下划仍然滚动聊天页面。
115
+ - `widget` 里调用 `window.mosaic.action()` 只有**真实用户手势**之后才会把动作发回模型:Widget 自己也在判断 `navigator.userActivation`,但决定权在宿主——消息的 nonce 就写在 Widget 自己的文档里,模型代码能读出来直接伪造,所以父页面独立再校验一次自己文档的激活状态。确实需要自行提交的 Widget 要在节点上写 `actions: "programmatic"`;无论哪种,整个会话共享「每分钟 10 次、单次 payload 16 KB(按 UTF-8 字节算)」的额度,新开一个 Widget 不会重置。浏览器若不提供 `navigator.userActivation`,非 `programmatic` 的 Widget 会被按「没有手势」处理,按钮不会回模型。
94
116
  - `widget` 是 iframe,拿不到宿主的 `ctx.locale`,因此内部文案由调用方自己写;主题靠宿主令牌值传入。围栏是独立的 React root,切换外观不会触发它重渲染,所以 widget 会观察 `body[data-ds-dark-theme]` 这一个外观属性并在变化时重发令牌——这是插件唯一读取的宿主属性。
95
117
  - Mermaid 资产走包内私有路由 `/dsh-mosaic/assets`;没有 Web 服务时 diagram 回落到内置 SVG 布局。
118
+ - 自有路由(`/prompt`、`/log`)先验 **Host 必须是本机权威**(loopback、监听地址,或 `config.trustedHosts` 里声明的),再验 `Sec-Fetch-Site` 与 `Origin`。只比 Origin 和 Host 挡不住 DNS rebinding:被 rebinding 的页面这两者都等于攻击者域名,看起来完全同源。宿主把 Web 服务绑到非 loopback 地址时,请把对应主机名写进 `config.trustedHosts`,否则这些路由会拒绝请求。
96
119
  - bundle patch 只插入 Mosaic 自己这一行,不改动任何其他插件。若你还装了别的围栏类插件,两者可以对同一段围栏各显一套,按需在 profile 里关掉其中一个即可。
97
120
  - 持久化只用「Host 数据目录里的一个 JSON 文件」,没有走 DSH 的 storageDomain。实测(rc.5):`storageDomain` 可用,但记录校验只在**重新打开**时执行(`put` 不校验);`single` 布局遇到版本变化会直接拒绝打开,`compatibleVersions` 也不适用。对一份 Prompt 来说这些能力换不来收益,反而多一个依赖和三条失败分支。
98
- - Prompt 编辑页是对平台形态的**补充,不是平台标准做法**。DSH 把 Prompt 视为 composition 配置(system-prompt 的 README 明确写着没有终端用户编辑 Prompt 的 API),标准平面是 `Config` + `cordis.patch.yml`,客户端改值应走 `@Remote`,插件自有数据应走存储服务。这里为了「只装一个包、不引入 peer 依赖」,写入用了自建的同源校验路由、持久化用了自建的 `mosaic/prompt.json`。将来宿主提供多行配置表单或标准 Remote 时,这块应当迁移过去。
121
+ - Prompt 编辑页是对平台形态的**补充,不是平台标准做法**。DSH 把 Prompt 视为 composition 配置(system-prompt 的 README 明确写着没有终端用户编辑 Prompt 的 API),标准平面是 `Config` + `cordis.patch.yml`,客户端改值应走 `@Remote`,插件自有数据应走存储服务。这里为了「只装一个包、不引入 peer 依赖」,写入用了自建路由(Host 权威校验 + 同源校验)、持久化用了自建的 `mosaic/prompt.json`。将来宿主提供多行配置表单或标准 Remote 时,这块应当迁移过去。
99
122
 
100
123
  ## 本地优先
101
124
 
@@ -131,9 +154,30 @@ dsh plugin --profile desktop add link:/absolute/path/to/dsh-mosaic
131
154
 
132
155
  安装后重启或刷新 DSH Web,新会话即可使用。bundle patch 只插入本插件一行,不会关闭或覆盖别人的插件。
133
156
 
157
+ **兼容性**:npm 版本独立于 DeepSeek Harness 版本,`1.0.x` 对应下方实测的 Harness 范围;声明式范围见 `package.json` 的 `engines.dsh`(当前 `>=0.2.1-rc.1 <0.3.0-0`)。
158
+
159
+ | 插件版本 | 实测通过的 DeepSeek Harness | 说明 |
160
+ | --- | --- | --- |
161
+ | `1.0.0` | `0.2.1-rc.5`(同时复核 rc.3 行为) | 首个稳定版;含 Widget 沙箱、路由权威、Prompt 并发等修正 |
162
+ | `0.2.1` | `0.2.1-rc.3`、`0.2.1-rc.5` | 上一版,npm 上的历史发布 |
163
+
164
+ Harness 升级到 `0.3.x` 后,请先在新版本上验证再放宽 `engines.dsh`,并在此表补一行。
165
+
166
+ ## 校验与建议
167
+
168
+ `validate_view` 和 `render_view` 除了报错,还会返回 `warnings=` 行,指出“能渲染但读起来费劲”的地方,例如:
169
+
170
+ - 表格超过 8 行、列表超过 7 项、图表超过 12 个点或只有 1 个点;
171
+ - 同一个视图里 `accent` 用在超过 2 个元素上;
172
+ - 卡片嵌套超过 2 层;
173
+ - 某类图超过它自己的可读规模(flowchart 9 节点、sequence 5 条生命线……);
174
+ - `widget` 里引用了外部 CDN 资源(沙箱没有网络,必定加载失败)。
175
+
176
+ 这些不阻塞渲染,只是给出改法。
177
+
134
178
  ## 限制
135
179
 
136
- - 80 节点、6 层嵌套、表格 40×8、图表 40 点、diagram 12 节点/20 边、widget 代码 20k。
180
+ - 硬上限:80 节点、6 层嵌套、表格 40×8、图表 40 点、diagram 12 节点/20 边、widget 代码 20k。可读规模见上文的表格与 `SKILL.md`。
137
181
  - 不读本地文件、不渲染 docx/pptx/xlsx;这些用 `link` 卡片交给宿主打开。
138
182
  - 不内置数学公式排版;公式可以放在 `code` 或普通文本里。
139
183
  - 围栏渲染依赖 DSH 当前的代码块 DOM;宿主结构变化时可能需要适配。
package/SKILL.md CHANGED
@@ -27,7 +27,7 @@ Rules:
27
27
  - Prefer one focal component; do not repeat the same data as both a table and a chart.
28
28
  - For files or Office documents, use a `link` card. Mosaic does not read local files or render Office; those open in the host app.
29
29
  - Media (`image`, `video`, `audio`, `pdf`) accepts an `http(s)` URL or a root-relative path.
30
- - `validate_view` is available for a preflight check on complex specs. For a flowchart longer than 6 nodes, prefer `direction: "TB"`; a long `LR` diagram scrolls horizontally.
30
+ - `validate_view` is a preflight check: besides errors it returns `warnings=` lines for a valid but heavy view. For a flowchart longer than 6 nodes, prefer `direction: "TB"`; a long `LR` diagram scrolls horizontally.
31
31
 
32
32
  ## Components
33
33
 
@@ -62,16 +62,45 @@ Rules:
62
62
  - `donut` is single-series; use `data` only.
63
63
  - For grouped bars or multi-line, use `series`.
64
64
  - Keep it to 40 points and 8 series.
65
- - `diagram`: `{kind: "flowchart|sequence|timeline|layers", title?, direction?: "TB|LR", ...}` — see the section below.
65
+ - `diagram`: `{kind, title?, legend?, ...}` — see the section below.
66
66
 
67
67
  ### Diagram
68
68
 
69
- When you show a flow, sequence, timeline, or layered architecture, use `diagram`. It is plain JSON; do not hand-write Mermaid markup for these unless the content needs a real Mermaid engine inside a widget. Flowchart and sequence nodes are rendered through a lazy-loaded Mermaid engine; timeline and layers use the built-in renderer. Every diagram card also has a `drawio` download button.
70
-
71
- - `flowchart`: `nodes: [{id, label, sub?, tone?: "accent|success|warning|danger|muted", shape?: "box|round|diamond"}], edges: [{from, to, label?, kind?: "solid|dashed|accent"}], direction?: "TB|LR"`
69
+ Use `diagram` for a flow, a sequence, a timeline, a hierarchy, or a two-axis map. It is plain JSON; do not hand-write Mermaid markup for these unless the content needs a real Mermaid engine inside a widget.
70
+
71
+ Pick the kind by the question the reader has, and stay near its readable size:
72
+
73
+ | The reader must see… | `kind` | Readable size |
74
+ |---|---|---|
75
+ | branches, retries, gates | `flowchart` | 9 nodes, 12 edges |
76
+ | who says what, in order | `sequence` | 5 lifelines, 12 messages |
77
+ | events on a line | `timeline` | 7 items |
78
+ | stacked levels | `layers` | 6 layers |
79
+ | two-axis positioning | `quadrant` | 12 items |
80
+ | parent → children | `tree` | 12 nodes, depth 4 |
81
+ | a drop-off across stages | `funnel` | 6 steps |
82
+ | overlap between sets | `venn` | 3 sets |
83
+ | hand-offs across owners | `swimlane` | 6 steps, 5 lanes |
84
+ | states and transitions | `state` | 9 states |
85
+ | tables and relations | `er` | 8 entities, 8 relations |
86
+ | scheduled work | `gantt` | 12 tasks |
87
+ | a topic map | `mindmap` | 12 branches |
88
+
89
+ Fields by kind:
90
+
91
+ - `flowchart` / `state`: `nodes: [{id, label, sub?, tone?: "accent|success|warning|danger|muted", shape?: "box|round|diamond"}], edges: [{from, to, label?, kind?: "solid|dashed|accent"}]`; `flowchart` also takes `direction?: "TB|LR"`, and `state` takes `initial?: id` and `final?: [id]`.
72
92
  - `sequence`: `participants: [{id, label}], messages: [{from, to, label, kind?: "sync|return|async"}]`
73
93
  - `timeline`: `items: [{title, desc?, time?}]`
74
94
  - `layers`: `layers: [{title, items: string[]}]`
95
+ - `quadrant`: `xAxis: [low, high], yAxis: [low, high], quadrants?: {tl, tr, bl, br}, items: [{label, x, y, tone?}]` — `x` and `y` are 0–1, with `(0,0)` at the bottom-left.
96
+ - `tree` / `mindmap`: `root: {label, sub?, tone?, children?: [same shape]}` — one root, at most four levels.
97
+ - `funnel`: `items: [{label, value}]` — two to eight stages, widest first.
98
+ - `venn`: `sets: [{label, tone?}], center?` — two or three sets.
99
+ - `swimlane`: `lanes: [string], steps: [{lane, label}]` — `lane` indexes `lanes`; steps run left to right in array order.
100
+ - `er`: `entities: [{name, fields: [{name, type?, key?: "PK|FK"}]}], relations?: [{from, to, label?, cardinality?: "one-to-one|one-to-many|many-to-one|many-to-many"}]`
101
+ - `gantt`: `tasks: [{name, start, end, section?}]` — dates are `YYYY-MM-DD`.
102
+
103
+ `legend: [{label, tone?}]` names what a colour means, for every kind. Add it when `tone` carries meaning (a focal step, a failure state); leave it out when the colours are decoration.
75
104
 
76
105
  A single `diagram` node fragment (this is not a complete fence; wrap it in `{"items":[…]}` under a `dsh-view` tag):
77
106
 
@@ -85,10 +114,10 @@ A single `diagram` node fragment (this is not a complete fence; wrap it in `{"it
85
114
  {"from":"start","to":"check"},
86
115
  {"from":"check","to":"hit","label":"是","kind":"accent"},
87
116
  {"from":"check","to":"miss","label":"否"}
88
- ]}
117
+ ],"legend":[{"label":"命中路径","tone":"accent"}]}
89
118
  ```
90
119
 
91
- Keep a flowchart to 12 nodes / 20 edges; sequence to 8 participants / 20 messages; timeline to 12 items; layers to 8 layers.
120
+ `flowchart`, `sequence`, `state`, `er`, `gantt`, and `mindmap` render through a lazy-loaded Mermaid engine; `timeline`, `layers`, `quadrant`, `tree`, `funnel`, `venn`, and `swimlane` are drawn by Mosaic itself. Every diagram card has `svg`, `png`, and `drawio` export buttons in its header.
92
121
 
93
122
  ### Media and links
94
123
 
@@ -122,7 +151,7 @@ A widget is for content the fixed components cannot express: a game, a physics/m
122
151
  - CSP: no network; inline scripts/styles and `data:`/`blob:` images or media only
123
152
  - no access to the host page, cookies, or local storage
124
153
 
125
- `window.mosaic.action(name, payload)` reaches the model only after a real user gesture inside the widget; set `actions: "programmatic"` when the widget must submit on its own (still capped at 10 calls a minute and 16 KB per payload).
154
+ `window.mosaic.action(name, payload)` reaches the model only after a real user gesture; the Host re-checks that gesture, because a widget can read the message nonce out of its own document. Set `actions: "programmatic"` when the widget must submit on its own. Either way the whole session shares a budget of 10 actions a minute and 16 KB per payload, so a widget cannot reset it by re-rendering.
126
155
 
127
156
  Inside the widget:
128
157
 
@@ -151,3 +180,34 @@ After an action, update the existing view instead of restating the whole answer.
151
180
  - Choose the smallest component that carries the structure; avoid decorative cards, repeated summaries, and nested scrolling.
152
181
  - Keep the tree under 80 nodes and 6 levels.
153
182
  - A malformed fence falls back to the original code block; a widget that fails to boot shows its source and an error strip.
183
+
184
+ ### What reads as decoration
185
+
186
+ Run this before emitting a view. Each row is a real habit, not a hypothetical:
187
+
188
+ | Anti-pattern | Do instead |
189
+ |---|---|
190
+ | Three equal-width `stat` cards as a header | One stat, or a table |
191
+ | `tone: "accent"` on more than two elements | Decide what is focal and accent only that |
192
+ | Cards nested more than two deep | Flatten one level |
193
+ | A table longer than 8 rows in chat | Add `filter`/`sort`, or split the table |
194
+ | A chart with one point | Write the sentence |
195
+ | A diagram of two boxes | Write the sentence |
196
+ | The same numbers in a table *and* a chart | Pick one |
197
+ | A component that only restates the paragraph above it | Delete it |
198
+ | A `widget` loading a CDN script | Inline the library; the sandbox has no network |
199
+
200
+ `validate_view` returns these as `warnings=` lines, so a valid but heavy view is named before you emit it.
201
+
202
+ ### Budgets
203
+
204
+ The hard caps only keep a view renderable. These are the sizes at which a chat card still reads at a glance:
205
+
206
+ | Component | Readable | Hard cap |
207
+ |---|---|---|
208
+ | `table` | 8 rows | 40 rows × 8 columns |
209
+ | `chart` | 12 points | 40 points, 8 series |
210
+ | `list` | 7 items | 30 items |
211
+ | `diagram` | per-kind table above | 12 nodes / 20 edges |
212
+ | focal accents per view | 2 | — |
213
+ | card nesting | 2 levels | 6 levels |
Binary file
@@ -4,7 +4,7 @@
4
4
 
5
5
  1. Create the GitHub repository. Add the topic `dsh-plugin`; the catalog's automated check looks for it.
6
6
  2. Fill `repository`, `homepage`, and `bugs` in `package.json` after the repository URL exists.
7
- 3. Keep at least one version published on npm before submitting to the catalog. The package version tracks the most recently verified DeepSeek Harness version (`0.2.1` today); plugin-only fixes bump the patch number until the next Harness verification realigns it. `npm pack --dry-run` must include `lib/assets/mermaid.min.js`, `screenshots.json`, and `assets/screenshots/`.
7
+ 3. Keep at least one version published on npm before submitting to the catalog. The package version follows plain semver (currently `1.0.0`) and is independent of the DeepSeek Harness version; the verified Harness range lives in `engines.dsh` and in the README compatibility table, and both are updated after each real verification. `npm pack --dry-run` must include `lib/assets/mermaid.min.js`, `screenshots.json`, and `assets/screenshots/`.
8
8
  4. Add the repository to the catalog with `docs/catalog-entry.example.yml` copied to `data/plugins/imwhatiam__dsh-mosaic.yml` in `awesome-dsh-plugin/awesome-dsh-plugin`. One entry per PR; the repo must be at least one day old.
9
9
  5. Add real screenshots under `assets/screenshots/` and list them in `screenshots.json`. Verified against installed DSH 0.2.1-rc.5: the Plugins page reads only `meta.title`, `meta.description` and the icon, so `screenshots.json` is a **catalog/community** convention, not a DSH one. Keep the paths inside the package and in sync with the filenames.
10
10
  6. The catalog `icon` must be a GitHub-hosted raster image. This repo ships `assets/icon-180.png`; SVG is rejected by the catalog.