dsh-md-plus 0.4.2 → 0.4.4

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,20 @@
1
1
  # 更新日志
2
2
 
3
- ## 0.4.2 —— 未发布
3
+ ## 0.4.4 —— 未发布
4
+
5
+ - 修复表格折叠按钮在低阈值和展开/收起后漂移、消失的问题:补齐表格折叠高度规则、重置滚动位置,并给按钮阴影留出空间。
6
+
7
+ ## 0.4.3 —— 2026-10-30
8
+
9
+ - 修复补高亮语言映射:`hbs` / `jade` / `tscn` / `tres` 分别使用 handlebars / pug / gdresource 语法着色。
10
+ - 修复 Mermaid 12.1 在占位标签兜底路径下的图种识别:补充 `radar-beta`、`treemap-beta`、`venn-beta`、`ishikawa-beta`、`wardley-beta`、`cynefin-beta`、`treeView-beta`、`flowchart-elk` 等,并去掉无效裸关键字的误判。
11
+ - 修复插件卸载后 debounce 扫描重新插入按钮、设置入口和折叠控件的问题,同时清理 `data-md-plus-zoom` 属性。
12
+ - 修复 localStorage 不可用时设置保存和配色切换连当前会话都不生效的问题。
13
+ - 修复运行时路由 ETag 只依赖文件大小和 mtime、内容更新后仍可能返回 304 的问题。
14
+ - 同步能力展示页和 README 预览图到当前 DSH 样式,并把所有版本引用更新为 0.4.3。
15
+ - 测试补到 78 条,新增别名语言、Mermaid 新图种、卸载清理、localStorage 降级和 ETag 回归用例。
16
+
17
+ ## 0.4.2 —— 2026-10-30
4
18
 
5
19
  - 修复鼠标悬浮表格或折叠内容时吞掉页面滚轮:移除表格容器的 `overscroll-behavior` containment,折叠态改为保留横向滚动、纵向 `overflow-y: clip`,并覆盖 DSH 宿主 `.tableScroll` 自带的横向 containment。
6
20
  - 修复宽表横向滚动条出现 / 消失时鼠标移入移出表格造成的上下跳动:取消 hover 阶段的 padding 切换,固定底部留白。
package/README.md CHANGED
@@ -27,11 +27,13 @@ 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 # 语法检查 + 73 条测试(先 build 再跑,check 不含 build)
30
+ npm run check # 语法检查 + 78 条测试(先 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
+ `npm run preview` / `scripts/showcase.mjs` 需要读取 DSH 安装目录里的主题和前端资源;如果本机 DSH 是 `app.asar` 包,请先解包到目录,并把 `DSH_APP_DIR` 指向该目录。
36
+
35
37
  ## 已知限制
36
38
 
37
39
  - DSH 0.2.1-rc.4 的头部语言名由宿主决定:不认识的围栏只显示占位文案「代码块」。插件会从宿主的 React fiber 里兜底读取真实语言,读不到时才退回正文嗅探;宿主改 React 结构后这条兜底可能失效。
