dsh-mosaic 1.1.0 → 1.1.2

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
@@ -4,6 +4,27 @@
4
4
  > 兼容性分两处表达:`package.json` 的 `engines.dsh` 给机器可读的范围,README「安装」一节给「插件版本 ↔ 实测 Harness 版本」对照表;每次在真实 Harness 上验证后更新这两处。
5
5
  > `0.2.1` 及各 `0.2.1-<标签>` 是改规则前的历史版本,保留用于追溯。
6
6
 
7
+ ## 1.1.2
8
+
9
+ ### 修复
10
+
11
+ - 示意图标签里的 `(`、`)`、`[`、`]`、`{`、`}`、`|` 会让 Mermaid 提前结束标签,`get_current_lock_id()` 这类标签直接解析失败。流程图节点与边标签现在统一加引号;`state`、`er` 等已加引号的语法不受影响。
12
+ - 标签里的双引号原本转义成 `#quot;`,但 Mermaid 12 不解析该实体,会原样显示成 `&quot;`;改为单引号,正常显示。
13
+ - `gantt` 的任务名与分组名遇到 `:` 会被当作标签分隔符,静默丢掉后半段;现在换成全角 `:`。
14
+ - `warnings=` 补上 flowchart 的边数与 sequence 的消息数检查——README 与 SKILL 早就写了这两个预算,代码此前只检查节点和生命线。
15
+
16
+ ### 清理
17
+
18
+ - 删除 `validate.js` 里两个无人调用的函数(`looksLikeSpec`、`takeString`)。
19
+ - 删除三条没有任何组件使用的 CSS(`.mosaic-chart-title`、`.mosaic-donut-label`、`.mosaic-error`)。
20
+ - 测试面收窄到测试真正用到的导出。
21
+
22
+ ## 1.1.1
23
+
24
+ 发布流程修正,运行时代码与 `1.1.0` 相同。
25
+
26
+ - `release.yml` 去掉了 `actions/setup-node@v4` 不认识的 `package-manager-cache` 输入。该行此前被忽略,每次发布都会打一条 unexpected-input 警告;缓存本就默认关闭,而这个 job 不安装任何依赖,因此直接删除并注明原因。
27
+
7
28
  ## 1.1.0
8
29
 
9
30
  ### 示意图
