@8btc/mditor 0.0.7 → 0.0.9

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.
@@ -1,21 +1,21 @@
1
- import {Constants} from "../constants";
2
- import {processHeading} from "../ir/process";
3
- import {processKeydown as irProcessKeydown} from "../ir/processKeydown";
4
- import {getMarkdown} from "../markdown/getMarkdown";
5
- import {previewImage} from "../preview/image";
6
- import {processHeading as processHeadingSV} from "../sv/process";
7
- import {processKeydown as mdProcessKeydown} from "../sv/processKeydown";
8
- import {setEditMode} from "../toolbar/EditMode";
9
- import {hidePanel} from "../toolbar/setToolbar";
10
- import {afterRenderEvent} from "../wysiwyg/afterRenderEvent";
11
- import {processKeydown} from "../wysiwyg/processKeydown";
12
- import {removeHeading, setHeading} from "../wysiwyg/setHeading";
13
- import {getEventName, isCtrl} from "./compatibility";
14
- import {execAfterRender, paste} from "./fixBrowserBehavior";
15
- import {getSelectText} from "./getSelectText";
16
- import {hasClosestByAttribute, hasClosestByMatchTag} from "./hasClosest";
17
- import {matchHotKey} from "./hotKey";
18
- import {getCursorPosition, getEditorRange} from "./selection";
1
+ import { Constants } from "../constants";
2
+ import { processHeading } from "../ir/process";
3
+ import { processKeydown as irProcessKeydown } from "../ir/processKeydown";
4
+ import { getMarkdown } from "../markdown/getMarkdown";
5
+ import { previewImage } from "../preview/image";
6
+ import { processHeading as processHeadingSV } from "../sv/process";
7
+ import { processKeydown as mdProcessKeydown } from "../sv/processKeydown";
8
+ import { setEditMode } from "../toolbar/EditMode";
9
+ import { hidePanel } from "../toolbar/setToolbar";
10
+ import { afterRenderEvent } from "../wysiwyg/afterRenderEvent";
11
+ import { processKeydown } from "../wysiwyg/processKeydown";
12
+ import { removeHeading, setHeading } from "../wysiwyg/setHeading";
13
+ import { getEventName, isCtrl } from "./compatibility";
14
+ import { execAfterRender, paste } from "./fixBrowserBehavior";
15
+ import { getSelectText } from "./getSelectText";
16
+ import { hasClosestByAttribute, hasClosestByMatchTag } from "./hasClosest";
17
+ import { matchHotKey } from "./hotKey";
18
+ import { getCursorPosition, getEditorRange } from "./selection";
19
19
 