package/lib/client.js CHANGED
@@ -178,6 +178,8 @@ window.__ModuleLoader__.load({
178
178
  var STYLE_ATTR = "data-md-plus-style";
179
179
  var RUNTIME_ATTR = "data-md-plus-runtime";
180
180
  var DEBOUNCE_MS = 160;
181
+ var memorySettings = null;
182
+ var memoryTheme = null;
181
183
  function readSettings() {
182
184
  var settings = { ...DEFAULT_SETTINGS };
183
185
  try {
@@ -194,11 +196,14 @@ window.__ModuleLoader__.load({
194
196
  if (Number.isFinite(value) && value >= 0) settings[key] = Math.min(4e3, Math.round(value));
195
197
  }
196
198
  }
199
+ memorySettings = { ...settings };
197
200
  } catch {
201
+ if (memorySettings !== null) return { ...DEFAULT_SETTINGS, ...memorySettings };
198
202
  }
199
203
  return settings;
200
204
  }
201
205
  function saveSettings(next) {
206
+ memorySettings = { ...next };
202
207
  try {
203
208
  window.localStorage.setItem(SETTINGS_KEY, JSON.stringify(next));
204
209
  } catch {
@@ -331,10 +336,10 @@ window.__ModuleLoader__.load({
331
336
  "gradle"
332
337
  ]);
333
338
  var MMD_LANGS = /* @__PURE__ */ new Set(["mermaid", "mermaidjs", "mmd"]);
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;
339
+ var MMD_FLOW = /^\s*(flowchart(?:-elk)?|graph)(?:\s+(?:TD|TB|BT|RL|LR))?\s*;?\s*$/i;
340
+ var MMD_PIE = /^\s*pie(?:\s+(?:showData|title)\b.*)?\s*;?\s*$/i;
336
341
  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;
342
+ 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-beta|treemap(?:-beta)?|venn-beta|ishikawa(?:-beta)?|wardley-beta|cynefin-beta|treeView-beta|info|eventmodeling|swimlane-beta|railroad-beta|railroad-abnf-beta|railroad-ebnf-beta|railroad-peg-beta)\s*;?\s*$/i;
338
343
  function icon(paths) {
339
344
  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>";
340
345
  return 'url("data:image/svg+xml,' + encodeURIComponent(svg) + '")';
@@ -446,7 +451,7 @@ window.__ModuleLoader__.load({
446
451
  // 用 clip 而不是 hidden:hidden 会建立隐形滚动容器,鼠标在折叠内容上滚轮会被它吞掉。
447
452
  "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{max-height:var(--md-plus-fold-max-height);overflow:clip}',
448
453
  "." + FOLD_BTN_CLASS + "{display:block;white-space:nowrap;margin:8px auto 0;padding:4px 12px;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 22%));border-radius:999px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-primary);font:12px/18px var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer;box-shadow:0 2px 8px rgb(0 0 0 / 12%);z-index:2}",
449
- "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"] .' + FOLD_BTN_CLASS + "{position:absolute;left:50%;bottom:8px;transform:translateX(-50%);margin:0}",
454
+ "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"] .' + FOLD_BTN_CLASS + "{position:absolute;left:50%;bottom:12px;transform:translateX(-50%);margin:0}",
450
455
  // ── 图片点击放大 ────────────────────────────────────────────────
451
456
  '[class*="_markdown_"] img{cursor:zoom-in}',
452
457
  "." + IMG_MODAL_CLASS + "{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:calc(var(--dsh-windows-titlebar-height,0px) + 16px) 24px 24px;background:rgb(0 0 0 / 72%)}",
@@ -524,7 +529,9 @@ window.__ModuleLoader__.load({
524
529
  TABLE + ".md-table-wide > table{width:100%;max-width:none;min-width:max-content}",
525
530
  // 折叠态保留横向滚动,但纵向必须 clip;横向滚动容器只有在
526
531
  // overscroll-behavior:auto 时才会把纵向滚轮交还给页面。
527
- TABLE + "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{overflow-x:auto;overflow-y:clip}'
532
+ // 这里同时写明 min/max-height:基础表格规则的 max-height 特异性更高,不补会压过设置值;
533
+ // 最小高度保证极低阈值下「展开」按钮仍完整可见。
534
+ TABLE + "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{min-height:48px;max-height:var(--md-plus-fold-max-height);overflow-x:auto;overflow-y:clip}'
528
535
  ].join("\n");
529
536
  var scripts = /* @__PURE__ */ new Map();
530
537
  var pending = /* @__PURE__ */ new Set();
@@ -533,6 +540,7 @@ window.__ModuleLoader__.load({
533
540
  var rescanQueued = false;
534
541
  var injected = /* @__PURE__ */ new Set();
535
542
  var diagramSeq = 0;
543
+ var disposed = false;
536
544
  function injectStyle() {
537
545
  if (document.querySelector("style[" + STYLE_ATTR + "]") !== null) return;
538
546
  var tag = document.createElement("style");
@@ -675,6 +683,7 @@ window.__ModuleLoader__.load({
675
683
  ) != null;
676
684
  }
677
685
  function schedule(block) {
686
+ if (disposed) return;
678
687
  if (block !== void 0) pending.add(block);
679
688
  else rescanQueued = true;
680
689
  if (timer !== null) return;
@@ -682,6 +691,7 @@ window.__ModuleLoader__.load({
682
691
  }
683
692
  function flush() {
684
693
  timer = null;
694
+ if (disposed) return;
685
695
  if (rescanQueued) {
686
696
  rescanQueued = false;
687
697
  for (const block of document.querySelectorAll(BLOCK)) pending.add(block);
@@ -843,7 +853,7 @@ window.__ModuleLoader__.load({
843
853
  if (lang === "") return;
844
854
  if (DSH_LANGS.has(lang)) return;
845
855
  var api = await loadScript(HL_URL, HL_GLOBAL);
846
- if (api === null || !api.supports(lang)) return;
856
+ if (disposed || api === null || !api.supports(lang)) return;
847
857
  if (!pre.isConnected) return;
848
858
  var code = pre.textContent ?? "";
849
859
  if (code.trim() === "") return;
@@ -855,6 +865,7 @@ window.__ModuleLoader__.load({
855
865
  } catch {
856
866
  return;
857
867
  }
868
+ if (disposed) return;
858
869
  if (typeof html !== "string" || html === "") return;
859
870
  if (!pre.isConnected || (pre.textContent ?? "") !== code) {
860
871
  schedule(block);
@@ -879,8 +890,13 @@ window.__ModuleLoader__.load({
879
890
  function getMermaidTheme() {
880
891
  try {
881
892
  var saved = window.localStorage.getItem(THEME_KEY);
882
- if (saved !== null && THEME_ORDER.indexOf(saved) !== -1) return saved;
893
+ if (saved !== null && THEME_ORDER.indexOf(saved) !== -1) {
894
+ memoryTheme = saved;
895
+ return saved;
896
+ }
897
+ memoryTheme = null;
883
898
  } catch {
899
+ if (memoryTheme !== null) return memoryTheme;
884
900
  }
885
901
  return "blue";
886
902
  }
@@ -898,6 +914,7 @@ window.__ModuleLoader__.load({
898
914
  var current = getMermaidTheme();
899
915
  var index = THEME_ORDER.indexOf(current);
900
916
  var next = THEME_ORDER[(index + 1) % THEME_ORDER.length];
917
+ memoryTheme = next;
901
918
  try {
902
919
  window.localStorage.setItem(THEME_KEY, next);
903
920
  } catch {
@@ -911,16 +928,18 @@ window.__ModuleLoader__.load({
911
928
  var key = "mmd\0" + code;
912
929
  if (block.getAttribute(KEY_ATTR) === key) return;
913
930
  var api = await loadScript(MMD_URL, MMD_GLOBAL);
914
- if (api === null) return;
931
+ if (disposed || api === null) return;
915
932
  if (!pre.isConnected) return;
916
933
  var svg;
917
934
  try {
918
935
  var dark = document.body.hasAttribute("data-ds-dark-theme");
919
936
  svg = await api.render("dsh-md-plus-mmd-" + ++diagramSeq, code, dark, getMermaidTheme());
920
937
  } catch (error) {
938
+ if (disposed) return;
921
939
  failDiagram(block, content, pre, key, error);
922
940
  return;
923
941
  }
942
+ if (disposed) return;
924
943
  if (typeof svg !== "string" || svg === "") return;
925
944
  if (svg.includes('aria-roledescription="error"')) {
926
945
  failDiagram(block, content, pre, key, new Error("mermaid \u8FD4\u56DE\u7684\u662F\u9519\u8BEF\u5361\u7247"));
@@ -1083,6 +1102,7 @@ window.__ModuleLoader__.load({
1083
1102
  target.setAttribute(FOLD_ATTR, "on");
1084
1103
  target.classList.add(FOLD_CLASS);
1085
1104
  target.style.setProperty("--md-plus-fold-max-height", limit + "px");
1105
+ if (target.hasAttribute(TABLE_DONE_ATTR)) target.scrollTop = 0;
1086
1106
  var button = document.createElement("button");
1087
1107
  button.type = "button";
1088
1108
  button.className = FOLD_BTN_CLASS;
@@ -1099,6 +1119,7 @@ window.__ModuleLoader__.load({
1099
1119
  button.addEventListener("click", () => {
1100
1120
  var folded = target.getAttribute(FOLD_ATTR) === "on";
1101
1121
  target.setAttribute(FOLD_ATTR, folded ? "off" : "on");
1122
+ if (target.hasAttribute(TABLE_DONE_ATTR)) target.scrollTop = 0;
1102
1123
  button.textContent = folded ? t("foldClose") : t("foldOpen");
1103
1124
  button.setAttribute("aria-expanded", folded ? "true" : "false");
1104
1125
  sync?.();
@@ -1394,6 +1415,7 @@ window.__ModuleLoader__.load({
1394
1415
  foldTable: readNumber(foldTable, DEFAULT_SETTINGS.foldTable),
1395
1416
  foldDiagram: readNumber(foldDiagram, DEFAULT_SETTINGS.foldDiagram)
1396
1417
  });
1418
+ memoryTheme = themeSelect.value;
1397
1419
  try {
1398
1420
  window.localStorage.setItem(THEME_KEY, themeSelect.value);
1399
1421
  } catch {
@@ -1626,6 +1648,7 @@ window.__ModuleLoader__.load({
1626
1648
  function apply0(ctx) {
1627
1649
  if (typeof document === "undefined") return;
1628
1650
  ctx.effect(() => {
1651
+ disposed = false;
1629
1652
  injectStyle();
1630
1653
  applyCodeFontSize();
1631
1654
  ensureSettingsButton();
@@ -1641,6 +1664,13 @@ window.__ModuleLoader__.load({
1641
1664
  });
1642
1665
  schedule();
1643
1666
  return () => {
1667
+ disposed = true;
1668
+ if (timer !== null) {
1669
+ window.clearTimeout(timer);
1670
+ timer = null;
1671
+ }
1672
+ pending.clear();
1673
+ rescanQueued = false;
1644
1674
  observer.disconnect();
1645
1675
  document.removeEventListener("click", onDocumentClick, true);
1646
1676
  for (const node of injected) node.remove();
@@ -1650,7 +1680,7 @@ window.__ModuleLoader__.load({
1650
1680
  for (const node of document.querySelectorAll("." + FOLD_BTN_CLASS)) node.remove();
1651
1681
  for (const node of document.querySelectorAll("[" + FOLD_ATTR + "]")) resetFold(node);
1652
1682
  for (const node of document.querySelectorAll("." + TABLE_TOOLS_CLASS)) node.remove();
1653
- for (const attr of [DONE_ATTR, KEY_ATTR, WRAP_ATTR, MMD_ATTR, TABLE_DONE_ATTR, "data-md-plus-lang"]) {
1683
+ for (const attr of [DONE_ATTR, KEY_ATTR, WRAP_ATTR, MMD_ATTR, TABLE_DONE_ATTR, "data-md-plus-lang", ZOOM_ATTR]) {
1654
1684
  for (const node of document.querySelectorAll("[" + attr + "]")) node.removeAttribute(attr);
1655
1685
  }
1656
1686
  for (const node of document.querySelectorAll("[data-md-plus-original-label]")) {