dsh-md-plus 0.4.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # 更新日志
2
2
 
3
- ## 0.4.0 —— 未发布
3
+ ## 0.4.1 —— 未发布
4
+
5
+ - 修复 DSH 代码块头部只显示占位文案时读不到真实围栏语言、补高亮失效的问题:现在会从宿主的 React fiber 读取真实 `lang`,读不到再退回正文嗅探,并把头部语言名显示成真实语言。
6
+ - 修复 `graph = nx.Graph()`、`pie = 3.14`、`timeline = []` 等普通代码被误判成 mermaid 图的问题:只嗅探第一行有效内容,并排除赋值和调用形态。
7
+ - 修复代码块折叠在 DSH 上不触发:DSH 的代码内容容器是 `display:contents`,插件没有可测量的盒子;现在给代码内容建立真实的块级盒模型。
8
+ - 修复表格吸顶:给表格容器增加纵向滚动上限,表头在容器内滚动时保持可见。
9
+ - 修复卸载/重载后高亮副本、折叠按钮和 `data-md-plus*` 属性残留,避免同一份代码显示两遍,重装后也能重新处理。
10
+ - 修复鼠标滚轮在 1 倍图表上拦截页面滚动:未放大且未按住 Ctrl/Meta 时放行;全屏查看仍可滚轮缩放。
11
+ - 修复切换主题后代码视图中的 mermaid 图不重画的问题。
12
+ - 修复设置面板「恢复默认」把折叠阈值写成 0、代码字号与宿主行高不匹配、字号输入越界后静默回退的问题。
13
+ - 修复 mermaid 渲染失败后仍显示无效缩放/全屏按钮的问题,失败块现在显示「重试」;运行时脚本加载失败也不再永久缓存失败结果。
14
+ - 测试补到 72 条,新增 React fiber 语言识别、mermaid 误判、代码折叠盒模型、卸载清理、滚轮放行、代码视图主题重画、设置默认值等回归用例。
15
+
16
+ ## 0.4.0 —— 2026-10-28
4
17
 
5
18
  - Mermaid 全屏工具栏新增「下载 SVG / 下载 PNG」,可以直接把图带进文档或演示文稿。
6
19
  - 代码块、表格和 Mermaid 图卡增加长内容折叠:超过阈值显示「展开 / 收起」,避免长日志和长表格撑满聊天。
package/README.md CHANGED
@@ -27,18 +27,18 @@ dsh plugin --profile desktop remove dsh-md-plus
27
27
  ```powershell
28
28
  npm install # 顺带跑 prepare → build
29
29
  npm run build # 产出 lib/ 下四个文件
30
- npm run check # 语法检查 + 65 条测试(先 build 再跑,check 不含 build)
30
+ npm run check # 语法检查 + 72 条测试(先 build 再跑,check 不含 build)
31
31
  npm run preview # 生成 preview.html(含真渲出来的 mermaid 图)
32
32
  npm run shot # 用 headless Chrome 把 preview.html 截成 docs/preview.png
33
33
  ```
34
34
 
35
35
  ## 已知限制
36
36
 
37
- - DSH 0.2.0-rc.2 上,头部语言名由宿主决定:不认这门语言就显示占位文案「代码块」,插件读不到围栏语言,补高亮基本失效。mermaid 因此改看正文里的类型声明来识别。
37
+ - DSH 0.2.1-rc.4 的头部语言名由宿主决定:不认识的围栏只显示占位文案「代码块」。插件会从宿主的 React fiber 里兜底读取真实语言,读不到时才退回正文嗅探;宿主改 React 结构后这条兜底可能失效。
38
38
  - `DSH_LANGS` 是 DSH 语言表的快照(108 条)。宿主加语言后这份快照会落后,落后只会多一次无效处理,不会出现双份高亮。
39
39
  - mermaid 12 的新图种(`usecase-beta`、`agentflow-beta`)标识符只认字母数字下划线,中文得写进标签:`actor Customer("用户")` 可以,`actor 用户` 会解析失败。
40
40
  - 高亮约 6.4 MB、mermaid 约 5.0 MB,按需下载,第一次用到时多花约 100ms 解析。
