dsh-md-plus 0.4.5 → 0.5.0
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 +10 -0
- package/README.md +5 -5
- package/lib/client.js +454 -37
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## 0.5.0 —— 2026-11-04
|
|
4
|
+
|
|
5
|
+
- 表格支持按设置冻结前 N 行 / 前 M 列,默认冻结 1 行表头、0 列。
|
|
6
|
+
- 修复表格展开后「收起」按钮横向跟着滚动条跑的问题:展开态按钮横向改用 sticky 钉在可视区中间,纵向仍保持滚到表尾才出现。
|
|
7
|
+
- 表格复制按钮增加 TSV 和富文本(HTML)格式。
|
|
8
|
+
- 设置面板增加冻结行数 / 列数设置、「全部展开 / 全部收起」折叠操作和设置导入 / 导出;底部操作区重新分组,工具操作与主操作分层排列。
|
|
9
|
+
- Mermaid 卡片增加「复制图源」按钮;普通代码块增加下载为文件按钮。
|
|
10
|
+
- 修复「代码字号」留空或 0 时并未真正跟随宿主的问题:此前回退 `inherit` 会把代码块字号撑成正文大小,现在回退宿主 `--dsh-code-font-size` 变量。
|
|
11
|
+
- 测试补到 91 条,补强冻结行列、TSV / 富文本复制、全局折叠、复制图源和下载按钮等回归用例。
|
|
12
|
+
|
|
3
13
|
## 0.4.5 —— 2026-11-03
|
|
4
14
|
|
|
5
15
|
- 修复保存设置时无条件重绘所有 Mermaid 图、重置所有折叠状态的问题:只对真正变化的设置项执行重绘或重新测量,用户手动展开的长代码块和长表格不会被折叠回去。
|
package/README.md
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
9
|
-
- **代码块**:统一成完整卡片,头部显示 `</>` 图标和语言名(无语言显示 `Plain text
|
|
10
|
-
- **Mermaid**:围栏渲染成 SVG,可在「图形 /
|
|
9
|
+
- **代码块**:统一成完整卡片,头部显示 `</>` 图标和语言名(无语言显示 `Plain text`),保留宿主原生的换行与复制按钮,并支持下载为文件;顶栏与卡片同色。
|
|
10
|
+
- **Mermaid**:围栏渲染成 SVG,可在「图形 / 代码」间切换;支持复制图源、全屏查看(滚轮缩放、拖拽、双击适应、Esc 关闭)、四套配色,以及下载 SVG / PNG。
|
|
11
11
|
- **长内容折叠**:代码块、表格、图表超过阈值自动折叠,底部出现「展开 / 收起」,长日志和长表格不会撑满聊天。
|
|
12
|
-
- **表格**:浅色 /
|
|
12
|
+
- **表格**:浅色 / 深色两套样式,支持表内横向滚动;可按设置冻结前 N 行 / 前 M 列(默认冻结 1 行表头、0 列);每张表上方右侧支持 CSV / TSV / Markdown / 富文本复制。
|
|
13
13
|
- **图片放大**:只在旧宿主没有原生预览按钮时兜底,点击图片打开暗色预览,Esc 或点遮罩关闭;新版 DSH 的原生预览、头像、图标、代码块和链接里的图片都不接管。
|
|
14
|
-
- **设置面板**:右下角齿轮可调 Mermaid
|
|
14
|
+
- **设置面板**:右下角齿轮可调 Mermaid 配色、代码字号、图表默认视图、表格冻结行数 / 列数和三类折叠阈值,也能一键全部展开 / 收起、导出 / 导入设置文件;阈值下方会注明按高度判断。设置存在 `localStorage`,界面文案跟随页面 `<html lang>` 切换中英文。
|
|
15
15
|
|
|
16
16
|
## 安装与卸载
|
|
17
17
|
|
|
@@ -27,7 +27,7 @@ dsh plugin --profile desktop remove dsh-md-plus
|
|
|
27
27
|
```powershell
|
|
28
28
|
npm install # 顺带跑 prepare → build
|
|
29
29
|
npm run build # 产出 lib/ 下四个文件
|
|
30
|
-
npm run check # 构建 + 语法检查 +
|
|
30
|
+
npm run check # 构建 + 语法检查 + 91 条测试(check 会先 build)
|
|
31
31
|
npm run release:dry # 维护者发版:先看 CHANGELOG 日期 + 提交 + tag + push 计划
|
|
32
32
|
npm run release # 维护者发版:改 CHANGELOG 日期 + 提交 + 打 tag + 推送(tag 会触发 GitHub Actions 自动发 npm)
|
|
33
33
|
npm run preview # 生成 preview.html(含真渲出来的 mermaid 图)
|
package/lib/client.js
CHANGED
|
@@ -18,6 +18,8 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
var BTN_CLASS = "md-plus-btn";
|
|
19
19
|
var ZOOM_IN_CLASS = "md-plus-zoom-in";
|
|
20
20
|
var ZOOM_OUT_CLASS = "md-plus-zoom-out";
|
|
21
|
+
var COPY_SOURCE_CLASS = "md-plus-copy-source";
|
|
22
|
+
var DOWNLOAD_CLASS = "md-plus-download";
|
|
21
23
|
var ZOOM_ATTR = "data-md-plus-zoom";
|
|
22
24
|
var ZOOM_STEP = 1.25;
|
|
23
25
|
var ZOOM_MAX = 6;
|
|
@@ -52,6 +54,10 @@ window.__ModuleLoader__.load({
|
|
|
52
54
|
toggleAria: "\u5207\u6362\u56FE\u5F62\u4E0E\u4EE3\u7801",
|
|
53
55
|
fullscreen: "\u5168\u5C4F\u67E5\u770B",
|
|
54
56
|
fullscreenAria: "\u5168\u5C4F\u67E5\u770B\u6D41\u7A0B\u56FE",
|
|
57
|
+
copySource: "\u590D\u5236\u56FE\u6E90",
|
|
58
|
+
copySourceAria: "\u590D\u5236 Mermaid \u56FE\u6E90",
|
|
59
|
+
downloadCode: "\u4E0B\u8F7D\u4EE3\u7801",
|
|
60
|
+
downloadCodeAria: "\u4E0B\u8F7D\u4EE3\u7801\u4E3A\u6587\u4EF6",
|
|
55
61
|
theme: "\u5207\u6362 Mermaid \u914D\u8272",
|
|
56
62
|
themeAria: "\u5207\u6362 Mermaid \u914D\u8272",
|
|
57
63
|
themePrefix: "\u914D\u8272\uFF1A",
|
|
@@ -68,9 +74,13 @@ window.__ModuleLoader__.load({
|
|
|
68
74
|
exportFailed: "\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u6539\u7528 SVG",
|
|
69
75
|
close: "\u5173\u95ED",
|
|
70
76
|
tableCsv: "CSV",
|
|
77
|
+
tableTsv: "TSV",
|
|
71
78
|
tableMd: "Markdown",
|
|
79
|
+
tableHtml: "\u5BCC\u6587\u672C",
|
|
72
80
|
copyCsvTitle: "\u590D\u5236\u8868\u683C\u4E3A CSV",
|
|
81
|
+
copyTsvTitle: "\u590D\u5236\u8868\u683C\u4E3A TSV",
|
|
73
82
|
copyMdTitle: "\u590D\u5236\u8868\u683C\u4E3A Markdown",
|
|
83
|
+
copyHtmlTitle: "\u590D\u5236\u8868\u683C\u4E3A\u5BCC\u6587\u672C",
|
|
74
84
|
copied: "\u5DF2\u590D\u5236",
|
|
75
85
|
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
76
86
|
imageViewer: "\u56FE\u7247\u9884\u89C8",
|
|
@@ -84,6 +94,8 @@ window.__ModuleLoader__.load({
|
|
|
84
94
|
settingsViewDiagram: "\u56FE\u5F62",
|
|
85
95
|
settingsViewCode: "\u4EE3\u7801",
|
|
86
96
|
settingsFold: "\u957F\u5185\u5BB9\u6298\u53E0\u9608\u503C",
|
|
97
|
+
settingsFoldGroup: "\u6298\u53E0\u5185\u5BB9",
|
|
98
|
+
settingsFileGroup: "\u8BBE\u7F6E\u6587\u4EF6",
|
|
87
99
|
settingsFoldCode: "\u4EE3\u7801\u5757",
|
|
88
100
|
settingsFoldTable: "\u8868\u683C",
|
|
89
101
|
settingsFoldDiagram: "\u56FE\u8868",
|
|
@@ -91,6 +103,15 @@ window.__ModuleLoader__.load({
|
|
|
91
103
|
settingsFoldTableHint: "\u8868\u683C\u9AD8\u5EA6\u8D85\u8FC7\u6B64\u503C\u540E\u6298\u53E0",
|
|
92
104
|
settingsFoldDiagramHint: "\u56FE\u5F62\u9AD8\u5EA6\u8D85\u8FC7\u6B64\u503C\u540E\u6298\u53E0",
|
|
93
105
|
settingsFoldHint: "\u5355\u4F4D px\uFF0C0 \u8868\u793A\u4E0D\u6298\u53E0",
|
|
106
|
+
settingsFreezeRows: "\u51BB\u7ED3\u884C\u6570",
|
|
107
|
+
settingsFreezeRowsHint: "\u9ED8\u8BA4\u51BB\u7ED3 1 \u884C\u8868\u5934\uFF0C0 \u8868\u793A\u4E0D\u51BB\u7ED3",
|
|
108
|
+
settingsFreezeCols: "\u51BB\u7ED3\u5217\u6570",
|
|
109
|
+
settingsFreezeColsHint: "\u9ED8\u8BA4 0 \u5217\uFF0C\u6309\u9700\u6307\u5B9A",
|
|
110
|
+
foldExpandAll: "\u5168\u90E8\u5C55\u5F00",
|
|
111
|
+
foldCollapseAll: "\u5168\u90E8\u6536\u8D77",
|
|
112
|
+
settingsExport: "\u5BFC\u51FA\u8BBE\u7F6E",
|
|
113
|
+
settingsImport: "\u5BFC\u5165\u8BBE\u7F6E",
|
|
114
|
+
settingsImportFailed: "\u5BFC\u5165\u5931\u8D25",
|
|
94
115
|
settingsSave: "\u4FDD\u5B58",
|
|
95
116
|
settingsReset: "\u6062\u590D\u9ED8\u8BA4",
|
|
96
117
|
settingsClose: "\u5173\u95ED"
|
|
@@ -106,6 +127,10 @@ window.__ModuleLoader__.load({
|
|
|
106
127
|
toggleAria: "Switch diagram and code",
|
|
107
128
|
fullscreen: "Fullscreen",
|
|
108
129
|
fullscreenAria: "View diagram fullscreen",
|
|
130
|
+
copySource: "Copy source",
|
|
131
|
+
copySourceAria: "Copy Mermaid source",
|
|
132
|
+
downloadCode: "Download code",
|
|
133
|
+
downloadCodeAria: "Download code as a file",
|
|
109
134
|
theme: "Change Mermaid theme",
|
|
110
135
|
themeAria: "Change Mermaid theme",
|
|
111
136
|
themePrefix: "Theme: ",
|
|
@@ -122,9 +147,13 @@ window.__ModuleLoader__.load({
|
|
|
122
147
|
exportFailed: "Export failed; try SVG instead",
|
|
123
148
|
close: "Close",
|
|
124
149
|
tableCsv: "CSV",
|
|
150
|
+
tableTsv: "TSV",
|
|
125
151
|
tableMd: "Markdown",
|
|
152
|
+
tableHtml: "Rich",
|
|
126
153
|
copyCsvTitle: "Copy table as CSV",
|
|
154
|
+
copyTsvTitle: "Copy table as TSV",
|
|
127
155
|
copyMdTitle: "Copy table as Markdown",
|
|
156
|
+
copyHtmlTitle: "Copy table as rich text",
|
|
128
157
|
copied: "Copied",
|
|
129
158
|
copyFailed: "Copy failed",
|
|
130
159
|
imageViewer: "Image preview",
|
|
@@ -138,6 +167,8 @@ window.__ModuleLoader__.load({
|
|
|
138
167
|
settingsViewDiagram: "Diagram",
|
|
139
168
|
settingsViewCode: "Code",
|
|
140
169
|
settingsFold: "Fold thresholds",
|
|
170
|
+
settingsFoldGroup: "Fold content",
|
|
171
|
+
settingsFileGroup: "Settings file",
|
|
141
172
|
settingsFoldCode: "Code block",
|
|
142
173
|
settingsFoldTable: "Table",
|
|
143
174
|
settingsFoldDiagram: "Diagram",
|
|
@@ -145,6 +176,15 @@ window.__ModuleLoader__.load({
|
|
|
145
176
|
settingsFoldTableHint: "Fold when the table is taller than this",
|
|
146
177
|
settingsFoldDiagramHint: "Fold when the diagram is taller than this",
|
|
147
178
|
settingsFoldHint: "Pixels; 0 disables folding",
|
|
179
|
+
settingsFreezeRows: "Freeze rows",
|
|
180
|
+
settingsFreezeRowsHint: "Defaults to 1 header row; 0 disables freezing",
|
|
181
|
+
settingsFreezeCols: "Freeze columns",
|
|
182
|
+
settingsFreezeColsHint: "Defaults to 0 columns; set as needed",
|
|
183
|
+
foldExpandAll: "Expand all",
|
|
184
|
+
foldCollapseAll: "Collapse all",
|
|
185
|
+
settingsExport: "Export settings",
|
|
186
|
+
settingsImport: "Import settings",
|
|
187
|
+
settingsImportFailed: "Import failed",
|
|
148
188
|
settingsSave: "Save",
|
|
149
189
|
settingsReset: "Reset to defaults",
|
|
150
190
|
settingsClose: "Close"
|
|
@@ -164,6 +204,9 @@ window.__ModuleLoader__.load({
|
|
|
164
204
|
var FOLD_LIMITS = { code: 480, table: 520, diagram: 720 };
|
|
165
205
|
var TABLE_TOOLS_CLASS = "md-plus-table-tools";
|
|
166
206
|
var TABLE_DONE_ATTR = "data-md-plus-table";
|
|
207
|
+
var FREEZE_ROW_CLASS = "md-plus-freeze-row";
|
|
208
|
+
var FREEZE_COL_CLASS = "md-plus-freeze-col";
|
|
209
|
+
var FREEZE_CROSS_CLASS = "md-plus-freeze-cross";
|
|
167
210
|
var IMG_MODAL_CLASS = "md-plus-img-modal";
|
|
168
211
|
var NATIVE_IMAGE_BUTTON = 'button[class*="imageButton"]';
|
|
169
212
|
var SETTINGS_BTN_CLASS = "md-plus-settings-btn";
|
|
@@ -174,7 +217,9 @@ window.__ModuleLoader__.load({
|
|
|
174
217
|
diagramView: "diagram",
|
|
175
218
|
foldCode: FOLD_LIMITS.code,
|
|
176
219
|
foldTable: FOLD_LIMITS.table,
|
|
177
|
-
foldDiagram: FOLD_LIMITS.diagram
|
|
220
|
+
foldDiagram: FOLD_LIMITS.diagram,
|
|
221
|
+
freezeRows: 1,
|
|
222
|
+
freezeCols: 0
|
|
178
223
|
};
|
|
179
224
|
var STYLE_ATTR = "data-md-plus-style";
|
|
180
225
|
var RUNTIME_ATTR = "data-md-plus-runtime";
|
|
@@ -199,6 +244,12 @@ window.__ModuleLoader__.load({
|
|
|
199
244
|
var value = Number(saved[key]);
|
|
200
245
|
if (Number.isFinite(value) && value >= 0) settings[key] = Math.min(4e3, Math.round(value));
|
|
201
246
|
}
|
|
247
|
+
for (var freezeKey of ["freezeRows", "freezeCols"]) {
|
|
248
|
+
var freezeValue = Number(saved[freezeKey]);
|
|
249
|
+
if (saved[freezeKey] !== void 0 && Number.isFinite(freezeValue) && freezeValue >= 0) {
|
|
250
|
+
settings[freezeKey] = Math.min(5, Math.round(freezeValue));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
202
253
|
}
|
|
203
254
|
memorySettings = { ...settings };
|
|
204
255
|
} catch {
|
|
@@ -356,6 +407,9 @@ window.__ModuleLoader__.load({
|
|
|
356
407
|
var ICON_COPY = icon(
|
|
357
408
|
"<rect x='9' y='9' width='12' height='12' rx='2.5'/><path d='M6 15H5.5A2.5 2.5 0 0 1 3 12.5v-7A2.5 2.5 0 0 1 5.5 3h7A2.5 2.5 0 0 1 15 5.5V6'/>"
|
|
358
409
|
);
|
|
410
|
+
var ICON_DOWNLOAD = icon(
|
|
411
|
+
"<path d='M12 3v12'/><path d='m7 10 5 5 5-5'/><path d='M5 21h14'/>"
|
|
412
|
+
);
|
|
359
413
|
var ICON_NOWRAP = icon(
|
|
360
414
|
"<path d='M3 12h12.5'/><path d='M12.5 8.5 16.5 12l-4 3.5'/><path d='M20 6v12'/>"
|
|
361
415
|
);
|
|
@@ -385,7 +439,7 @@ window.__ModuleLoader__.load({
|
|
|
385
439
|
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";
|
|
386
440
|
var CSS = [
|
|
387
441
|
// ── 卡片:整块一个底色,顶栏不再是独立色带 ──────────────────────
|
|
388
|
-
".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 + "}",
|
|
442
|
+
".md-code-block{--md-plus-mmd-min-height:200px;--md-plus-icon:" + ICON_CODE + ";--md-plus-icon-copy:" + ICON_COPY + ";--md-plus-icon-download:" + ICON_DOWNLOAD + ";--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 + "}",
|
|
389
443
|
// bannerWrap 的原底色是 --dsw-alias-bg-base(#fff),会跟卡片灰拉开一条带
|
|
390
444
|
".md-code-block > div:has(> " + BANNER + "){background:transparent}",
|
|
391
445
|
// ── 头部:收紧,并把空着的左边填上 ─────────────────────────────
|
|
@@ -401,8 +455,10 @@ window.__ModuleLoader__.load({
|
|
|
401
455
|
// ── 间距:头部与首行代码之间补 6px ──────────────────────────────
|
|
402
456
|
// pre 的行高本身就在首行文字上方留了约半行空白;补 6px 后实测间距约 17px
|
|
403
457
|
// (原来给 0 时只有 11px,贴着看太挤)
|
|
404
|
-
|
|
405
|
-
|
|
458
|
+
// 未设字号时不能回退 inherit:pre 的父级是正文,会把宿主的代码字号
|
|
459
|
+
// (--dsh-code-font-size)盖成正文大小;回退到宿主变量才是真「跟随宿主」。
|
|
460
|
+
".md-code-block pre{padding:6px 16px 16px;font-size:var(--md-plus-code-font-size,var(--dsh-code-font-size,11px))}",
|
|
461
|
+
".md-code-block pre{line-height:var(--md-plus-code-line-height,calc(19px + var(--dsh-code-font-delta,0px)))}",
|
|
406
462
|
// DSH 0.2.1 把内容容器设成 display:contents,折叠需要真实的盒子才能量高度和裁剪
|
|
407
463
|
".md-code-block > [data-code-block-content]{display:block}",
|
|
408
464
|
// ── 头部按钮:文字换成图标 ──────────────────────────────────────
|
|
@@ -423,6 +479,8 @@ window.__ModuleLoader__.load({
|
|
|
423
479
|
".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}",
|
|
424
480
|
".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}",
|
|
425
481
|
".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}",
|
|
482
|
+
".md-code-block " + BANNER + " ." + COPY_SOURCE_CLASS + "::before{-webkit-mask:var(--md-plus-icon-copy) center/contain no-repeat;mask:var(--md-plus-icon-copy) center/contain no-repeat}",
|
|
483
|
+
".md-code-block " + BANNER + " ." + DOWNLOAD_CLASS + "::before{-webkit-mask:var(--md-plus-icon-download) center/contain no-repeat;mask:var(--md-plus-icon-download) center/contain no-repeat}",
|
|
426
484
|
".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}",
|
|
427
485
|
"[" + 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%))}",
|
|
428
486
|
// 已经到最小 / 最大时,对应那颗按钮变淡且不可点
|
|
@@ -471,19 +529,29 @@ window.__ModuleLoader__.load({
|
|
|
471
529
|
"." + 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}",
|
|
472
530
|
"." + 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%)}",
|
|
473
531
|
"." + 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%)}",
|
|
474
|
-
"." + SETTINGS_MODAL_CLASS + "-title{margin:0 0
|
|
475
|
-
"." + SETTINGS_MODAL_CLASS + "-row{display:flex;align-items:center;gap:12px;margin:
|
|
532
|
+
"." + SETTINGS_MODAL_CLASS + "-title{margin:0 0 10px;font:600 14px/22px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-primary)}",
|
|
533
|
+
"." + SETTINGS_MODAL_CLASS + "-row{display:flex;align-items:center;gap:12px;min-height:36px;margin:0 0 10px}",
|
|
476
534
|
"." + 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)}",
|
|
477
535
|
"." + SETTINGS_MODAL_CLASS + "-label-title{display:block}",
|
|
478
536
|
"." + 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)}",
|
|
479
537
|
"." + 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)}",
|
|
538
|
+
"." + SETTINGS_MODAL_CLASS + "-section{margin:14px 0 8px;font:600 12px/18px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-secondary)}",
|
|
480
539
|
"." + 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)}",
|
|
481
540
|
"." + 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)}",
|
|
482
|
-
"." + SETTINGS_MODAL_CLASS + "-
|
|
483
|
-
"." + SETTINGS_MODAL_CLASS + "-
|
|
484
|
-
"." + SETTINGS_MODAL_CLASS + "-actions
|
|
541
|
+
"." + SETTINGS_MODAL_CLASS + "-footer{margin-top:18px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 18%))}",
|
|
542
|
+
"." + SETTINGS_MODAL_CLASS + "-utilities{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 18px;margin:0 0 14px}",
|
|
543
|
+
"." + SETTINGS_MODAL_CLASS + "-fold-actions,." + SETTINGS_MODAL_CLASS + "-transfer-actions{display:flex;align-items:center;gap:8px}",
|
|
544
|
+
"." + SETTINGS_MODAL_CLASS + "-utility-label{font:11px/16px var(--dsw-font-family-sans,system-ui,sans-serif);color:var(--dsw-alias-label-tertiary);white-space:nowrap}",
|
|
545
|
+
"." + SETTINGS_MODAL_CLASS + "-segment{display:inline-flex;align-items:center;gap:1px;padding:2px;border:1px solid var(--dsw-alias-border-secondary,rgb(127 127 127 / 18%));border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgb(127 127 127 / 5%))}",
|
|
546
|
+
"." + SETTINGS_MODAL_CLASS + "-segment button{appearance:none;border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary);font:12px/18px var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer}",
|
|
547
|
+
"." + SETTINGS_MODAL_CLASS + "-segment button:hover{background:var(--dsw-alias-interactive-bg-hover,rgb(127 127 127 / 12%));color:var(--dsw-alias-label-primary)}",
|
|
548
|
+
"." + SETTINGS_MODAL_CLASS + "-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-top:0}",
|
|
549
|
+
"." + SETTINGS_MODAL_CLASS + "-actions button{appearance:none;border:0;border-radius:7px;min-height:30px;padding:5px 12px;background:transparent;color:var(--dsw-alias-label-secondary);font:12px/18px var(--dsw-font-family-sans,system-ui,sans-serif);cursor:pointer}",
|
|
550
|
+
"." + SETTINGS_MODAL_CLASS + "-actions button:first-child{margin-right:auto;color:var(--dsw-alias-label-tertiary)}",
|
|
551
|
+
"." + SETTINGS_MODAL_CLASS + "-actions button:not(." + SETTINGS_MODAL_CLASS + "-primary):hover{background:var(--dsw-alias-interactive-bg-hover,rgb(127 127 127 / 12%));color:var(--dsw-alias-label-primary)}",
|
|
485
552
|
"." + SETTINGS_MODAL_CLASS + "-actions ." + SETTINGS_MODAL_CLASS + "-primary{background:var(--dsw-alias-button-primary-fill,#4c6ef5);color:var(--dsw-alias-label-primary-foreground,#fff)}",
|
|
486
553
|
"." + SETTINGS_MODAL_CLASS + "-actions ." + SETTINGS_MODAL_CLASS + "-primary:hover{background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,#4c6ef5))}",
|
|
554
|
+
"." + SETTINGS_MODAL_CLASS + "-fold-actions button:focus-visible,." + SETTINGS_MODAL_CLASS + "-transfer-actions button:focus-visible,." + SETTINGS_MODAL_CLASS + "-actions button:focus-visible{outline:2px solid var(--dsw-alias-button-primary-fill,#4c6ef5);outline-offset:1px}",
|
|
487
555
|
// 高亮宿主不参与布局,让里面的 <pre> 直接吃到卡片样式
|
|
488
556
|
"." + HOST_CLASS + "{display:contents}",
|
|
489
557
|
// 藏掉纯文本回退,只留着色结果;原节点仍在,复制读的还是它
|
|
@@ -519,8 +587,15 @@ window.__ModuleLoader__.load({
|
|
|
519
587
|
TABLE + " > table tr:last-child td{border-bottom:0}",
|
|
520
588
|
// 宽表横向扫读时给个落点
|
|
521
589
|
TABLE + " > table tbody tr:hover td{background:var(--md-plus-tbl-hover)}",
|
|
522
|
-
//
|
|
523
|
-
TABLE + " > table
|
|
590
|
+
// 冻结行 / 列:具体偏移由客户端按实际行列尺寸算好,支持设置多行多列
|
|
591
|
+
TABLE + " > table ." + FREEZE_ROW_CLASS + "{position:sticky;z-index:2;box-shadow:0 1px 0 var(--md-plus-tbl-line)}",
|
|
592
|
+
TABLE + " > table th." + FREEZE_ROW_CLASS + "{background:var(--md-plus-tbl-head)}",
|
|
593
|
+
TABLE + " > table td." + FREEZE_ROW_CLASS + "{background:var(--dsw-alias-bg-base,#fff)}",
|
|
594
|
+
TABLE + " > table ." + FREEZE_COL_CLASS + "{position:sticky;z-index:1;box-shadow:1px 0 0 var(--md-plus-tbl-line)}",
|
|
595
|
+
TABLE + " > table th." + FREEZE_COL_CLASS + "{background:var(--md-plus-tbl-head)}",
|
|
596
|
+
TABLE + " > table td." + FREEZE_COL_CLASS + "{background:var(--dsw-alias-bg-base,#fff)}",
|
|
597
|
+
TABLE + " > table ." + FREEZE_CROSS_CLASS + "{z-index:3}",
|
|
598
|
+
TABLE + " > table tbody tr:hover td." + FREEZE_ROW_CLASS + "," + TABLE + " > table tbody tr:hover td." + FREEZE_COL_CLASS + "{background:var(--md-plus-tbl-hover)}",
|
|
524
599
|
// 复制按钮放在表尾右侧:不压住表头文字,表格折叠时也能随展开一起出现
|
|
525
600
|
TABLE + " > table > caption." + TABLE_TOOLS_CLASS + "{caption-side:top;position:sticky;right:0;width:0;height:30px;margin-left:auto;padding:0;text-align:right;z-index:2}",
|
|
526
601
|
TABLE + " > table > caption." + TABLE_TOOLS_CLASS + " > span{position:absolute;right:0;top:0;display:flex;gap:0;white-space:nowrap}",
|
|
@@ -537,7 +612,11 @@ window.__ModuleLoader__.load({
|
|
|
537
612
|
// overscroll-behavior:auto 时才会把纵向滚轮交还给页面。
|
|
538
613
|
// 这里同时写明 min/max-height:基础表格规则的 max-height 特异性更高,不补会压过设置值;
|
|
539
614
|
// 最小高度保证极低阈值下「展开」按钮仍完整可见。
|
|
540
|
-
TABLE + "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{min-height:48px;max-height:var(--md-plus-fold-max-height);overflow-x:auto;overflow-y:clip}'
|
|
615
|
+
TABLE + "." + FOLD_CLASS + "[" + FOLD_ATTR + '="on"]{min-height:48px;max-height:var(--md-plus-fold-max-height);overflow-x:auto;overflow-y:clip}',
|
|
616
|
+
// 展开态的「收起」按钮在表尾文档流里,纵向保持滚到底才出现;
|
|
617
|
+
// 横向用 sticky 钉在可视区中间,宽表左右滚动时不会跟着表内容跑掉。
|
|
618
|
+
// 折叠态按钮是绝对定位,横向跟随由 sync() 写 left 完成。
|
|
619
|
+
TABLE + " ." + FOLD_BTN_CLASS + "{position:sticky;left:50%;transform:translateX(-50%)}"
|
|
541
620
|
].join("\n");
|
|
542
621
|
var scripts = /* @__PURE__ */ new Map();
|
|
543
622
|
var pending = /* @__PURE__ */ new Set();
|
|
@@ -550,6 +629,9 @@ window.__ModuleLoader__.load({
|
|
|
550
629
|
var timer = null;
|
|
551
630
|
var rescanQueued = false;
|
|
552
631
|
var injected = /* @__PURE__ */ new Set();
|
|
632
|
+
var tableFreezeObserver = null;
|
|
633
|
+
var tableFreezeTargets = /* @__PURE__ */ new Set();
|
|
634
|
+
var tableFreezeState = /* @__PURE__ */ new WeakMap();
|
|
553
635
|
var diagramSeq = 0;
|
|
554
636
|
var disposed = false;
|
|
555
637
|
function injectStyle() {
|
|
@@ -717,6 +799,7 @@ window.__ModuleLoader__.load({
|
|
|
717
799
|
if (!node.isConnected) injected.delete(node);
|
|
718
800
|
}
|
|
719
801
|
cleanupFoldTargets();
|
|
802
|
+
cleanupTableFreezeTargets();
|
|
720
803
|
diagramKindCache = /* @__PURE__ */ new WeakMap();
|
|
721
804
|
if (rescanQueued) {
|
|
722
805
|
rescanQueued = false;
|
|
@@ -760,17 +843,22 @@ window.__ModuleLoader__.load({
|
|
|
760
843
|
}
|
|
761
844
|
schedule();
|
|
762
845
|
}
|
|
763
|
-
var
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
else block.setAttribute(WRAP_ATTR, "on");
|
|
771
|
-
}
|
|
846
|
+
var WRAP_BUTTON = {
|
|
847
|
+
cls: TOOL_CLASS,
|
|
848
|
+
titleKey: "wrapTitle",
|
|
849
|
+
ariaKey: "wrapAria",
|
|
850
|
+
run: (block) => {
|
|
851
|
+
if (block.getAttribute(WRAP_ATTR) === "on") block.removeAttribute(WRAP_ATTR);
|
|
852
|
+
else block.setAttribute(WRAP_ATTR, "on");
|
|
772
853
|
}
|
|
773
|
-
|
|
854
|
+
};
|
|
855
|
+
var DOWNLOAD_BUTTON = {
|
|
856
|
+
cls: DOWNLOAD_CLASS,
|
|
857
|
+
titleKey: "downloadCode",
|
|
858
|
+
ariaKey: "downloadCodeAria",
|
|
859
|
+
run: (block) => downloadCode(block)
|
|
860
|
+
};
|
|
861
|
+
var PLAIN_BUTTONS = [WRAP_BUTTON, DOWNLOAD_BUTTON];
|
|
774
862
|
var MMD_BUTTONS = [
|
|
775
863
|
{ cls: ZOOM_OUT_CLASS, titleKey: "zoomOut", ariaKey: "zoomOutAria", run: (block) => zoomBy(block, 1 / ZOOM_STEP) },
|
|
776
864
|
{ cls: ZOOM_IN_CLASS, titleKey: "zoomIn", ariaKey: "zoomInAria", run: (block) => zoomBy(block, ZOOM_STEP) },
|
|
@@ -799,6 +887,12 @@ window.__ModuleLoader__.load({
|
|
|
799
887
|
titleKey: "theme",
|
|
800
888
|
ariaKey: "themeAria",
|
|
801
889
|
run: (block) => cycleMermaidTheme(block)
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
cls: COPY_SOURCE_CLASS,
|
|
893
|
+
titleKey: "copySource",
|
|
894
|
+
ariaKey: "copySourceAria",
|
|
895
|
+
run: (block, button) => copyDiagramSource(block, button)
|
|
802
896
|
}
|
|
803
897
|
];
|
|
804
898
|
var FAILED_BUTTONS = [
|
|
@@ -832,7 +926,8 @@ window.__ModuleLoader__.load({
|
|
|
832
926
|
var label = readBannerLabel(block);
|
|
833
927
|
var lang = readLang(block);
|
|
834
928
|
if (lang !== "" && isPlaceholderLabel(label)) paintLanguageLabel(block, lang);
|
|
835
|
-
var
|
|
929
|
+
var hasNativeWrap = findNativeWrap(nativeButtons) !== null;
|
|
930
|
+
var specs = block.getAttribute(MMD_ATTR) === "failed" ? FAILED_BUTTONS : diagramKind(block) !== null ? MMD_BUTTONS : hasNativeWrap ? [DOWNLOAD_BUTTON] : PLAIN_BUTTONS;
|
|
836
931
|
for (const existing of action.querySelectorAll("." + BTN_CLASS)) {
|
|
837
932
|
if (specs.some((spec) => existing.classList.contains(spec.cls))) continue;
|
|
838
933
|
existing.remove();
|
|
@@ -850,7 +945,7 @@ window.__ModuleLoader__.load({
|
|
|
850
945
|
created.title = themeText;
|
|
851
946
|
created.setAttribute("aria-label", themeText);
|
|
852
947
|
}
|
|
853
|
-
created.addEventListener("click", () => spec.run(block));
|
|
948
|
+
created.addEventListener("click", () => spec.run(block, created));
|
|
854
949
|
action.insertBefore(created, copy);
|
|
855
950
|
injected.add(created);
|
|
856
951
|
}
|
|
@@ -1195,7 +1290,11 @@ window.__ModuleLoader__.load({
|
|
|
1195
1290
|
button.textContent = t("foldOpen");
|
|
1196
1291
|
button.setAttribute("aria-expanded", "false");
|
|
1197
1292
|
var sync = target.hasAttribute(TABLE_DONE_ATTR) ? () => {
|
|
1198
|
-
|
|
1293
|
+
if (target.getAttribute(FOLD_ATTR) === "on") {
|
|
1294
|
+
button.style.left = target.scrollLeft + target.clientWidth / 2 + "px";
|
|
1295
|
+
} else {
|
|
1296
|
+
button.style.removeProperty("left");
|
|
1297
|
+
}
|
|
1199
1298
|
} : null;
|
|
1200
1299
|
if (sync !== null) {
|
|
1201
1300
|
target.addEventListener("scroll", sync, { passive: true });
|
|
@@ -1238,6 +1337,22 @@ window.__ModuleLoader__.load({
|
|
|
1238
1337
|
(row) => row.map((value) => /[",\n]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value).join(",")
|
|
1239
1338
|
).join("\n");
|
|
1240
1339
|
}
|
|
1340
|
+
function tableToTsv(rows) {
|
|
1341
|
+
return rows.map((row) => row.map((value) => value.replace(/\t/g, " ")).join(" ")).join("\n");
|
|
1342
|
+
}
|
|
1343
|
+
function escapeHtml(text) {
|
|
1344
|
+
return text.replace(
|
|
1345
|
+
/[&<>"']/g,
|
|
1346
|
+
(character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character]
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
function tableToHtml(rows) {
|
|
1350
|
+
if (rows.length === 0) return "";
|
|
1351
|
+
var cell = (tag, value) => "<" + tag + ">" + escapeHtml(value) + "</" + tag + ">";
|
|
1352
|
+
var header = "<thead><tr>" + rows[0].map((value) => cell("th", value)).join("") + "</tr></thead>";
|
|
1353
|
+
var body = rows.slice(1).map((row) => "<tr>" + row.map((value) => cell("td", value)).join("") + "</tr>");
|
|
1354
|
+
return "<table>" + header + (body.length === 0 ? "" : "<tbody>" + body.join("") + "</tbody>") + "</table>";
|
|
1355
|
+
}
|
|
1241
1356
|
function tableToMarkdown(rows) {
|
|
1242
1357
|
if (rows.length === 0) return "";
|
|
1243
1358
|
var width = Math.max(...rows.map((row) => row.length));
|
|
@@ -1246,6 +1361,36 @@ window.__ModuleLoader__.load({
|
|
|
1246
1361
|
for (var index = 1; index < rows.length; index += 1) output.push(line(rows[index]));
|
|
1247
1362
|
return output.join("\n");
|
|
1248
1363
|
}
|
|
1364
|
+
var CODE_EXTENSIONS = {
|
|
1365
|
+
bash: "sh",
|
|
1366
|
+
batch: "bat",
|
|
1367
|
+
c: "c",
|
|
1368
|
+
cpp: "cpp",
|
|
1369
|
+
csharp: "cs",
|
|
1370
|
+
css: "css",
|
|
1371
|
+
go: "go",
|
|
1372
|
+
html: "html",
|
|
1373
|
+
java: "java",
|
|
1374
|
+
javascript: "js",
|
|
1375
|
+
json: "json",
|
|
1376
|
+
jsx: "jsx",
|
|
1377
|
+
kotlin: "kt",
|
|
1378
|
+
markdown: "md",
|
|
1379
|
+
mermaid: "mmd",
|
|
1380
|
+
php: "php",
|
|
1381
|
+
python: "py",
|
|
1382
|
+
ruby: "rb",
|
|
1383
|
+
rust: "rs",
|
|
1384
|
+
shell: "sh",
|
|
1385
|
+
shellscript: "sh",
|
|
1386
|
+
sql: "sql",
|
|
1387
|
+
swift: "swift",
|
|
1388
|
+
tsx: "tsx",
|
|
1389
|
+
typescript: "ts",
|
|
1390
|
+
xml: "xml",
|
|
1391
|
+
yaml: "yml",
|
|
1392
|
+
yml: "yml"
|
|
1393
|
+
};
|
|
1249
1394
|
async function copyText(text) {
|
|
1250
1395
|
if (window.navigator.clipboard !== void 0 && typeof window.navigator.clipboard.writeText === "function") {
|
|
1251
1396
|
try {
|
|
@@ -1269,6 +1414,55 @@ window.__ModuleLoader__.load({
|
|
|
1269
1414
|
area.remove();
|
|
1270
1415
|
return ok;
|
|
1271
1416
|
}
|
|
1417
|
+
async function copyRichText(text, html) {
|
|
1418
|
+
if (window.navigator.clipboard !== void 0 && typeof window.navigator.clipboard.write === "function" && typeof window.ClipboardItem === "function") {
|
|
1419
|
+
try {
|
|
1420
|
+
await window.navigator.clipboard.write([
|
|
1421
|
+
new window.ClipboardItem({
|
|
1422
|
+
"text/plain": new Blob([text], { type: "text/plain" }),
|
|
1423
|
+
"text/html": new Blob([html], { type: "text/html" })
|
|
1424
|
+
})
|
|
1425
|
+
]);
|
|
1426
|
+
return true;
|
|
1427
|
+
} catch {
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return copyText(text);
|
|
1431
|
+
}
|
|
1432
|
+
async function copyDiagramSource(block, button) {
|
|
1433
|
+
var pre = block.querySelector(CONTENT + " pre");
|
|
1434
|
+
if (pre === null) return;
|
|
1435
|
+
var ok = false;
|
|
1436
|
+
try {
|
|
1437
|
+
ok = await copyText(pre.textContent ?? "");
|
|
1438
|
+
} catch {
|
|
1439
|
+
ok = false;
|
|
1440
|
+
}
|
|
1441
|
+
if (button !== void 0) {
|
|
1442
|
+
var original = button.title;
|
|
1443
|
+
var label = ok ? t("copied") : t("copyFailed");
|
|
1444
|
+
button.title = label;
|
|
1445
|
+
button.setAttribute("aria-label", label);
|
|
1446
|
+
window.setTimeout(() => {
|
|
1447
|
+
button.title = original;
|
|
1448
|
+
button.setAttribute("aria-label", original);
|
|
1449
|
+
}, 1200);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function downloadCode(block) {
|
|
1453
|
+
var pre = block.querySelector(CONTENT + " pre");
|
|
1454
|
+
if (pre === null) return;
|
|
1455
|
+
var lang = readLang(block);
|
|
1456
|
+
var ext = CODE_EXTENSIONS[lang] ?? "txt";
|
|
1457
|
+
try {
|
|
1458
|
+
downloadBlob(
|
|
1459
|
+
new Blob([pre.textContent ?? ""], { type: "text/plain;charset=utf-8" }),
|
|
1460
|
+
"code-snippet." + ext
|
|
1461
|
+
);
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
console.warn("[dsh-md-plus] \u4EE3\u7801\u4E0B\u8F7D\u5931\u8D25", error);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1272
1466
|
function flashButton(button, text) {
|
|
1273
1467
|
var original = button.getAttribute("data-md-plus-label") ?? button.textContent;
|
|
1274
1468
|
button.setAttribute("data-md-plus-label", original);
|
|
@@ -1277,9 +1471,88 @@ window.__ModuleLoader__.load({
|
|
|
1277
1471
|
button.textContent = original;
|
|
1278
1472
|
}, 1200);
|
|
1279
1473
|
}
|
|
1474
|
+
function clearTableFreeze(table) {
|
|
1475
|
+
for (const cell of table.querySelectorAll(
|
|
1476
|
+
"." + FREEZE_ROW_CLASS + ",." + FREEZE_COL_CLASS + ",." + FREEZE_CROSS_CLASS
|
|
1477
|
+
)) {
|
|
1478
|
+
cell.classList.remove(FREEZE_ROW_CLASS, FREEZE_COL_CLASS, FREEZE_CROSS_CLASS);
|
|
1479
|
+
cell.style.removeProperty("top");
|
|
1480
|
+
cell.style.removeProperty("left");
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function observeTableFreeze(table) {
|
|
1484
|
+
if (tableFreezeTargets.has(table)) return;
|
|
1485
|
+
tableFreezeTargets.add(table);
|
|
1486
|
+
if (typeof window.ResizeObserver !== "function") return;
|
|
1487
|
+
if (tableFreezeObserver === null) {
|
|
1488
|
+
tableFreezeObserver = new window.ResizeObserver((entries) => {
|
|
1489
|
+
for (const entry of entries) {
|
|
1490
|
+
if (!entry.target.isConnected) {
|
|
1491
|
+
cleanupTableFreezeTargets();
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
applyTableFreeze(entry.target);
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
tableFreezeObserver.observe(table);
|
|
1499
|
+
}
|
|
1500
|
+
function applyTableFreeze(table) {
|
|
1501
|
+
var dataTable = table.querySelector("table");
|
|
1502
|
+
if (dataTable === null) return;
|
|
1503
|
+
var settings = readSettings();
|
|
1504
|
+
var freezeRows = Math.max(0, Math.min(5, Number(settings.freezeRows) || 0));
|
|
1505
|
+
var freezeCols = Math.max(0, Math.min(5, Number(settings.freezeCols) || 0));
|
|
1506
|
+
var width = dataTable.scrollWidth;
|
|
1507
|
+
var height = dataTable.scrollHeight;
|
|
1508
|
+
var hasFrozenCells = dataTable.querySelector("." + FREEZE_ROW_CLASS + ",." + FREEZE_COL_CLASS) !== null;
|
|
1509
|
+
var state = tableFreezeState.get(table);
|
|
1510
|
+
if (state !== void 0 && state.rows === freezeRows && state.cols === freezeCols && state.width === width && state.height === height && (freezeRows === 0 && freezeCols === 0 || hasFrozenCells)) {
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
clearTableFreeze(table);
|
|
1514
|
+
tableFreezeState.set(table, { rows: freezeRows, cols: freezeCols, width, height });
|
|
1515
|
+
if (freezeRows === 0 && freezeCols === 0) return;
|
|
1516
|
+
var rows = Array.from(dataTable.rows);
|
|
1517
|
+
var top = 0;
|
|
1518
|
+
for (var rowIndex = 0; rowIndex < rows.length; rowIndex += 1) {
|
|
1519
|
+
var row = rows[rowIndex];
|
|
1520
|
+
var cells = Array.from(row.cells);
|
|
1521
|
+
var frozenRow = rowIndex < freezeRows;
|
|
1522
|
+
if (frozenRow) {
|
|
1523
|
+
for (const cell2 of cells) cell2.classList.add(FREEZE_ROW_CLASS);
|
|
1524
|
+
}
|
|
1525
|
+
var left = 0;
|
|
1526
|
+
for (var cellIndex = 0; cellIndex < cells.length; cellIndex += 1) {
|
|
1527
|
+
var cell = cells[cellIndex];
|
|
1528
|
+
if (frozenRow) cell.style.top = top + "px";
|
|
1529
|
+
if (cellIndex < freezeCols) {
|
|
1530
|
+
cell.classList.add(FREEZE_COL_CLASS);
|
|
1531
|
+
if (frozenRow) cell.classList.add(FREEZE_CROSS_CLASS);
|
|
1532
|
+
cell.style.left = left + "px";
|
|
1533
|
+
left += cell.getBoundingClientRect().width;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
if (frozenRow) top += row.getBoundingClientRect().height;
|
|
1537
|
+
}
|
|
1538
|
+
observeTableFreeze(table);
|
|
1539
|
+
}
|
|
1540
|
+
function cleanupTableFreezeTargets() {
|
|
1541
|
+
for (const table of tableFreezeTargets) {
|
|
1542
|
+
if (table.isConnected) continue;
|
|
1543
|
+
if (tableFreezeObserver !== null) tableFreezeObserver.unobserve(table);
|
|
1544
|
+
tableFreezeTargets.delete(table);
|
|
1545
|
+
tableFreezeState.delete(table);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function applyTableFreezeSettings() {
|
|
1549
|
+
tableFreezeState = /* @__PURE__ */ new WeakMap();
|
|
1550
|
+
for (const table of document.querySelectorAll(TABLE)) applyTableFreeze(table);
|
|
1551
|
+
}
|
|
1280
1552
|
function decorateTable(table) {
|
|
1281
1553
|
var dataTable = table.querySelector("table");
|
|
1282
1554
|
if (dataTable === null) return;
|
|
1555
|
+
applyTableFreeze(table);
|
|
1283
1556
|
var first = dataTable.firstElementChild;
|
|
1284
1557
|
if (first !== null && first.classList.contains(TABLE_TOOLS_CLASS)) return;
|
|
1285
1558
|
table.setAttribute(TABLE_DONE_ATTR, "1");
|
|
@@ -1287,9 +1560,11 @@ window.__ModuleLoader__.load({
|
|
|
1287
1560
|
tools.className = TABLE_TOOLS_CLASS;
|
|
1288
1561
|
var group = document.createElement("span");
|
|
1289
1562
|
group.className = TABLE_TOOLS_CLASS + "-group";
|
|
1290
|
-
for (const [kind, label, titleKey, convert] of [
|
|
1291
|
-
["csv", t("tableCsv"), "copyCsvTitle", tableToCsv],
|
|
1292
|
-
["
|
|
1563
|
+
for (const [kind, label, titleKey, convert, rich] of [
|
|
1564
|
+
["csv", t("tableCsv"), "copyCsvTitle", tableToCsv, false],
|
|
1565
|
+
["tsv", t("tableTsv"), "copyTsvTitle", tableToTsv, false],
|
|
1566
|
+
["md", t("tableMd"), "copyMdTitle", tableToMarkdown, false],
|
|
1567
|
+
["html", t("tableHtml"), "copyHtmlTitle", tableToHtml, true]
|
|
1293
1568
|
]) {
|
|
1294
1569
|
const button = document.createElement("button");
|
|
1295
1570
|
button.type = "button";
|
|
@@ -1299,7 +1574,8 @@ window.__ModuleLoader__.load({
|
|
|
1299
1574
|
button.addEventListener("click", async () => {
|
|
1300
1575
|
var ok = false;
|
|
1301
1576
|
try {
|
|
1302
|
-
|
|
1577
|
+
var rows = tableRows(dataTable);
|
|
1578
|
+
ok = rich ? await copyRichText(tableToTsv(rows), convert(rows)) : await copyText(convert(rows));
|
|
1303
1579
|
} catch {
|
|
1304
1580
|
ok = false;
|
|
1305
1581
|
}
|
|
@@ -1460,6 +1736,34 @@ window.__ModuleLoader__.load({
|
|
|
1460
1736
|
}
|
|
1461
1737
|
scanFolds();
|
|
1462
1738
|
}
|
|
1739
|
+
function setFoldExpanded(target, expanded) {
|
|
1740
|
+
var state = target.getAttribute(FOLD_ATTR);
|
|
1741
|
+
if (state !== "on" && state !== "off") return;
|
|
1742
|
+
target.setAttribute(FOLD_ATTR, expanded ? "off" : "on");
|
|
1743
|
+
if (target.hasAttribute(TABLE_DONE_ATTR)) target.scrollTop = 0;
|
|
1744
|
+
var button = target.querySelector("." + FOLD_BTN_CLASS);
|
|
1745
|
+
if (button !== null) {
|
|
1746
|
+
button.textContent = expanded ? t("foldClose") : t("foldOpen");
|
|
1747
|
+
button.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
1748
|
+
}
|
|
1749
|
+
foldSyncs.get(target)?.();
|
|
1750
|
+
}
|
|
1751
|
+
function setAllFolds(expanded) {
|
|
1752
|
+
for (const target of document.querySelectorAll("[" + FOLD_ATTR + "]")) {
|
|
1753
|
+
setFoldExpanded(target, expanded);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
function exportSettings() {
|
|
1757
|
+
try {
|
|
1758
|
+
var payload = { settings: readSettings(), mermaidTheme: getMermaidTheme() };
|
|
1759
|
+
downloadBlob(
|
|
1760
|
+
new Blob([JSON.stringify(payload, null, 2)], { type: "application/json;charset=utf-8" }),
|
|
1761
|
+
"dsh-md-plus-settings.json"
|
|
1762
|
+
);
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
console.warn("[dsh-md-plus] \u8BBE\u7F6E\u5BFC\u51FA\u5931\u8D25", error);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1463
1767
|
function openSettings() {
|
|
1464
1768
|
if (document.querySelector("." + SETTINGS_MODAL_CLASS) !== null) return;
|
|
1465
1769
|
var current = readSettings();
|
|
@@ -1484,11 +1788,9 @@ window.__ModuleLoader__.load({
|
|
|
1484
1788
|
themeSelect.value = getMermaidTheme();
|
|
1485
1789
|
panel.appendChild(settingRow(t("settingsTheme"), themeSelect));
|
|
1486
1790
|
var fontInput = numberInput(current.codeFontSize, 10, 20);
|
|
1487
|
-
panel.appendChild(
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
fontHint.textContent = t("settingsFontSizeHint");
|
|
1491
|
-
panel.appendChild(fontHint);
|
|
1791
|
+
panel.appendChild(
|
|
1792
|
+
settingRow(t("settingsFontSize"), fontInput, t("settingsFontSizeHint"))
|
|
1793
|
+
);
|
|
1492
1794
|
var viewSelect = document.createElement("select");
|
|
1493
1795
|
for (const [value, label] of [
|
|
1494
1796
|
["diagram", t("settingsViewDiagram")],
|
|
@@ -1502,7 +1804,7 @@ window.__ModuleLoader__.load({
|
|
|
1502
1804
|
viewSelect.value = current.diagramView;
|
|
1503
1805
|
panel.appendChild(settingRow(t("settingsView"), viewSelect));
|
|
1504
1806
|
var foldHint = document.createElement("p");
|
|
1505
|
-
foldHint.className = SETTINGS_MODAL_CLASS + "-
|
|
1807
|
+
foldHint.className = SETTINGS_MODAL_CLASS + "-section";
|
|
1506
1808
|
foldHint.textContent = t("settingsFold") + " \xB7 " + t("settingsFoldHint");
|
|
1507
1809
|
panel.appendChild(foldHint);
|
|
1508
1810
|
var foldCode = numberInput(current.foldCode, 0, 4e3);
|
|
@@ -1511,6 +1813,56 @@ window.__ModuleLoader__.load({
|
|
|
1511
1813
|
panel.appendChild(settingRow(t("settingsFoldCode"), foldCode, t("settingsFoldCodeHint")));
|
|
1512
1814
|
panel.appendChild(settingRow(t("settingsFoldTable"), foldTable, t("settingsFoldTableHint")));
|
|
1513
1815
|
panel.appendChild(settingRow(t("settingsFoldDiagram"), foldDiagram, t("settingsFoldDiagramHint")));
|
|
1816
|
+
var freezeRowsInput = numberInput(current.freezeRows, 0, 5);
|
|
1817
|
+
var freezeColsInput = numberInput(current.freezeCols, 0, 5);
|
|
1818
|
+
panel.appendChild(
|
|
1819
|
+
settingRow(t("settingsFreezeRows"), freezeRowsInput, t("settingsFreezeRowsHint"))
|
|
1820
|
+
);
|
|
1821
|
+
panel.appendChild(
|
|
1822
|
+
settingRow(t("settingsFreezeCols"), freezeColsInput, t("settingsFreezeColsHint"))
|
|
1823
|
+
);
|
|
1824
|
+
var foldActions = document.createElement("div");
|
|
1825
|
+
foldActions.className = SETTINGS_MODAL_CLASS + "-fold-actions";
|
|
1826
|
+
foldActions.setAttribute("role", "group");
|
|
1827
|
+
foldActions.setAttribute("aria-label", t("settingsFoldGroup"));
|
|
1828
|
+
var foldLabel = document.createElement("span");
|
|
1829
|
+
foldLabel.className = SETTINGS_MODAL_CLASS + "-utility-label";
|
|
1830
|
+
foldLabel.textContent = t("settingsFoldGroup");
|
|
1831
|
+
var foldSegment = document.createElement("span");
|
|
1832
|
+
foldSegment.className = SETTINGS_MODAL_CLASS + "-segment";
|
|
1833
|
+
var expandAllButton = document.createElement("button");
|
|
1834
|
+
expandAllButton.type = "button";
|
|
1835
|
+
expandAllButton.textContent = t("foldExpandAll");
|
|
1836
|
+
var collapseAllButton = document.createElement("button");
|
|
1837
|
+
collapseAllButton.type = "button";
|
|
1838
|
+
collapseAllButton.textContent = t("foldCollapseAll");
|
|
1839
|
+
foldSegment.appendChild(expandAllButton);
|
|
1840
|
+
foldSegment.appendChild(collapseAllButton);
|
|
1841
|
+
foldActions.appendChild(foldLabel);
|
|
1842
|
+
foldActions.appendChild(foldSegment);
|
|
1843
|
+
var transferActions = document.createElement("div");
|
|
1844
|
+
transferActions.className = SETTINGS_MODAL_CLASS + "-transfer-actions";
|
|
1845
|
+
transferActions.setAttribute("role", "group");
|
|
1846
|
+
transferActions.setAttribute("aria-label", t("settingsFileGroup"));
|
|
1847
|
+
var transferLabel = document.createElement("span");
|
|
1848
|
+
transferLabel.className = SETTINGS_MODAL_CLASS + "-utility-label";
|
|
1849
|
+
transferLabel.textContent = t("settingsFileGroup");
|
|
1850
|
+
var transferSegment = document.createElement("span");
|
|
1851
|
+
transferSegment.className = SETTINGS_MODAL_CLASS + "-segment";
|
|
1852
|
+
var exportButton = document.createElement("button");
|
|
1853
|
+
exportButton.type = "button";
|
|
1854
|
+
exportButton.textContent = t("settingsExport");
|
|
1855
|
+
var importButton = document.createElement("button");
|
|
1856
|
+
importButton.type = "button";
|
|
1857
|
+
importButton.textContent = t("settingsImport");
|
|
1858
|
+
var fileInput = document.createElement("input");
|
|
1859
|
+
fileInput.type = "file";
|
|
1860
|
+
fileInput.accept = ".json,application/json";
|
|
1861
|
+
fileInput.style.display = "none";
|
|
1862
|
+
transferSegment.appendChild(exportButton);
|
|
1863
|
+
transferSegment.appendChild(importButton);
|
|
1864
|
+
transferActions.appendChild(transferLabel);
|
|
1865
|
+
transferActions.appendChild(transferSegment);
|
|
1514
1866
|
var actions = document.createElement("div");
|
|
1515
1867
|
actions.className = SETTINGS_MODAL_CLASS + "-actions";
|
|
1516
1868
|
var resetButton = document.createElement("button");
|
|
@@ -1526,7 +1878,16 @@ window.__ModuleLoader__.load({
|
|
|
1526
1878
|
actions.appendChild(resetButton);
|
|
1527
1879
|
actions.appendChild(closeButton);
|
|
1528
1880
|
actions.appendChild(saveButton);
|
|
1529
|
-
|
|
1881
|
+
var utilities = document.createElement("div");
|
|
1882
|
+
utilities.className = SETTINGS_MODAL_CLASS + "-utilities";
|
|
1883
|
+
utilities.appendChild(foldActions);
|
|
1884
|
+
utilities.appendChild(transferActions);
|
|
1885
|
+
var footer = document.createElement("div");
|
|
1886
|
+
footer.className = SETTINGS_MODAL_CLASS + "-footer";
|
|
1887
|
+
footer.appendChild(utilities);
|
|
1888
|
+
footer.appendChild(actions);
|
|
1889
|
+
panel.appendChild(footer);
|
|
1890
|
+
panel.appendChild(fileInput);
|
|
1530
1891
|
overlay.appendChild(panel);
|
|
1531
1892
|
var previousFocus = document.activeElement;
|
|
1532
1893
|
var releaseFocus = null;
|
|
@@ -1555,6 +1916,48 @@ window.__ModuleLoader__.load({
|
|
|
1555
1916
|
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
1556
1917
|
return Math.min(20, Math.max(10, Math.round(value)));
|
|
1557
1918
|
}
|
|
1919
|
+
function importSettingsFile(file) {
|
|
1920
|
+
file.text().then((text) => {
|
|
1921
|
+
var parsed = JSON.parse(text);
|
|
1922
|
+
var source = parsed !== null && typeof parsed === "object" && parsed.settings !== null && typeof parsed.settings === "object" ? parsed.settings : parsed;
|
|
1923
|
+
if (source === null || typeof source !== "object") throw new Error("\u8BBE\u7F6E\u683C\u5F0F\u65E0\u6548");
|
|
1924
|
+
var font = Number(source.codeFontSize);
|
|
1925
|
+
if (Number.isFinite(font)) {
|
|
1926
|
+
fontInput.value = font > 0 ? String(Math.min(20, Math.max(10, Math.round(font)))) : "";
|
|
1927
|
+
}
|
|
1928
|
+
for (const [key, input, max] of [
|
|
1929
|
+
["foldCode", foldCode, 4e3],
|
|
1930
|
+
["foldTable", foldTable, 4e3],
|
|
1931
|
+
["foldDiagram", foldDiagram, 4e3],
|
|
1932
|
+
["freezeRows", freezeRowsInput, 5],
|
|
1933
|
+
["freezeCols", freezeColsInput, 5]
|
|
1934
|
+
]) {
|
|
1935
|
+
var value = Number(source[key]);
|
|
1936
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
1937
|
+
input.value = String(Math.min(max, Math.round(value)));
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
if (source.diagramView === "code" || source.diagramView === "diagram") {
|
|
1941
|
+
viewSelect.value = source.diagramView;
|
|
1942
|
+
}
|
|
1943
|
+
var theme = parsed !== null && typeof parsed === "object" ? parsed.mermaidTheme : null;
|
|
1944
|
+
if (typeof theme === "string" && THEME_ORDER.indexOf(theme) !== -1) {
|
|
1945
|
+
themeSelect.value = theme;
|
|
1946
|
+
}
|
|
1947
|
+
saveButton.click();
|
|
1948
|
+
}).catch(() => {
|
|
1949
|
+
flashButton(importButton, t("settingsImportFailed"));
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
exportButton.addEventListener("click", exportSettings);
|
|
1953
|
+
importButton.addEventListener("click", () => fileInput.click());
|
|
1954
|
+
fileInput.addEventListener("change", () => {
|
|
1955
|
+
var file = fileInput.files?.[0];
|
|
1956
|
+
if (file !== null && file !== void 0) importSettingsFile(file);
|
|
1957
|
+
fileInput.value = "";
|
|
1958
|
+
});
|
|
1959
|
+
expandAllButton.addEventListener("click", () => setAllFolds(true));
|
|
1960
|
+
collapseAllButton.addEventListener("click", () => setAllFolds(false));
|
|
1558
1961
|
resetButton.addEventListener("click", () => {
|
|
1559
1962
|
themeSelect.value = "blue";
|
|
1560
1963
|
fontInput.value = "";
|
|
@@ -1562,6 +1965,8 @@ window.__ModuleLoader__.load({
|
|
|
1562
1965
|
foldCode.value = String(DEFAULT_SETTINGS.foldCode);
|
|
1563
1966
|
foldTable.value = String(DEFAULT_SETTINGS.foldTable);
|
|
1564
1967
|
foldDiagram.value = String(DEFAULT_SETTINGS.foldDiagram);
|
|
1968
|
+
freezeRowsInput.value = String(DEFAULT_SETTINGS.freezeRows);
|
|
1969
|
+
freezeColsInput.value = String(DEFAULT_SETTINGS.freezeCols);
|
|
1565
1970
|
});
|
|
1566
1971
|
saveButton.addEventListener("click", () => {
|
|
1567
1972
|
var previous = readSettings();
|
|
@@ -1572,7 +1977,9 @@ window.__ModuleLoader__.load({
|
|
|
1572
1977
|
diagramView: viewSelect.value === "code" ? "code" : "diagram",
|
|
1573
1978
|
foldCode: readNumber(foldCode, DEFAULT_SETTINGS.foldCode),
|
|
1574
1979
|
foldTable: readNumber(foldTable, DEFAULT_SETTINGS.foldTable),
|
|
1575
|
-
foldDiagram: readNumber(foldDiagram, DEFAULT_SETTINGS.foldDiagram)
|
|
1980
|
+
foldDiagram: readNumber(foldDiagram, DEFAULT_SETTINGS.foldDiagram),
|
|
1981
|
+
freezeRows: readNumber(freezeRowsInput, DEFAULT_SETTINGS.freezeRows, 5),
|
|
1982
|
+
freezeCols: readNumber(freezeColsInput, DEFAULT_SETTINGS.freezeCols, 5)
|
|
1576
1983
|
};
|
|
1577
1984
|
saveSettings(next);
|
|
1578
1985
|
memoryTheme = nextTheme;
|
|
@@ -1582,6 +1989,7 @@ window.__ModuleLoader__.load({
|
|
|
1582
1989
|
} catch {
|
|
1583
1990
|
}
|
|
1584
1991
|
applyCodeFontSize();
|
|
1992
|
+
applyTableFreezeSettings();
|
|
1585
1993
|
refreshThemeButtons();
|
|
1586
1994
|
if (previousTheme !== nextTheme) redrawDiagrams();
|
|
1587
1995
|
if (previous.codeFontSize !== next.codeFontSize || previous.foldCode !== next.foldCode || previous.foldTable !== next.foldTable || previous.foldDiagram !== next.foldDiagram) {
|
|
@@ -1832,6 +2240,7 @@ window.__ModuleLoader__.load({
|
|
|
1832
2240
|
themeCache = null;
|
|
1833
2241
|
injectStyle();
|
|
1834
2242
|
applyCodeFontSize();
|
|
2243
|
+
applyTableFreezeSettings();
|
|
1835
2244
|
ensureSettingsButton();
|
|
1836
2245
|
var root = document.body ?? document.documentElement;
|
|
1837
2246
|
document.addEventListener("click", onDocumentClick, true);
|
|
@@ -1853,6 +2262,14 @@ window.__ModuleLoader__.load({
|
|
|
1853
2262
|
pending.clear();
|
|
1854
2263
|
rescanQueued = false;
|
|
1855
2264
|
for (const target of [...foldTargets]) resetFold(target);
|
|
2265
|
+
for (const table of [...tableFreezeTargets]) clearTableFreeze(table);
|
|
2266
|
+
tableFreezeTargets.clear();
|
|
2267
|
+
if (tableFreezeObserver !== null) {
|
|
2268
|
+
tableFreezeObserver.disconnect();
|
|
2269
|
+
tableFreezeObserver = null;
|
|
2270
|
+
}
|
|
2271
|
+
tableFreezeState = /* @__PURE__ */ new WeakMap();
|
|
2272
|
+
for (const table of document.querySelectorAll(TABLE)) clearTableFreeze(table);
|
|
1856
2273
|
observer.disconnect();
|
|
1857
2274
|
document.removeEventListener("click", onDocumentClick, true);
|
|
1858
2275
|
for (const node of injected) node.remove();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-md-plus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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",
|