dsh-md-plus 0.3.1 → 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 +61 -33
- package/README.md +18 -50
- package/lib/client.js +899 -40
- package/lib/mermaid-runtime.js +295 -295
- package/package.json +67 -67
package/lib/client.js
CHANGED
|
@@ -25,14 +25,201 @@ window.__ModuleLoader__.load({
|
|
|
25
25
|
var ZOOM_MIN_MODAL = 0.1;
|
|
26
26
|
var ZOOM_MIN_EPSILON = 1e-3;
|
|
27
27
|
var EXPAND_CLASS = "md-plus-expand";
|
|
28
|
+
var THEME_CLASS = "md-plus-theme";
|
|
29
|
+
var RETRY_CLASS = "md-plus-retry";
|
|
30
|
+
var THEME_KEY = "dsh-md-plus-mermaid-theme";
|
|
31
|
+
var THEME_ORDER = ["blue", "neutral", "forest", "contrast"];
|
|
32
|
+
var THEME_LABELS = {
|
|
33
|
+
zh: { blue: "\u84DD\u8272", neutral: "\u4E2D\u6027", forest: "\u68EE\u6797", contrast: "\u9AD8\u5BF9\u6BD4" },
|
|
34
|
+
en: { blue: "Blue", neutral: "Neutral", forest: "Forest", contrast: "High contrast" }
|
|
35
|
+
};
|
|
36
|
+
function detectLang() {
|
|
37
|
+
var declared = (document.documentElement.lang || "").toLowerCase();
|
|
38
|
+
if (declared.startsWith("zh")) return "zh";
|
|
39
|
+
if (declared.startsWith("en")) return "en";
|
|
40
|
+
var nav = (window.navigator.language || "").toLowerCase();
|
|
41
|
+
return nav.startsWith("zh") ? "zh" : "en";
|
|
42
|
+
}
|
|
43
|
+
var TEXT = {
|
|
44
|
+
zh: {
|
|
45
|
+
wrapTitle: "\u4E0D\u6362\u884C\uFF08\u6A2A\u5411\u6EDA\u52A8\uFF09",
|
|
46
|
+
wrapAria: "\u5207\u6362\u4EE3\u7801\u6362\u884C",
|
|
47
|
+
zoomOut: "\u7F29\u5C0F",
|
|
48
|
+
zoomOutAria: "\u7F29\u5C0F\u6D41\u7A0B\u56FE",
|
|
49
|
+
zoomIn: "\u653E\u5927",
|
|
50
|
+
zoomInAria: "\u653E\u5927\u6D41\u7A0B\u56FE",
|
|
51
|
+
toggle: "\u5207\u6362\u56FE\u5F62 / \u4EE3\u7801",
|
|
52
|
+
toggleAria: "\u5207\u6362\u56FE\u5F62\u4E0E\u4EE3\u7801",
|
|
53
|
+
fullscreen: "\u5168\u5C4F\u67E5\u770B",
|
|
54
|
+
fullscreenAria: "\u5168\u5C4F\u67E5\u770B\u6D41\u7A0B\u56FE",
|
|
55
|
+
theme: "\u5207\u6362 Mermaid \u914D\u8272",
|
|
56
|
+
themeAria: "\u5207\u6362 Mermaid \u914D\u8272",
|
|
57
|
+
themePrefix: "\u914D\u8272\uFF1A",
|
|
58
|
+
foldOpen: "\u5C55\u5F00",
|
|
59
|
+
foldClose: "\u6536\u8D77",
|
|
60
|
+
retry: "\u91CD\u8BD5",
|
|
61
|
+
retryAria: "\u91CD\u65B0\u6E32\u67D3\u56FE\u8868",
|
|
62
|
+
renderFailed: "mermaid \u6E32\u67D3\u5931\u8D25\uFF1A",
|
|
63
|
+
diagram: "\u6D41\u7A0B\u56FE",
|
|
64
|
+
diagramAria: "\u6D41\u7A0B\u56FE\u5168\u5C4F\u67E5\u770B",
|
|
65
|
+
fit: "\u9002\u5E94\u6574\u56FE",
|
|
66
|
+
downloadSvg: "\u4E0B\u8F7D SVG",
|
|
67
|
+
downloadPng: "\u4E0B\u8F7D PNG",
|
|
68
|
+
close: "\u5173\u95ED",
|
|
69
|
+
tableCsv: "CSV",
|
|
70
|
+
tableMd: "Markdown",
|
|
71
|
+
copyCsvTitle: "\u590D\u5236\u8868\u683C\u4E3A CSV",
|
|
72
|
+
copyMdTitle: "\u590D\u5236\u8868\u683C\u4E3A Markdown",
|
|
73
|
+
copied: "\u5DF2\u590D\u5236",
|
|
74
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
75
|
+
imageViewer: "\u56FE\u7247\u9884\u89C8",
|
|
76
|
+
imageClose: "\u5173\u95ED\u56FE\u7247\u9884\u89C8",
|
|
77
|
+
settingsOpen: "\u6253\u5F00 Markdown+ \u8BBE\u7F6E",
|
|
78
|
+
settingsTitle: "Markdown+ \u8BBE\u7F6E",
|
|
79
|
+
settingsTheme: "Mermaid \u914D\u8272",
|
|
80
|
+
settingsFontSize: "\u4EE3\u7801\u5B57\u53F7",
|
|
81
|
+
settingsFontSizeHint: "\u7559\u7A7A\u6216 0 \u8868\u793A\u8DDF\u968F\u5BBF\u4E3B",
|
|
82
|
+
settingsView: "\u56FE\u8868\u9ED8\u8BA4\u89C6\u56FE",
|
|
83
|
+
settingsViewDiagram: "\u56FE\u5F62",
|
|
84
|
+
settingsViewCode: "\u4EE3\u7801",
|
|
85
|
+
settingsFold: "\u957F\u5185\u5BB9\u6298\u53E0\u9608\u503C",
|
|
86
|
+
settingsFoldCode: "\u4EE3\u7801\u5757",
|
|
87
|
+
settingsFoldTable: "\u8868\u683C",
|
|
88
|
+
settingsFoldDiagram: "\u56FE\u8868",
|
|
89
|
+
settingsFoldCodeHint: "\u5185\u5BB9\u9AD8\u5EA6\u8D85\u8FC7\u6B64\u503C\u540E\u6298\u53E0",
|
|
90
|
+
settingsFoldTableHint: "\u8868\u683C\u9AD8\u5EA6\u8D85\u8FC7\u6B64\u503C\u540E\u6298\u53E0",
|
|
91
|
+
settingsFoldDiagramHint: "\u56FE\u5F62\u9AD8\u5EA6\u8D85\u8FC7\u6B64\u503C\u540E\u6298\u53E0",
|
|
92
|
+
settingsFoldHint: "\u5355\u4F4D px\uFF0C0 \u8868\u793A\u4E0D\u6298\u53E0",
|
|
93
|
+
settingsSave: "\u4FDD\u5B58",
|
|
94
|
+
settingsReset: "\u6062\u590D\u9ED8\u8BA4",
|
|
95
|
+
settingsClose: "\u5173\u95ED"
|
|
96
|
+
},
|
|
97
|
+
en: {
|
|
98
|
+
wrapTitle: "No wrap (horizontal scroll)",
|
|
99
|
+
wrapAria: "Toggle code wrapping",
|
|
100
|
+
zoomOut: "Zoom out",
|
|
101
|
+
zoomOutAria: "Zoom out diagram",
|
|
102
|
+
zoomIn: "Zoom in",
|
|
103
|
+
zoomInAria: "Zoom in diagram",
|
|
104
|
+
toggle: "Switch diagram / code",
|
|
105
|
+
toggleAria: "Switch diagram and code",
|
|
106
|
+
fullscreen: "Fullscreen",
|
|
107
|
+
fullscreenAria: "View diagram fullscreen",
|
|
108
|
+
theme: "Change Mermaid theme",
|
|
109
|
+
themeAria: "Change Mermaid theme",
|
|
110
|
+
themePrefix: "Theme: ",
|
|
111
|
+
foldOpen: "Expand",
|
|
112
|
+
foldClose: "Collapse",
|
|
113
|
+
retry: "Retry",
|
|
114
|
+
retryAria: "Retry diagram",
|
|
115
|
+
renderFailed: "Mermaid render failed: ",
|
|
116
|
+
diagram: "Diagram",
|
|
117
|
+
diagramAria: "Diagram fullscreen viewer",
|
|
118
|
+
fit: "Fit",
|
|
119
|
+
downloadSvg: "Download SVG",
|
|
120
|
+
downloadPng: "Download PNG",
|
|
121
|
+
close: "Close",
|
|
122
|
+
tableCsv: "CSV",
|
|
123
|
+
tableMd: "Markdown",
|
|
124
|
+
copyCsvTitle: "Copy table as CSV",
|
|
125
|
+
copyMdTitle: "Copy table as Markdown",
|
|
126
|
+
copied: "Copied",
|
|
127
|
+
copyFailed: "Copy failed",
|
|
128
|
+
imageViewer: "Image preview",
|
|
129
|
+
imageClose: "Close image preview",
|
|
130
|
+
settingsOpen: "Open Markdown+ settings",
|
|
131
|
+
settingsTitle: "Markdown+ settings",
|
|
132
|
+
settingsTheme: "Mermaid theme",
|
|
133
|
+
settingsFontSize: "Code font size",
|
|
134
|
+
settingsFontSizeHint: "Leave empty or 0 to follow the host",
|
|
135
|
+
settingsView: "Default diagram view",
|
|
136
|
+
settingsViewDiagram: "Diagram",
|
|
137
|
+
settingsViewCode: "Code",
|
|
138
|
+
settingsFold: "Fold thresholds",
|
|
139
|
+
settingsFoldCode: "Code block",
|
|
140
|
+
settingsFoldTable: "Table",
|
|
141
|
+
settingsFoldDiagram: "Diagram",
|
|
142
|
+
settingsFoldCodeHint: "Fold when the block is taller than this",
|
|
143
|
+
settingsFoldTableHint: "Fold when the table is taller than this",
|
|
144
|
+
settingsFoldDiagramHint: "Fold when the diagram is taller than this",
|
|
145
|
+
settingsFoldHint: "Pixels; 0 disables folding",
|
|
146
|
+
settingsSave: "Save",
|
|
147
|
+
settingsReset: "Reset to defaults",
|
|
148
|
+
settingsClose: "Close"
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
function t(key) {
|
|
152
|
+
var lang = detectLang();
|
|
153
|
+
return TEXT[lang][key] ?? TEXT.en[key] ?? key;
|
|
154
|
+
}
|
|
28
155
|
var MODAL_CLASS = "md-plus-mmd-modal";
|
|
29
156
|
var DONE_ATTR = "data-md-plus";
|
|
30
157
|
var KEY_ATTR = "data-md-plus-key";
|
|
31
158
|
var WRAP_ATTR = "data-md-plus-wrap";
|
|
32
159
|
var MMD_ATTR = "data-md-plus-mmd";
|
|
160
|
+
var FOLD_ATTR = "data-md-plus-fold";
|
|
161
|
+
var FOLD_CLASS = "md-plus-fold";
|
|
162
|
+
var FOLD_BTN_CLASS = "md-plus-fold-toggle";
|
|
163
|
+
var FOLD_LIMITS = { code: 480, table: 520, diagram: 720 };
|
|
164
|
+
var TABLE_TOOLS_CLASS = "md-plus-table-tools";
|
|
165
|
+
var TABLE_DONE_ATTR = "data-md-plus-table";
|
|
166
|
+
var IMG_MODAL_CLASS = "md-plus-img-modal";
|
|
167
|
+
var NATIVE_IMAGE_BUTTON = 'button[class*="imageButton"]';
|
|
168
|
+
var SETTINGS_BTN_CLASS = "md-plus-settings-btn";
|
|
169
|
+
var SETTINGS_MODAL_CLASS = "md-plus-settings-modal";
|
|
170
|
+
var SETTINGS_KEY = "dsh-md-plus-settings";
|
|
171
|
+
var DEFAULT_SETTINGS = {
|
|
172
|
+
codeFontSize: 0,
|
|
173
|
+
diagramView: "diagram",
|
|
174
|
+
foldCode: FOLD_LIMITS.code,
|
|
175
|
+
foldTable: FOLD_LIMITS.table,
|
|
176
|
+
foldDiagram: FOLD_LIMITS.diagram
|
|
177
|
+
};
|
|
33
178
|
var STYLE_ATTR = "data-md-plus-style";
|
|
34
179
|
var RUNTIME_ATTR = "data-md-plus-runtime";
|
|
35
180
|
var DEBOUNCE_MS = 160;
|
|
181
|
+
function readSettings() {
|
|
182
|
+
var settings = { ...DEFAULT_SETTINGS };
|
|
183
|
+
try {
|
|
184
|
+
var raw = window.localStorage.getItem(SETTINGS_KEY);
|
|
185
|
+
var saved = raw === null ? null : JSON.parse(raw);
|
|
186
|
+
if (saved !== null && typeof saved === "object") {
|
|
187
|
+
var size = Number(saved.codeFontSize);
|
|
188
|
+
if (Number.isFinite(size) && size >= 10 && size <= 20) settings.codeFontSize = Math.round(size);
|
|
189
|
+
if (saved.diagramView === "code" || saved.diagramView === "diagram") {
|
|
190
|
+
settings.diagramView = saved.diagramView;
|
|
191
|
+
}
|
|
192
|
+
for (var key of ["foldCode", "foldTable", "foldDiagram"]) {
|
|
193
|
+
var value = Number(saved[key]);
|
|
194
|
+
if (Number.isFinite(value) && value >= 0) settings[key] = Math.min(4e3, Math.round(value));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
return settings;
|
|
200
|
+
}
|
|
201
|
+
function saveSettings(next) {
|
|
202
|
+
try {
|
|
203
|
+
window.localStorage.setItem(SETTINGS_KEY, JSON.stringify(next));
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function foldLimit(kind) {
|
|
208
|
+
var settings = readSettings();
|
|
209
|
+
if (kind === "code") return settings.foldCode;
|
|
210
|
+
if (kind === "table") return settings.foldTable;
|
|
211
|
+
return settings.foldDiagram;
|
|
212
|
+
}
|
|
213
|
+
function applyCodeFontSize() {
|
|
214
|
+
var size = readSettings().codeFontSize;
|
|
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
|
+
}
|
|
222
|
+
}
|
|
36
223
|
var DSH_LANGS = /* @__PURE__ */ new Set([
|
|
37
224
|
"typescript",
|
|
38
225
|
"ts",
|
|
@@ -144,7 +331,10 @@ window.__ModuleLoader__.load({
|
|
|
144
331
|
"gradle"
|
|
145
332
|
]);
|
|
146
333
|
var MMD_LANGS = /* @__PURE__ */ new Set(["mermaid", "mermaidjs", "mmd"]);
|
|
147
|
-
var
|
|
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;
|
|
148
338
|
function icon(paths) {
|
|
149
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>";
|
|
150
340
|
return 'url("data:image/svg+xml,' + encodeURIComponent(svg) + '")';
|
|
@@ -170,12 +360,21 @@ window.__ModuleLoader__.load({
|
|
|
170
360
|
var ICON_EXPAND = icon(
|
|
171
361
|
"<path d='M8 3H3v5'/><path d='M16 3h5v5'/><path d='M3 16v5h5'/><path d='M21 16v5h-5'/>"
|
|
172
362
|
);
|
|
363
|
+
var ICON_THEME = icon(
|
|
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'/>"
|
|
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
|
+
);
|
|
369
|
+
var ICON_GEAR = icon(
|
|
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'/>"
|
|
371
|
+
);
|
|
173
372
|
var TABLE = '[class*="_markdown_"] div:has(> table)';
|
|
174
373
|
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";
|
|
175
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";
|
|
176
375
|
var CSS = [
|
|
177
376
|
// ── 卡片:整块一个底色,顶栏不再是独立色带 ──────────────────────
|
|
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 + "}",
|
|
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 + "}",
|
|
179
378
|
// bannerWrap 的原底色是 --dsw-alias-bg-base(#fff),会跟卡片灰拉开一条带
|
|
180
379
|
".md-code-block > div:has(> " + BANNER + "){background:transparent}",
|
|
181
380
|
// ── 头部:收紧,并把空着的左边填上 ─────────────────────────────
|
|
@@ -187,14 +386,20 @@ window.__ModuleLoader__.load({
|
|
|
187
386
|
".md-code-block " + BANNER + " > :first-child::before{content:'';flex:none;width:14px;height:14px;background:currentColor;color:var(--dsw-alias-label-tertiary);-webkit-mask:var(--md-plus-icon) center/contain no-repeat;mask:var(--md-plus-icon) center/contain no-repeat}",
|
|
188
387
|
// 没有语言时补个占位名,否则整行左边就是一片空白
|
|
189
388
|
".md-code-block " + BANNER + " > :first-child:empty::after{content:'Plain text'}",
|
|
389
|
+
":lang(zh) .md-code-block " + BANNER + " > :first-child:empty::after{content:'\u7EAF\u6587\u672C'}",
|
|
190
390
|
// ── 间距:头部与首行代码之间补 6px ──────────────────────────────
|
|
191
391
|
// pre 的行高本身就在首行文字上方留了约半行空白;补 6px 后实测间距约 17px
|
|
192
392
|
// (原来给 0 时只有 11px,贴着看太挤)
|
|
193
|
-
".md-code-block pre{padding:6px 16px 16px}",
|
|
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}",
|
|
194
397
|
// ── 头部按钮:文字换成图标 ──────────────────────────────────────
|
|
195
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}",
|
|
196
399
|
// font-size:0 只藏住「复制 / 已复制」的字面,可访问名仍在
|
|
197
|
-
".md-code-block " + BANNER + "
|
|
400
|
+
".md-code-block " + BANNER + " ." + BTN_CLASS + "::before{content:'';width:15px;height:15px;background:currentColor}",
|
|
401
|
+
// 旧版宿主的复制按钮只有文字,没有 SVG 图标,才需要补一枚;新版已有 SVG,不再叠加
|
|
402
|
+
".md-code-block " + BANNER + " button:last-child: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}",
|
|
198
403
|
".md-code-block " + BANNER + " button:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover,rgb(127 127 127 / 12%))}",
|
|
199
404
|
".md-code-block " + BANNER + " button:focus-visible{outline:2px solid var(--dsw-alias-button-primary-fill,#4c6ef5);outline-offset:1px}",
|
|
200
405
|
// ── 普通块:不换行切换 ──────────────────────────────────────────
|
|
@@ -206,6 +411,8 @@ window.__ModuleLoader__.load({
|
|
|
206
411
|
".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}",
|
|
207
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}",
|
|
208
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}",
|
|
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}",
|
|
209
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%))}",
|
|
210
417
|
// 已经到最小 / 最大时,对应那颗按钮变淡且不可点
|
|
211
418
|
"[" + ZOOM_ATTR + '="min"] .' + ZOOM_OUT_CLASS + ",[" + ZOOM_ATTR + '="max"] .' + ZOOM_IN_CLASS + "{opacity:.3;pointer-events:none}",
|
|
@@ -234,6 +441,37 @@ window.__ModuleLoader__.load({
|
|
|
234
441
|
"." + MODAL_CLASS + "-viewport{flex:1;min-height:0}",
|
|
235
442
|
"." + MODAL_CLASS + "-stage{display:inline-flex;align-items:center;justify-content:center;min-height:0;flex:none}",
|
|
236
443
|
"." + MODAL_CLASS + " svg{display:block}",
|
|
444
|
+
// ── 长内容折叠:代码块、表格、图表卡共用一套按钮 ───────────────
|
|
445
|
+
"." + FOLD_CLASS + "{position:relative}",
|
|
446
|
+
"." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{max-height:var(--md-plus-fold-max-height);overflow:hidden}',
|
|
447
|
+
"." + FOLD_BTN_CLASS + "{display:block;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}",
|
|
448
|
+
"." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"] .' + FOLD_BTN_CLASS + "{position:absolute;left:50%;bottom:8px;transform:translateX(-50%);margin:0}",
|
|
449
|
+
// ── 图片点击放大 ────────────────────────────────────────────────
|
|
450
|
+
'[class*="_markdown_"] img{cursor:zoom-in}',
|
|
451
|
+
"." + 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%)}",
|
|
452
|
+
"." + IMG_MODAL_CLASS + " img{max-width:92vw;max-height:86vh;border-radius:8px;background:#fff;box-shadow:0 24px 80px rgb(0 0 0 / 35%)}",
|
|
453
|
+
"." + IMG_MODAL_CLASS + "-close{position:absolute;top:calc(var(--dsh-windows-titlebar-height,0px) + 18px);right:24px;width:32px;height:32px;padding:0;border:0;border-radius:999px;background:rgb(255 255 255 / 14%);color:#fff;font:20px/1 var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer}",
|
|
454
|
+
"." + IMG_MODAL_CLASS + "-close:hover{background:rgb(255 255 255 / 24%)}",
|
|
455
|
+
// ── 设置面板 ────────────────────────────────────────────────────
|
|
456
|
+
":root{--md-plus-icon-gear:" + ICON_GEAR + "}",
|
|
457
|
+
"." + SETTINGS_BTN_CLASS + "{position:fixed;right:16px;bottom:76px;z-index:2147482000;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 18%));border-radius:999px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-tertiary);opacity:.45;cursor:pointer;box-shadow:0 2px 10px rgb(0 0 0 / 12%);transition:opacity .12s ease,color .12s ease}",
|
|
458
|
+
"." + SETTINGS_BTN_CLASS + ":hover{opacity:1;color:var(--dsw-alias-label-primary)}",
|
|
459
|
+
"." + SETTINGS_BTN_CLASS + "::before{content:'';width:16px;height:16px;background:currentColor;-webkit-mask:var(--md-plus-icon-gear) center/contain no-repeat;mask:var(--md-plus-icon-gear) center/contain no-repeat}",
|
|
460
|
+
"." + SETTINGS_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%)}",
|
|
461
|
+
"." + SETTINGS_MODAL_CLASS + "-panel{width:min(440px,100%);max-height:86vh;overflow:auto;padding:16px 18px 14px;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%)}",
|
|
462
|
+
"." + SETTINGS_MODAL_CLASS + "-title{margin:0 0 6px;font:600 14px/22px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-primary)}",
|
|
463
|
+
"." + SETTINGS_MODAL_CLASS + "-row{display:flex;align-items:center;gap:12px;margin:11px 0}",
|
|
464
|
+
"." + SETTINGS_MODAL_CLASS + "-label{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0;font:13px/20px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-primary)}",
|
|
465
|
+
"." + SETTINGS_MODAL_CLASS + "-label-title{display:block}",
|
|
466
|
+
"." + SETTINGS_MODAL_CLASS + "-label-hint{display:block;font:11px/16px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-tertiary)}",
|
|
467
|
+
"." + SETTINGS_MODAL_CLASS + "-hint{margin:-4px 0 8px;font:11px/16px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-tertiary)}",
|
|
468
|
+
"." + SETTINGS_MODAL_CLASS + " input[type=number]{width:78px;padding:4px 6px;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 22%));border-radius:6px;background:var(--dsw-alias-bg-layer-2,rgb(127 127 127 / 6%));color:var(--dsw-alias-label-primary);font:13px/18px var(--dsw-font-family-sans,system-ui,sans-serif)}",
|
|
469
|
+
"." + SETTINGS_MODAL_CLASS + " select{padding:4px 6px;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 22%));border-radius:6px;background:var(--dsw-alias-bg-layer-2,rgb(127 127 127 / 6%));color:var(--dsw-alias-label-primary);font:13px/18px var(--dsw-font-family-sans,system-ui,sans-serif)}",
|
|
470
|
+
"." + SETTINGS_MODAL_CLASS + "-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}",
|
|
471
|
+
"." + SETTINGS_MODAL_CLASS + "-actions button{appearance:none;border:0;border-radius:6px;padding:5px 12px;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}",
|
|
472
|
+
"." + SETTINGS_MODAL_CLASS + "-actions button:not(." + SETTINGS_MODAL_CLASS + "-primary):hover{background:var(--dsw-alias-interactive-bg-active,rgb(76 110 245 / 12%))}",
|
|
473
|
+
"." + SETTINGS_MODAL_CLASS + "-actions ." + SETTINGS_MODAL_CLASS + "-primary{background:var(--dsw-alias-button-primary-fill,#4c6ef5);color:var(--dsw-alias-label-primary-foreground,#fff)}",
|
|
474
|
+
"." + SETTINGS_MODAL_CLASS + "-actions ." + SETTINGS_MODAL_CLASS + "-primary:hover{background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,#4c6ef5))}",
|
|
237
475
|
// 高亮宿主不参与布局,让里面的 <pre> 直接吃到卡片样式
|
|
238
476
|
"." + HOST_CLASS + "{display:contents}",
|
|
239
477
|
// 藏掉纯文本回退,只留着色结果;原节点仍在,复制读的还是它
|
|
@@ -242,12 +480,12 @@ window.__ModuleLoader__.load({
|
|
|
242
480
|
// 抓手用结构选择器而不是 DSH 的哈希类名:外层是「唯一子元素是 table 的 div」。
|
|
243
481
|
// 前面挂 [class*="_markdown_"] 有两个作用——只认 markdown 渲染出来的表格,
|
|
244
482
|
// 同时把特异性顶到 (0,1,4),压过 DSH 自己的 .tableScroll th (0,1,1)。
|
|
245
|
-
TABLE + "{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 + "}",
|
|
246
484
|
"body[data-ds-dark-theme] " + TABLE + "{" + TABLE_VARS_DARK + "}",
|
|
247
485
|
// 整张表一个 1px 边框 + 小圆角。
|
|
248
486
|
// 注意必须是 separate:collapse 模式下 Chrome 会忽略单元格的圆角,
|
|
249
487
|
// 表头的灰底会在四角戳出直角。border-spacing 归零所以不会有缝。
|
|
250
|
-
TABLE + " > table{border:1px solid var(--md-plus-tbl-line);border-collapse:separate;border-spacing:0;border-radius:var(--md-plus-tbl-radius)
|
|
488
|
+
TABLE + " > table{border:1px solid var(--md-plus-tbl-line);border-collapse:separate;border-spacing:0;border-radius:var(--md-plus-tbl-radius)}",
|
|
251
489
|
// 表头:浅灰底 + 加粗深色字(DSH 原本没有底色)
|
|
252
490
|
TABLE + " > table th{background:var(--md-plus-tbl-head);color:var(--md-plus-tbl-head-fg);font-weight:600;padding:10px 16px;border-bottom:1px solid var(--md-plus-tbl-line);border-top:0}",
|
|
253
491
|
TABLE + " > table td{padding:10px 16px;border-bottom:1px solid var(--md-plus-tbl-line)}",
|
|
@@ -266,6 +504,12 @@ window.__ModuleLoader__.load({
|
|
|
266
504
|
TABLE + " > table tr:last-child td{border-bottom:0}",
|
|
267
505
|
// 宽表横向扫读时给个落点
|
|
268
506
|
TABLE + " > table tbody tr:hover td{background:var(--md-plus-tbl-hover)}",
|
|
507
|
+
// 长表往下滚时表头留在视口顶部;底色不透明才不会透出下面的行
|
|
508
|
+
TABLE + " > table thead th{position:sticky;top:0;z-index:1}",
|
|
509
|
+
// 复制按钮放在表尾右侧:不压住表头文字,表格折叠时也能随展开一起出现
|
|
510
|
+
TABLE + " > table > caption." + TABLE_TOOLS_CLASS + "{caption-side:top;text-align:right;padding:0 0 6px}",
|
|
511
|
+
TABLE + " > table > caption." + TABLE_TOOLS_CLASS + " button{appearance:none;border:1px solid var(--md-plus-tbl-line);border-radius:5px;padding:2px 8px;margin-left:4px;background:transparent;color:var(--dsw-alias-label-secondary);font:11px/16px var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer}",
|
|
512
|
+
TABLE + " > table > caption." + TABLE_TOOLS_CLASS + " button:hover{color:var(--dsw-alias-label-primary);background:var(--md-plus-tbl-hover)}",
|
|
269
513
|
// 宽表(≥4 列)不收着不行。宿主给这类表加了 .md-table-wide,并让它「出血」:
|
|
270
514
|
// 包裹层撑到面板宽,再用 padding-left 把表推到正文栏左侧。那个 padding 会吃掉
|
|
271
515
|
// 可用宽度,结果是表格比正文宽出一截、右边还被裁掉,而滚动条只在鼠标悬停时
|
|
@@ -300,37 +544,130 @@ window.__ModuleLoader__.load({
|
|
|
300
544
|
script.src = url;
|
|
301
545
|
script.async = true;
|
|
302
546
|
script.setAttribute(RUNTIME_ATTR, "1");
|
|
303
|
-
script.addEventListener(
|
|
304
|
-
|
|
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
|
+
);
|
|
305
565
|
document.head.appendChild(script);
|
|
306
566
|
});
|
|
307
567
|
scripts.set(url, promise);
|
|
308
568
|
return promise;
|
|
309
569
|
}
|
|
310
|
-
function
|
|
570
|
+
function readBannerLabel(block) {
|
|
311
571
|
var banner = block.querySelector(BANNER);
|
|
312
|
-
var info = banner?.firstElementChild;
|
|
572
|
+
var info = banner?.querySelector('[class*="_language_"]') ?? banner?.firstElementChild;
|
|
313
573
|
return (info?.textContent ?? "").trim().toLowerCase();
|
|
314
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
|
+
}
|
|
315
628
|
function sniffDiagramType(code) {
|
|
316
|
-
var
|
|
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");
|
|
317
640
|
if (text.startsWith("---")) {
|
|
318
641
|
var blank = text.indexOf("\n");
|
|
319
642
|
var close = blank < 0 ? -1 : text.indexOf("\n---", blank);
|
|
320
643
|
text = close < 0 ? "" : text.slice(close + 4);
|
|
321
644
|
}
|
|
322
|
-
var
|
|
323
|
-
|
|
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];
|
|
324
657
|
}
|
|
325
658
|
function diagramKind(block) {
|
|
326
|
-
|
|
659
|
+
var lang = readLang(block);
|
|
660
|
+
if (MMD_LANGS.has(lang)) return "diagram";
|
|
661
|
+
if (lang !== "") return null;
|
|
327
662
|
var pre = block.querySelector(CONTENT + " pre");
|
|
328
663
|
if (pre === null) return null;
|
|
329
664
|
return sniffDiagramType(pre.textContent ?? "") === null ? null : "diagram";
|
|
330
665
|
}
|
|
331
666
|
function isOurs(node) {
|
|
332
667
|
var element = node?.nodeType === 1 ? node : node?.parentElement;
|
|
333
|
-
return element?.closest?.(
|
|
668
|
+
return element?.closest?.(
|
|
669
|
+
"." + HOST_CLASS + ", ." + BTN_CLASS + ", ." + MMD_CLASS + ", ." + FOLD_BTN_CLASS + ", ." + TABLE_TOOLS_CLASS + ", ." + IMG_MODAL_CLASS + ", ." + SETTINGS_BTN_CLASS + ", ." + SETTINGS_MODAL_CLASS
|
|
670
|
+
) != null;
|
|
334
671
|
}
|
|
335
672
|
function schedule(block) {
|
|
336
673
|
if (block !== void 0) pending.add(block);
|
|
@@ -350,6 +687,8 @@ window.__ModuleLoader__.load({
|
|
|
350
687
|
decorate(block);
|
|
351
688
|
void enhance(block);
|
|
352
689
|
}
|
|
690
|
+
scanFolds();
|
|
691
|
+
ensureSettingsButton();
|
|
353
692
|
}
|
|
354
693
|
function onMutations(records) {
|
|
355
694
|
for (const record of records) {
|
|
@@ -371,7 +710,8 @@ window.__ModuleLoader__.load({
|
|
|
371
710
|
}
|
|
372
711
|
}
|
|
373
712
|
function redrawDiagrams() {
|
|
374
|
-
for (const block of document.querySelectorAll("[" + MMD_ATTR +
|
|
713
|
+
for (const block of document.querySelectorAll("[" + MMD_ATTR + "]")) {
|
|
714
|
+
if (block.getAttribute(MMD_ATTR) === "failed") continue;
|
|
375
715
|
block.removeAttribute(KEY_ATTR);
|
|
376
716
|
pending.add(block);
|
|
377
717
|
}
|
|
@@ -380,8 +720,8 @@ window.__ModuleLoader__.load({
|
|
|
380
720
|
var PLAIN_BUTTONS = [
|
|
381
721
|
{
|
|
382
722
|
cls: TOOL_CLASS,
|
|
383
|
-
|
|
384
|
-
|
|
723
|
+
titleKey: "wrapTitle",
|
|
724
|
+
ariaKey: "wrapAria",
|
|
385
725
|
run: (block) => {
|
|
386
726
|
if (block.getAttribute(WRAP_ATTR) === "on") block.removeAttribute(WRAP_ATTR);
|
|
387
727
|
else block.setAttribute(WRAP_ATTR, "on");
|
|
@@ -389,24 +729,56 @@ window.__ModuleLoader__.load({
|
|
|
389
729
|
}
|
|
390
730
|
];
|
|
391
731
|
var MMD_BUTTONS = [
|
|
392
|
-
{ cls: ZOOM_OUT_CLASS,
|
|
393
|
-
{ cls: ZOOM_IN_CLASS,
|
|
732
|
+
{ cls: ZOOM_OUT_CLASS, titleKey: "zoomOut", ariaKey: "zoomOutAria", run: (block) => zoomBy(block, 1 / ZOOM_STEP) },
|
|
733
|
+
{ cls: ZOOM_IN_CLASS, titleKey: "zoomIn", ariaKey: "zoomInAria", run: (block) => zoomBy(block, ZOOM_STEP) },
|
|
394
734
|
{
|
|
395
735
|
cls: TOGGLE_CLASS,
|
|
396
|
-
|
|
397
|
-
|
|
736
|
+
titleKey: "toggle",
|
|
737
|
+
ariaKey: "toggleAria",
|
|
398
738
|
run: (block) => {
|
|
399
739
|
var next = block.getAttribute(MMD_ATTR) === "code" ? "diagram" : "code";
|
|
400
740
|
block.setAttribute(MMD_ATTR, next);
|
|
741
|
+
var viewport = block.querySelector("." + MMD_CLASS + "-viewport");
|
|
742
|
+
if (viewport !== null) {
|
|
743
|
+
resetFold(viewport);
|
|
744
|
+
scheduleFold(viewport, foldLimit("diagram"));
|
|
745
|
+
}
|
|
401
746
|
}
|
|
402
747
|
},
|
|
403
748
|
{
|
|
404
749
|
cls: EXPAND_CLASS,
|
|
405
|
-
|
|
406
|
-
|
|
750
|
+
titleKey: "fullscreen",
|
|
751
|
+
ariaKey: "fullscreenAria",
|
|
407
752
|
run: (block) => openFullscreen(block)
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
cls: THEME_CLASS,
|
|
756
|
+
titleKey: "theme",
|
|
757
|
+
ariaKey: "themeAria",
|
|
758
|
+
run: (block) => cycleMermaidTheme(block)
|
|
759
|
+
}
|
|
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
|
+
}
|
|
408
772
|
}
|
|
409
773
|
];
|
|
774
|
+
function findNativeWrap(buttons) {
|
|
775
|
+
if (buttons === void 0) return null;
|
|
776
|
+
for (const button of buttons) {
|
|
777
|
+
var label = (button.getAttribute("aria-label") ?? "") + " " + (button.title ?? "") + " " + (button.textContent ?? "");
|
|
778
|
+
if (/换行|wrap/i.test(label)) return button;
|
|
779
|
+
}
|
|
780
|
+
return buttons.length >= 2 ? buttons[0] : null;
|
|
781
|
+
}
|
|
410
782
|
function decorate(block) {
|
|
411
783
|
if (!block.isConnected) return;
|
|
412
784
|
var banner = block.querySelector(BANNER);
|
|
@@ -414,7 +786,10 @@ window.__ModuleLoader__.load({
|
|
|
414
786
|
var copy = nativeButtons === void 0 || nativeButtons.length === 0 ? null : nativeButtons[nativeButtons.length - 1];
|
|
415
787
|
var action = copy?.parentElement;
|
|
416
788
|
if (action === null || action === void 0) return;
|
|
417
|
-
var
|
|
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;
|
|
418
793
|
for (const existing of action.querySelectorAll("." + BTN_CLASS)) {
|
|
419
794
|
if (specs.some((spec) => existing.classList.contains(spec.cls))) continue;
|
|
420
795
|
existing.remove();
|
|
@@ -425,8 +800,13 @@ window.__ModuleLoader__.load({
|
|
|
425
800
|
var created = document.createElement("button");
|
|
426
801
|
created.type = "button";
|
|
427
802
|
created.className = BTN_CLASS + " " + spec.cls;
|
|
428
|
-
created.title = spec.
|
|
429
|
-
created.setAttribute("aria-label", spec.
|
|
803
|
+
created.title = t(spec.titleKey);
|
|
804
|
+
created.setAttribute("aria-label", t(spec.ariaKey));
|
|
805
|
+
if (spec.cls === THEME_CLASS) {
|
|
806
|
+
var themeText = themeLabel(getMermaidTheme());
|
|
807
|
+
created.title = themeText;
|
|
808
|
+
created.setAttribute("aria-label", themeText);
|
|
809
|
+
}
|
|
430
810
|
created.addEventListener("click", () => spec.run(block));
|
|
431
811
|
action.insertBefore(created, copy);
|
|
432
812
|
injected.add(created);
|
|
@@ -450,11 +830,12 @@ window.__ModuleLoader__.load({
|
|
|
450
830
|
var pre = content.querySelector("pre");
|
|
451
831
|
if (pre === null || pre.classList.contains("shiki")) return;
|
|
452
832
|
var lang = readLang(block);
|
|
453
|
-
if (lang
|
|
833
|
+
if (lang !== "" && isPlaceholderLabel(readBannerLabel(block))) paintLanguageLabel(block, lang);
|
|
454
834
|
if (diagramKind(block) !== null) {
|
|
455
835
|
await renderDiagram(block, content, pre);
|
|
456
836
|
return;
|
|
457
837
|
}
|
|
838
|
+
if (lang === "") return;
|
|
458
839
|
if (DSH_LANGS.has(lang)) return;
|
|
459
840
|
var api = await loadScript(HL_URL, HL_GLOBAL);
|
|
460
841
|
if (api === null || !api.supports(lang)) return;
|
|
@@ -484,9 +865,40 @@ window.__ModuleLoader__.load({
|
|
|
484
865
|
if (shikiPre === null) return;
|
|
485
866
|
shikiPre.classList.add("md-plus-pre");
|
|
486
867
|
content.querySelector("." + HOST_CLASS)?.remove();
|
|
868
|
+
resetFold(content);
|
|
487
869
|
pre.after(host);
|
|
488
870
|
block.setAttribute(KEY_ATTR, key);
|
|
489
871
|
block.setAttribute(DONE_ATTR, "done");
|
|
872
|
+
scheduleFold(content, foldLimit("code"));
|
|
873
|
+
}
|
|
874
|
+
function getMermaidTheme() {
|
|
875
|
+
try {
|
|
876
|
+
var saved = window.localStorage.getItem(THEME_KEY);
|
|
877
|
+
if (saved !== null && THEME_ORDER.indexOf(saved) !== -1) return saved;
|
|
878
|
+
} catch {
|
|
879
|
+
}
|
|
880
|
+
return "blue";
|
|
881
|
+
}
|
|
882
|
+
function themeLabel(name) {
|
|
883
|
+
return t("themePrefix") + (THEME_LABELS[detectLang()][name] ?? name);
|
|
884
|
+
}
|
|
885
|
+
function refreshThemeButtons() {
|
|
886
|
+
var label = themeLabel(getMermaidTheme());
|
|
887
|
+
for (const button of document.querySelectorAll("." + THEME_CLASS)) {
|
|
888
|
+
button.title = label;
|
|
889
|
+
button.setAttribute("aria-label", label);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function cycleMermaidTheme(block) {
|
|
893
|
+
var current = getMermaidTheme();
|
|
894
|
+
var index = THEME_ORDER.indexOf(current);
|
|
895
|
+
var next = THEME_ORDER[(index + 1) % THEME_ORDER.length];
|
|
896
|
+
try {
|
|
897
|
+
window.localStorage.setItem(THEME_KEY, next);
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
refreshThemeButtons();
|
|
901
|
+
redrawDiagrams();
|
|
490
902
|
}
|
|
491
903
|
async function renderDiagram(block, content, pre) {
|
|
492
904
|
var code = (pre.textContent ?? "").trim();
|
|
@@ -499,7 +911,7 @@ window.__ModuleLoader__.load({
|
|
|
499
911
|
var svg;
|
|
500
912
|
try {
|
|
501
913
|
var dark = document.body.hasAttribute("data-ds-dark-theme");
|
|
502
|
-
svg = await api.render("dsh-md-plus-mmd-" + ++diagramSeq, code, dark);
|
|
914
|
+
svg = await api.render("dsh-md-plus-mmd-" + ++diagramSeq, code, dark, getMermaidTheme());
|
|
503
915
|
} catch (error) {
|
|
504
916
|
failDiagram(block, content, pre, key, error);
|
|
505
917
|
return;
|
|
@@ -524,9 +936,12 @@ window.__ModuleLoader__.load({
|
|
|
524
936
|
pre.before(host);
|
|
525
937
|
content.querySelector("." + MMD_CLASS + "-error")?.remove();
|
|
526
938
|
block.setAttribute(KEY_ATTR, key);
|
|
527
|
-
|
|
939
|
+
var view = block.getAttribute(MMD_ATTR);
|
|
940
|
+
if (view !== "code" && view !== "diagram") block.setAttribute(MMD_ATTR, readSettings().diagramView);
|
|
528
941
|
zooms.delete(block);
|
|
529
942
|
paint(block);
|
|
943
|
+
scheduleFold(viewport, foldLimit("diagram"));
|
|
944
|
+
decorate(block);
|
|
530
945
|
}
|
|
531
946
|
var zooms = /* @__PURE__ */ new WeakMap();
|
|
532
947
|
function zoomState(block) {
|
|
@@ -577,11 +992,12 @@ window.__ModuleLoader__.load({
|
|
|
577
992
|
clampPan(block, state);
|
|
578
993
|
paint(block);
|
|
579
994
|
}
|
|
580
|
-
function bindViewport(block, viewport, resetView) {
|
|
995
|
+
function bindViewport(block, viewport, resetView, alwaysZoom) {
|
|
581
996
|
var drag = null;
|
|
582
997
|
viewport.addEventListener(
|
|
583
998
|
"wheel",
|
|
584
999
|
(event) => {
|
|
1000
|
+
if (!alwaysZoom && !event.ctrlKey && !event.metaKey && zoomState(block).scale <= 1.001) return;
|
|
585
1001
|
event.preventDefault();
|
|
586
1002
|
zoomBy(block, event.deltaY < 0 ? ZOOM_STEP : 1 / ZOOM_STEP, event.clientX, event.clientY);
|
|
587
1003
|
},
|
|
@@ -627,6 +1043,362 @@ window.__ModuleLoader__.load({
|
|
|
627
1043
|
paint(block);
|
|
628
1044
|
});
|
|
629
1045
|
}
|
|
1046
|
+
function resetFold(target) {
|
|
1047
|
+
if (target === null) return;
|
|
1048
|
+
target.removeAttribute(FOLD_ATTR);
|
|
1049
|
+
target.classList.remove(FOLD_CLASS);
|
|
1050
|
+
target.style.removeProperty("--md-plus-fold-max-height");
|
|
1051
|
+
target.querySelector("." + FOLD_BTN_CLASS)?.remove();
|
|
1052
|
+
}
|
|
1053
|
+
function scheduleFold(target, limit) {
|
|
1054
|
+
if (target === null) return;
|
|
1055
|
+
if (!(limit > 0)) {
|
|
1056
|
+
resetFold(target);
|
|
1057
|
+
target.setAttribute(FOLD_ATTR, "none");
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (target.getAttribute(FOLD_ATTR) === "none") {
|
|
1061
|
+
if (target.scrollHeight <= limit) return;
|
|
1062
|
+
target.removeAttribute(FOLD_ATTR);
|
|
1063
|
+
}
|
|
1064
|
+
if (target.getAttribute(FOLD_ATTR) !== null) return;
|
|
1065
|
+
var schedule2 = window.requestAnimationFrame ?? ((cb) => window.setTimeout(cb, 0));
|
|
1066
|
+
schedule2(() => {
|
|
1067
|
+
if (!target.isConnected || target.getAttribute(FOLD_ATTR) !== null) return;
|
|
1068
|
+
if (target.scrollHeight <= limit) {
|
|
1069
|
+
target.setAttribute(FOLD_ATTR, "none");
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
target.setAttribute(FOLD_ATTR, "on");
|
|
1073
|
+
target.classList.add(FOLD_CLASS);
|
|
1074
|
+
target.style.setProperty("--md-plus-fold-max-height", limit + "px");
|
|
1075
|
+
var button = document.createElement("button");
|
|
1076
|
+
button.type = "button";
|
|
1077
|
+
button.className = FOLD_BTN_CLASS;
|
|
1078
|
+
button.textContent = t("foldOpen");
|
|
1079
|
+
button.setAttribute("aria-expanded", "false");
|
|
1080
|
+
button.addEventListener("click", () => {
|
|
1081
|
+
var folded = target.getAttribute(FOLD_ATTR) === "on";
|
|
1082
|
+
target.setAttribute(FOLD_ATTR, folded ? "off" : "on");
|
|
1083
|
+
button.textContent = folded ? t("foldClose") : t("foldOpen");
|
|
1084
|
+
button.setAttribute("aria-expanded", folded ? "true" : "false");
|
|
1085
|
+
});
|
|
1086
|
+
target.appendChild(button);
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
function scanFolds() {
|
|
1090
|
+
for (const block of document.querySelectorAll(BLOCK)) {
|
|
1091
|
+
if (block.closest("[data-streaming]") !== null) continue;
|
|
1092
|
+
if (diagramKind(block) !== null) continue;
|
|
1093
|
+
scheduleFold(block.querySelector(CONTENT), foldLimit("code"));
|
|
1094
|
+
}
|
|
1095
|
+
for (const table of document.querySelectorAll(TABLE)) {
|
|
1096
|
+
if (table.closest("[data-streaming]") !== null) continue;
|
|
1097
|
+
decorateTable(table);
|
|
1098
|
+
scheduleFold(table, foldLimit("table"));
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function cellText(cell) {
|
|
1102
|
+
return (cell.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
1103
|
+
}
|
|
1104
|
+
function tableRows(table) {
|
|
1105
|
+
return Array.from(table.rows).map((row) => Array.from(row.cells).map(cellText));
|
|
1106
|
+
}
|
|
1107
|
+
function tableToCsv(rows) {
|
|
1108
|
+
return rows.map(
|
|
1109
|
+
(row) => row.map((value) => /[",\n]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value).join(",")
|
|
1110
|
+
).join("\n");
|
|
1111
|
+
}
|
|
1112
|
+
function tableToMarkdown(rows) {
|
|
1113
|
+
if (rows.length === 0) return "";
|
|
1114
|
+
var width = Math.max(...rows.map((row) => row.length));
|
|
1115
|
+
var line = (row) => "| " + row.concat(Array(width - row.length).fill("")).map((value) => value.replace(/\|/g, "\\|")).join(" | ") + " |";
|
|
1116
|
+
var output = [line(rows[0]), "| " + Array(width).fill("---").join(" | ") + " |"];
|
|
1117
|
+
for (var index = 1; index < rows.length; index += 1) output.push(line(rows[index]));
|
|
1118
|
+
return output.join("\n");
|
|
1119
|
+
}
|
|
1120
|
+
async function copyText(text) {
|
|
1121
|
+
if (window.navigator.clipboard !== void 0 && typeof window.navigator.clipboard.writeText === "function") {
|
|
1122
|
+
await window.navigator.clipboard.writeText(text);
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
var area = document.createElement("textarea");
|
|
1126
|
+
area.value = text;
|
|
1127
|
+
area.style.position = "fixed";
|
|
1128
|
+
area.style.opacity = "0";
|
|
1129
|
+
document.body.appendChild(area);
|
|
1130
|
+
area.select();
|
|
1131
|
+
var ok = false;
|
|
1132
|
+
try {
|
|
1133
|
+
ok = document.execCommand("copy");
|
|
1134
|
+
} catch {
|
|
1135
|
+
ok = false;
|
|
1136
|
+
}
|
|
1137
|
+
area.remove();
|
|
1138
|
+
return ok;
|
|
1139
|
+
}
|
|
1140
|
+
function flashButton(button, text) {
|
|
1141
|
+
var original = button.getAttribute("data-md-plus-label") ?? button.textContent;
|
|
1142
|
+
button.setAttribute("data-md-plus-label", original);
|
|
1143
|
+
button.textContent = text;
|
|
1144
|
+
window.setTimeout(() => {
|
|
1145
|
+
button.textContent = original;
|
|
1146
|
+
}, 1200);
|
|
1147
|
+
}
|
|
1148
|
+
function decorateTable(table) {
|
|
1149
|
+
var dataTable = table.querySelector("table");
|
|
1150
|
+
if (dataTable === null) return;
|
|
1151
|
+
var first = dataTable.firstElementChild;
|
|
1152
|
+
if (first !== null && first.classList.contains(TABLE_TOOLS_CLASS)) return;
|
|
1153
|
+
table.setAttribute(TABLE_DONE_ATTR, "1");
|
|
1154
|
+
var tools = document.createElement("caption");
|
|
1155
|
+
tools.className = TABLE_TOOLS_CLASS;
|
|
1156
|
+
for (const [kind, label, titleKey, convert] of [
|
|
1157
|
+
["csv", t("tableCsv"), "copyCsvTitle", tableToCsv],
|
|
1158
|
+
["md", t("tableMd"), "copyMdTitle", tableToMarkdown]
|
|
1159
|
+
]) {
|
|
1160
|
+
var button = document.createElement("button");
|
|
1161
|
+
button.type = "button";
|
|
1162
|
+
button.textContent = label;
|
|
1163
|
+
button.title = t(titleKey);
|
|
1164
|
+
button.setAttribute("aria-label", t(titleKey));
|
|
1165
|
+
button.addEventListener("click", async () => {
|
|
1166
|
+
var ok = false;
|
|
1167
|
+
try {
|
|
1168
|
+
ok = await copyText(convert(tableRows(dataTable)));
|
|
1169
|
+
} catch {
|
|
1170
|
+
ok = false;
|
|
1171
|
+
}
|
|
1172
|
+
flashButton(button, ok ? t("copied") : t("copyFailed"));
|
|
1173
|
+
});
|
|
1174
|
+
tools.appendChild(button);
|
|
1175
|
+
}
|
|
1176
|
+
dataTable.insertBefore(tools, dataTable.firstChild);
|
|
1177
|
+
}
|
|
1178
|
+
function openImage(source, alt) {
|
|
1179
|
+
if (document.querySelector("." + IMG_MODAL_CLASS) !== null) return;
|
|
1180
|
+
var overlay = document.createElement("div");
|
|
1181
|
+
overlay.className = IMG_MODAL_CLASS;
|
|
1182
|
+
overlay.setAttribute("role", "dialog");
|
|
1183
|
+
overlay.setAttribute("aria-modal", "true");
|
|
1184
|
+
overlay.setAttribute("aria-label", t("imageViewer"));
|
|
1185
|
+
var image = document.createElement("img");
|
|
1186
|
+
image.src = source;
|
|
1187
|
+
image.alt = alt;
|
|
1188
|
+
var closeButton = document.createElement("button");
|
|
1189
|
+
closeButton.type = "button";
|
|
1190
|
+
closeButton.className = IMG_MODAL_CLASS + "-close";
|
|
1191
|
+
closeButton.textContent = "\xD7";
|
|
1192
|
+
closeButton.setAttribute("aria-label", t("imageClose"));
|
|
1193
|
+
var previousFocus = document.activeElement;
|
|
1194
|
+
var closed = false;
|
|
1195
|
+
function closeViewer() {
|
|
1196
|
+
if (closed) return;
|
|
1197
|
+
closed = true;
|
|
1198
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1199
|
+
overlay.remove();
|
|
1200
|
+
if (previousFocus !== null && typeof previousFocus.focus === "function") previousFocus.focus();
|
|
1201
|
+
}
|
|
1202
|
+
function onKey(event) {
|
|
1203
|
+
if (event.key === "Escape") {
|
|
1204
|
+
event.preventDefault();
|
|
1205
|
+
closeViewer();
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
overlay.addEventListener("pointerdown", (event) => {
|
|
1209
|
+
if (event.target === overlay) closeViewer();
|
|
1210
|
+
});
|
|
1211
|
+
closeButton.addEventListener("click", closeViewer);
|
|
1212
|
+
document.addEventListener("keydown", onKey, true);
|
|
1213
|
+
overlay.appendChild(image);
|
|
1214
|
+
overlay.appendChild(closeButton);
|
|
1215
|
+
document.body.appendChild(overlay);
|
|
1216
|
+
closeButton.focus();
|
|
1217
|
+
}
|
|
1218
|
+
function onDocumentClick(event) {
|
|
1219
|
+
if (event.button !== void 0 && event.button !== 0) return;
|
|
1220
|
+
var target = event.target;
|
|
1221
|
+
var image = target?.closest?.("img");
|
|
1222
|
+
if (image === null || image === void 0) return;
|
|
1223
|
+
if (image.closest("." + IMG_MODAL_CLASS + ", .md-code-block, ." + SETTINGS_MODAL_CLASS) !== null) return;
|
|
1224
|
+
if (image.closest('[class*="_markdown_"]') === null) return;
|
|
1225
|
+
if (image.closest(NATIVE_IMAGE_BUTTON) !== null || image.closest("a") !== null) return;
|
|
1226
|
+
event.preventDefault();
|
|
1227
|
+
openImage(image.currentSrc || image.src, image.alt || "");
|
|
1228
|
+
}
|
|
1229
|
+
function settingRow(labelText, control, hintText) {
|
|
1230
|
+
var row = document.createElement("div");
|
|
1231
|
+
row.className = SETTINGS_MODAL_CLASS + "-row";
|
|
1232
|
+
var label = document.createElement("span");
|
|
1233
|
+
label.className = SETTINGS_MODAL_CLASS + "-label";
|
|
1234
|
+
var title = document.createElement("span");
|
|
1235
|
+
title.className = SETTINGS_MODAL_CLASS + "-label-title";
|
|
1236
|
+
title.textContent = labelText;
|
|
1237
|
+
label.appendChild(title);
|
|
1238
|
+
if (hintText !== void 0) {
|
|
1239
|
+
var hint = document.createElement("span");
|
|
1240
|
+
hint.className = SETTINGS_MODAL_CLASS + "-label-hint";
|
|
1241
|
+
hint.textContent = hintText;
|
|
1242
|
+
label.appendChild(hint);
|
|
1243
|
+
}
|
|
1244
|
+
row.appendChild(label);
|
|
1245
|
+
row.appendChild(control);
|
|
1246
|
+
return row;
|
|
1247
|
+
}
|
|
1248
|
+
function numberInput(value, min, max) {
|
|
1249
|
+
var input = document.createElement("input");
|
|
1250
|
+
input.type = "number";
|
|
1251
|
+
input.min = String(min);
|
|
1252
|
+
input.max = String(max);
|
|
1253
|
+
input.value = value > 0 ? String(value) : "";
|
|
1254
|
+
input.placeholder = "0";
|
|
1255
|
+
return input;
|
|
1256
|
+
}
|
|
1257
|
+
function rescanFolds() {
|
|
1258
|
+
for (const node of document.querySelectorAll("[" + FOLD_ATTR + "]")) resetFold(node);
|
|
1259
|
+
for (const viewport of document.querySelectorAll("." + MMD_CLASS + "-viewport")) {
|
|
1260
|
+
resetFold(viewport);
|
|
1261
|
+
scheduleFold(viewport, foldLimit("diagram"));
|
|
1262
|
+
}
|
|
1263
|
+
scanFolds();
|
|
1264
|
+
}
|
|
1265
|
+
function openSettings() {
|
|
1266
|
+
if (document.querySelector("." + SETTINGS_MODAL_CLASS) !== null) return;
|
|
1267
|
+
var current = readSettings();
|
|
1268
|
+
var overlay = document.createElement("div");
|
|
1269
|
+
overlay.className = SETTINGS_MODAL_CLASS;
|
|
1270
|
+
overlay.setAttribute("role", "dialog");
|
|
1271
|
+
overlay.setAttribute("aria-modal", "true");
|
|
1272
|
+
overlay.setAttribute("aria-label", t("settingsTitle"));
|
|
1273
|
+
var panel = document.createElement("div");
|
|
1274
|
+
panel.className = SETTINGS_MODAL_CLASS + "-panel";
|
|
1275
|
+
var title = document.createElement("h2");
|
|
1276
|
+
title.className = SETTINGS_MODAL_CLASS + "-title";
|
|
1277
|
+
title.textContent = t("settingsTitle");
|
|
1278
|
+
panel.appendChild(title);
|
|
1279
|
+
var themeSelect = document.createElement("select");
|
|
1280
|
+
for (const name of THEME_ORDER) {
|
|
1281
|
+
var option = document.createElement("option");
|
|
1282
|
+
option.value = name;
|
|
1283
|
+
option.textContent = THEME_LABELS[detectLang()][name] ?? name;
|
|
1284
|
+
themeSelect.appendChild(option);
|
|
1285
|
+
}
|
|
1286
|
+
themeSelect.value = getMermaidTheme();
|
|
1287
|
+
panel.appendChild(settingRow(t("settingsTheme"), themeSelect));
|
|
1288
|
+
var fontInput = numberInput(current.codeFontSize, 10, 20);
|
|
1289
|
+
panel.appendChild(settingRow(t("settingsFontSize"), fontInput));
|
|
1290
|
+
var fontHint = document.createElement("p");
|
|
1291
|
+
fontHint.className = SETTINGS_MODAL_CLASS + "-hint";
|
|
1292
|
+
fontHint.textContent = t("settingsFontSizeHint");
|
|
1293
|
+
panel.appendChild(fontHint);
|
|
1294
|
+
var viewSelect = document.createElement("select");
|
|
1295
|
+
for (const [value, label] of [
|
|
1296
|
+
["diagram", t("settingsViewDiagram")],
|
|
1297
|
+
["code", t("settingsViewCode")]
|
|
1298
|
+
]) {
|
|
1299
|
+
var viewOption = document.createElement("option");
|
|
1300
|
+
viewOption.value = value;
|
|
1301
|
+
viewOption.textContent = label;
|
|
1302
|
+
viewSelect.appendChild(viewOption);
|
|
1303
|
+
}
|
|
1304
|
+
viewSelect.value = current.diagramView;
|
|
1305
|
+
panel.appendChild(settingRow(t("settingsView"), viewSelect));
|
|
1306
|
+
var foldHint = document.createElement("p");
|
|
1307
|
+
foldHint.className = SETTINGS_MODAL_CLASS + "-hint";
|
|
1308
|
+
foldHint.textContent = t("settingsFold") + " \xB7 " + t("settingsFoldHint");
|
|
1309
|
+
panel.appendChild(foldHint);
|
|
1310
|
+
var foldCode = numberInput(current.foldCode, 0, 4e3);
|
|
1311
|
+
var foldTable = numberInput(current.foldTable, 0, 4e3);
|
|
1312
|
+
var foldDiagram = numberInput(current.foldDiagram, 0, 4e3);
|
|
1313
|
+
panel.appendChild(settingRow(t("settingsFoldCode"), foldCode, t("settingsFoldCodeHint")));
|
|
1314
|
+
panel.appendChild(settingRow(t("settingsFoldTable"), foldTable, t("settingsFoldTableHint")));
|
|
1315
|
+
panel.appendChild(settingRow(t("settingsFoldDiagram"), foldDiagram, t("settingsFoldDiagramHint")));
|
|
1316
|
+
var actions = document.createElement("div");
|
|
1317
|
+
actions.className = SETTINGS_MODAL_CLASS + "-actions";
|
|
1318
|
+
var resetButton = document.createElement("button");
|
|
1319
|
+
resetButton.type = "button";
|
|
1320
|
+
resetButton.textContent = t("settingsReset");
|
|
1321
|
+
var closeButton = document.createElement("button");
|
|
1322
|
+
closeButton.type = "button";
|
|
1323
|
+
closeButton.textContent = t("settingsClose");
|
|
1324
|
+
var saveButton = document.createElement("button");
|
|
1325
|
+
saveButton.type = "button";
|
|
1326
|
+
saveButton.className = SETTINGS_MODAL_CLASS + "-primary";
|
|
1327
|
+
saveButton.textContent = t("settingsSave");
|
|
1328
|
+
actions.appendChild(resetButton);
|
|
1329
|
+
actions.appendChild(closeButton);
|
|
1330
|
+
actions.appendChild(saveButton);
|
|
1331
|
+
panel.appendChild(actions);
|
|
1332
|
+
overlay.appendChild(panel);
|
|
1333
|
+
var previousFocus = document.activeElement;
|
|
1334
|
+
var closed = false;
|
|
1335
|
+
function closeSettings() {
|
|
1336
|
+
if (closed) return;
|
|
1337
|
+
closed = true;
|
|
1338
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1339
|
+
overlay.remove();
|
|
1340
|
+
if (previousFocus !== null && typeof previousFocus.focus === "function") previousFocus.focus();
|
|
1341
|
+
}
|
|
1342
|
+
function onKey(event) {
|
|
1343
|
+
if (event.key === "Escape") {
|
|
1344
|
+
event.preventDefault();
|
|
1345
|
+
closeSettings();
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
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) {
|
|
1353
|
+
var value = Number(input.value);
|
|
1354
|
+
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
1355
|
+
return Math.min(20, Math.max(10, Math.round(value)));
|
|
1356
|
+
}
|
|
1357
|
+
resetButton.addEventListener("click", () => {
|
|
1358
|
+
themeSelect.value = "blue";
|
|
1359
|
+
fontInput.value = "";
|
|
1360
|
+
viewSelect.value = DEFAULT_SETTINGS.diagramView;
|
|
1361
|
+
foldCode.value = String(DEFAULT_SETTINGS.foldCode);
|
|
1362
|
+
foldTable.value = String(DEFAULT_SETTINGS.foldTable);
|
|
1363
|
+
foldDiagram.value = String(DEFAULT_SETTINGS.foldDiagram);
|
|
1364
|
+
});
|
|
1365
|
+
saveButton.addEventListener("click", () => {
|
|
1366
|
+
saveSettings({
|
|
1367
|
+
codeFontSize: readFontSize(fontInput),
|
|
1368
|
+
diagramView: viewSelect.value === "code" ? "code" : "diagram",
|
|
1369
|
+
foldCode: readNumber(foldCode, DEFAULT_SETTINGS.foldCode),
|
|
1370
|
+
foldTable: readNumber(foldTable, DEFAULT_SETTINGS.foldTable),
|
|
1371
|
+
foldDiagram: readNumber(foldDiagram, DEFAULT_SETTINGS.foldDiagram)
|
|
1372
|
+
});
|
|
1373
|
+
try {
|
|
1374
|
+
window.localStorage.setItem(THEME_KEY, themeSelect.value);
|
|
1375
|
+
} catch {
|
|
1376
|
+
}
|
|
1377
|
+
applyCodeFontSize();
|
|
1378
|
+
refreshThemeButtons();
|
|
1379
|
+
redrawDiagrams();
|
|
1380
|
+
rescanFolds();
|
|
1381
|
+
closeSettings();
|
|
1382
|
+
});
|
|
1383
|
+
closeButton.addEventListener("click", closeSettings);
|
|
1384
|
+
overlay.addEventListener("pointerdown", (event) => {
|
|
1385
|
+
if (event.target === overlay) closeSettings();
|
|
1386
|
+
});
|
|
1387
|
+
document.addEventListener("keydown", onKey, true);
|
|
1388
|
+
document.body.appendChild(overlay);
|
|
1389
|
+
saveButton.focus();
|
|
1390
|
+
}
|
|
1391
|
+
function ensureSettingsButton() {
|
|
1392
|
+
if (document.querySelector("." + SETTINGS_BTN_CLASS) !== null) return;
|
|
1393
|
+
if (document.querySelector('[class*="_markdown_"], .md-code-block') === null) return;
|
|
1394
|
+
var button = document.createElement("button");
|
|
1395
|
+
button.type = "button";
|
|
1396
|
+
button.className = SETTINGS_BTN_CLASS;
|
|
1397
|
+
button.title = t("settingsOpen");
|
|
1398
|
+
button.setAttribute("aria-label", t("settingsOpen"));
|
|
1399
|
+
button.addEventListener("click", openSettings);
|
|
1400
|
+
document.body.appendChild(button);
|
|
1401
|
+
}
|
|
630
1402
|
function fitToView(scope) {
|
|
631
1403
|
var stage = stageOf(scope);
|
|
632
1404
|
if (stage === null) return;
|
|
@@ -651,6 +1423,64 @@ window.__ModuleLoader__.load({
|
|
|
651
1423
|
var rect = svg.getBoundingClientRect();
|
|
652
1424
|
return { width: rect.width, height: rect.height };
|
|
653
1425
|
}
|
|
1426
|
+
function serializeSvg(svg) {
|
|
1427
|
+
var clone = svg.cloneNode(true);
|
|
1428
|
+
var size = svgSize(svg);
|
|
1429
|
+
clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1430
|
+
if (!clone.getAttribute("xmlns:xlink")) {
|
|
1431
|
+
clone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
1432
|
+
}
|
|
1433
|
+
if (size.width > 0) clone.setAttribute("width", String(size.width));
|
|
1434
|
+
if (size.height > 0) clone.setAttribute("height", String(size.height));
|
|
1435
|
+
return '<?xml version="1.0" encoding="UTF-8"?>\n' + new XMLSerializer().serializeToString(clone);
|
|
1436
|
+
}
|
|
1437
|
+
function downloadBlob(blob, filename) {
|
|
1438
|
+
var url = URL.createObjectURL(blob);
|
|
1439
|
+
var link = document.createElement("a");
|
|
1440
|
+
link.href = url;
|
|
1441
|
+
link.download = filename;
|
|
1442
|
+
document.body.appendChild(link);
|
|
1443
|
+
link.click();
|
|
1444
|
+
link.remove();
|
|
1445
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1446
|
+
}
|
|
1447
|
+
function downloadSvg(svg) {
|
|
1448
|
+
try {
|
|
1449
|
+
downloadBlob(new Blob([serializeSvg(svg)], { type: "image/svg+xml;charset=utf-8" }), "mermaid-diagram.svg");
|
|
1450
|
+
} catch (error) {
|
|
1451
|
+
console.warn("[dsh-md-plus] SVG \u5BFC\u51FA\u5931\u8D25", error);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
function downloadPng(svg) {
|
|
1455
|
+
var size = svgSize(svg);
|
|
1456
|
+
if (size.width <= 0 || size.height <= 0) return;
|
|
1457
|
+
var image = new window.Image();
|
|
1458
|
+
var objectUrl = "";
|
|
1459
|
+
image.onload = function() {
|
|
1460
|
+
try {
|
|
1461
|
+
var scale = Math.min(2, 4096 / Math.max(size.width, size.height));
|
|
1462
|
+
var canvas = document.createElement("canvas");
|
|
1463
|
+
canvas.width = Math.max(1, Math.round(size.width * scale));
|
|
1464
|
+
canvas.height = Math.max(1, Math.round(size.height * scale));
|
|
1465
|
+
var context = canvas.getContext("2d");
|
|
1466
|
+
if (context === null) throw new Error("canvas \u4E0D\u53EF\u7528");
|
|
1467
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1468
|
+
canvas.toBlob((blob) => {
|
|
1469
|
+
if (blob !== null) downloadBlob(blob, "mermaid-diagram.png");
|
|
1470
|
+
}, "image/png");
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
console.warn("[dsh-md-plus] PNG \u5BFC\u51FA\u5931\u8D25", error);
|
|
1473
|
+
} finally {
|
|
1474
|
+
if (objectUrl !== "") URL.revokeObjectURL(objectUrl);
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
image.onerror = function() {
|
|
1478
|
+
if (objectUrl !== "") URL.revokeObjectURL(objectUrl);
|
|
1479
|
+
console.warn("[dsh-md-plus] PNG \u5BFC\u51FA\u5931\u8D25\uFF1ASVG \u65E0\u6CD5\u8F7D\u5165");
|
|
1480
|
+
};
|
|
1481
|
+
objectUrl = URL.createObjectURL(new Blob([serializeSvg(svg)], { type: "image/svg+xml;charset=utf-8" }));
|
|
1482
|
+
image.src = objectUrl;
|
|
1483
|
+
}
|
|
654
1484
|
function openFullscreen(block) {
|
|
655
1485
|
if (document.querySelector("." + MODAL_CLASS) !== null) return;
|
|
656
1486
|
var svg = block.querySelector("." + MMD_CLASS + " svg");
|
|
@@ -660,31 +1490,39 @@ window.__ModuleLoader__.load({
|
|
|
660
1490
|
overlay.className = MODAL_CLASS;
|
|
661
1491
|
overlay.setAttribute("role", "dialog");
|
|
662
1492
|
overlay.setAttribute("aria-modal", "true");
|
|
663
|
-
overlay.setAttribute("aria-label", "
|
|
1493
|
+
overlay.setAttribute("aria-label", t("diagramAria"));
|
|
664
1494
|
var panel = document.createElement("div");
|
|
665
1495
|
panel.className = MODAL_CLASS + "-panel";
|
|
666
1496
|
var bar = document.createElement("div");
|
|
667
1497
|
bar.className = MODAL_CLASS + "-bar";
|
|
668
1498
|
var title = document.createElement("div");
|
|
669
1499
|
title.className = MODAL_CLASS + "-title";
|
|
670
|
-
title.textContent = "
|
|
1500
|
+
title.textContent = t("diagram");
|
|
671
1501
|
var actions = document.createElement("div");
|
|
672
1502
|
actions.className = MODAL_CLASS + "-actions";
|
|
673
1503
|
var fitButton = document.createElement("button");
|
|
674
1504
|
fitButton.type = "button";
|
|
675
|
-
fitButton.textContent = "
|
|
1505
|
+
fitButton.textContent = t("fit");
|
|
1506
|
+
var svgButton = document.createElement("button");
|
|
1507
|
+
svgButton.type = "button";
|
|
1508
|
+
svgButton.textContent = t("downloadSvg");
|
|
1509
|
+
var pngButton = document.createElement("button");
|
|
1510
|
+
pngButton.type = "button";
|
|
1511
|
+
pngButton.textContent = t("downloadPng");
|
|
676
1512
|
var zoomOut = document.createElement("button");
|
|
677
1513
|
zoomOut.type = "button";
|
|
678
1514
|
zoomOut.textContent = "\u2212";
|
|
679
|
-
zoomOut.setAttribute("aria-label", "
|
|
1515
|
+
zoomOut.setAttribute("aria-label", t("zoomOutAria"));
|
|
680
1516
|
var zoomIn = document.createElement("button");
|
|
681
1517
|
zoomIn.type = "button";
|
|
682
1518
|
zoomIn.textContent = "+";
|
|
683
|
-
zoomIn.setAttribute("aria-label", "
|
|
1519
|
+
zoomIn.setAttribute("aria-label", t("zoomInAria"));
|
|
684
1520
|
var closeButton = document.createElement("button");
|
|
685
1521
|
closeButton.type = "button";
|
|
686
|
-
closeButton.textContent = "
|
|
1522
|
+
closeButton.textContent = t("close");
|
|
687
1523
|
actions.appendChild(fitButton);
|
|
1524
|
+
actions.appendChild(svgButton);
|
|
1525
|
+
actions.appendChild(pngButton);
|
|
688
1526
|
actions.appendChild(zoomOut);
|
|
689
1527
|
actions.appendChild(zoomIn);
|
|
690
1528
|
actions.appendChild(closeButton);
|
|
@@ -735,12 +1573,14 @@ window.__ModuleLoader__.load({
|
|
|
735
1573
|
if (event.target === overlay) closeViewer();
|
|
736
1574
|
});
|
|
737
1575
|
fitButton.addEventListener("click", reset);
|
|
1576
|
+
svgButton.addEventListener("click", () => downloadSvg(svg));
|
|
1577
|
+
pngButton.addEventListener("click", () => downloadPng(svg));
|
|
738
1578
|
zoomOut.addEventListener("click", () => zoomBy(overlay, 1 / ZOOM_STEP));
|
|
739
1579
|
zoomIn.addEventListener("click", () => zoomBy(overlay, ZOOM_STEP));
|
|
740
1580
|
closeButton.addEventListener("click", closeViewer);
|
|
741
1581
|
document.addEventListener("keydown", onKey, true);
|
|
742
1582
|
zooms.set(overlay, { scale: 1, x: 0, y: 0, min: ZOOM_MIN_MODAL });
|
|
743
|
-
bindViewport(overlay, viewport, reset);
|
|
1583
|
+
bindViewport(overlay, viewport, reset, true);
|
|
744
1584
|
document.documentElement.style.overflow = "hidden";
|
|
745
1585
|
document.body.appendChild(overlay);
|
|
746
1586
|
closeButton.focus();
|
|
@@ -752,17 +1592,21 @@ window.__ModuleLoader__.load({
|
|
|
752
1592
|
var note = content.querySelector("." + MMD_CLASS + "-error") ?? document.createElement("p");
|
|
753
1593
|
note.className = MMD_CLASS + "-error";
|
|
754
1594
|
var reason = (error?.message ?? String(error)).split("\n")[0] ?? "";
|
|
755
|
-
note.textContent = "
|
|
1595
|
+
note.textContent = t("renderFailed") + reason;
|
|
756
1596
|
content.appendChild(note);
|
|
757
1597
|
block.setAttribute(KEY_ATTR, key);
|
|
758
1598
|
block.setAttribute(MMD_ATTR, "failed");
|
|
1599
|
+
decorate(block);
|
|
759
1600
|
console.warn("[dsh-md-plus] mermaid \u6E32\u67D3\u5931\u8D25", error);
|
|
760
1601
|
}
|
|
761
1602
|
function apply0(ctx) {
|
|
762
1603
|
if (typeof document === "undefined") return;
|
|
763
1604
|
ctx.effect(() => {
|
|
764
1605
|
injectStyle();
|
|
1606
|
+
applyCodeFontSize();
|
|
1607
|
+
ensureSettingsButton();
|
|
765
1608
|
var root = document.body ?? document.documentElement;
|
|
1609
|
+
document.addEventListener("click", onDocumentClick, true);
|
|
766
1610
|
var observer = new MutationObserver(onMutations);
|
|
767
1611
|
observer.observe(root, {
|
|
768
1612
|
childList: true,
|
|
@@ -774,14 +1618,29 @@ window.__ModuleLoader__.load({
|
|
|
774
1618
|
schedule();
|
|
775
1619
|
return () => {
|
|
776
1620
|
observer.disconnect();
|
|
1621
|
+
document.removeEventListener("click", onDocumentClick, true);
|
|
777
1622
|
for (const node of injected) node.remove();
|
|
778
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();
|
|
1627
|
+
for (const node of document.querySelectorAll("[" + FOLD_ATTR + "]")) resetFold(node);
|
|
1628
|
+
for (const node of document.querySelectorAll("." + TABLE_TOOLS_CLASS)) node.remove();
|
|
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");
|
|
1635
|
+
}
|
|
779
1636
|
for (const node of document.querySelectorAll(
|
|
780
|
-
"." + MMD_CLASS + ", ." + MMD_CLASS + "-error, ." + MODAL_CLASS
|
|
1637
|
+
"." + MMD_CLASS + ", ." + MMD_CLASS + "-error, ." + MODAL_CLASS + ", ." + IMG_MODAL_CLASS + ", ." + SETTINGS_BTN_CLASS + ", ." + SETTINGS_MODAL_CLASS
|
|
781
1638
|
)) {
|
|
782
1639
|
node.remove();
|
|
783
1640
|
}
|
|
784
1641
|
document.documentElement.style.overflow = "";
|
|
1642
|
+
document.documentElement.style.removeProperty("--md-plus-code-font-size");
|
|
1643
|
+
document.documentElement.style.removeProperty("--md-plus-code-line-height");
|
|
785
1644
|
document.querySelector("style[" + STYLE_ATTR + "]")?.remove();
|
|
786
1645
|
for (const script of document.querySelectorAll("script[" + RUNTIME_ATTR + "]")) {
|
|
787
1646
|
script.remove();
|