dsh-mosaic 0.2.1

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 (47) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +151 -0
  4. package/SKILL.md +153 -0
  5. package/THIRD_PARTY_NOTICES.md +79 -0
  6. package/assets/icon-180.png +0 -0
  7. package/assets/icon.svg +13 -0
  8. package/assets/screenshots/preview.jpg +0 -0
  9. package/cordis.patch.yml +10 -0
  10. package/docs/catalog-entry.example.yml +9 -0
  11. package/docs/publishing.md +29 -0
  12. package/lib/assets/licenses/braintree-sanitize-url.LICENSE +21 -0
  13. package/lib/assets/licenses/chevrotain.LICENSE +202 -0
  14. package/lib/assets/licenses/cytoscape-cose-bilkent.LICENSE +21 -0
  15. package/lib/assets/licenses/cytoscape-fcose.LICENSE +19 -0
  16. package/lib/assets/licenses/cytoscape.LICENSE +19 -0
  17. package/lib/assets/licenses/d3-sankey.LICENSE +27 -0
  18. package/lib/assets/licenses/d3.LICENSE +13 -0
  19. package/lib/assets/licenses/dagre-d3-es.LICENSE +23 -0
  20. package/lib/assets/licenses/dayjs.LICENSE +21 -0
  21. package/lib/assets/licenses/dompurify.LICENSE +202 -0
  22. package/lib/assets/licenses/elkjs.LICENSE +263 -0
  23. package/lib/assets/licenses/es-toolkit.LICENSE +21 -0
  24. package/lib/assets/licenses/iconify-utils.LICENSE +21 -0
  25. package/lib/assets/licenses/katex.LICENSE +21 -0
  26. package/lib/assets/licenses/khroma.LICENSE +21 -0
  27. package/lib/assets/licenses/marked.LICENSE +44 -0
  28. package/lib/assets/licenses/mermaid-js-parser.LICENSE +21 -0
  29. package/lib/assets/licenses/mermaid.LICENSE +21 -0
  30. package/lib/assets/licenses/roughjs.LICENSE +21 -0
  31. package/lib/assets/licenses/stylis.LICENSE +21 -0
  32. package/lib/assets/licenses/ts-dedent.LICENSE +21 -0
  33. package/lib/assets/licenses/types-d3.LICENSE +21 -0
  34. package/lib/assets/licenses/upsetjs-venn.js.LICENSE +22 -0
  35. package/lib/assets/licenses/uuid.LICENSE +9 -0
  36. package/lib/assets/mermaid.min.js +4330 -0
  37. package/lib/client.js +3191 -0
  38. package/lib/index.js +365 -0
  39. package/lib/log.js +212 -0
  40. package/lib/prompt-store.js +354 -0
  41. package/lib/render-tool.js +162 -0
  42. package/lib/validate-tool.js +160 -0
  43. package/lib/validate.js +1070 -0
  44. package/locale/en.json +6 -0
  45. package/locale/zh.json +6 -0
  46. package/package.json +66 -0
  47. package/screenshots.json +3 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ > 版本规则:插件版本与**最近一次验证通过的 DeepSeek Harness 版本**保持一致。当前对齐 `0.2.1`(实测 rc.3 与 rc.5)。