package/README.md CHANGED
@@ -89,7 +89,7 @@ $DSH_HOME/logs/dsh-mosaic/YYYY-MM-DD.jsonl # 未设置 DSH_HOME 时是 ~/.dsh/
89
89
  ```
90
90
 
91
91
  - 一行一个 JSON 对象,字段固定:`ts`、`level`(debug/info/warn/error)、`event`,其余按事件附带(如 `tool`、`ms`、`status`、`route`、`source`、`reason`)。
92
- - 记录什么:激活/停用、Prompt 的读取与保存(来源、字符数、是否落盘)、工具执行(名称、结果状态、耗时)、路由请求(方法、状态码、耗时)、技能被读取、以及浏览器侧上报的失败(`client.` 前缀:围栏解析失败、Mermaid 失败、Prompt 读写失败、Widget 动作被限流/超限丢弃)。
92
+ - 记录什么:激活/停用、Prompt 的读取与保存(来源、字符数、是否落盘)、工具执行(名称、结果状态、耗时)、路由请求(方法、状态码、耗时)、技能被读取、以及浏览器侧上报的失败(`client.` 前缀:围栏解析失败、Mermaid 失败、示意图导出失败、Prompt 读写失败、Widget 动作被限流/超限丢弃)。
93
93
  - 不记录什么:Prompt 正文、组件 JSON、任何文本字段的内容。字段名为 `prompt`/`text`/`code`/`content`/`secret`/`token`/`password`/`apiKey` 时只写字符数;其它字符串超过 300 字符会被截断。
94
94
  - 体积:单文件超过 2 MB 滚动为 `<日期>.1.jsonl`;超过 7 天的文件在下次启动时删除;文件权限 `0600`。写不进去时不会影响插件,只在宿主日志里告警一次。
95
95
  - 浏览器侧的失败通过同源 `POST /dsh-mosaic/log` 上报(请求体上限 4 KB,非本机权威或跨站一律 403,客户端每分钟最多 20 条);接口在日志落盘之后才回 204,看到成功就是真的写进去了。
@@ -108,7 +108,7 @@ grep -c '"event":"tool.failed"' "$DSH_HOME"/logs/dsh-mosaic/*.jsonl
108
108
 
109
109
  ## 已知边界
110
110
 
111
- - 本插件的 npm 版本独立递增(当前 `1.0.0`),不再与 Harness 版本号对齐;兼容范围由 `package.json` 的 `engines.dsh` 声明,最近一次实测的 Harness 版本见「安装」一节。
111
+ - 本插件的 npm 版本独立递增(当前 `1.1.2`),不再与 Harness 版本号对齐;兼容范围由 `package.json` 的 `engines.dsh` 声明,最近一次实测的 Harness 版本见「安装」一节。
112
112
  - 围栏渲染靠扫描页面里的代码块实现:0.2.1 全系列(实测 rc.3 与 rc.5,master 同样)都没有给插件开放「渲染 fenced code block」的扩展点。工具通道 `render_view` + `tool.call.toolview` 是原生做法,围栏是它的补充。
113
113
  - 从 0.2.1 起代码块由 Shiki 渲染,`<code>` 上**没有** `language-*` 类;识别靠「代码块位于 assistant 行内 + 正文能解析成合法视图 spec」这条路径,用户消息、工具结果、推理块里的代码块都被排除。
114
114
  - `diagram` 与 `chart` 自带缩放/平移:卡片右上角有 `−`/`+`/`⟲`/`⤢`,放大后按住图面拖动即可平移,`Ctrl/⌘ + 滚轮`缩放,双击复位;示意图按原始尺寸渲染,不会被压缩到卡片宽度。触屏上图表区域只拦横向拖动(`touch-action: pan-y`),手指上下划仍然滚动聊天页面。
@@ -154,10 +154,13 @@ dsh plugin --profile desktop add link:/absolute/path/to/dsh-mosaic
154
154
 
155
155
  安装后重启或刷新 DSH Web,新会话即可使用。bundle patch 只插入本插件一行,不会关闭或覆盖别人的插件。
156
156
 
157
- **兼容性**:npm 版本独立于 DeepSeek Harness 版本,`1.0.x` 对应下方实测的 Harness 范围;声明式范围见 `package.json` 的 `engines.dsh`(当前 `>=0.2.1-rc.1 <0.3.0-0`)。
157
+ **兼容性**:npm 版本独立于 DeepSeek Harness 版本,`1.1.x` 对应下方实测的 Harness 范围;声明式范围见 `package.json` 的 `engines.dsh`(当前 `>=0.2.1-rc.1 <0.3.0-0`)。
158
158
 
159
159
  | 插件版本 | 实测通过的 DeepSeek Harness | 说明 |
160
160
  | --- | --- | --- |
161
+ | `1.1.2` | `0.2.1-rc.5` | 修复示意图标签里的括号/方括号/花括号导致解析失败;补上边与消息的可读预算 |
162
+ | `1.1.1` | `0.2.1-rc.5` | 运行时代码与 `1.1.0` 相同;仅修正发布工作流的无效输入 |
163
+ | `1.1.0` | `0.2.1-rc.5` | 13 种示意图、PNG/SVG 导出、`warnings=` 可读规模提示、布局修正 |
161
164
  | `1.0.0` | `0.2.1-rc.5`(同时复核 rc.3 行为) | 首个稳定版;含 Widget 沙箱、路由权威、Prompt 并发等修正 |
162
165
  | `0.2.1` | `0.2.1-rc.3`、`0.2.1-rc.5` | 上一版,npm 上的历史发布 |
163
166
 
package/SKILL.md CHANGED
@@ -117,7 +117,7 @@ A single `diagram` node fragment (this is not a complete fence; wrap it in `{"it
117
117
  ],"legend":[{"label":"命中路径","tone":"accent"}]}
