dsh-md-plus 0.2.0 → 0.3.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 +18 -0
- package/README.md +6 -17
- package/lib/client.js +238 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## 0.3.1 —— 2026-10-28
|
|
4
|
+
|
|
5
|
+
- 修掉宿主原生按钮已有 SVG 图标时,插件又叠加一层复制图标造成的重复显示;插件按钮仍插在原生换行与复制按钮之间。
|
|
6
|
+
- 测试补到 55 条,新增全屏打开、尺寸恢复、Esc 关闭与重复图标回归用例。
|
|
7
|
+
|
|
8
|
+
## 0.3.0 —— 2026-10-28
|
|
9
|
+
|
|
10
|
+
- mermaid 卡片新增「全屏查看」:复杂时序图、长流程图不再挤在聊天栏里看。覆盖层默认适应整图,支持滚轮缩放、拖拽、双击适应和 Esc 关闭。
|
|
11
|
+
- 全屏里的缩放范围与聊天内分开:聊天内保持 1–6 倍,全屏可以缩到 10%,方便先看全局再放大细节。
|
|
12
|
+
- 测试补到 54 条,新增全屏打开、尺寸恢复与 Esc 关闭用例。
|
|
13
|
+
|
|
14
|
+
## 0.2.1 —— 2026-10-16
|
|
15
|
+
|
|
16
|
+
- 修掉「同一份代码显示两遍」:`DSH_LANGS` 还是老版本的语言表,`dart`、`scala`、`vue`、`powershell`、`csv`、`diff` 等 66 个别名被当成「DSH 不认识」,插件抢先补了一层高亮;随后 DSH 的懒加载语法到位、React 把 plain `<pre>` 换成自己的高亮节点,插件那份副本成了野节点留在原地,于是两份都在。现在语言表对齐 108 条别名。
|
|
17
|
+
- 加了一层兜底:DSH 接管某块后,插件撤掉自己插的副本(清 `data-md-plus` 标记)。语言表以后再落后也不会留永久双份,最多白干一次。
|
|
18
|
+
- README:修正「`dart` 不在 DSH 语言表里」的错误说法,示例换成 DSH 真不认的 `gdscript`。
|
|
19
|
+
- 测试扩到 53 条,新增「DSH 接管后不留副本」的回归用例;补高亮的用例换成插件独有语言,不再拿 `dart` 当反例。
|
|
20
|
+
|
|
3
21
|
## 0.2.0 —— 2026-10-08
|
|
4
22
|
|
|
5
23
|
- 修掉 mermaid 围栏在 DSH 0.2.0-rc.2 上画不出来(issue #1):头部语言名被占位文案「代码块」顶掉,插件读不到围栏语言;现在认不出标签就看正文的类型声明。
|
package/README.md
CHANGED
|
@@ -19,26 +19,14 @@ DSH 原生的头部是「小灰字 + 文字复制按钮」,没写语言时左
|
|
|
19
19
|
|
|
20
20
|
样式是纯 CSS 打在 `.md-code-block` 上,所以所有语言(包括 DSH 自己高亮的 JSON/TS/Python)都吃同一套外观。想调间距,改 `src/client.js` 里 banner 的 `padding` 末位和 pre 的 `padding` 首位。
|
|
21
21
|
|
|
22
|
-
### 语法高亮
|
|
23
22
|
|
|
24
|
-
DSH 只认它语言表里的语言,其余走纯文本回退,没颜色。`dart` 就不在表里:
|
|
25
|
-
|
|
26
|
-
```dart
|
|
27
|
-
class CodeCard extends StatelessWidget {
|
|
28
|
-
const CodeCard({super.key, required this.language});
|
|
29
|
-
|
|
30
|
-
final String language;
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
插件把这类块捡出来用自带的 Shiki 上色,DSH 认的语言一律不碰。
|
|
35
|
-
|
|
36
|
-
⚠️ 在 DSH 0.2.0-rc.2 上这套暂时失效,原因见文末「已知边界」。
|
|
37
23
|
|
|
38
24
|
### mermaid 图形
|
|
39
25
|
|
|
40
26
|
mermaid 围栏由插件渲染成 SVG,头部右侧可在「图形 / 代码」之间切换,复制按钮复制的仍是图源。配色和节点圆角在 `src/mermaid-theme.js`。
|
|
41
27
|
|
|
28
|
+
复杂时序图、长流程图可以点右上角的全屏图标:在覆盖层里默认「适应整图」,也支持滚轮缩放、拖拽和双击适应,按 Esc 关闭。聊天内的普通缩放仍然保留。
|
|
29
|
+
|
|
42
30
|
认图先看头部语言名(`mermaid` / `mmd` / `mermaidjs`),认不出再看正文里的类型声明(`flowchart`、`graph`、`sequenceDiagram` 等)。0.2.0-rc.2 上头部是占位文案,走的就是正文这条。
|
|
43
31
|
|
|
44
32
|
### 表格样式
|
|
@@ -67,7 +55,7 @@ dsh plugin --profile desktop remove dsh-md-plus
|
|
|
67
55
|
```powershell
|
|
68
56
|
npm install # 顺带跑 prepare → build
|
|
69
57
|
npm run build # 产出 lib/ 下四个文件
|
|
70
|
-
npm run check # 语法检查 +
|
|
58
|
+
npm run check # 语法检查 + 53 条测试
|
|
71
59
|
npm run preview # 生成 preview.html(含真渲出来的 mermaid 图)
|
|
72
60
|
npm run shot # 用 headless Chrome 把 preview.html 截成 docs/preview.png
|
|
73
61
|
```
|
|
@@ -78,8 +66,9 @@ npm run shot # 用 headless Chrome 把 preview.html 截成 docs/preview.png
|
|
|
78
66
|
|
|
79
67
|
## 已知边界
|
|
80
68
|
|
|
81
|
-
- 0.2.0-rc.2
|
|
82
|
-
- 同一个原因让补高亮在 0.2.0-rc.2
|
|
69
|
+
- 0.2.0-rc.2 起,头部那行语言名由宿主决定显示什么:认这门语言就显示语言名,不认就显示本地化占位文案(中文「代码块」)。认不出来就读不到围栏语言,插件无法转化,且还不报错。mermaid 这条改看正文:跳过 `%%` 注释和开头的 `---` frontmatter,正文里有行以 mermaid 类型关键字开头就当图。这条只管认图,不参与补高亮的判断。
|
|
70
|
+
- 同一个原因让补高亮在 0.2.0-rc.2 上基本失效:`gdscript`、`nushell` 这类 DSH 不认的语言,头部是占位文案,插件问不到 Shiki 该用哪套语法,只能没颜色。反过来 DSH 认得的那 108 种(`dart`、`vue`、`scala` …)由 DSH 自己上色,插件不碰。
|
|
71
|
+
- `DSH_LANGS` 是 DSH `LANG_ALIASES` 的快照(对齐 108 条)。DSH 加语言后这份快照会落后,落后只会让它多管一门 DSH 也在管的高亮 —— 撤回兜底能保证不出现双份,但会白干一次高亮;想干净就跟着同步。
|
|
83
72
|
- mermaid 12 的新图种(`usecase-beta`、`agentflow-beta`)标识符只认字母数字下划线,中文得写进标签:`actor Customer("用户")` 可以,`actor 用户` 会解析失败。
|
|
84
73
|
- 高亮约 6.4 MB、mermaid 约 5.0 MB,按需下载,第一次用到时多花约 100ms 解析。
|
|
85
74
|
- 依赖宿主 `.md-code-block`、`[data-code-block-content]`、`[data-code-block-banner]`、`data-streaming` 这几个钩子,DSH 改了要跟着改 `src/client.js` 顶部的常量和 `MMD_TYPE`。
|
package/lib/client.js
CHANGED
|
@@ -21,6 +21,11 @@ window.__ModuleLoader__.load({
|
|
|
21
21
|
var ZOOM_ATTR = "data-md-plus-zoom";
|
|
22
22
|
var ZOOM_STEP = 1.25;
|
|
23
23
|
var ZOOM_MAX = 6;
|
|
24
|
+
var ZOOM_MIN_INLINE = 1;
|
|
25
|
+
var ZOOM_MIN_MODAL = 0.1;
|
|
26
|
+
var ZOOM_MIN_EPSILON = 1e-3;
|
|
27
|
+
var EXPAND_CLASS = "md-plus-expand";
|
|
28
|
+
var MODAL_CLASS = "md-plus-mmd-modal";
|
|
24
29
|
var DONE_ATTR = "data-md-plus";
|
|
25
30
|
var KEY_ATTR = "data-md-plus-key";
|
|
26
31
|
var WRAP_ATTR = "data-md-plus-wrap";
|
|
@@ -70,7 +75,73 @@ window.__ModuleLoader__.load({
|
|
|
70
75
|
"less",
|
|
71
76
|
"sql",
|
|
72
77
|
"xml",
|
|
73
|
-
"lua"
|
|
78
|
+
"lua",
|
|
79
|
+
"bat",
|
|
80
|
+
"batch",
|
|
81
|
+
"powershell",
|
|
82
|
+
"ps1",
|
|
83
|
+
"ps",
|
|
84
|
+
"fish",
|
|
85
|
+
"properties",
|
|
86
|
+
"dotenv",
|
|
87
|
+
"env",
|
|
88
|
+
"log",
|
|
89
|
+
"csv",
|
|
90
|
+
"diff",
|
|
91
|
+
"patch",
|
|
92
|
+
"http",
|
|
93
|
+
"rst",
|
|
94
|
+
"latex",
|
|
95
|
+
"tex",
|
|
96
|
+
"bibtex",
|
|
97
|
+
"bib",
|
|
98
|
+
"asciidoc",
|
|
99
|
+
"adoc",
|
|
100
|
+
"r",
|
|
101
|
+
"julia",
|
|
102
|
+
"jl",
|
|
103
|
+
"dart",
|
|
104
|
+
"scala",
|
|
105
|
+
"clojure",
|
|
106
|
+
"clj",
|
|
107
|
+
"erlang",
|
|
108
|
+
"erl",
|
|
109
|
+
"elixir",
|
|
110
|
+
"ex",
|
|
111
|
+
"exs",
|
|
112
|
+
"haskell",
|
|
113
|
+
"hs",
|
|
114
|
+
"fsharp",
|
|
115
|
+
"fs",
|
|
116
|
+
"fsi",
|
|
117
|
+
"fsx",
|
|
118
|
+
"vb",
|
|
119
|
+
"vbnet",
|
|
120
|
+
"perl",
|
|
121
|
+
"pl",
|
|
122
|
+
"pm",
|
|
123
|
+
"verilog",
|
|
124
|
+
"v",
|
|
125
|
+
"system-verilog",
|
|
126
|
+
"systemverilog",
|
|
127
|
+
"sv",
|
|
128
|
+
"svh",
|
|
129
|
+
"graphql",
|
|
130
|
+
"gql",
|
|
131
|
+
"proto",
|
|
132
|
+
"protobuf",
|
|
133
|
+
"hcl",
|
|
134
|
+
"tf",
|
|
135
|
+
"tfvars",
|
|
136
|
+
"nix",
|
|
137
|
+
"vue",
|
|
138
|
+
"svelte",
|
|
139
|
+
"make",
|
|
140
|
+
"makefile",
|
|
141
|
+
"mk",
|
|
142
|
+
"cmake",
|
|
143
|
+
"groovy",
|
|
144
|
+
"gradle"
|
|
74
145
|
]);
|
|
75
146
|
var MMD_LANGS = /* @__PURE__ */ new Set(["mermaid", "mermaidjs", "mmd"]);
|
|
76
147
|
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;
|
|
@@ -96,12 +167,15 @@ window.__ModuleLoader__.load({
|
|
|
96
167
|
var ICON_ZOOM_OUT = icon(
|
|
97
168
|
"<circle cx='10.5' cy='10.5' r='7'/><path d='M20.5 20.5 15.6 15.6'/><path d='M7.5 10.5h6'/>"
|
|
98
169
|
);
|
|
170
|
+
var ICON_EXPAND = icon(
|
|
171
|
+
"<path d='M8 3H3v5'/><path d='M16 3h5v5'/><path d='M3 16v5h5'/><path d='M21 16v5h-5'/>"
|
|
172
|
+
);
|
|
99
173
|
var TABLE = '[class*="_markdown_"] div:has(> table)';
|
|
100
174
|
var TABLE_VARS = "--md-plus-tbl-radius:8px;--md-plus-tbl-radius-inner:7px;--md-plus-tbl-line:#ebebeb;--md-plus-tbl-head:#f7f7f7;--md-plus-tbl-head-fg:#181818;--md-plus-tbl-hover:#fafafa";
|
|
101
175
|
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";
|
|
102
176
|
var CSS = [
|
|
103
177
|
// ── 卡片:整块一个底色,顶栏不再是独立色带 ──────────────────────
|
|
104
|
-
".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 + "}",
|
|
178
|
+
".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 + "}",
|
|
105
179
|
// bannerWrap 的原底色是 --dsw-alias-bg-base(#fff),会跟卡片灰拉开一条带
|
|
106
180
|
".md-code-block > div:has(> " + BANNER + "){background:transparent}",
|
|
107
181
|
// ── 头部:收紧,并把空着的左边填上 ─────────────────────────────
|
|
@@ -120,7 +194,7 @@ window.__ModuleLoader__.load({
|
|
|
120
194
|
// ── 头部按钮:文字换成图标 ──────────────────────────────────────
|
|
121
195
|
".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}",
|
|
122
196
|
// font-size:0 只藏住「复制 / 已复制」的字面,可访问名仍在
|
|
123
|
-
".md-code-block " + BANNER + " button::before{content:'';width:15px;height:15px;background:currentColor;-webkit-mask:var(--md-plus-icon-copy) center/contain no-repeat;mask:var(--md-plus-icon-copy) center/contain no-repeat}",
|
|
197
|
+
".md-code-block " + BANNER + " button:not(:has(svg))::before{content:'';width:15px;height:15px;background:currentColor;-webkit-mask:var(--md-plus-icon-copy) center/contain no-repeat;mask:var(--md-plus-icon-copy) center/contain no-repeat}",
|
|
124
198
|
".md-code-block " + BANNER + " button:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover,rgb(127 127 127 / 12%))}",
|
|
125
199
|
".md-code-block " + BANNER + " button:focus-visible{outline:2px solid var(--dsw-alias-button-primary-fill,#4c6ef5);outline-offset:1px}",
|
|
126
200
|
// ── 普通块:不换行切换 ──────────────────────────────────────────
|
|
@@ -131,6 +205,7 @@ window.__ModuleLoader__.load({
|
|
|
131
205
|
".md-code-block " + BANNER + " ." + TOGGLE_CLASS + "::before{-webkit-mask:var(--md-plus-icon-diagram) center/contain no-repeat;mask:var(--md-plus-icon-diagram) center/contain no-repeat}",
|
|
132
206
|
".md-code-block " + BANNER + " ." + ZOOM_IN_CLASS + "::before{-webkit-mask:var(--md-plus-icon-zoom-in) center/contain no-repeat;mask:var(--md-plus-icon-zoom-in) center/contain no-repeat}",
|
|
133
207
|
".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}",
|
|
208
|
+
".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}",
|
|
134
209
|
"[" + 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%))}",
|
|
135
210
|
// 已经到最小 / 最大时,对应那颗按钮变淡且不可点
|
|
136
211
|
"[" + ZOOM_ATTR + '="min"] .' + ZOOM_OUT_CLASS + ",[" + ZOOM_ATTR + '="max"] .' + ZOOM_IN_CLASS + "{opacity:.3;pointer-events:none}",
|
|
@@ -147,6 +222,18 @@ window.__ModuleLoader__.load({
|
|
|
147
222
|
"." + MMD_CLASS + "-stage{transform-origin:0 0;display:flex;flex-direction:column;justify-content:center;min-height:var(--md-plus-mmd-min-height)}",
|
|
148
223
|
"." + MMD_CLASS + " svg{display:block;margin:0 auto;max-width:100%;height:auto}",
|
|
149
224
|
"." + MMD_CLASS + "-error{margin:0 16px 12px;padding:8px 12px;border-radius:8px;font:12px/18px var(--ds-font-family-code,monospace);white-space:pre-wrap;color:var(--dsw-alias-state-error-primary,#d33);background:var(--dsw-alias-bg-layer-2,rgb(127 127 127 / 8%))}",
|
|
225
|
+
// ── 全屏查看:复杂时序图不挤在聊天栏里,默认按整图铺满视口 ─────
|
|
226
|
+
"." + 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 / 48%)}",
|
|
227
|
+
"." + MODAL_CLASS + "-panel{display:flex;flex-direction:column;width:min(1600px,100%);height:min(1000px,100%);overflow:hidden;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 18%));border-radius:12px;background:var(--dsw-alias-bg-base,#fff);box-shadow:0 24px 80px rgb(0 0 0 / 25%)}",
|
|
228
|
+
"." + MODAL_CLASS + "-bar{display:flex;align-items:center;gap:8px;flex:none;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 18%))}",
|
|
229
|
+
"." + MODAL_CLASS + "-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:600 13px/18px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-primary)}",
|
|
230
|
+
"." + MODAL_CLASS + "-actions{display:flex;align-items:center;gap:6px;flex:none}",
|
|
231
|
+
"." + MODAL_CLASS + "-actions button{appearance:none;border:0;border-radius:6px;padding:5px 10px;background:var(--dsw-alias-interactive-bg-hover,rgb(127 127 127 / 12%));color:var(--dsw-alias-label-primary);font:12px/18px var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer}",
|
|
232
|
+
"." + MODAL_CLASS + "-actions button:hover{background:var(--dsw-alias-interactive-bg-active,rgb(76 110 245 / 12%))}",
|
|
233
|
+
"." + MODAL_CLASS + "-body{display:flex;flex:1;min-height:0;padding:16px}",
|
|
234
|
+
"." + MODAL_CLASS + "-viewport{flex:1;min-height:0}",
|
|
235
|
+
"." + MODAL_CLASS + "-stage{display:inline-flex;align-items:center;justify-content:center;min-height:0;flex:none}",
|
|
236
|
+
"." + MODAL_CLASS + " svg{display:block}",
|
|
150
237
|
// 高亮宿主不参与布局,让里面的 <pre> 直接吃到卡片样式
|
|
151
238
|
"." + HOST_CLASS + "{display:contents}",
|
|
152
239
|
// 藏掉纯文本回退,只留着色结果;原节点仍在,复制读的还是它
|
|
@@ -312,12 +399,19 @@ window.__ModuleLoader__.load({
|
|
|
312
399
|
var next = block.getAttribute(MMD_ATTR) === "code" ? "diagram" : "code";
|
|
313
400
|
block.setAttribute(MMD_ATTR, next);
|
|
314
401
|
}
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
cls: EXPAND_CLASS,
|
|
405
|
+
title: "\u5168\u5C4F\u67E5\u770B",
|
|
406
|
+
aria: "\u5168\u5C4F\u67E5\u770B\u6D41\u7A0B\u56FE",
|
|
407
|
+
run: (block) => openFullscreen(block)
|
|
315
408
|
}
|
|
316
409
|
];
|
|
317
410
|
function decorate(block) {
|
|
318
411
|
if (!block.isConnected) return;
|
|
319
412
|
var banner = block.querySelector(BANNER);
|
|
320
|
-
var
|
|
413
|
+
var nativeButtons = banner?.querySelectorAll("button:not(." + BTN_CLASS + ")");
|
|
414
|
+
var copy = nativeButtons === void 0 || nativeButtons.length === 0 ? null : nativeButtons[nativeButtons.length - 1];
|
|
321
415
|
var action = copy?.parentElement;
|
|
322
416
|
if (action === null || action === void 0) return;
|
|
323
417
|
var specs = readLang(block) !== "" && diagramKind(block) !== null ? MMD_BUTTONS : PLAIN_BUTTONS;
|
|
@@ -338,11 +432,21 @@ window.__ModuleLoader__.load({
|
|
|
338
432
|
injected.add(created);
|
|
339
433
|
}
|
|
340
434
|
}
|
|
435
|
+
function releaseTakenOver(block, content) {
|
|
436
|
+
var host = content.querySelector("." + HOST_CLASS);
|
|
437
|
+
if (host === null) return false;
|
|
438
|
+
if (content.querySelector("pre.shiki:not(.md-plus-pre)") === null) return false;
|
|
439
|
+
host.remove();
|
|
440
|
+
block.removeAttribute(KEY_ATTR);
|
|
441
|
+
block.removeAttribute(DONE_ATTR);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
341
444
|
async function enhance(block) {
|
|
342
445
|
if (!block.isConnected) return;
|
|
343
446
|
if (block.closest("[data-streaming]") !== null) return;
|
|
344
447
|
var content = block.querySelector(CONTENT);
|
|
345
448
|
if (content === null) return;
|
|
449
|
+
if (releaseTakenOver(block, content)) return;
|
|
346
450
|
var pre = content.querySelector("pre");
|
|
347
451
|
if (pre === null || pre.classList.contains("shiki")) return;
|
|
348
452
|
var lang = readLang(block);
|
|
@@ -428,7 +532,7 @@ window.__ModuleLoader__.load({
|
|
|
428
532
|
function zoomState(block) {
|
|
429
533
|
var state = zooms.get(block);
|
|
430
534
|
if (state === void 0) {
|
|
431
|
-
state = { scale: 1, x: 0, y: 0 };
|
|
535
|
+
state = { scale: 1, x: 0, y: 0, min: ZOOM_MIN_INLINE };
|
|
432
536
|
zooms.set(block, state);
|
|
433
537
|
}
|
|
434
538
|
return state;
|
|
@@ -445,7 +549,7 @@ window.__ModuleLoader__.load({
|
|
|
445
549
|
stage.parentElement.setAttribute("data-zoomed", state.scale > 1.001 ? "on" : "off");
|
|
446
550
|
block.setAttribute(
|
|
447
551
|
ZOOM_ATTR,
|
|
448
|
-
state.scale <=
|
|
552
|
+
state.scale <= state.min + ZOOM_MIN_EPSILON ? "min" : state.scale >= ZOOM_MAX ? "max" : "mid"
|
|
449
553
|
);
|
|
450
554
|
}
|
|
451
555
|
function clampPan(block, state) {
|
|
@@ -461,7 +565,7 @@ window.__ModuleLoader__.load({
|
|
|
461
565
|
var stage = stageOf(block);
|
|
462
566
|
if (stage === null) return;
|
|
463
567
|
var state = zoomState(block);
|
|
464
|
-
var next = Math.min(ZOOM_MAX, Math.max(
|
|
568
|
+
var next = Math.min(ZOOM_MAX, Math.max(state.min, state.scale * factor));
|
|
465
569
|
if (Math.abs(next - state.scale) < 1e-3) return;
|
|
466
570
|
var box = stage.parentElement.getBoundingClientRect();
|
|
467
571
|
var anchorX = (clientX ?? box.left + box.width / 2) - box.left;
|
|
@@ -473,7 +577,7 @@ window.__ModuleLoader__.load({
|
|
|
473
577
|
clampPan(block, state);
|
|
474
578
|
paint(block);
|
|
475
579
|
}
|
|
476
|
-
function bindViewport(block, viewport) {
|
|
580
|
+
function bindViewport(block, viewport, resetView) {
|
|
477
581
|
var drag = null;
|
|
478
582
|
viewport.addEventListener(
|
|
479
583
|
"wheel",
|
|
@@ -515,10 +619,134 @@ window.__ModuleLoader__.load({
|
|
|
515
619
|
viewport.addEventListener("pointerup", stopDrag);
|
|
516
620
|
viewport.addEventListener("pointercancel", stopDrag);
|
|
517
621
|
viewport.addEventListener("dblclick", () => {
|
|
622
|
+
if (resetView !== void 0) {
|
|
623
|
+
resetView();
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
518
626
|
zooms.delete(block);
|
|
519
627
|
paint(block);
|
|
520
628
|
});
|
|
521
629
|
}
|
|
630
|
+
function fitToView(scope) {
|
|
631
|
+
var stage = stageOf(scope);
|
|
632
|
+
if (stage === null) return;
|
|
633
|
+
var box = stage.parentElement.getBoundingClientRect();
|
|
634
|
+
var svg = stage.querySelector("svg");
|
|
635
|
+
var width = stage.offsetWidth || parseFloat(svg?.getAttribute("width") ?? "") || 0;
|
|
636
|
+
var height = stage.offsetHeight || parseFloat(svg?.getAttribute("height") ?? "") || 0;
|
|
637
|
+
if (box.width <= 0 || box.height <= 0 || width <= 0 || height <= 0) return;
|
|
638
|
+
var state = zoomState(scope);
|
|
639
|
+
var scale = Math.min(1, (box.width - 32) / width, (box.height - 32) / height);
|
|
640
|
+
state.scale = Math.max(state.min, scale);
|
|
641
|
+
state.x = 0;
|
|
642
|
+
state.y = 0;
|
|
643
|
+
clampPan(scope, state);
|
|
644
|
+
paint(scope);
|
|
645
|
+
}
|
|
646
|
+
function svgSize(svg) {
|
|
647
|
+
var box = (svg.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
|
|
648
|
+
if (box.length === 4 && box[2] > 0 && box[3] > 0) {
|
|
649
|
+
return { width: box[2], height: box[3] };
|
|
650
|
+
}
|
|
651
|
+
var rect = svg.getBoundingClientRect();
|
|
652
|
+
return { width: rect.width, height: rect.height };
|
|
653
|
+
}
|
|
654
|
+
function openFullscreen(block) {
|
|
655
|
+
if (document.querySelector("." + MODAL_CLASS) !== null) return;
|
|
656
|
+
var svg = block.querySelector("." + MMD_CLASS + " svg");
|
|
657
|
+
if (svg === null) return;
|
|
658
|
+
var size = svgSize(svg);
|
|
659
|
+
var overlay = document.createElement("div");
|
|
660
|
+
overlay.className = MODAL_CLASS;
|
|
661
|
+
overlay.setAttribute("role", "dialog");
|
|
662
|
+
overlay.setAttribute("aria-modal", "true");
|
|
663
|
+
overlay.setAttribute("aria-label", "\u6D41\u7A0B\u56FE\u5168\u5C4F\u67E5\u770B");
|
|
664
|
+
var panel = document.createElement("div");
|
|
665
|
+
panel.className = MODAL_CLASS + "-panel";
|
|
666
|
+
var bar = document.createElement("div");
|
|
667
|
+
bar.className = MODAL_CLASS + "-bar";
|
|
668
|
+
var title = document.createElement("div");
|
|
669
|
+
title.className = MODAL_CLASS + "-title";
|
|
670
|
+
title.textContent = "\u6D41\u7A0B\u56FE";
|
|
671
|
+
var actions = document.createElement("div");
|
|
672
|
+
actions.className = MODAL_CLASS + "-actions";
|
|
673
|
+
var fitButton = document.createElement("button");
|
|
674
|
+
fitButton.type = "button";
|
|
675
|
+
fitButton.textContent = "\u9002\u5E94\u6574\u56FE";
|
|
676
|
+
var zoomOut = document.createElement("button");
|
|
677
|
+
zoomOut.type = "button";
|
|
678
|
+
zoomOut.textContent = "\u2212";
|
|
679
|
+
zoomOut.setAttribute("aria-label", "\u7F29\u5C0F");
|
|
680
|
+
var zoomIn = document.createElement("button");
|
|
681
|
+
zoomIn.type = "button";
|
|
682
|
+
zoomIn.textContent = "+";
|
|
683
|
+
zoomIn.setAttribute("aria-label", "\u653E\u5927");
|
|
684
|
+
var closeButton = document.createElement("button");
|
|
685
|
+
closeButton.type = "button";
|
|
686
|
+
closeButton.textContent = "\u5173\u95ED";
|
|
687
|
+
actions.appendChild(fitButton);
|
|
688
|
+
actions.appendChild(zoomOut);
|
|
689
|
+
actions.appendChild(zoomIn);
|
|
690
|
+
actions.appendChild(closeButton);
|
|
691
|
+
bar.appendChild(title);
|
|
692
|
+
bar.appendChild(actions);
|
|
693
|
+
panel.appendChild(bar);
|
|
694
|
+
var body = document.createElement("div");
|
|
695
|
+
body.className = MODAL_CLASS + "-body";
|
|
696
|
+
var viewport = document.createElement("div");
|
|
697
|
+
viewport.className = MMD_CLASS + "-viewport " + MODAL_CLASS + "-viewport";
|
|
698
|
+
var stage = document.createElement("div");
|
|
699
|
+
stage.className = MMD_CLASS + "-stage " + MODAL_CLASS + "-stage";
|
|
700
|
+
var clone = svg.cloneNode(true);
|
|
701
|
+
if (size.width > 0 && size.height > 0) {
|
|
702
|
+
clone.setAttribute("width", String(size.width));
|
|
703
|
+
clone.setAttribute("height", String(size.height));
|
|
704
|
+
clone.style.width = size.width + "px";
|
|
705
|
+
clone.style.height = size.height + "px";
|
|
706
|
+
}
|
|
707
|
+
clone.style.maxWidth = "none";
|
|
708
|
+
stage.appendChild(clone);
|
|
709
|
+
viewport.appendChild(stage);
|
|
710
|
+
body.appendChild(viewport);
|
|
711
|
+
panel.appendChild(body);
|
|
712
|
+
overlay.appendChild(panel);
|
|
713
|
+
var previousFocus = document.activeElement;
|
|
714
|
+
var previousOverflow = document.documentElement.style.overflow;
|
|
715
|
+
var closed = false;
|
|
716
|
+
var reset = () => fitToView(overlay);
|
|
717
|
+
function closeViewer() {
|
|
718
|
+
if (closed) return;
|
|
719
|
+
closed = true;
|
|
720
|
+
document.removeEventListener("keydown", onKey, true);
|
|
721
|
+
overlay.remove();
|
|
722
|
+
document.documentElement.style.overflow = previousOverflow;
|
|
723
|
+
zooms.delete(overlay);
|
|
724
|
+
if (previousFocus !== null && typeof previousFocus.focus === "function") {
|
|
725
|
+
previousFocus.focus();
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function onKey(event) {
|
|
729
|
+
if (event.key === "Escape") {
|
|
730
|
+
event.preventDefault();
|
|
731
|
+
closeViewer();
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
overlay.addEventListener("pointerdown", (event) => {
|
|
735
|
+
if (event.target === overlay) closeViewer();
|
|
736
|
+
});
|
|
737
|
+
fitButton.addEventListener("click", reset);
|
|
738
|
+
zoomOut.addEventListener("click", () => zoomBy(overlay, 1 / ZOOM_STEP));
|
|
739
|
+
zoomIn.addEventListener("click", () => zoomBy(overlay, ZOOM_STEP));
|
|
740
|
+
closeButton.addEventListener("click", closeViewer);
|
|
741
|
+
document.addEventListener("keydown", onKey, true);
|
|
742
|
+
zooms.set(overlay, { scale: 1, x: 0, y: 0, min: ZOOM_MIN_MODAL });
|
|
743
|
+
bindViewport(overlay, viewport, reset);
|
|
744
|
+
document.documentElement.style.overflow = "hidden";
|
|
745
|
+
document.body.appendChild(overlay);
|
|
746
|
+
closeButton.focus();
|
|
747
|
+
var schedule2 = window.requestAnimationFrame ?? ((cb) => window.setTimeout(cb, 0));
|
|
748
|
+
schedule2(reset);
|
|
749
|
+
}
|
|
522
750
|
function failDiagram(block, content, pre, key, error) {
|
|
523
751
|
content.querySelector("." + MMD_CLASS)?.remove();
|
|
524
752
|
var note = content.querySelector("." + MMD_CLASS + "-error") ?? document.createElement("p");
|
|
@@ -549,10 +777,11 @@ window.__ModuleLoader__.load({
|
|
|
549
777
|
for (const node of injected) node.remove();
|
|
550
778
|
injected.clear();
|
|
551
779
|
for (const node of document.querySelectorAll(
|
|
552
|
-
"." + MMD_CLASS + ", ." + MMD_CLASS + "-error"
|
|
780
|
+
"." + MMD_CLASS + ", ." + MMD_CLASS + "-error, ." + MODAL_CLASS
|
|
553
781
|
)) {
|
|
554
782
|
node.remove();
|
|
555
783
|
}
|
|
784
|
+
document.documentElement.style.overflow = "";
|
|
556
785
|
document.querySelector("style[" + STYLE_ATTR + "]")?.remove();
|
|
557
786
|
for (const script of document.querySelectorAll("script[" + RUNTIME_ATTR + "]")) {
|
|
558
787
|
script.remove();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-md-plus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|