20
20
  export const focusEvent = (vditor: IVditor, editorElement: HTMLElement) => {
21
21
  editorElement.addEventListener("focus", () => {
@@ -27,27 +27,40 @@ export const focusEvent = (vditor: IVditor, editorElement: HTMLElement) => {
27
27
  };
28
28
 
29
29
  export const dblclickEvent = (vditor: IVditor, editorElement: HTMLElement) => {
30
- editorElement.addEventListener("dblclick", (event: MouseEvent & { target: HTMLElement }) => {
31
- if (event.target.tagName === "IMG") {
32
- if (vditor.options.image.preview) {
33
- vditor.options.image.preview(event.target);
34
- } else if (vditor.options.image.isPreview) {
35
- previewImage(event.target as HTMLImageElement, vditor.options.lang, vditor.options.theme);
30
+ editorElement.addEventListener(
31
+ "dblclick",
32
+ (event: MouseEvent & { target: HTMLElement }) => {
33
+ if (event.target.tagName === "IMG") {
34
+ if (vditor.options.image.preview) {
35
+ vditor.options.image.preview(event.target);
36
+ } else if (vditor.options.image.isPreview) {
37
+ previewImage(
38
+ event.target as HTMLImageElement,
39
+ vditor.options.lang,
40
+ vditor.options.theme
41
+ );
42
+ }
36
43
  }
37
44
  }
38
- });
45
+ );
39
46
  };
40
47
 
41
48
  export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
42
49
  editorElement.addEventListener("blur", (event) => {
43
50
  if (vditor.currentMode === "ir") {
44
- const expandElement = vditor.ir.element.querySelector(".vditor-ir__node--expand");
51
+ const expandElement = vditor.ir.element.querySelector(
52
+ ".vditor-ir__node--expand"
53
+ );
45
54
  if (expandElement) {
46
55
  expandElement.classList.remove("vditor-ir__node--expand");
47
56
  }
48
- } else if (vditor.currentMode === "wysiwyg" &&
49
- !vditor.wysiwyg.selectPopover.contains(event.relatedTarget as HTMLElement)) {
50
- vditor.wysiwyg.hideComment();
57
+ } else if (
58
+ vditor.currentMode === "wysiwyg" &&
59
+ !vditor.wysiwyg.selectPopover.contains(
60
+ event.relatedTarget as HTMLElement
61
+ )
62
+ ) {
63
+ vditor.wysiwyg.hideSelectionPopover();
51
64
  }
52
65
  vditor[vditor.currentMode].range = getEditorRange(vditor);
53
66
  if (vditor.options.blur) {
@@ -59,14 +72,26 @@ export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
59
72
  export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
60
73
  editorElement.addEventListener("dragstart", (event) => {
61
74
  // 选中编辑器中的文字进行拖拽
62
- event.dataTransfer.setData(Constants.DROP_EDITOR, Constants.DROP_EDITOR);
75
+ event.dataTransfer.setData(
76
+ Constants.DROP_EDITOR,
77
+ Constants.DROP_EDITOR
78
+ );
63
79
  });
64
- editorElement.addEventListener("drop",
65
- (event: ClipboardEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
80
+ editorElement.addEventListener(
81
+ "drop",
82
+ (
83
+ event: ClipboardEvent & {
84
+ dataTransfer?: DataTransfer;
85
+ target: HTMLElement;
86
+ }
87
+ ) => {
66
88
  if (event.dataTransfer.getData(Constants.DROP_EDITOR)) {
67
89
  // 编辑器内选中文字拖拽
68
90
  execAfterRender(vditor);
69
- } else if (event.dataTransfer.types.includes("Files") || event.dataTransfer.types.includes("text/html")) {
91
+ } else if (
92
+ event.dataTransfer.types.includes("Files") ||
93
+ event.dataTransfer.types.includes("text/html")
94
+ ) {
70
95
  // 外部文件拖入编辑器中或者编辑器内选中文字拖拽
71
96
  paste(vditor, event, {
72
97
  pasteCode: (code: string) => {
@@ -74,197 +99,284 @@ export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
74
99
  },
75
100
  });
76
101
  }
77
- });
102
+ }
103
+ );
78
104
  };
79
105
 
80
- export const copyEvent =
81
- (vditor: IVditor, editorElement: HTMLElement, copy: (event: ClipboardEvent, vditor: IVditor) => void) => {
82
- editorElement.addEventListener("copy", (event: ClipboardEvent) => copy(event, vditor));
83
- };
106
+ export const copyEvent = (
107
+ vditor: IVditor,
108
+ editorElement: HTMLElement,
109
+ copy: (event: ClipboardEvent, vditor: IVditor) => void
110
+ ) => {
111
+ editorElement.addEventListener("copy", (event: ClipboardEvent) =>
112
+ copy(event, vditor)
113
+ );
114
+ };
84
115
 
85
- export const cutEvent =
86
- (vditor: IVditor, editorElement: HTMLElement, copy: (event: ClipboardEvent, vditor: IVditor) => void) => {
87
- editorElement.addEventListener("cut", (event: ClipboardEvent) => {
88
- copy(event, vditor);
89
- // 获取 comment
90
- if (vditor.options.comment.enable && vditor.currentMode === "wysiwyg") {
91
- vditor.wysiwyg.getComments(vditor);
92
- }
93
- document.execCommand("delete");
94
- });
95
- };
116
+ export const cutEvent = (
117
+ vditor: IVditor,
118
+ editorElement: HTMLElement,
119
+ copy: (event: ClipboardEvent, vditor: IVditor) => void
120
+ ) => {
121
+ editorElement.addEventListener("cut", (event: ClipboardEvent) => {
122
+ copy(event, vditor);
123
+ // 获取 comment
124
+ if (vditor.options.comment.enable && vditor.currentMode === "wysiwyg") {
125
+ vditor.wysiwyg.getComments(vditor);
126
+ }
127
+ document.execCommand("delete");
128
+ });
129
+ };
96
130
 
97
131
  export const scrollCenter = (vditor: IVditor) => {
98
132
  if (vditor.currentMode === "wysiwyg" && vditor.options.comment.enable) {
99
- vditor.options.comment.adjustTop(vditor.wysiwyg.getComments(vditor, true));
133
+ vditor.options.comment.adjustTop(
134
+ vditor.wysiwyg.getComments(vditor, true)
135
+ );
100
136
  }
101
137
  if (!vditor.options.typewriterMode) {
102
138
  return;
103
139
  }
104
140
  const editorElement = vditor[vditor.currentMode].element;
105
141
  const cursorTop = getCursorPosition(editorElement).top;
106
- if (vditor.options.height === "auto" && !vditor.element.classList.contains("vditor--fullscreen")) {
107
- window.scrollTo(window.scrollX,
108
- cursorTop + vditor.element.offsetTop + vditor.toolbar.element.offsetHeight - window.innerHeight / 2 + 10);
142
+ if (
143
+ vditor.options.height === "auto" &&
144
+ !vditor.element.classList.contains("vditor--fullscreen")
145
+ ) {
146
+ window.scrollTo(
147
+ window.scrollX,
148
+ cursorTop +
149
+ vditor.element.offsetTop +
150
+ vditor.toolbar.element.offsetHeight -
151
+ window.innerHeight / 2 +
152
+ 10
153
+ );
109
154
  }
110
- if (vditor.options.height !== "auto" || vditor.element.classList.contains("vditor--fullscreen")) {
111
- editorElement.scrollTop = cursorTop + editorElement.scrollTop - editorElement.clientHeight / 2 + 10;
155
+ if (
156
+ vditor.options.height !== "auto" ||
157
+ vditor.element.classList.contains("vditor--fullscreen")
158
+ ) {
159
+ editorElement.scrollTop =
160
+ cursorTop +
161
+ editorElement.scrollTop -
162
+ editorElement.clientHeight / 2 +
163
+ 10;
112
164
  }
113
165
  };
114
166
 
115
167
  export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
116
- editorElement.addEventListener("keydown", (event: KeyboardEvent & { target: HTMLElement }) => {
117
- if (!event.isComposing && vditor.options.keydown) {
118
- vditor.options.keydown(event);
119
- }
120
- // hint: 上下选择
121
- if ((vditor.options.hint.extend.length > 1 || vditor.toolbar.elements.emoji) &&
122
- vditor.hint.select(event, vditor)) {
123
- return;
124
- }
125
-
126
- // 重置 comment
127
- if (vditor.options.comment.enable && vditor.currentMode === "wysiwyg" &&
128
- (event.key === "Backspace" || matchHotKey("⌘X", event))) {
129
- vditor.wysiwyg.getComments(vditor);
130
- }
131
-
132
- if (vditor.currentMode === "sv") {
133
- if (mdProcessKeydown(vditor, event)) {
134
- return;
168
+ editorElement.addEventListener(
169
+ "keydown",
170
+ (event: KeyboardEvent & { target: HTMLElement }) => {
171
+ if (!event.isComposing && vditor.options.keydown) {
172
+ vditor.options.keydown(event);
135
173
  }
136
- } else if (vditor.currentMode === "wysiwyg") {
137
- if (processKeydown(vditor, event)) {
174
+ // hint: 上下选择
175
+ if (
176
+ (vditor.options.hint.extend.length > 1 ||
177
+ vditor.toolbar.elements.emoji) &&
178
+ vditor.hint.select(event, vditor)
179
+ ) {
138
180
  return;
139
181
  }
140
- } else if (vditor.currentMode === "ir") {
141
- if (irProcessKeydown(vditor, event)) {
142
- return;
182
+
183
+ // 重置 comment
184
+ if (
185
+ vditor.options.comment.enable &&
186
+ vditor.currentMode === "wysiwyg" &&
187
+ (event.key === "Backspace" || matchHotKey("⌘X", event))
188
+ ) {
189
+ vditor.wysiwyg.getComments(vditor);
143
190
  }
144
- }
145
191
 
146
- if (vditor.options.ctrlEnter && matchHotKey("⌘Enter", event)) {
147
- vditor.options.ctrlEnter(getMarkdown(vditor));
148
- event.preventDefault();
149
- return;
150
- }
192
+ if (vditor.currentMode === "sv") {
193
+ if (mdProcessKeydown(vditor, event)) {
194
+ return;
195
+ }
196
+ } else if (vditor.currentMode === "wysiwyg") {
197
+ if (processKeydown(vditor, event)) {
198
+ return;
199
+ }
200
+ } else if (vditor.currentMode === "ir") {
201
+ if (irProcessKeydown(vditor, event)) {
202
+ return;
203
+ }
204
+ }
151
205
 
152
- // undo
153
- if (matchHotKey("⌘Z", event) && !vditor.toolbar.elements.undo) {
154
- vditor.undo.undo(vditor);
155
- event.preventDefault();
156
- return;
157
- }
206
+ if (vditor.options.ctrlEnter && matchHotKey("⌘Enter", event)) {
207
+ vditor.options.ctrlEnter(getMarkdown(vditor));
208
+ event.preventDefault();
209
+ return;
210
+ }
158
211
 
159
- // redo
160
- if (matchHotKey("⌘Y", event) && !vditor.toolbar.elements.redo) {
161
- vditor.undo.redo(vditor);
162
- event.preventDefault();
163
- return;
164
- }
212
+ // undo
213
+ if (matchHotKey("⌘Z", event) && !vditor.toolbar.elements.undo) {
214
+ vditor.undo.undo(vditor);
215
+ event.preventDefault();
216
+ return;
217
+ }
165
218
 
166
- // esc
167
- if (event.key === "Escape") {
168
- if (vditor.hint.element.style.display === "block") {
169
- vditor.hint.element.style.display = "none";
170
- } else if (vditor.options.esc && !event.isComposing) {
171
- vditor.options.esc(getMarkdown(vditor));
219
+ // redo
220
+ if (matchHotKey("⌘Y", event) && !vditor.toolbar.elements.redo) {
221
+ vditor.undo.redo(vditor);
222
+ event.preventDefault();
223
+ return;
172
224
  }
173
- event.preventDefault();
174
- return;
175
- }
176
225
 
177
- // h1 - h6 hotkey
178
- if (isCtrl(event) && event.altKey && !event.shiftKey && /^Digit[1-6]$/.test(event.code)) {
179
- if (vditor.currentMode === "wysiwyg") {
180
- const tagName = event.code.replace("Digit", "H");
181
- if (hasClosestByMatchTag(getSelection().getRangeAt(0).startContainer, tagName)) {
182
- removeHeading(vditor);
183
- } else {
184
- setHeading(vditor, tagName);
226
+ // esc
227
+ if (event.key === "Escape") {
228
+ if (vditor.hint.element.style.display === "block") {
229
+ vditor.hint.element.style.display = "none";
230
+ } else if (vditor.options.esc && !event.isComposing) {
231
+ vditor.options.esc(getMarkdown(vditor));
185
232
  }
186
- afterRenderEvent(vditor);
187
- } else if (vditor.currentMode === "sv") {
188
- processHeadingSV(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
189
- } else if (vditor.currentMode === "ir") {
190
- processHeading(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
233
+ event.preventDefault();
234
+ return;
191
235
  }
192
- event.preventDefault();
193
- return true;
194
- }
195
236
 
196
- // toggle edit mode
197
- if (isCtrl(event) && event.altKey && !event.shiftKey && /^Digit[7-9]$/.test(event.code)) {
198
- if (event.code === "Digit7") {
199
- setEditMode(vditor, "wysiwyg", event);
200
- } else if (event.code === "Digit8") {
201
- setEditMode(vditor, "ir", event);
202
- } else if (event.code === "Digit9") {
203
- setEditMode(vditor, "sv", event);
237
+ // h1 - h6 hotkey
238
+ if (
239
+ isCtrl(event) &&
240
+ event.altKey &&
241
+ !event.shiftKey &&
242
+ /^Digit[1-6]$/.test(event.code)
243
+ ) {
244
+ if (vditor.currentMode === "wysiwyg") {
245
+ const tagName = event.code.replace("Digit", "H");
246
+ if (
247
+ hasClosestByMatchTag(
248
+ getSelection().getRangeAt(0).startContainer,
249
+ tagName
250
+ )
251
+ ) {
252
+ removeHeading(vditor);
253
+ } else {
254
+ setHeading(vditor, tagName);
255
+ }
256
+ afterRenderEvent(vditor);
257
+ } else if (vditor.currentMode === "sv") {
258
+ processHeadingSV(
259
+ vditor,
260
+ "#".repeat(
261
+ parseInt(event.code.replace("Digit", ""), 10)
262
+ ) + " "
263
+ );
264
+ } else if (vditor.currentMode === "ir") {
265
+ processHeading(
266
+ vditor,
267
+ "#".repeat(
268
+ parseInt(event.code.replace("Digit", ""), 10)
269
+ ) + " "
270
+ );
271
+ }
272
+ event.preventDefault();
273
+ return true;
204
274
  }
205
- return true;
206
- }
207
275
 
208
- // toolbar action
209
- vditor.options.toolbar.find((menuItem: IMenuItem) => {
210
- if (!menuItem.hotkey || menuItem.toolbar) {
211
- if (menuItem.toolbar) {
212
- const sub = menuItem.toolbar.find((subMenuItem: IMenuItem) => {
213
- if (!subMenuItem.hotkey) {
214
- return false;
215
- }
216
- if (matchHotKey(subMenuItem.hotkey, event)) {
217
- vditor.toolbar.elements[subMenuItem.name].children[0]
218
- .dispatchEvent(new CustomEvent(getEventName()));
219
- event.preventDefault();
220
- return true;
221
- }
222
- });
223
- return sub ? true : false;
276
+ // toggle edit mode
277
+ if (
278
+ isCtrl(event) &&
279
+ event.altKey &&
280
+ !event.shiftKey &&
281
+ /^Digit[7-9]$/.test(event.code)
282
+ ) {
283
+ if (event.code === "Digit7") {
284
+ setEditMode(vditor, "wysiwyg", event);
285
+ } else if (event.code === "Digit8") {
286
+ setEditMode(vditor, "ir", event);
287
+ } else if (event.code === "Digit9") {
288
+ setEditMode(vditor, "sv", event);
224
289
  }
225
- return false;
226
- }
227
- if (matchHotKey(menuItem.hotkey, event)) {
228
- vditor.toolbar.elements[menuItem.name].children[0].dispatchEvent(new CustomEvent(getEventName()));
229
- event.preventDefault();
230
290
  return true;
231
291
  }
232
- });
233
- });
292
+
293
+ // toolbar action
294
+ vditor.options.toolbar.find((menuItem: IMenuItem) => {
295
+ if (!menuItem.hotkey || menuItem.toolbar) {
296
+ if (menuItem.toolbar) {
297
+ const sub = menuItem.toolbar.find(
298
+ (subMenuItem: IMenuItem) => {
299
+ if (!subMenuItem.hotkey) {
300
+ return false;
301
+ }
302
+ if (matchHotKey(subMenuItem.hotkey, event)) {
303
+ vditor.toolbar.elements[
304
+ subMenuItem.name
305
+ ].children[0].dispatchEvent(
306
+ new CustomEvent(getEventName())
307
+ );
308
+ event.preventDefault();
309
+ return true;
310
+ }
311
+ }
312
+ );
313
+ return sub ? true : false;
314
+ }
315
+ return false;
316
+ }
317
+ if (matchHotKey(menuItem.hotkey, event)) {
318
+ vditor.toolbar.elements[
319
+ menuItem.name
320
+ ].children[0].dispatchEvent(
321
+ new CustomEvent(getEventName())
322
+ );
323
+ event.preventDefault();
324
+ return true;
325
+ }
326
+ });
327
+ }
328
+ );
234
329
  };
235
330
 
236
331
  /**
237
- * 选区事件处理:根据选中内容触发回调与评论按钮展示
238
- * - 当启用评论模式且开启内联面板时,显示评论按钮面板
332
+ * 选区事件处理(含防抖):根据选中内容显示/隐藏选择浮窗
333
+ * - 受 options.selectionPopover.enable 控制
334
+ * - 防抖间隔使用 options.selectionPopover.debounceDelay(默认 150ms)
239
335
  */
240
336
  export const selectEvent = (vditor: IVditor, editorElement: HTMLElement) => {
241
- editorElement.addEventListener("selectstart", (event: Event & { target: HTMLElement }) => {
242
- editorElement.onmouseup = () => {
243
- setTimeout(() => { // 鼠标放开后 range 没有即时更新
244
- const selectText = getSelectText(vditor[vditor.currentMode].element);
245
- if (selectText.trim()) {
246
- if (vditor.currentMode === "wysiwyg" && vditor.options.comment.enable) {
247
- if (!hasClosestByAttribute(event.target, "data-type", "footnotes-block") &&
248
- !hasClosestByAttribute(event.target, "data-type", "link-ref-defs-block")) {
249
- if (vditor.options.inlinePopover?.enable) {
250
- vditor.wysiwyg.showComment();
251
- }
252
- } else {
253
- vditor.wysiwyg.hideComment();
254
- }
255
- }
256
- if (vditor.options.select) {
257
- vditor.options.select(selectText);
258
- }
259
- } else {
260
- if (vditor.currentMode === "wysiwyg" && vditor.options.comment.enable) {
261
- vditor.wysiwyg.hideComment();
262
- }
263
- if (typeof vditor.options.unSelect === 'function') {
264
- vditor.options.unSelect();
265
- }
337
+ let debounceTimer = 0;
338
+ const handleSelection = () => {
339
+ window.clearTimeout(debounceTimer);
340
+ const delay =
341
+ vditor.options.selectionPopover?.debounceDelay ?? 150;
342
+ debounceTimer = window.setTimeout(() => {
343
+ const selectText = getSelectText(
344
+ vditor[vditor.currentMode].element
345
+ );
346
+ if (selectText.trim()) {
347
+ if (
348
+ vditor.currentMode === "wysiwyg" &&
349
+ vditor.options.selectionPopover?.enable
350
+ ) {
351
+ vditor.wysiwyg.showSelectionPopover();
266
352
  }
267
- });
268
- };
353
+ if (vditor.options.select) {
354
+ vditor.options.select(selectText);
355
+ }
356
+ } else {
357
+ if (vditor.currentMode === "wysiwyg") {
358
+ vditor.wysiwyg.hideSelectionPopover();
359
+ }
360
+ if (typeof vditor.options.unSelect === "function") {
361
+ vditor.options.unSelect();
362
+ }
363
+ }
364
+ }, delay);
365
+ };
366
+
367
+ editorElement.addEventListener(
368
+ "selectstart",
369
+ (event: Event & { target: HTMLElement }) => {
370
+ editorElement.onmouseup = handleSelection;
371
+ }
372
+ );
373
+
374
+ editorElement.addEventListener("keyup", (event: KeyboardEvent) => {
375
+ if (event.shiftKey && (event.key.startsWith("Arrow") || event.key === "Home" || event.key === "End")) {
376
+ handleSelection();
377
+ } else if (!event.shiftKey && (event.key.startsWith("Arrow") || event.key === "Escape")) {
378
+ // Cancel selection or move cursor without selection
379
+ handleSelection();
380
+ }
269
381
  });
270
382
  };
@@ -24,30 +24,30 @@ export const processPasteCode = (html: string, text: string, type = "sv") => {
24
24
  // 适配单独粘贴公式,使用 $$ 包装
25
25
  const formulaRegex = new RegExp(
26
26
  "^(?:" +
27
- [
28
- // 原有:基础数学命令(求和、分式、三角函数等)
29
- /\\(sum|prod|frac|int|sqrt|lim|log|sin|cos|tan|displaystyle|textstyle|mathbb|mathbf|overline|underline|vec|hat|bar|cdot|leq|geq|neq|approx|to|xrightarrow|rightarrow|leftarrow|infty|partial|nabla)/
30
- .source,
31
- // 原有:公式环境(如\begin{align}、\begin{equation}等)
32
- /\\begin\{[^}]+\}/.source,
33
- // 原有:左右括号命令(如\left(、\right]等)
34
- /\\left|\\right/.source,
35
- // 原有:化学/物理单位命令(如\ce{、\pu{)
36
- /\\(ce|pu)\{/.source,
37
- // 新增:希腊字母命令(如\alpha、\beta、\Gamma等)
38
- /\\[a-zA-Z]+\b(?!\d)/.source,
39
- // 新增:带数字/符号的命令(如\H_2、\text{...}、\mathbf{...}等)
40
- /\\[a-zA-Z]+\d*\{/.source,
41
- // 新增:特殊符号命令(如\$、\%、\&等)
42
- /\\[^\w]/.source,
43
- // 新增:上下标相关命令(如\underset、\overset)
44
- /\\(underset|overset|substack|boxed|bbox)/.source,
45
- // 新增:矩阵/数组环境起始(如\begin{matrix})
46
- /\\begin\{(matrix|array|pmatrix|bmatrix|vmatrix)\}/.source,
47
- // 新增:其他数学函数(如 \operatorname)
48
- /\\operatorname/.source,
49
- ].join("|") +
50
- ")"
27
+ [
28
+ // 原有:基础数学命令(求和、分式、三角函数等)
29
+ /\\(sum|prod|frac|int|sqrt|lim|log|sin|cos|tan|displaystyle|textstyle|mathbb|mathbf|overline|underline|vec|hat|bar|cdot|leq|geq|neq|approx|to|xrightarrow|rightarrow|leftarrow|infty|partial|nabla)/
30
+ .source,
31
+ // 原有:公式环境(如\begin{align}、\begin{equation}等)
32
+ /\\begin\{[^}]+\}/.source,
33
+ // 原有:左右括号命令(如\left(、\right]等)
34
+ /\\left|\\right/.source,
35
+ // 原有:化学/物理单位命令(如\ce{、\pu{)
36
+ /\\(ce|pu)\{/.source,
37
+ // 新增:希腊字母命令(如\alpha、\beta、\Gamma等)
38
+ /\\[a-zA-Z]+\b(?!\d)/.source,
39
+ // 新增:带数字/符号的命令(如\H_2、\text{...}、\mathbf{...}等)
40
+ /\\[a-zA-Z]+\d*\{/.source,
41
+ // 新增:特殊符号命令(如\$、\%、\&等)
42
+ /\\[^\w]/.source,
43
+ // 新增:上下标相关命令(如\underset、\overset)
44
+ /\\(underset|overset|substack|boxed|bbox)/.source,
45
+ // 新增:矩阵/数组环境起始(如\begin{matrix})
46
+ /\\begin\{(matrix|array|pmatrix|bmatrix|vmatrix)\}/.source,
47
+ // 新增:其他数学函数(如 \operatorname)
48
+ /\\operatorname/.source,
49
+ ].join("|") +
50
+ ")"
51
51
  );
52
52
 
53
53
  if (formulaRegex.test(trimmedText)) {
@@ -197,6 +197,25 @@ export const processCodeRender = (
197
197
  cdn: vditor.options.cdn,
198
198
  math: vditor.options.preview.math,
199
199
  });
200
+ } else if (language === "selection") {
201
+ // 渲染文件选择标签
202
+ const content = previewPanel.textContent.trim();
203
+ const lines = content.split('\n');
204
+ const tags = lines.map(line => {
205
+ const match = line.trim().match(/^(.+?)\s+(\d+-\d+)$/);
206
+ if (match) {
207
+ const [, filename, lineRange] = match;
208
+ return `<div class="vditor-selection-tag">
209
+ <span class="vditor-selection-tag__file">${filename}</span>
210
+ <span class="vditor-selection-tag__lines">${lineRange}</span>
211
+ </div>`;
212
+ }
213
+ return '';
214
+ }).filter(tag => tag).join('');
215
+
216
+ if (tags) {
217
+ previewPanel.innerHTML = tags;
218
+ }
200
219
  } else {
201
220
  const cRender = vditor.options.customRenders.find((item) => {
202
221
  if (item.language === language) {
@@ -43,7 +43,7 @@ export const afterRenderEvent = (
43
43
  }
44
44
 
45
45
  try {
46
- if (vditor.options.lineNumber) {
46
+ if (vditor.options.lineNumber?.enable) {
47
47
  attachLineNumbersToBlocksThrottled(
48
48
  vditor.wysiwyg.element,
49
49
  text