118
118
  ```
119
119
 
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.
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 exports `svg` and `png` from its header; `drawio` appears for the kinds it can express as vertices and edges (`flowchart`, `sequence`, `timeline`, `layers`, `state`).
121
121
 
122
122
  ### Media and links
123
123
 
package/lib/client.js CHANGED
@@ -340,14 +340,12 @@ window.__ModuleLoader__.load({
340
340
  .mosaic-diff-new{background:color-mix(in srgb,var(--m-success) 7%,var(--m-bg))}
341
341
  .mosaic-chart{width:100%;height:auto;display:block;overflow:visible}
342
342
  .mosaic-chart text{font-size:10px;fill:var(--m-muted)}
343
- .mosaic-chart .mosaic-chart-title{font-size:12px;font-weight:600;fill:var(--m-text)}
344
343
  .mosaic-chart .mosaic-axis{stroke:var(--m-border);stroke-width:1}
345
344
  .mosaic-chart .mosaic-axis-label{font-size:9px;fill:var(--m-muted);font-variant-numeric:tabular-nums}
346
345
  .mosaic-chart .mosaic-grid-line{stroke:var(--m-border);stroke-width:1;opacity:.45}
347
346
  .mosaic-chart .mosaic-line{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
348
347
  .mosaic-chart .mosaic-dot{stroke:var(--m-bg);stroke-width:1.5}
349
348
  .mosaic-chart .mosaic-bar{shape-rendering:geometricPrecision}
350
- .mosaic-chart .mosaic-donut-label{font-size:11px;fill:var(--m-text)}
351
349
  .mosaic-diagram-wrap{max-width:100%;overflow:hidden;padding-bottom:2px}
352
350
  .mosaic-diagram{display:block;width:auto;max-width:none;height:auto;margin:0 auto;overflow:visible}
353
351
  .mosaic-mermaid{max-width:100%;padding:2px 0}
@@ -482,7 +480,6 @@ window.__ModuleLoader__.load({
482
480
  .mosaic-overlay-body[data-figure="true"]{width:min(1440px,97vw);overflow:hidden}
483
481
  .mosaic-overlay-image{display:block;max-width:100%;max-height:76vh;margin:0 auto;border-radius:10px;object-fit:contain}
484
482
  .mosaic-diagnostic{margin:0 0 6px;padding:6px 10px;border:1px solid color-mix(in srgb,var(--m-danger) 40%,transparent);border-radius:7px;background:color-mix(in srgb,var(--m-danger) 10%,transparent);color:var(--m-danger);font-size:12px;line-height:1.5;white-space:pre-wrap}
485
- .mosaic-error{margin:8px 0;padding:8px 10px;border:1px solid color-mix(in srgb,var(--m-danger) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--m-danger) 8%,var(--m-bg));color:var(--m-danger);font-size:12px}
486
483
  .mosaic-skeleton{display:flex;flex-direction:column;gap:8px;padding:10px 0}
487
484
  .mosaic-skeleton span{display:block;height:10px;border-radius:5px;background:var(--m-bg2);animation:mosaic-pulse 1.2s ease-in-out infinite}
488
485
  .mosaic-skeleton span:nth-child(2){width:70%}
@@ -1528,7 +1525,9 @@ window.__ModuleLoader__.load({
1528
1525
 
1529
1526
  function SpacerNode() {
1530
1527
  return h('div', { className: 'mosaic-spacer' })
1531
- }function numberValue(value) {
1528
+ }
1529
+
1530
+ function numberValue(value) {
1532
1531
  if (typeof value === 'number' && Number.isFinite(value)) return value
1533
1532
  const text = String(value ?? '').trim().replace(/[,$£€¥%]/g, '')
1534
1533
  if (text === '') return null
@@ -2480,11 +2479,19 @@ window.__ModuleLoader__.load({
2480
2479
  function mermaidLabel(value) {
2481
2480
  return String(value ?? '')
2482
2481
  .replace(/\s+/g, ' ')
2483
- .replace(/"/g, '#quot;')
2484
- .replace(/\|/g, '#124;')
2482
+ .replace(/"/g, "'")
2485
2483
  .trim()
2486
2484
  }
2487
2485
 
2486
+ /**
2487
+ * A quoted Mermaid label. Unquoted labels end at `()`, `[]`, `{}` or `|`,
2488
+ * so a label like `get_current_lock_id()` fails to parse; quoting is the
2489
+ * only form that survives them.
2490
+ */
2491
+ function mermaidQuoted(value) {
2492
+ return `"${mermaidLabel(value)}"`
2493
+ }
2494
+
2488
2495
  /** Parse a token colour into `[r, g, b]`; `undefined` when it is not a plain colour. */
2489
2496
  function parseColor(value) {
2490
2497
  const text = String(value ?? '').trim()
@@ -2601,6 +2608,11 @@ window.__ModuleLoader__.load({
2601
2608
  return lines.join('\n')
2602
2609
  }
2603
2610
 
2611
+ /** A gantt name ends at the first `:`, so a literal one would truncate it. */
2612
+ function ganttLabel(value) {
2613
+ return mermaidLabel(value).replace(/:/g, '\uff1a')
2614
+ }
2615
+
2604
2616
  function ganttMermaid(node) {
2605
2617
  const lines = ['gantt', ' dateFormat YYYY-MM-DD', ' axisFormat %m-%d']
2606
2618
  let section
@@ -2608,10 +2620,10 @@ window.__ModuleLoader__.load({
2608
2620
  for (const task of Array.isArray(node.tasks) ? node.tasks : []) {
2609
2621
  if (task.section && task.section !== section) {
2610
2622
  section = task.section
2611
- lines.push(` section ${mermaidLabel(section)}`)
2623
+ lines.push(` section ${ganttLabel(section)}`)
2612
2624
  }
2613
2625
  index += 1
2614
- lines.push(` ${mermaidLabel(task.name)} :t${index}, ${task.start}, ${task.end}`)
2626
+ lines.push(` ${ganttLabel(task.name)} :t${index}, ${task.start}, ${task.end}`)
2615
2627
  }
2616
2628
  return lines.join('\n')
2617
2629
  }
@@ -2659,8 +2671,7 @@ window.__ModuleLoader__.load({
2659
2671
  const usedTones = new Set()
2660
2672
  const lines = [`flowchart ${node.direction === 'LR' ? 'LR' : 'TB'}`]
2661
2673
  nodes.forEach((entry) => {
2662
- const label = mermaidLabel(entry.label)
2663
- const withSub = entry.sub ? `${label}<br/>${mermaidLabel(entry.sub)}` : label
2674
+ const withSub = mermaidQuoted(entry.sub ? `${entry.label}<br/>${entry.sub}` : entry.label)
2664
2675
  const id = idOf.get(entry.id)
2665
2676
  if (entry.shape === 'diamond') lines.push(` ${id}{${withSub}}`)
2666
2677
  else if (entry.shape === 'round') lines.push(` ${id}(${withSub})`)
@@ -2676,7 +2687,7 @@ window.__ModuleLoader__.load({
2676
2687
  const to = idOf.get(edge.to)
2677
2688
  if (!from || !to) return
2678
2689
  const arrow = edge.kind === 'dashed' ? '-.->' : edge.kind === 'accent' ? '==>' : '-->'
2679
- const label = edge.label ? `|${mermaidLabel(edge.label)}|` : ''
2690
+ const label = edge.label ? `|${mermaidQuoted(edge.label)}|` : ''
2680
2691
  lines.push(` ${from} ${arrow}${label} ${to}`)
2681
2692
  })
2682
2693
  if (baseStyle !== undefined) lines.push(` classDef mosaic_base ${baseStyle}`)
@@ -4386,7 +4397,6 @@ window.__ModuleLoader__.load({
4386
4397
  // `inject`, so this extra key is inert at runtime.
4387
4398
  const __test = {
4388
4399
  widgetDocument,
4389
- widgetParts,
4390
4400
  WIDGET_CSP,
4391
4401
  widgetBridge,
4392
4402
  textWidth,
@@ -4397,8 +4407,6 @@ window.__ModuleLoader__.load({
4397
4407
  layoutVenn,
4398
4408
  layoutSwimlane,
4399
4409
  toMermaid,
4400
- diagrams: { QuadrantDiagram, TreeDiagram, FunnelDiagram, VennDiagram, SwimlaneDiagram, DiagramLegend },
4401
- inlineSvgMarkup,
4402
4410
  parseView,
4403
4411
  }
4404
4412
 
package/lib/validate.js CHANGED
@@ -74,10 +74,6 @@ function asBoolean(value) {
74
74
  return typeof value === 'boolean' ? value : undefined
75
75
  }
76
76
 
77
- function looksLikeSpec(value) {
78
- return isRecord(value) && Array.isArray(value.items)
79
- }
80
-
81
77
  function looksLikeNode(value) {
82
78
  return isRecord(value) && typeof value.type === 'string'
83
79
  }
@@ -103,12 +99,6 @@ function fail(state, path, message) {
103
99
  state.errors.push(`${path}: ${message}`)
104
100
  }
105
101
 
106
- function takeString(state, path, value, cap = LIMITS.maxString) {
107
- const result = asString(value, cap)
108
- if (result === undefined) fail(state, path, 'must be a string')
109
- return result
110
- }
111
-
112
102
  function takeOptionalNumber(state, path, value, min, max) {
113
103
  if (value === undefined) return undefined
114
104
  const result = asNumber(value, min, max)
@@ -1362,13 +1352,19 @@ const READABLE = {
1362
1352
 
1363
1353
  /** Counts per diagram kind that a reader can take in without a guide. */
1364
1354
  const DIAGRAM_READABLE = {
1365
- flowchart: { limit: 9, count: (node) => node.nodes.length, noun: 'nodes' },
1366
- state: { limit: 9, count: (node) => node.nodes.length, noun: 'states' },
1367
- sequence: { limit: 5, count: (node) => node.participants.length, noun: 'lifelines' },
1368
- timeline: { limit: 7, count: (node) => node.items.length, noun: 'items' },
1369
- layers: { limit: 6, count: (node) => node.layers.length, noun: 'layers' },
1370
- funnel: { limit: 6, count: (node) => node.items.length, noun: 'steps' },
1371
- swimlane: { limit: 6, count: (node) => node.steps.length, noun: 'steps' },
1355
+ flowchart: [
1356
+ { limit: 9, count: (node) => node.nodes.length, noun: 'nodes' },
1357
+ { limit: 12, count: (node) => node.edges.length, noun: 'edges' },
1358
+ ],
1359
+ state: [{ limit: 9, count: (node) => node.nodes.length, noun: 'states' }],
1360
+ sequence: [
1361
+ { limit: 5, count: (node) => node.participants.length, noun: 'lifelines' },
1362
+ { limit: 12, count: (node) => node.messages.length, noun: 'messages' },
1363
+ ],
1364
+ timeline: [{ limit: 7, count: (node) => node.items.length, noun: 'items' }],
1365
+ layers: [{ limit: 6, count: (node) => node.layers.length, noun: 'layers' }],
1366
+ funnel: [{ limit: 6, count: (node) => node.items.length, noun: 'steps' }],
1367
+ swimlane: [{ limit: 6, count: (node) => node.steps.length, noun: 'steps' }],
1372
1368
  }
1373
1369
 
1374
1370
  const REMOTE_ASSET_RE = /<(?:script|img|iframe|link|video|audio|source)[^>]+(?:src|href)\s*=\s*["']?(?:https?:)?\/\//iu
@@ -1421,8 +1417,7 @@ export function lintView(spec) {
1421
1417
  if (Array.isArray(node.nodes)) {
1422
1418
  accents += node.nodes.filter((entry) => entry?.tone === 'accent').length
1423
1419
  }
1424
- const budget = DIAGRAM_READABLE[node.kind]
1425
- if (budget !== undefined) {
1420
+ for (const budget of DIAGRAM_READABLE[node.kind] ?? []) {
1426
1421
  const count = budget.count(node)
1427
1422
  if (count > budget.limit) {
1428
1423
  add(`${path}: ${count} ${budget.noun}; keep ${budget.limit} or fewer, or split`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mosaic",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "description": "Lightweight visual answers for DeepSeek Harness: one compact visual style, an editable model prompt, dsh-view components, Mermaid diagrams, a sandboxed HTML/JS escape hatch, local-first interactions, and link-only media cards.",
6
6
  "author": "imwhatiam",