4
+ > 同一 Harness 版本下的插件自身修复按补丁号递增(`0.2.1` → `0.2.2` ……);一旦换到新的 Harness 版本验证通过,版本号立即改成该 Harness 版本。
5
+
6
+ ## 0.2.1
7
+
8
+
9
+ - Prompt 注册的生命周期修正:更新或读取覆盖之后,插件卸载会撤销**当前**的 section(此前留下的是第一个);异步读取在卸载后不再注册。
10
+ - Prompt 读取接口也做同源校验;响应不再包含 Host 绝对路径;并发保存串行化,磁盘与状态不会再分叉。
11
+ - 覆盖文件改为原子写入:同目录独占临时文件 + `fsync` + `rename` + 目录同步,权限 `0600`;失败时旧文件保持原样,临时文件清理。
12
+ - 读取覆盖时区分「文件不存在」「内容损坏」「不可读」:后两种会在设置页提示并按内置内容运行,原文件保留以便修复。
13
+ - Widget 动作三重限制:默认要求真实用户手势(`navigator.userActivation`)、每分钟最多 10 次、单次 payload 不超过 16 KB;需要自动提交时在节点上显式写 `actions: "programmatic"`。
14
+ - `SKILL.md` 的 frontmatter 描述加上引号,成为合法 YAML。
15
+ - 删除仅被测试引用的 `lib/widget.js`(与客户端沙箱文档重复且已分叉)。
16
+ - 新增诊断日志:`$DSH_HOME/logs/dsh-mosaic/<日期>.jsonl`,一行一个结构化事件(激活/停用、Prompt 生命周期、工具执行、路由、技能、浏览器侧失败),按天分文件、2 MB 滚动、保留 7 天、权限 `0600`;用户内容只记长度不记正文;`config.logLevel` 可调级别。
17
+
18
+ ## 0.4.1
19
+
20
+ - Mermaid now ships as the unmodified official 12.2.0 distribution
21
+ (`lib/assets/mermaid.min.js`, MIT) instead of a wrapper derived from another
22
+ plugin. Rendering, theme mapping and SVG sanitizing are this package's own code.
23
+ - The lazy asset URL is versioned (`?v=…`) and answered with an immutable cache entry;
24
+ the client asks the Host for the URL first and keeps one literal fallback.
25
+ - `THIRD_PARTY_NOTICES.md` lists every dependency bundled inside the Mermaid build with
26
+ its license, and the license texts ship under `lib/assets/licenses/`.
27
+ - The bundle patch no longer disables another plugin: it only inserts this package's
28
+ row.
29
+
30
+ ## 0.4.0
31
+
32
+ - One visual style: the three-theme system (编辑 / 卡片 / 玻璃), its picker, tokens and
33
+ stored keys are removed.
34
+ - The model prompt is editable again, this time properly: the settings page shows the
35
+ effective text (the built-in prompt by default), saves an override, and restores the
36
+ default. Precedence is user override > `config.prompt` > built-in; the override lives
37
+ in the Host data directory, and the write route accepts same-origin JSON only.
38
+ - Visible UI copy moved to the Client locale service with `en` and `zh` dictionaries.
39
+ - Styles are tagged with this package's identity so the client module system can reclaim
40
+ them; resources register through named effects.
41
+ - The fullscreen surface renders through a portal inside its own component instead of a
42
+ second React root on `document.body`.
43
+ - The lazy Mermaid asset moved to the package-private `/dsh-mosaic/assets` route and
44
+ registers on `window.__MosaicAssets__`.
45
+ - Theme values come from host tokens only; the plugin no longer guesses light/dark.
46
+ - The settings page is a localized explanation of the config key; copying a block's JSON
47
+ moved into the block header.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-mosaic contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # dsh-mosaic
2
+
3
+ Mosaic 是一个轻量的 DeepSeek Harness 可视化回答插件。它把模型的回答从纯文本升级为**紧凑的原生 React 组件**,并保留一个受控的**沙箱 HTML/JS 逃生舱**,用来承载固定组件做不了的游戏、模拟器、音频、地图或自定义画布。
4
+
5
+ 它是**可选增强**,不强制每个回答都带 UI:当指标、对比、流程或交互用组件更容易扫读时,模型可以输出 `dsh-view`;纯文字回答同样有效,不需要为了“用插件”而加组件。
6
+
7
+ 它刻意不追求“什么都能画”:
8
+
9
+ - 主包没有 ECharts / Three.js;flowchart / sequence 用官方 Mermaid 发行包,作为懒加载资产,只在出现 diagram 时下载(原始 5.5 MB,gzip 后约 1.3 MB,带版本号可长期缓存)。
10
+ - 每个 diagram 卡片右上角有 `drawio` 按钮,可导出标准 mxGraph XML,在 draw.io 里继续编辑。
11
+ - 组件目录保持精简,字段、校验、提示词来自同一份定义。
12
+ - 普通文件/Office 只给链接,不在聊天里硬解。
13
+ - 交互默认在本地完成;只有声明了 `action` 的组件才回模型。
14
+
15
+ ## 预览
16
+
17
+ 实际 DSH Web 页面渲染:
18
+
19
+ ![Mosaic 预览](https://raw.githubusercontent.com/imwhatiam/dsh-mosaic/main/assets/screenshots/preview.jpg)
20
+
21
+ ## 两个通道
22
+
23
+ **正文围栏**:在回答里写 `dsh-view` JSON。围栏标记必须正好是 `dsh-view`;如果模型误用 `json`,客户端在 assistant 行内有一层兼容识别,但不要依赖它。
24
+
25
+ ````text
26
+ ```dsh-view
27
+ {"title":"Ops snapshot","items":[
28
+ {"type":"stat","label":"Uptime","value":"99.96%","delta":"+0.02%"},
29
+ {"type":"callout","content":"No incidents in the last 7 days."}
30
+ ]}
31
+ ```
32
+ ````
33
+
34
+ **工具卡片**:模型调用 `render_view`,适合独立面板、表格、图表或小组件。另有 `validate_view` 做发出前校验。
35
+
36
+ ## 一套视觉风格 + 可覆盖的模型 Prompt
37
+
38
+ 界面只有一套轻卡片风格,颜色全部取自主题令牌(`--dsw-alias-*`),因此自动跟随 DSH 的浅色/深色与字号,不再有主题切换器。
39
+
40
+ 插件向模型贡献一段说明可视化能力的 Prompt,默认是包内文本。
41
+
42
+ **Settings → Mosaic** 就是这段文本的编辑页:打开即显示当前生效的内容(默认就是内置文案),可以直接改、点「保存」,随时点「恢复默认」回到插件自带文案。保存后从下一轮模型请求开始生效,所有浏览器看到的是同一份,重启也在。
43
+
44
+ 生效顺序是「你保存的覆盖 > 配置文件里的 `config.prompt` > 内置文本」。想在 profile 里批量固定一份基线,仍然可以在 `cordis.patch.yml` 给 mosaic 行加 `config.prompt`;留空、类型不对或超过 128 KB 会回落。
45
+
46
+ 覆盖内容存在 Host 数据目录的 `mosaic/prompt.json`,不进 profile 的组合文件,所以升级、换浏览器都不丢。写入是原子的(临时文件 + fsync + rename,权限 `0600`),失败时旧内容保持原样;写不进去时页面会明确提示「本次仅在进程内生效」。文件如果损坏或读不出来,页面同样会提示,并按内置内容运行、保留原文件供你修复。
47
+
48
+ 读写接口都只接受**同源**请求(核对浏览器的 `Sec-Fetch-Site` 与 `Origin`),写请求还要求 `content-type: application/json`;响应里不带 Host 的绝对路径。跨站页面既读不到也改不了这段文本。渲染出的卡片右上角另有「复制 JSON」,用于取回该块的围栏文本。
49
+
50
+ ## 组件
51
+
52
+ - 布局:`text` `row` `col` `grid` `card` `divider` `spacer`
53
+ - 展示:`stat` `badge` `progress` `list` `table` `keyvalue` `steps` `callout` `code` `diff` `chart` `diagram`
54
+ - 媒体:`image` `video` `audio` `pdf` `link`
55
+ - 交互:`button` `input` `select` `slider` `tabs` `quiz`
56
+ - 高级:`widget`
57
+
58
+ `diff` 带行号和 `-`/`+` gutter,长行自动换行。
59
+
60
+ 完整字段见 [`SKILL.md`](./SKILL.md)。
61
+
62
+ ## 日志
63
+
64
+ 插件会把自己的运行情况写进一个 JSONL 文件,出问题时优先看它:
65
+
66
+ ```
67
+ $DSH_HOME/logs/dsh-mosaic/YYYY-MM-DD.jsonl # 未设置 DSH_HOME 时是 ~/.dsh/logs/dsh-mosaic/
68
+ ```
69
+
70
+ - 一行一个 JSON 对象,字段固定:`ts`、`level`(debug/info/warn/error)、`event`,其余按事件附带(如 `tool`、`ms`、`status`、`route`、`source`、`reason`)。
71
+ - 记录什么:激活/停用、Prompt 的读取与保存(来源、字符数、是否落盘)、工具执行(名称、结果状态、耗时)、路由请求(方法、状态码、耗时)、技能被读取、以及浏览器侧上报的失败(`client.` 前缀:围栏解析失败、Mermaid 失败、Prompt 读写失败、Widget 动作被限流/超限丢弃)。
72
+ - 不记录什么:Prompt 正文、组件 JSON、任何文本字段的内容。字段名为 `prompt`/`text`/`code`/`content`/`secret`/`token`/`password`/`apiKey` 时只写字符数;其它字符串超过 300 字符会被截断。
73
+ - 体积:单文件超过 2 MB 滚动为 `<日期>.1.jsonl`;超过 7 天的文件在下次启动时删除;文件权限 `0600`。写不进去时不会影响插件,只在宿主日志里告警一次。
74
+ - 浏览器侧的失败通过同源 `POST /dsh-mosaic/log` 上报(请求体上限 4 KB,跨站 403,客户端每分钟最多 20 条)。
75
+ - 默认级别 `info`;想要更细可以给 mosaic 行加 `config.logLevel: debug`(或 `warn` 减少噪音)。
76
+
77
+ 排查示例:
78
+
79
+ ```bash
80
+ # 只看错误
81
+ grep -h '"level":"error"' "$DSH_HOME"/logs/dsh-mosaic/*.jsonl
82
+ # 看最近一次激活和 Prompt 来源
83
+ grep -h '"event":"plugin.activate"\|"event":"prompt.hydrate"' "$DSH_HOME"/logs/dsh-mosaic/*.jsonl | tail -5
84
+ # 统计工具失败次数
85
+ grep -c '"event":"tool.failed"' "$DSH_HOME"/logs/dsh-mosaic/*.jsonl
86
+ ```
87
+
88
+ ## 已知边界
89
+
90
+ - 版本号跟随「最近一次验证通过的 DeepSeek Harness 版本」:当前是 `0.2.1`(实测 rc.3、rc.5)。同一 Harness 版本下的插件修复递增补丁号;换到新 Harness 版本验证后再把版本号对齐过去。
91
+ - 围栏渲染靠扫描页面里的代码块实现:0.2.1 全系列(实测 rc.3 与 rc.5,master 同样)都没有给插件开放「渲染 fenced code block」的扩展点。工具通道 `render_view` + `tool.call.toolview` 是原生做法,围栏是它的补充。
92
+ - 从 0.2.1 起代码块由 Shiki 渲染,`<code>` 上**没有** `language-*` 类;识别靠「代码块位于 assistant 行内 + 正文能解析成合法视图 spec」这条路径,用户消息、工具结果、推理块里的代码块都被排除。
93
+ - `widget` 里调用 `window.mosaic.action()` 只有**真实用户手势**之后才会把动作发回模型(用浏览器的 `navigator.userActivation` 判断,Widget 自己伪造不了);确实需要自行提交的 Widget 要在节点上写 `actions: "programmatic"`,并且仍受「每分钟 10 次、单次 16 KB」的限制。
94
+ - `widget` 是 iframe,拿不到宿主的 `ctx.locale`,因此内部文案由调用方自己写;主题靠宿主令牌值传入。围栏是独立的 React root,切换外观不会触发它重渲染,所以 widget 会观察 `body[data-ds-dark-theme]` 这一个外观属性并在变化时重发令牌——这是插件唯一读取的宿主属性。
95
+ - Mermaid 资产走包内私有路由 `/dsh-mosaic/assets`;没有 Web 服务时 diagram 回落到内置 SVG 布局。
96
+ - bundle patch 只插入 Mosaic 自己这一行,不改动任何其他插件。若你还装了别的围栏类插件,两者可以对同一段围栏各显一套,按需在 profile 里关掉其中一个即可。
97
+ - 持久化只用「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 时,这块应当迁移过去。
99
+
100
+ ## 本地优先
101
+
102
+ 表格排序/筛选/导出、tabs、quiz 判题、输入值、slider 拖动都在浏览器内完成。`button` 的 `local` 支持 `reset`、`copy`、`open`、`fullscreen`;只有 `action` 会把一条 `[mosaic-action]` 用户消息发回模型。
103
+
104
+ 状态按「会话 + 槽位 + 内容指纹」写入 `localStorage`,最多保留 100 块,刷新或重开会话时恢复。界面文案(按钮、提示、诊断)走 DSH 的 Client locale 服务,内置 `en` 与 `zh` 两套。
105
+
106
+ ## 沙箱 widget
107
+
108
+ `widget.code` 是 HTML 片段或完整文档,渲染在:
109
+
110
+ ```html
111
+ <iframe sandbox="allow-scripts allow-forms allow-popups" srcDoc="..."></iframe>
112
+ ```
113
+
114
+ - 不透明源(没有 `allow-same-origin`),读不到宿主页面、cookie、storage。
115
+ - CSP:没有网络;脚本仅内联/eval/blob;图片和媒体只允许 `data:`/`blob:`。
116
+ - 宿主校验 `event.source` 和每次挂载的 nonce。
117
+ - 主题通过 `--mosaic-*` CSS 变量注入;widget 用 `window.mosaic.action(name, payload)` 回传动作,用 `window.mosaic.resize()` 主动报高度。
118
+ - 外部 CDN 脚本会被 CSP 拦掉;需要库时把代码内联进 widget。
119
+
120
+ ## 安装
121
+
122
+ 在 DSH Plugin Manager 中安装本插件,或使用命令行:
123
+
124
+ ```bash
125
+ # 发布到 npm 后(普通 dsh web profile)
126
+ dsh plugin --profile web add dsh-mosaic
127
+
128
+ # 本地开发目录
129
+ dsh plugin --profile desktop add link:/absolute/path/to/dsh-mosaic
130
+ ```
131
+
132
+ 安装后重启或刷新 DSH Web,新会话即可使用。bundle patch 只插入本插件一行,不会关闭或覆盖别人的插件。
133
+
134
+ ## 限制
135
+
136
+ - 80 节点、6 层嵌套、表格 40×8、图表 40 点、diagram 12 节点/20 边、widget 代码 20k。
137
+ - 不读本地文件、不渲染 docx/pptx/xlsx;这些用 `link` 卡片交给宿主打开。
138
+ - 不内置数学公式排版;公式可以放在 `code` 或普通文本里。
139
+ - 围栏渲染依赖 DSH 当前的代码块 DOM;宿主结构变化时可能需要适配。
140
+
141
+ ## 开发
142
+
143
+ ```bash
144
+ node --test test/*.test.mjs
145
+ node --check lib/index.js
146
+ node --check lib/client.js
147
+ ```
148
+
149
+ ## License
150
+
151
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,153 @@
1
+ ---
2
+ name: mosaic
3
+ description: "Use Mosaic when a visual component makes a DeepSeek Harness answer easier to scan: dsh-view components, local-first interactions, diagrams, and sandboxed HTML/JS widgets. It is optional; prose is fine when no component helps."
4
+ ---
5
+
6
+ # Mosaic — compact visual answers
7
+
8
+ Mosaic is optional. Use it when a component makes the answer easier to scan; plain prose is fine. Do not add UI just because the answer has structure or numbers.
9
+
10
+ Mosaic renders a small declarative component tree inside a `dsh-view` fence, or as a `render_view` tool card. Use the smallest format that fits; do not decorate an answer that a sentence already explains.
11
+
12
+ ## Fence
13
+
14
+ ````text
15
+ ```dsh-view
16
+ {"title":"Ops snapshot","gap":12,"items":[
17
+ {"type":"stat","label":"Uptime","value":"99.96%","delta":"+0.02%"},
18
+ {"type":"callout","content":"No incidents in the last 7 days."}
19
+ ]}
20
+ ```
21
+ ````
22
+
23
+ Rules:
24
+
25
+ - Put the fence in the reply body, never in reasoning. The opening tag must be exactly `dsh-view`; a `json`/`text`/unlabelled fence normally stays a code block. The client has a narrow legacy fallback for a complete spec in an assistant row, but never rely on it.
26
+ - Keep the JSON strict; no comments, trailing commas, or Markdown inside text fields.
27
+ - Prefer one focal component; do not repeat the same data as both a table and a chart.
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
+ - 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.
31
+
32
+ ## Components
33
+
34
+ ### Layout
35
+
36
+ - `text`: `{content, size?: "display|h1|h2|h3|body|muted|caption", align?: "left|center|right", tone?: "danger|success|warning|info|muted", mono?: true}`
37
+ - `row`: `{items, gap?, align?: "start|center|end|between", wrap?}`
38
+ - `col`: `{items, gap?, width?: 1-100}`
39
+ - `grid`: `{cols: 1-6, items, gap?}`; a child can carry `"span": 2` for a bento layout
40
+ - `card`: `{items, title?, eyebrow?, tone?: "info|success|warning|danger", flat?}`
41
+ - `divider`: `{label?}`
42
+ - `spacer`: `{}`
43
+
44
+ ### Display
45
+
46
+ - `stat`: `{label, value, delta?, spark?: [number, ...]}` — delta starting with `+` is green, `-` is red; spark is a micro trend line (2–20 points).
47
+ - `badge`: `{label, tone?: "success|warn|danger|accent"}`
48
+ - `progress`: `{value: 0-100, label?, valueLabel?, variant?: "bar|ring", target?}`
49
+ - `table`: `{columns: string[], rows: (string|number)[][], types?: ("text|num|delta|bar|badge")[], total?, export?, filter?: field-id, sort?: field-id}`
50
+ - Header click sorts locally (asc → desc → off). `types` controls right alignment, signed colors, bars, badges.
51
+ - `filter` binds to an `input`/`select` with that `id`; typing filters rows locally.
52
+ - `sort` binds a `select` whose value is a column name.
53
+ - `export:true` adds copy-as-Markdown / copy-as-CSV chips.
54
+ - `total:true` appends a numeric total row.
55
+ - `list`: `{items: string[] | {title, desc}[] | nodes[], filter?: field-id}`
56
+ - `keyvalue`: `{pairs: [{key, value}]}`
57
+ - `steps`: `{steps: [{title, desc?}], current?: number}`
58
+ - `callout`: `{content, title?, tone?: "info|success|warning|error"}`
59
+ - `code`: `{code, lang?}`
60
+ - `diff`: `{diffs: [{path, newText, oldText?}]}`
61
+ - `chart`: `{kind: "bars|line|donut", data?: [{label, value}], series?: [{label, data: [{label, value}]}], stacked?, title?}`
62
+ - `donut` is single-series; use `data` only.
63
+ - For grouped bars or multi-line, use `series`.
64
+ - Keep it to 40 points and 8 series.
65
+ - `diagram`: `{kind: "flowchart|sequence|timeline|layers", title?, direction?: "TB|LR", ...}` — see the section below.
66
+
67
+ ### Diagram
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"`
72
+ - `sequence`: `participants: [{id, label}], messages: [{from, to, label, kind?: "sync|return|async"}]`
73
+ - `timeline`: `items: [{title, desc?, time?}]`
74
+ - `layers`: `layers: [{title, items: string[]}]`
75
+
76
+ A single `diagram` node fragment (this is not a complete fence; wrap it in `{"items":[…]}` under a `dsh-view` tag):
77
+
78
+ ```text
79
+ {"type":"diagram","kind":"flowchart","direction":"TB","nodes":[
80
+ {"id":"start","label":"收到请求","tone":"accent"},
81
+ {"id":"check","label":"缓存命中?","shape":"diamond"},
82
+ {"id":"hit","label":"返回缓存","tone":"success"},
83
+ {"id":"miss","label":"回源计算","tone":"warning"}
84
+ ],"edges":[
85
+ {"from":"start","to":"check"},
86
+ {"from":"check","to":"hit","label":"是","kind":"accent"},
87
+ {"from":"check","to":"miss","label":"否"}
88
+ ]}
89
+ ```
90
+
91
+ Keep a flowchart to 12 nodes / 20 edges; sequence to 8 participants / 20 messages; timeline to 12 items; layers to 8 layers.
92
+
93
+ ### Media and links
94
+
95
+ - `image`: `{src, alt?, caption?}`
96
+ - `video`: `{src, poster?, aspect?: "16:9|4:3|1:1|9:16", caption?}`
97
+ - `audio`: `{src, caption?}`
98
+ - `pdf`: `{src, title?}`
99
+ - `link`: `{label, href, description?}` — `http(s)` or `mailto`; for a local file/Office document, point at the host URL or describe where to open it.
100
+
101
+ Images can be expanded to a full-screen lightbox; video/audio use the browser's native controls.
102
+
103
+ ### Interactive
104
+
105
+ - `button`: `{label, action?, local?, text?, url?, tone?: "primary|danger|success|ghost", full?}`
106
+ - `action` sends `[mosaic-action]` to the model.
107
+ - `local` runs in the browser: `reset`, `copy` (needs `text`), `open` (needs `url`), `fullscreen`.
108
+ - No `action` and no `local` renders a disabled control.
109
+ - `input`: `{id?, label?, placeholder?, value?, action?}` — Enter or blur sends when `action` is set; `id` makes the value available to a sibling table/list filter and to a submit-style action.
110
+ - `select`: `{options: string[], id?, label?, selected?, action?}`
111
+ - `slider`: `{min?, max?, value?, step?, id?, label?, action?}` — drag is debounced; local value updates immediately.
112
+ - `tabs`: `{tabs: [{label, items}]}`
113
+ - `quiz`: `{question, options: [{label, correct?, feedback?}], explanation?}` — local grading, no model round trip; retry is built in.
114
+
115
+ ### Sandboxed widget
116
+
117
+ `widget`: `{code, height?: 80-900, title?, caption?, actions?}`
118
+
119
+ A widget is for content the fixed components cannot express: a game, a physics/math simulator, a Web Audio instrument, a map, or a custom canvas. `code` is an HTML fragment or a complete document. It runs in an iframe with:
120
+
121
+ - `sandbox="allow-scripts allow-forms allow-popups"` (opaque origin; no `allow-same-origin`)
122
+ - CSP: no network; inline scripts/styles and `data:`/`blob:` images or media only
123
+ - no access to the host page, cookies, or local storage
124
+
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).
126
+
127
+ Inside the widget:
128
+
129
+ ```js
130
+ window.mosaic.action('apply', { value: 42 })
131
+ window.mosaic.resize()
132
+ ```
133
+
134
+ The parent passes theme variables as CSS custom properties: `--mosaic-bg`, `--mosaic-text`, `--mosaic-muted`, `--mosaic-border`, `--mosaic-accent`, `--mosaic-success`, `--mosaic-warn`, `--mosaic-danger`, `--mosaic-card`. Use these instead of hard-coded colors.
135
+
136
+ A visible title and a short caption make the widget easier to scan. Prefer fixed components when a table, chart, or callout expresses the content.
137
+
138
+ ## Actions
139
+
140
+ A component action sends one user message:
141
+
142
+ ```text
143
+ [mosaic-action] refresh。用户刚刚在界面中触发了动作 "refresh",请根据组件数据更新回答,并用 dsh-view 输出更新后的界面。 组件数据: {...}
144
+ ```
145
+
146
+ After an action, update the existing view instead of restating the whole answer. Only send an action for work that needs the model or a tool. Sorting, filtering, tabs, quiz grading, copy, and full-screen are local.
147
+
148
+ ## Presentation guidance (optional)
149
+
150
+ - Use Mosaic when a component makes the answer easier to scan; prose is fine when it does not.
151
+ - Choose the smallest component that carries the structure; avoid decorative cards, repeated summaries, and nested scrolling.
152
+ - Keep the tree under 80 nodes and 6 levels.
153
+ - A malformed fence falls back to the original code block; a widget that fails to boot shows its source and an error strip.
@@ -0,0 +1,79 @@
1
+ # Third-party notices
2
+
3
+ dsh-mosaic itself is released under the MIT License; see [`LICENSE`](./LICENSE).
4
+
5
+ ## Mermaid
6
+
7
+ `lib/assets/mermaid.min.js` is the **unmodified** official distribution build of
8
+ [Mermaid](https://github.com/mermaid-js/mermaid) **12.2.0**, taken from the npm
9
+ package `mermaid@12.2.0`, file `dist/mermaid.min.js`.
10
+
11
+ | | |
12
+ | --- | --- |
13
+ | Upstream | https://github.com/mermaid-js/mermaid |
14
+ | Package | https://www.npmjs.com/package/mermaid/v/12.2.0 |
15
+ | License | MIT |
16
+ | SHA-256 | `a8a6a6f805b074a8261a2825c86f99c8054acb3b35f50bb7e24a4f9f2c3c3b9f` |
17
+ | Local changes | none (byte-identical to the published file) |
18
+
19
+ The MIT text shipped with that release is in
20
+ [`lib/assets/licenses/mermaid.LICENSE`](./lib/assets/licenses/mermaid.LICENSE).
21
+
22
+ Mosaic only calls the public `globalThis.mermaid` API (`initialize`, `render`) and
23
+ sanitizes the returned SVG before it enters the page; the rendering adapter is this
24
+ package's own source, not part of the bundled file.
25
+
26
+ ## Dependencies bundled inside that Mermaid build
27
+
28
+ The official build embeds Mermaid's runtime dependencies. All of them are permissive
29
+ except the three marked below; the license text of each ships under
30
+ `lib/assets/licenses/`, so a redistribution carries every required notice.
31
+
32
+ | Package | Version | License |
33
+ | --- | --- | --- |
34
+ | @braintree/sanitize-url | 7.1.2 | MIT |
35
+ | @iconify/utils | 3.1.7 | MIT |
36
+ | @mermaid-js/parser | 2.0.1 | MIT |
37
+ | @types/d3 | 7.4.3 | MIT |
38
+ | @upsetjs/venn.js | 2.0.0 | MIT |
39
+ | chevrotain | 13.2.0 | **Apache-2.0** |
40
+ | cytoscape | 3.34.4 | MIT |
41
+ | cytoscape-cose-bilkent | 4.1.0 | MIT |
42
+ | cytoscape-fcose | 2.2.0 | MIT |
43
+ | d3 | 7.9.0 | ISC |
44
+ | d3-sankey | 0.12.3 | BSD-3-Clause |
45
+ | dagre-d3-es | 7.0.14 | MIT |
46
+ | dayjs | 1.11.23 | MIT |
47
+ | dompurify | 3.4.17 | **MPL-2.0 OR Apache-2.0** (used as Apache-2.0) |
48
+ | elkjs | 0.12.1 | **EPL-2.0 OR GPL-3.0-or-later** (used as EPL-2.0) |
49
+ | es-toolkit | 1.53.0 | MIT |
50
+ | katex | 0.19.1 | MIT |
51
+ | khroma | 2.1.0 | MIT |
52
+ | marked | 18.1.2 | MIT |
53
+ | roughjs | 4.6.6 | MIT |
54
+ | stylis | 4.4.0 | MIT |
55
+ | ts-dedent | 2.3.0 | MIT |
56
+ | uuid | 14.0.3 | MIT |
57
+
58
+ What those three obligations mean here:
59
+
60
+ - **chevrotain (Apache-2.0)** — the Apache text ships and the file is redistributed
61
+ unmodified, so the NOTICE requirement is met by that text plus the attribution above.
62
+ - **dompurify (MPL-2.0 OR Apache-2.0)** — the Apache-2.0 option is taken; its text ships.
63
+ - **elkjs (EPL-2.0)** — weak, file-level copyleft. The bundled copy is unmodified and
64
+ its source is available upstream (https://github.com/kieler/elkjs); the EPL text
65
+ ships. Nothing in this repository incorporates elkjs source, so the MIT license of
66
+ dsh-mosaic is unaffected.
67
+
68
+ ## Icons and screenshots
69
+
70
+ `assets/icon.svg`, `assets/icon-180.png` and `assets/screenshots/preview.jpg` were made
71
+ for this project and ship under the same MIT license as the package. The preview image
72
+ is a screenshot of this plugin running in DeepSeek Harness and contains no third-party
73
+ artwork.
74
+
75
+ ## draw.io / maxGraph
76
+
77
+ No draw.io or maxGraph runtime is bundled. The optional `.drawio` export writes the
78
+ standard mxGraph XML format, so the file opens in [draw.io](https://www.drawio.com/)
79
+ (Apache-2.0). Nothing from those projects is required for the export to work.
Binary file
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Mosaic">
2
+ <defs>
3
+ <linearGradient id="m-bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#1d4ed8"/>
5
+ <stop offset="1" stop-color="#0f766e"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect x="2" y="2" width="60" height="60" rx="14" fill="url(#m-bg)"/>
9
+ <rect x="10" y="10" width="20" height="20" rx="5" fill="#ffffff" fill-opacity="0.94"/>
10
+ <rect x="34" y="10" width="20" height="20" rx="5" fill="#ffffff" fill-opacity="0.72"/>
11
+ <rect x="10" y="34" width="20" height="20" rx="5" fill="#ffffff" fill-opacity="0.72"/>
12
+ <rect x="34" y="34" width="20" height="20" rx="5" fill="#ffffff" fill-opacity="0.46"/>
13
+ </svg>
Binary file
@@ -0,0 +1,10 @@
1
+ # dsh-mosaic bundle patch: insert this package's own row.
2
+ # It touches no other entry, so a user composition decides which plugins run.
3
+ - insert:
4
+ - id: mosaic
5
+ name: dsh-mosaic
6
+ config: {}
7
+ # Override the contributed model prompt here, e.g.:
8
+ # config:
9
+ # prompt: 'Only render a dsh-view fence when a table or chart helps.'
10
+ # logLevel: debug # debug | info | warn | error (diagnostic JSONL verbosity)
@@ -0,0 +1,9 @@
1
+ # Submit this file to https://github.com/awesome-dsh-plugin/awesome-dsh-plugin
2
+ # as data/plugins/imwhatiam__dsh-mosaic.yml after the GitHub repo exists.
3
+ url: https://github.com/imwhatiam/dsh-mosaic
4
+ name: imwhatiam/dsh-mosaic
5
+ category: ui
6
+ description:
7
+ en: 'Renders structured visual answers in DeepSeek Harness: metrics, charts, tables, diagrams, media and interactive widgets, with one compact visual style, an editable model prompt, and local-first interactions.'
8
+ zh: '为 DeepSeek Harness 渲染结构化可视化回答:指标、图表、表格、流程图、媒体和交互组件;提供一套紧凑视觉风格、可编辑的模型 Prompt,交互默认在浏览器本地完成。'
9
+ icon: https://raw.githubusercontent.com/imwhatiam/dsh-mosaic/main/assets/icon-180.png
@@ -0,0 +1,29 @@
1
+ # Publishing dsh-mosaic
2
+
3
+ ## Before the first public release
4
+
5
+ 1. Create the GitHub repository. Add the topic `dsh-plugin`; the catalog's automated check looks for it.
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/`.
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
+ 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
+ 6. The catalog `icon` must be a GitHub-hosted raster image. This repo ships `assets/icon-180.png`; SVG is rejected by the catalog.
11
+ 7. Do not edit the generated catalog README by hand. Change only your YAML entry.
12
+ 8. The README preview uses an absolute `raw.githubusercontent.com` URL because npm does not resolve relative images. In-app detail documents (`meta.detail` + `docs/DETAIL.md`) are a newer-host feature and are deliberately not declared for 0.2.1-rc.3.
13
+
14
+ ## Repository hygiene
15
+
16
+ - Do not commit `node_modules/`, `.pnpm-store/`, `tests/`, `.DS_Store`, or build/tarball artifacts. `.gitignore` covers the usual cases.
17
+ - Keep `LICENSE`, `THIRD_PARTY_NOTICES.md`, and the Mermaid asset attribution in the repository.
18
+ - The bundle patch only inserts the plugin's own row; it never disables or patches another package. If a user runs two fence renderers, that is their composition to resolve.
19
+ - The Mermaid asset is a lazy 3.2 MB JavaScript file. It is included in the npm tarball but not requested until a flowchart or sequence appears.
20
+ - The widget is an opaque-origin sandbox with no network. Do not add a network-backed script to it.
21
+
22
+ ## Release checks
23
+
24
+ ```sh
25
+ npm test
26
+ npm pack --dry-run
27
+ ```
28
+
29
+ The package is unscoped `dsh-mosaic`; npm returned 404 for that name on 2026-10-29, but recheck before publishing. If it is taken, use your npm scope and update the install command in the README and the catalog entry.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Braintree
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.