41
- - 依赖宿主 `.md-code-block`、`[data-code-block-content]`、`[data-code-block-banner]`、`data-streaming` 等钩子,DSH 改版后要同步调整 `src/client.js`。
41
+ - 依赖宿主 `.md-code-block`、`[data-code-block-content]`、`[data-code-block-banner]`、`data-streaming` 等钩子,以及 React fiber 里的 `lang`;DSH 改版后要同步调整 `src/client.js`。
42
42
 
43
43
  目前只影响代码块、mermaid、表格、正文图片和显示设置,不改 Markdown 的解析结果。
44
44
 
package/lib/client.js CHANGED
@@ -26,6 +26,7 @@ window.__ModuleLoader__.load({
26
26
  var ZOOM_MIN_EPSILON = 1e-3;
27
27
  var EXPAND_CLASS = "md-plus-expand";
28
28
  var THEME_CLASS = "md-plus-theme";
29
+ var RETRY_CLASS = "md-plus-retry";
29
30
  var THEME_KEY = "dsh-md-plus-mermaid-theme";
30
31
  var THEME_ORDER = ["blue", "neutral", "forest", "contrast"];
31
32
  var THEME_LABELS = {
@@ -56,6 +57,8 @@ window.__ModuleLoader__.load({
56
57
  themePrefix: "\u914D\u8272\uFF1A",
57
58
  foldOpen: "\u5C55\u5F00",
58
59
  foldClose: "\u6536\u8D77",
60
+ retry: "\u91CD\u8BD5",
61
+ retryAria: "\u91CD\u65B0\u6E32\u67D3\u56FE\u8868",
59
62
  renderFailed: "mermaid \u6E32\u67D3\u5931\u8D25\uFF1A",
60
63
  diagram: "\u6D41\u7A0B\u56FE",
61
64
  diagramAria: "\u6D41\u7A0B\u56FE\u5168\u5C4F\u67E5\u770B",
@@ -107,6 +110,8 @@ window.__ModuleLoader__.load({
107
110
  themePrefix: "Theme: ",
108
111
  foldOpen: "Expand",
109
112
  foldClose: "Collapse",
113
+ retry: "Retry",
114
+ retryAria: "Retry diagram",
110
115
  renderFailed: "Mermaid render failed: ",
111
116
  diagram: "Diagram",
112
117
  diagramAria: "Diagram fullscreen viewer",
@@ -207,8 +212,13 @@ window.__ModuleLoader__.load({
207
212
  }
208
213
  function applyCodeFontSize() {
209
214
  var size = readSettings().codeFontSize;
210
- if (size > 0) document.documentElement.style.setProperty("--md-plus-code-font-size", size + "px");
211
- else document.documentElement.style.removeProperty("--md-plus-code-font-size");
215
+ if (size > 0) {
216
+ document.documentElement.style.setProperty("--md-plus-code-font-size", size + "px");
217
+ document.documentElement.style.setProperty("--md-plus-code-line-height", Math.round(size * 1.6) + "px");
218
+ } else {
219
+ document.documentElement.style.removeProperty("--md-plus-code-font-size");
220
+ document.documentElement.style.removeProperty("--md-plus-code-line-height");
221
+ }
212
222
  }
213
223
  var DSH_LANGS = /* @__PURE__ */ new Set([
214
224
  "typescript",
@@ -321,7 +331,10 @@ window.__ModuleLoader__.load({
321
331
  "gradle"
322
332
  ]);
323
333
  var MMD_LANGS = /* @__PURE__ */ new Set(["mermaid", "mermaidjs", "mmd"]);
324
- var MMD_TYPE = /^\s*(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram(-v2)?|erDiagram|journey|gantt|pie|mindmap|timeline|gitGraph|quadrantChart|xychart-beta|sankey-beta|block-beta|packet-beta|architecture-beta|usecase-beta|agentflow-beta|C4Context|requirementDiagram|kanban|radar|treemap|venn|ishikawa|wardley|cynefin|treeView|zenuml)\b/im;
334
+ var MMD_FLOW = /^\s*(flowchart|graph)(?:\s+(?:TD|TB|BT|RL|LR))?\s*;?\s*$/i;
335
+ var MMD_PIE = /^\s*pie(?:\s+title\b.*)?\s*;?\s*$/i;
336
+ var MMD_GIT = /^\s*gitGraph(?:\s+(?:LR|TB|BT|RL))?:?\s*;?\s*$/i;
337
+ var MMD_SINGLE = /^\s*(sequenceDiagram|classDiagram|stateDiagram(?:-v2)?|erDiagram|journey|gantt|mindmap|timeline|quadrantChart|xychart-beta|sankey-beta|block-beta|packet-beta|architecture-beta|usecase-beta|agentflow-beta|C4Context|requirementDiagram|kanban|radar|treemap|venn|ishikawa|wardley|cynefin|treeView|zenuml)\s*;?\s*$/i;
325
338
  function icon(paths) {
326
339
  var svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>" + paths + "</svg>";
327
340
  return 'url("data:image/svg+xml,' + encodeURIComponent(svg) + '")';
@@ -350,6 +363,9 @@ window.__ModuleLoader__.load({
350
363
  var ICON_THEME = icon(
351
364
  "<path d='M12 3s5 5.5 5 9a5 5 0 0 1-10 0c0-3.5 5-9 5-9z'/><path d='M9.5 12a2.5 2.5 0 0 0 5 0'/>"
352
365
  );
366
+ var ICON_RETRY = icon(
367
+ "<path d='M20 11a8 8 0 1 0-2.34 5.66'/><path d='M20 4v7h-7'/>"
368
+ );
353
369
  var ICON_GEAR = icon(
354
370
  "<circle cx='12' cy='12' r='3'/><path d='M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1 1.55V21a2 2 0 1 1-4 0v-.09a1.7 1.7 0 0 0-1.11-1.55 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.55-1H3a2 2 0 1 1 0-4h.09a1.7 1.7 0 0 0 1.55-1.11 1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-1.55V3a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1 1.55 1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.4 9c.14.36.48.6.87.61H21a2 2 0 1 1 0 4h-.09a1.7 1.7 0 0 0-1.55 1z'/>"
355
371
  );
@@ -358,7 +374,7 @@ window.__ModuleLoader__.load({
358
374
  var TABLE_VARS_DARK = "--md-plus-tbl-line:#2f2f2f;--md-plus-tbl-head:#1f1f1f;--md-plus-tbl-head-fg:#f9fafb;--md-plus-tbl-hover:#1f1f1f";
359
375
  var CSS = [
360
376
  // ── 卡片:整块一个底色,顶栏不再是独立色带 ──────────────────────
361
- ".md-code-block{--md-plus-mmd-min-height:200px;--md-plus-icon:" + ICON_CODE + ";--md-plus-icon-copy:" + ICON_COPY + ";--md-plus-icon-nowrap:" + ICON_NOWRAP + ";--md-plus-icon-diagram:" + ICON_DIAGRAM + ";--md-plus-icon-zoom-in:" + ICON_ZOOM_IN + ";--md-plus-icon-zoom-out:" + ICON_ZOOM_OUT + ";--md-plus-icon-expand:" + ICON_EXPAND + ";--md-plus-icon-theme:" + ICON_THEME + "}",
377
+ ".md-code-block{--md-plus-mmd-min-height:200px;--md-plus-icon:" + ICON_CODE + ";--md-plus-icon-copy:" + ICON_COPY + ";--md-plus-icon-nowrap:" + ICON_NOWRAP + ";--md-plus-icon-diagram:" + ICON_DIAGRAM + ";--md-plus-icon-zoom-in:" + ICON_ZOOM_IN + ";--md-plus-icon-zoom-out:" + ICON_ZOOM_OUT + ";--md-plus-icon-expand:" + ICON_EXPAND + ";--md-plus-icon-theme:" + ICON_THEME + ";--md-plus-icon-retry:" + ICON_RETRY + "}",
362
378
  // bannerWrap 的原底色是 --dsw-alias-bg-base(#fff),会跟卡片灰拉开一条带
363
379
  ".md-code-block > div:has(> " + BANNER + "){background:transparent}",
364
380
  // ── 头部:收紧,并把空着的左边填上 ─────────────────────────────
@@ -375,6 +391,9 @@ window.__ModuleLoader__.load({
375
391
  // pre 的行高本身就在首行文字上方留了约半行空白;补 6px 后实测间距约 17px
376
392
  // (原来给 0 时只有 11px,贴着看太挤)
377
393
  ".md-code-block pre{padding:6px 16px 16px;font-size:var(--md-plus-code-font-size,inherit)}",
394
+ ".md-code-block pre{line-height:var(--md-plus-code-line-height,inherit)}",
395
+ // DSH 0.2.1 把内容容器设成 display:contents,折叠需要真实的盒子才能量高度和裁剪
396
+ ".md-code-block > [data-code-block-content]{display:block}",
378
397
  // ── 头部按钮:文字换成图标 ──────────────────────────────────────
379
398
  ".md-code-block " + BANNER + " button{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:0;border-radius:6px;font-size:0;cursor:pointer;background:transparent;color:var(--dsw-alias-label-tertiary);transition:background-color .12s ease,color .12s ease}",
380
399
  // font-size:0 只藏住「复制 / 已复制」的字面,可访问名仍在
@@ -393,6 +412,7 @@ window.__ModuleLoader__.load({
393
412
  ".md-code-block " + BANNER + " ." + ZOOM_OUT_CLASS + "::before{-webkit-mask:var(--md-plus-icon-zoom-out) center/contain no-repeat;mask:var(--md-plus-icon-zoom-out) center/contain no-repeat}",
394
413
  ".md-code-block " + BANNER + " ." + EXPAND_CLASS + "::before{-webkit-mask:var(--md-plus-icon-expand) center/contain no-repeat;mask:var(--md-plus-icon-expand) center/contain no-repeat}",
395
414
  ".md-code-block " + BANNER + " ." + THEME_CLASS + "::before{-webkit-mask:var(--md-plus-icon-theme) center/contain no-repeat;mask:var(--md-plus-icon-theme) center/contain no-repeat}",
415
+ ".md-code-block " + BANNER + " ." + RETRY_CLASS + "::before{-webkit-mask:var(--md-plus-icon-retry) center/contain no-repeat;mask:var(--md-plus-icon-retry) center/contain no-repeat}",
396
416
  "[" + MMD_ATTR + '="diagram"] .' + TOGGLE_CLASS + "{color:var(--dsw-alias-button-primary-fill,#4c6ef5);background:var(--dsw-alias-interactive-bg-active,rgb(76 110 245 / 12%))}",
397
417
  // 已经到最小 / 最大时,对应那颗按钮变淡且不可点
398
418
  "[" + ZOOM_ATTR + '="min"] .' + ZOOM_OUT_CLASS + ",[" + ZOOM_ATTR + '="max"] .' + ZOOM_IN_CLASS + "{opacity:.3;pointer-events:none}",
@@ -460,7 +480,7 @@ window.__ModuleLoader__.load({
460
480
  // 抓手用结构选择器而不是 DSH 的哈希类名:外层是「唯一子元素是 table 的 div」。
461
481
  // 前面挂 [class*="_markdown_"] 有两个作用——只认 markdown 渲染出来的表格,
462
482
  // 同时把特异性顶到 (0,1,4),压过 DSH 自己的 .tableScroll th (0,1,1)。
463
- TABLE + "{position:relative;margin-block:16px;" + TABLE_VARS + "}",
483
+ TABLE + "{position:relative;margin-block:16px;max-height:min(70vh,720px);overflow-y:auto;overscroll-behavior-y:contain;" + TABLE_VARS + "}",
464
484
  "body[data-ds-dark-theme] " + TABLE + "{" + TABLE_VARS_DARK + "}",
465
485
  // 整张表一个 1px 边框 + 小圆角。
466
486
  // 注意必须是 separate:collapse 模式下 Chrome 会忽略单元格的圆角,
@@ -524,30 +544,121 @@ window.__ModuleLoader__.load({
524
544
  script.src = url;
525
545
  script.async = true;
526
546
  script.setAttribute(RUNTIME_ATTR, "1");
527
- script.addEventListener("load", () => resolve(window[globalName] ?? null), { once: true });
528
- script.addEventListener("error", () => resolve(null), { once: true });
547
+ script.addEventListener(
548
+ "load",
549
+ () => {
550
+ var api = window[globalName];
551
+ if (api === void 0) scripts.delete(url);
552
+ resolve(api ?? null);
553
+ },
554
+ { once: true }
555
+ );
556
+ script.addEventListener(
557
+ "error",
558
+ () => {
559
+ scripts.delete(url);
560
+ script.remove();
561
+ resolve(null);
562
+ },
563
+ { once: true }
564
+ );
529
565
  document.head.appendChild(script);
530
566
  });
531
567
  scripts.set(url, promise);
532
568
  return promise;
533
569
  }
534
- function readLang(block) {
570
+ function readBannerLabel(block) {
535
571
  var banner = block.querySelector(BANNER);
536
- var info = banner?.firstElementChild;
572
+ var info = banner?.querySelector('[class*="_language_"]') ?? banner?.firstElementChild;
537
573
  return (info?.textContent ?? "").trim().toLowerCase();
538
574
  }
575
+ var PLACEHOLDER_LABELS = /* @__PURE__ */ new Set(["", "\u4EE3\u7801\u5757", "code block", "code", "plain text", "plaintext", "\u7EAF\u6587\u672C"]);
576
+ var FIBER_KEY = "__reactFiber$";
577
+ function isPlaceholderLabel(label) {
578
+ return PLACEHOLDER_LABELS.has(label);
579
+ }
580
+ function readFiberLang(block) {
581
+ var info = null;
582
+ var nodes = [block, block.querySelector(CONTENT)];
583
+ for (var node of nodes) {
584
+ if (node === null || node === void 0) continue;
585
+ var names;
586
+ try {
587
+ names = Object.getOwnPropertyNames(node);
588
+ } catch {
589
+ continue;
590
+ }
591
+ for (var name of names) {
592
+ if (name.lastIndexOf(FIBER_KEY, 0) !== 0) continue;
593
+ var fiber = node[name];
594
+ for (var depth = 0; fiber !== null && fiber !== void 0 && depth < 24; depth += 1) {
595
+ var props = fiber.memoizedProps;
596
+ if (props !== null && typeof props === "object" && Object.prototype.hasOwnProperty.call(props, "lang")) {
597
+ var value = typeof props.lang === "string" ? props.lang.trim().toLowerCase() : "";
598
+ info = { hasLang: value !== "", lang: value };
599
+ break;
600
+ }
601
+ fiber = fiber.return;
602
+ }
603
+ if (info !== null) break;
604
+ }
605
+ if (info !== null) break;
606
+ }
607
+ return info;
608
+ }
609
+ function readLang(block) {
610
+ var label = readBannerLabel(block);
611
+ if (!isPlaceholderLabel(label)) return label;
612
+ var info = readFiberLang(block);
613
+ return info === null ? "" : info.lang;
614
+ }
615
+ function paintLanguageLabel(block, lang) {
616
+ if (lang === "") return;
617
+ var banner = block.querySelector(BANNER);
618
+ var heading = banner?.firstElementChild;
619
+ if (heading === null || heading === void 0) return;
620
+ var target = banner.querySelector('[class*="_language_"]') ?? heading;
621
+ if ((target.textContent ?? "").trim().toLowerCase() === lang) return;
622
+ if (!target.hasAttribute("data-md-plus-original-label")) {
623
+ target.setAttribute("data-md-plus-original-label", target.textContent ?? "");
624
+ }
625
+ target.textContent = lang;
626
+ target.setAttribute("data-md-plus-lang", lang);
627
+ }
539
628
  function sniffDiagramType(code) {
540
- var text = code;
629
+ var lines = code.split("\n");
630
+ var start = 0;
631
+ while (start < lines.length) {
632
+ var line = lines[start].trim();
633
+ if (line === "" || line.startsWith("%%")) {
634
+ start += 1;
635
+ continue;
636
+ }
637
+ break;
638
+ }
639
+ var text = lines.slice(start).join("\n");
541
640
  if (text.startsWith("---")) {
542
641
  var blank = text.indexOf("\n");
543
642
  var close = blank < 0 ? -1 : text.indexOf("\n---", blank);
544
643
  text = close < 0 ? "" : text.slice(close + 4);
545
644
  }
546
- var body = text.split("\n").filter((line) => !line.trimStart().startsWith("%%")).join("\n");
547
- return MMD_TYPE.exec(body)?.[1] ?? null;
645
+ var first = text.split("\n").find((line2) => {
646
+ var value2 = line2.trim();
647
+ return value2 !== "" && !value2.startsWith("%%");
648
+ });
649
+ if (first === void 0) return null;
650
+ var value = first.trim();
651
+ var flow = MMD_FLOW.exec(value);
652
+ if (flow !== null) return flow[1];
653
+ if (MMD_PIE.test(value)) return "pie";
654
+ if (MMD_GIT.test(value)) return "gitGraph";
655
+ var single = MMD_SINGLE.exec(value);
656
+ return single === null ? null : single[1];
548
657
  }
549
658
  function diagramKind(block) {
550
- if (MMD_LANGS.has(readLang(block))) return "diagram";
659
+ var lang = readLang(block);
660
+ if (MMD_LANGS.has(lang)) return "diagram";
661
+ if (lang !== "") return null;
551
662
  var pre = block.querySelector(CONTENT + " pre");
552
663
  if (pre === null) return null;
553
664
  return sniffDiagramType(pre.textContent ?? "") === null ? null : "diagram";
@@ -599,7 +710,8 @@ window.__ModuleLoader__.load({
599
710
  }
600
711
  }
601
712
  function redrawDiagrams() {
602
- for (const block of document.querySelectorAll("[" + MMD_ATTR + '="diagram"]')) {
713
+ for (const block of document.querySelectorAll("[" + MMD_ATTR + "]")) {
714
+ if (block.getAttribute(MMD_ATTR) === "failed") continue;
603
715
  block.removeAttribute(KEY_ATTR);
604
716
  pending.add(block);
605
717
  }
@@ -646,6 +758,19 @@ window.__ModuleLoader__.load({
646
758
  run: (block) => cycleMermaidTheme(block)
647
759
  }
648
760
  ];
761
+ var FAILED_BUTTONS = [
762
+ {
763
+ cls: RETRY_CLASS,
764
+ titleKey: "retry",
765
+ ariaKey: "retryAria",
766
+ run: (block) => {
767
+ block.removeAttribute(KEY_ATTR);
768
+ block.removeAttribute(MMD_ATTR);
769
+ block.querySelector("." + MMD_CLASS + "-error")?.remove();
770
+ void enhance(block);
771
+ }
772
+ }
773
+ ];
649
774
  function findNativeWrap(buttons) {
650
775
  if (buttons === void 0) return null;
651
776
  for (const button of buttons) {
@@ -661,7 +786,10 @@ window.__ModuleLoader__.load({
661
786
  var copy = nativeButtons === void 0 || nativeButtons.length === 0 ? null : nativeButtons[nativeButtons.length - 1];
662
787
  var action = copy?.parentElement;
663
788
  if (action === null || action === void 0) return;
664
- var specs = readLang(block) !== "" && diagramKind(block) !== null ? MMD_BUTTONS : findNativeWrap(nativeButtons) !== null ? [] : PLAIN_BUTTONS;
789
+ var label = readBannerLabel(block);
790
+ var lang = readLang(block);
791
+ if (lang !== "" && isPlaceholderLabel(label)) paintLanguageLabel(block, lang);
792
+ var specs = block.getAttribute(MMD_ATTR) === "failed" ? FAILED_BUTTONS : diagramKind(block) !== null ? MMD_BUTTONS : findNativeWrap(nativeButtons) !== null ? [] : PLAIN_BUTTONS;
665
793
  for (const existing of action.querySelectorAll("." + BTN_CLASS)) {
666
794
  if (specs.some((spec) => existing.classList.contains(spec.cls))) continue;
667
795
  existing.remove();
@@ -675,9 +803,9 @@ window.__ModuleLoader__.load({
675
803
  created.title = t(spec.titleKey);
676
804
  created.setAttribute("aria-label", t(spec.ariaKey));
677
805
  if (spec.cls === THEME_CLASS) {
678
- var label = themeLabel(getMermaidTheme());
679
- created.title = label;
680
- created.setAttribute("aria-label", label);
806
+ var themeText = themeLabel(getMermaidTheme());
807
+ created.title = themeText;
808
+ created.setAttribute("aria-label", themeText);
681
809
  }
682
810
  created.addEventListener("click", () => spec.run(block));
683
811
  action.insertBefore(created, copy);
@@ -702,11 +830,12 @@ window.__ModuleLoader__.load({
702
830
  var pre = content.querySelector("pre");
703
831
  if (pre === null || pre.classList.contains("shiki")) return;
704
832
  var lang = readLang(block);
705
- if (lang === "") return;
833
+ if (lang !== "" && isPlaceholderLabel(readBannerLabel(block))) paintLanguageLabel(block, lang);
706
834
  if (diagramKind(block) !== null) {
707
835
  await renderDiagram(block, content, pre);
708
836
  return;
709
837
  }
838
+ if (lang === "") return;
710
839
  if (DSH_LANGS.has(lang)) return;
711
840
  var api = await loadScript(HL_URL, HL_GLOBAL);
712
841
  if (api === null || !api.supports(lang)) return;
@@ -812,6 +941,7 @@ window.__ModuleLoader__.load({
812
941
  zooms.delete(block);
813
942
  paint(block);
814
943
  scheduleFold(viewport, foldLimit("diagram"));
944
+ decorate(block);
815
945
  }
816
946
  var zooms = /* @__PURE__ */ new WeakMap();
817
947
  function zoomState(block) {
@@ -862,11 +992,12 @@ window.__ModuleLoader__.load({
862
992
  clampPan(block, state);
863
993
  paint(block);
864
994
  }
865
- function bindViewport(block, viewport, resetView) {
995
+ function bindViewport(block, viewport, resetView, alwaysZoom) {
866
996
  var drag = null;
867
997
  viewport.addEventListener(
868
998
  "wheel",
869
999
  (event) => {
1000
+ if (!alwaysZoom && !event.ctrlKey && !event.metaKey && zoomState(block).scale <= 1.001) return;
870
1001
  event.preventDefault();
871
1002
  zoomBy(block, event.deltaY < 0 ? ZOOM_STEP : 1 / ZOOM_STEP, event.clientX, event.clientY);
872
1003
  },
@@ -1214,21 +1345,26 @@ window.__ModuleLoader__.load({
1214
1345
  closeSettings();
1215
1346
  }
1216
1347
  }
1217
- function readNumber(input, fallback) {
1348
+ function readNumber(input, fallback, max) {
1349
+ var value = Number(input.value);
1350
+ return Number.isFinite(value) && value >= 0 ? Math.min(max ?? 4e3, Math.round(value)) : fallback;
1351
+ }
1352
+ function readFontSize(input) {
1218
1353
  var value = Number(input.value);
1219
- return Number.isFinite(value) && value >= 0 ? Math.min(4e3, Math.round(value)) : fallback;
1354
+ if (!Number.isFinite(value) || value <= 0) return 0;
1355
+ return Math.min(20, Math.max(10, Math.round(value)));
1220
1356
  }
1221
1357
  resetButton.addEventListener("click", () => {
1222
1358
  themeSelect.value = "blue";
1223
1359
  fontInput.value = "";
1224
1360
  viewSelect.value = DEFAULT_SETTINGS.diagramView;
1225
- foldCode.value = "";
1226
- foldTable.value = "";
1227
- foldDiagram.value = "";
1361
+ foldCode.value = String(DEFAULT_SETTINGS.foldCode);
1362
+ foldTable.value = String(DEFAULT_SETTINGS.foldTable);
1363
+ foldDiagram.value = String(DEFAULT_SETTINGS.foldDiagram);
1228
1364
  });
1229
1365
  saveButton.addEventListener("click", () => {
1230
1366
  saveSettings({
1231
- codeFontSize: readNumber(fontInput, 0),
1367
+ codeFontSize: readFontSize(fontInput),
1232
1368
  diagramView: viewSelect.value === "code" ? "code" : "diagram",
1233
1369
  foldCode: readNumber(foldCode, DEFAULT_SETTINGS.foldCode),
1234
1370
  foldTable: readNumber(foldTable, DEFAULT_SETTINGS.foldTable),
@@ -1444,7 +1580,7 @@ window.__ModuleLoader__.load({
1444
1580
  closeButton.addEventListener("click", closeViewer);
1445
1581
  document.addEventListener("keydown", onKey, true);
1446
1582
  zooms.set(overlay, { scale: 1, x: 0, y: 0, min: ZOOM_MIN_MODAL });
1447
- bindViewport(overlay, viewport, reset);
1583
+ bindViewport(overlay, viewport, reset, true);
1448
1584
  document.documentElement.style.overflow = "hidden";
1449
1585
  document.body.appendChild(overlay);
1450
1586
  closeButton.focus();
@@ -1460,6 +1596,7 @@ window.__ModuleLoader__.load({
1460
1596
  content.appendChild(note);
1461
1597
  block.setAttribute(KEY_ATTR, key);
1462
1598
  block.setAttribute(MMD_ATTR, "failed");
1599
+ decorate(block);
1463
1600
  console.warn("[dsh-md-plus] mermaid \u6E32\u67D3\u5931\u8D25", error);
1464
1601
  }
1465
1602
  function apply0(ctx) {
@@ -1484,10 +1621,17 @@ window.__ModuleLoader__.load({
1484
1621
  document.removeEventListener("click", onDocumentClick, true);
1485
1622
  for (const node of injected) node.remove();
1486
1623
  injected.clear();
1624
+ for (const node of document.querySelectorAll("." + BTN_CLASS)) node.remove();
1625
+ for (const node of document.querySelectorAll("." + HOST_CLASS)) node.remove();
1626
+ for (const node of document.querySelectorAll("." + FOLD_BTN_CLASS)) node.remove();
1487
1627
  for (const node of document.querySelectorAll("[" + FOLD_ATTR + "]")) resetFold(node);
1488
1628
  for (const node of document.querySelectorAll("." + TABLE_TOOLS_CLASS)) node.remove();
1489
- for (const table of document.querySelectorAll("[" + TABLE_DONE_ATTR + "]")) {
1490
- table.removeAttribute(TABLE_DONE_ATTR);
1629
+ for (const attr of [DONE_ATTR, KEY_ATTR, WRAP_ATTR, MMD_ATTR, TABLE_DONE_ATTR, "data-md-plus-lang"]) {
1630
+ for (const node of document.querySelectorAll("[" + attr + "]")) node.removeAttribute(attr);
1631
+ }
1632
+ for (const node of document.querySelectorAll("[data-md-plus-original-label]")) {
1633
+ node.textContent = node.getAttribute("data-md-plus-original-label") ?? node.textContent;
1634
+ node.removeAttribute("data-md-plus-original-label");
1491
1635
  }
1492
1636
  for (const node of document.querySelectorAll(
1493
1637
  "." + MMD_CLASS + ", ." + MMD_CLASS + "-error, ." + MODAL_CLASS + ", ." + IMG_MODAL_CLASS + ", ." + SETTINGS_BTN_CLASS + ", ." + SETTINGS_MODAL_CLASS
@@ -1496,6 +1640,7 @@ window.__ModuleLoader__.load({
1496
1640
  }
1497
1641
  document.documentElement.style.overflow = "";
1498
1642
  document.documentElement.style.removeProperty("--md-plus-code-font-size");
1643
+ document.documentElement.style.removeProperty("--md-plus-code-line-height");
1499
1644
  document.querySelector("style[" + STYLE_ATTR + "]")?.remove();
1500
1645
  for (const script of document.querySelectorAll("script[" + RUNTIME_ATTR + "]")) {
1501
1646
  script.remove();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-md-plus",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Markdown display enhancements for DeepSeek Harness Web: code block restyle, syntax highlighting for languages DSH does not ship, and built-in mermaid diagrams.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",