miyuan-editor 0.0.3

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.
Files changed (58) hide show
  1. package/README.md +525 -0
  2. package/dist/core/index.cjs.js +40 -0
  3. package/dist/core/index.esm.js +4 -0
  4. package/dist/dist-5Q_Z9Ell.js +6390 -0
  5. package/dist/dist-5Q_Z9Ell.js.map +1 -0
  6. package/dist/dist-CMM6n8DO.cjs +4629 -0
  7. package/dist/dist-CMM6n8DO.cjs.map +1 -0
  8. package/dist/dist-CRSJDo2G.cjs +6617 -0
  9. package/dist/dist-CRSJDo2G.cjs.map +1 -0
  10. package/dist/dist-CZw77IJK.js +4612 -0
  11. package/dist/dist-CZw77IJK.js.map +1 -0
  12. package/dist/dist-CnVrDtsI.js +556 -0
  13. package/dist/dist-CnVrDtsI.js.map +1 -0
  14. package/dist/dist-rItBfhNb.cjs +591 -0
  15. package/dist/dist-rItBfhNb.cjs.map +1 -0
  16. package/dist/export-utils-CYaNoyVg.cjs +167 -0
  17. package/dist/export-utils-CYaNoyVg.cjs.map +1 -0
  18. package/dist/export-utils-DN0Gu8Vu.js +144 -0
  19. package/dist/export-utils-DN0Gu8Vu.js.map +1 -0
  20. package/dist/extension-BPFuYyzN.cjs +338 -0
  21. package/dist/extension-BPFuYyzN.cjs.map +1 -0
  22. package/dist/extension-Cl6x5MDR.js +321 -0
  23. package/dist/extension-Cl6x5MDR.js.map +1 -0
  24. package/dist/extensions/index.cjs.js +3462 -0
  25. package/dist/extensions/index.cjs.js.map +1 -0
  26. package/dist/extensions/index.esm.js +3412 -0
  27. package/dist/extensions/index.esm.js.map +1 -0
  28. package/dist/prompt-B4AOP8f_.js +24143 -0
  29. package/dist/prompt-B4AOP8f_.js.map +1 -0
  30. package/dist/prompt-CGLw2O21.cjs +25530 -0
  31. package/dist/prompt-CGLw2O21.cjs.map +1 -0
  32. package/dist/react/index.cjs.js +839 -0
  33. package/dist/react/index.cjs.js.map +1 -0
  34. package/dist/react/index.esm.js +820 -0
  35. package/dist/react/index.esm.js.map +1 -0
  36. package/dist/shortcut-panel-BskGXV8n.js +49468 -0
  37. package/dist/shortcut-panel-BskGXV8n.js.map +1 -0
  38. package/dist/shortcut-panel-yP4RPTFt.cjs +49563 -0
  39. package/dist/shortcut-panel-yP4RPTFt.cjs.map +1 -0
  40. package/dist/toc-extension-BESc0uEW.js +150 -0
  41. package/dist/toc-extension-BESc0uEW.js.map +1 -0
  42. package/dist/toc-extension-SRvSuskn.cjs +173 -0
  43. package/dist/toc-extension-SRvSuskn.cjs.map +1 -0
  44. package/dist/toolbar-config-Cgc9mV2v.js +243 -0
  45. package/dist/toolbar-config-Cgc9mV2v.js.map +1 -0
  46. package/dist/toolbar-config-Cjt_fPMi.cjs +260 -0
  47. package/dist/toolbar-config-Cjt_fPMi.cjs.map +1 -0
  48. package/dist/ui/index.cjs.js +18 -0
  49. package/dist/ui/index.esm.js +3 -0
  50. package/dist/vue/index.cjs.js +323 -0
  51. package/dist/vue/index.cjs.js.map +1 -0
  52. package/dist/vue/index.esm.js +307 -0
  53. package/dist/vue/index.esm.js.map +1 -0
  54. package/dist/vue2/index.cjs.js +323 -0
  55. package/dist/vue2/index.cjs.js.map +1 -0
  56. package/dist/vue2/index.esm.js +307 -0
  57. package/dist/vue2/index.esm.js.map +1 -0
  58. package/package.json +116 -0
@@ -0,0 +1,3462 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../prompt-CGLw2O21.cjs");
3
+ const require_dist = require("../dist-CRSJDo2G.cjs");
4
+ const require_dist$1 = require("../dist-CMM6n8DO.cjs");
5
+ const require_dist$2 = require("../dist-rItBfhNb.cjs");
6
+ const require_toc_extension = require("../toc-extension-SRvSuskn.cjs");
7
+ //#region src/extensions/doc-extension.ts
8
+ /**
9
+ * DocExtension: the root document node.
10
+ */
11
+ function DocExtension() {
12
+ return {
13
+ name: "doc",
14
+ type: "extension",
15
+ addNodes() {
16
+ return { doc: { content: "block+" } };
17
+ }
18
+ };
19
+ }
20
+ //#endregion
21
+ //#region src/extensions/text-paragraph-extension.ts
22
+ var MAX_INDENT = 7;
23
+ var INDENT_STEP = 2;
24
+ /**
25
+ * TextExtension: the inline text leaf node.
26
+ */
27
+ function TextExtension() {
28
+ return {
29
+ name: "text",
30
+ type: "extension",
31
+ addNodes() {
32
+ return { text: { group: "inline" } };
33
+ }
34
+ };
35
+ }
36
+ /** Helper: compute inline style from attrs (textAlign + indent + lineHeight) */
37
+ function blockStyle(attrs) {
38
+ const parts = [];
39
+ const align = attrs.textAlign;
40
+ if (align && align !== "left") parts.push(`text-align: ${align}`);
41
+ const indent = attrs.indent || 0;
42
+ if (indent > 0) parts.push(`margin-left: ${indent * INDENT_STEP}em`);
43
+ const lineHeight = attrs.lineHeight;
44
+ if (lineHeight) parts.push(`line-height: ${lineHeight}`);
45
+ return parts.length ? parts.join("; ") : void 0;
46
+ }
47
+ /** Helper: parse text-align from DOM element */
48
+ function parseAlign(dom) {
49
+ const match = (dom.getAttribute("style") || "").match(/text-align:\s*(left|center|right)/);
50
+ if (match) return match[1];
51
+ return null;
52
+ }
53
+ /** Helper: parse indent level from DOM element */
54
+ function parseIndent(dom) {
55
+ const match = (dom.getAttribute("style") || "").match(/margin-left:\s*([\d.]+)em/);
56
+ if (!match) return 0;
57
+ const em = parseFloat(match[1]);
58
+ return Math.round(em / INDENT_STEP);
59
+ }
60
+ /** Helper: parse line-height from DOM element */
61
+ function parseLineHeight(dom) {
62
+ const match = (dom.getAttribute("style") || "").match(/line-height:\s*([\d.]+)/);
63
+ if (!match) return null;
64
+ return parseFloat(match[1]);
65
+ }
66
+ /**
67
+ * ParagraphExtension: the basic block node for text content.
68
+ * Supports textAlign attribute.
69
+ */
70
+ function ParagraphExtension() {
71
+ return {
72
+ name: "paragraph",
73
+ type: "node",
74
+ addNodes() {
75
+ return { paragraph: {
76
+ group: "block",
77
+ content: "inline*",
78
+ attrs: {
79
+ textAlign: { default: null },
80
+ indent: { default: 0 },
81
+ lineHeight: { default: null }
82
+ },
83
+ toDOM(node) {
84
+ const style = blockStyle(node.attrs);
85
+ return style ? [
86
+ "p",
87
+ { style },
88
+ 0
89
+ ] : ["p", 0];
90
+ },
91
+ parseDOM: [{
92
+ tag: "p",
93
+ getAttrs(dom) {
94
+ const el = dom;
95
+ return {
96
+ textAlign: parseAlign(el),
97
+ indent: parseIndent(el),
98
+ lineHeight: parseLineHeight(el)
99
+ };
100
+ }
101
+ }]
102
+ } };
103
+ },
104
+ addCommands() {
105
+ return {
106
+ setTextAlign: (...args) => {
107
+ const align = args[0];
108
+ return setTextAlignCommand(align, ["paragraph", "heading"]);
109
+ },
110
+ indent: () => indentCommand(1, ["paragraph", "heading"]),
111
+ outdent: () => indentCommand(-1, ["paragraph", "heading"])
112
+ };
113
+ },
114
+ addKeyboardShortcuts() {
115
+ return {
116
+ Tab: indentCommand(1, ["paragraph", "heading"]),
117
+ "Shift-Tab": indentCommand(-1, ["paragraph", "heading"])
118
+ };
119
+ }
120
+ };
121
+ }
122
+ /**
123
+ * Create a command that sets textAlign on the given block types.
124
+ */
125
+ function setTextAlignCommand(align, nodeNames) {
126
+ return (state, dispatch) => {
127
+ const { from, to } = state.selection;
128
+ let applicable = false;
129
+ state.doc.nodesBetween(from, to, (node, pos) => {
130
+ if (!nodeNames.includes(node.type.name)) return;
131
+ applicable = true;
132
+ if (dispatch) {
133
+ const tr = state.tr;
134
+ const newAlign = node.attrs.textAlign === align ? null : align;
135
+ tr.setNodeMarkup(pos, null, {
136
+ ...node.attrs,
137
+ textAlign: newAlign
138
+ });
139
+ dispatch(tr);
140
+ }
141
+ });
142
+ return applicable;
143
+ };
144
+ }
145
+ /**
146
+ * Create a command that changes indent level by delta (+1 or -1).
147
+ */
148
+ function indentCommand(delta, nodeNames) {
149
+ return (state, dispatch) => {
150
+ const { from, to } = state.selection;
151
+ let applicable = false;
152
+ state.doc.nodesBetween(from, to, (node, pos) => {
153
+ if (!nodeNames.includes(node.type.name)) return;
154
+ const current = node.attrs.indent || 0;
155
+ const next = Math.max(0, Math.min(MAX_INDENT, current + delta));
156
+ if (next === current) return;
157
+ applicable = true;
158
+ if (dispatch) {
159
+ const tr = state.tr;
160
+ tr.setNodeMarkup(pos, null, {
161
+ ...node.attrs,
162
+ indent: next
163
+ });
164
+ dispatch(tr);
165
+ }
166
+ });
167
+ return applicable;
168
+ };
169
+ }
170
+ //#endregion
171
+ //#region src/extensions/mark-extensions.ts
172
+ function BoldExtension() {
173
+ return {
174
+ name: "bold",
175
+ type: "mark",
176
+ addMarks() {
177
+ return { bold: {
178
+ toDOM() {
179
+ return ["strong", 0];
180
+ },
181
+ parseDOM: [{ tag: "strong" }, {
182
+ tag: "b",
183
+ getAttrs: (node) => node.style.fontWeight !== "normal" && null
184
+ }]
185
+ } };
186
+ },
187
+ addCommands() {
188
+ return { bold: () => (state, dispatch) => {
189
+ const mt = state.schema.marks["bold"];
190
+ if (!mt) return false;
191
+ return require_dist.toggleMark(mt)(state, dispatch);
192
+ } };
193
+ },
194
+ addKeyboardShortcuts() {
195
+ return { "Mod-b": (state, dispatch) => {
196
+ const mt = state.schema.marks["bold"];
197
+ if (!mt) return false;
198
+ return require_dist.toggleMark(mt)(state, dispatch);
199
+ } };
200
+ }
201
+ };
202
+ }
203
+ function ItalicExtension() {
204
+ return {
205
+ name: "italic",
206
+ type: "mark",
207
+ addMarks() {
208
+ return { italic: {
209
+ toDOM() {
210
+ return ["em", 0];
211
+ },
212
+ parseDOM: [{ tag: "em" }, { tag: "i" }]
213
+ } };
214
+ },
215
+ addCommands() {
216
+ return { italic: () => (state, dispatch) => {
217
+ const mt = state.schema.marks["italic"];
218
+ if (!mt) return false;
219
+ return require_dist.toggleMark(mt)(state, dispatch);
220
+ } };
221
+ },
222
+ addKeyboardShortcuts() {
223
+ return { "Mod-i": (state, dispatch) => {
224
+ const mt = state.schema.marks["italic"];
225
+ if (!mt) return false;
226
+ return require_dist.toggleMark(mt)(state, dispatch);
227
+ } };
228
+ }
229
+ };
230
+ }
231
+ function CodeExtension() {
232
+ return {
233
+ name: "code",
234
+ type: "mark",
235
+ addMarks() {
236
+ return { code: {
237
+ excludes: "_",
238
+ toDOM() {
239
+ return ["code", 0];
240
+ },
241
+ parseDOM: [{ tag: "code" }]
242
+ } };
243
+ },
244
+ addCommands() {
245
+ return { code: () => (state, dispatch) => {
246
+ const mt = state.schema.marks["code"];
247
+ if (!mt) return false;
248
+ return require_dist.toggleMark(mt)(state, dispatch);
249
+ } };
250
+ },
251
+ addKeyboardShortcuts() {
252
+ return { "Mod-e": (state, dispatch) => {
253
+ const mt = state.schema.marks["code"];
254
+ if (!mt) return false;
255
+ return require_dist.toggleMark(mt)(state, dispatch);
256
+ } };
257
+ }
258
+ };
259
+ }
260
+ function StrikeExtension() {
261
+ return {
262
+ name: "strike",
263
+ type: "mark",
264
+ addMarks() {
265
+ return { strike: {
266
+ toDOM() {
267
+ return ["s", 0];
268
+ },
269
+ parseDOM: [
270
+ { tag: "s" },
271
+ { tag: "del" },
272
+ { tag: "strike" }
273
+ ]
274
+ } };
275
+ },
276
+ addCommands() {
277
+ return { strike: () => (state, dispatch) => {
278
+ const mt = state.schema.marks["strike"];
279
+ if (!mt) return false;
280
+ return require_dist.toggleMark(mt)(state, dispatch);
281
+ } };
282
+ }
283
+ };
284
+ }
285
+ function UnderlineExtension() {
286
+ return {
287
+ name: "underline",
288
+ type: "mark",
289
+ addMarks() {
290
+ return { underline: {
291
+ toDOM() {
292
+ return ["u", 0];
293
+ },
294
+ parseDOM: [{ tag: "u" }]
295
+ } };
296
+ },
297
+ addCommands() {
298
+ return { underline: () => (state, dispatch) => {
299
+ const mt = state.schema.marks["underline"];
300
+ if (!mt) return false;
301
+ return require_dist.toggleMark(mt)(state, dispatch);
302
+ } };
303
+ },
304
+ addKeyboardShortcuts() {
305
+ return { "Mod-u": (state, dispatch) => {
306
+ const mt = state.schema.marks["underline"];
307
+ if (!mt) return false;
308
+ return require_dist.toggleMark(mt)(state, dispatch);
309
+ } };
310
+ }
311
+ };
312
+ }
313
+ function LinkExtension() {
314
+ return {
315
+ name: "link",
316
+ type: "mark",
317
+ addMarks() {
318
+ return { link: {
319
+ attrs: {
320
+ href: {},
321
+ target: { default: "_blank" }
322
+ },
323
+ inclusive: false,
324
+ toDOM(mark) {
325
+ const { href, target } = mark.attrs;
326
+ return [
327
+ "a",
328
+ {
329
+ href,
330
+ target
331
+ },
332
+ 0
333
+ ];
334
+ },
335
+ parseDOM: [{
336
+ tag: "a[href]",
337
+ getAttrs(dom) {
338
+ const el = dom;
339
+ return {
340
+ href: el.getAttribute("href"),
341
+ target: el.getAttribute("target")
342
+ };
343
+ }
344
+ }]
345
+ } };
346
+ },
347
+ addCommands() {
348
+ return { setLink: (...args) => {
349
+ const attrs = args[0] ?? {};
350
+ return (state, dispatch) => {
351
+ const mt = state.schema.marks["link"];
352
+ if (!mt) return false;
353
+ return require_dist.toggleMark(mt, attrs)(state, dispatch);
354
+ };
355
+ } };
356
+ }
357
+ };
358
+ }
359
+ var DEFAULT_COLORS = [
360
+ "#000000",
361
+ "#434343",
362
+ "#666666",
363
+ "#999999",
364
+ "#b7b7b7",
365
+ "#cccccc",
366
+ "#d9d9d9",
367
+ "#ffffff",
368
+ "#980000",
369
+ "#ff0000",
370
+ "#ff9900",
371
+ "#ffff00",
372
+ "#00ff00",
373
+ "#00ffff",
374
+ "#4a86e8",
375
+ "#0000ff",
376
+ "#9900ff",
377
+ "#ff00ff",
378
+ "#f4cccc",
379
+ "#fce5cd",
380
+ "#fff2cc",
381
+ "#d9ead3",
382
+ "#d0e0e3",
383
+ "#c9daf8",
384
+ "#e4d7f5",
385
+ "#ea9999",
386
+ "#f9cb9c",
387
+ "#ffe599",
388
+ "#b6d7a8",
389
+ "#a2c4c9",
390
+ "#a4c2f4",
391
+ "#d5a6bd"
392
+ ];
393
+ function TextColorExtension(config) {
394
+ return {
395
+ name: "textColor",
396
+ type: "mark",
397
+ options: { colors: config?.colors ?? DEFAULT_COLORS },
398
+ addMarks() {
399
+ return { textColor: {
400
+ attrs: { color: {} },
401
+ toDOM(mark) {
402
+ return [
403
+ "span",
404
+ { style: `color: ${mark.attrs.color}` },
405
+ 0
406
+ ];
407
+ },
408
+ parseDOM: [{
409
+ tag: "span[style*=\"color\"]",
410
+ getAttrs(dom) {
411
+ const color = dom.style.color;
412
+ return color ? { color } : false;
413
+ }
414
+ }]
415
+ } };
416
+ },
417
+ addCommands() {
418
+ return { setTextColor: (...args) => {
419
+ const color = args[0];
420
+ return (state, dispatch) => {
421
+ const mt = state.schema.marks["textColor"];
422
+ if (!mt) return false;
423
+ const { from, to } = state.selection;
424
+ if (from === to) return false;
425
+ if (dispatch) {
426
+ let tr = state.tr;
427
+ tr = tr.removeMark(from, to, mt);
428
+ if (color) tr = tr.addMark(from, to, mt.create({ color }));
429
+ dispatch(tr);
430
+ }
431
+ return true;
432
+ };
433
+ } };
434
+ }
435
+ };
436
+ }
437
+ function BackgroundColorExtension(config) {
438
+ return {
439
+ name: "backgroundColor",
440
+ type: "mark",
441
+ options: { colors: config?.colors ?? DEFAULT_COLORS },
442
+ addMarks() {
443
+ return { backgroundColor: {
444
+ attrs: { color: {} },
445
+ toDOM(mark) {
446
+ return [
447
+ "span",
448
+ { style: `background-color: ${mark.attrs.color}` },
449
+ 0
450
+ ];
451
+ },
452
+ parseDOM: [{
453
+ tag: "span[style*=\"background-color\"]",
454
+ getAttrs(dom) {
455
+ const color = dom.style.backgroundColor;
456
+ return color ? { color } : false;
457
+ }
458
+ }]
459
+ } };
460
+ },
461
+ addCommands() {
462
+ return { setBackgroundColor: (...args) => {
463
+ const color = args[0];
464
+ return (state, dispatch) => {
465
+ const mt = state.schema.marks["backgroundColor"];
466
+ if (!mt) return false;
467
+ const { from, to } = state.selection;
468
+ if (from === to) return false;
469
+ if (dispatch) {
470
+ let tr = state.tr;
471
+ tr = tr.removeMark(from, to, mt);
472
+ if (color) tr = tr.addMark(from, to, mt.create({ color }));
473
+ dispatch(tr);
474
+ }
475
+ return true;
476
+ };
477
+ } };
478
+ }
479
+ };
480
+ }
481
+ var DEFAULT_FONT_SIZES = [
482
+ {
483
+ label: "12",
484
+ value: "12px"
485
+ },
486
+ {
487
+ label: "14",
488
+ value: "14px"
489
+ },
490
+ {
491
+ label: "16",
492
+ value: "16px"
493
+ },
494
+ {
495
+ label: "18",
496
+ value: "18px"
497
+ },
498
+ {
499
+ label: "20",
500
+ value: "20px"
501
+ },
502
+ {
503
+ label: "24",
504
+ value: "24px"
505
+ },
506
+ {
507
+ label: "28",
508
+ value: "28px"
509
+ },
510
+ {
511
+ label: "32",
512
+ value: "32px"
513
+ },
514
+ {
515
+ label: "36",
516
+ value: "36px"
517
+ },
518
+ {
519
+ label: "48",
520
+ value: "48px"
521
+ }
522
+ ];
523
+ function normalizeFontSize(value) {
524
+ const num = typeof value === "string" ? parseFloat(value) : value;
525
+ if (isNaN(num) || num <= 0) return "";
526
+ return `${Math.round(num)}px`;
527
+ }
528
+ function FontSizeExtension(config) {
529
+ return {
530
+ name: "fontSize",
531
+ type: "mark",
532
+ options: { sizes: config?.sizes ?? DEFAULT_FONT_SIZES },
533
+ addMarks() {
534
+ return { fontSize: {
535
+ attrs: { size: {} },
536
+ toDOM(mark) {
537
+ return [
538
+ "span",
539
+ { style: `font-size: ${normalizeFontSize(mark.attrs.size)}` },
540
+ 0
541
+ ];
542
+ },
543
+ parseDOM: [{
544
+ tag: "span[style*=\"font-size\"]",
545
+ getAttrs(dom) {
546
+ const raw = dom.style.fontSize;
547
+ const size = normalizeFontSize(raw);
548
+ return size ? { size } : false;
549
+ }
550
+ }]
551
+ } };
552
+ },
553
+ addCommands() {
554
+ return { setFontSize: (...args) => {
555
+ const size = normalizeFontSize(args[0]);
556
+ return (state, dispatch) => {
557
+ const mt = state.schema.marks["fontSize"];
558
+ if (!mt) return false;
559
+ const { from, to } = state.selection;
560
+ if (from === to) return false;
561
+ if (dispatch) {
562
+ let tr = state.tr;
563
+ tr = tr.removeMark(from, to, mt);
564
+ if (size) tr = tr.addMark(from, to, mt.create({ size }));
565
+ dispatch(tr);
566
+ }
567
+ return true;
568
+ };
569
+ } };
570
+ }
571
+ };
572
+ }
573
+ var DEFAULT_FONT_FAMILIES = [
574
+ {
575
+ label: "默认",
576
+ value: ""
577
+ },
578
+ {
579
+ label: "宋体",
580
+ value: "SimSun, serif"
581
+ },
582
+ {
583
+ label: "黑体",
584
+ value: "SimHei, sans-serif"
585
+ },
586
+ {
587
+ label: "微软雅黑",
588
+ value: "Microsoft YaHei, sans-serif"
589
+ },
590
+ {
591
+ label: "楷体",
592
+ value: "KaiTi, serif"
593
+ },
594
+ {
595
+ label: "Arial",
596
+ value: "Arial, sans-serif"
597
+ },
598
+ {
599
+ label: "Times New Roman",
600
+ value: "Times New Roman, serif"
601
+ },
602
+ {
603
+ label: "Courier New",
604
+ value: "Courier New, monospace"
605
+ },
606
+ {
607
+ label: "Georgia",
608
+ value: "Georgia, serif"
609
+ },
610
+ {
611
+ label: "Verdana",
612
+ value: "Verdana, sans-serif"
613
+ }
614
+ ];
615
+ function FontFamilyExtension(config) {
616
+ return {
617
+ name: "fontFamily",
618
+ type: "mark",
619
+ options: { families: config?.families ?? DEFAULT_FONT_FAMILIES },
620
+ addMarks() {
621
+ return { fontFamily: {
622
+ attrs: { family: {} },
623
+ toDOM(mark) {
624
+ return [
625
+ "span",
626
+ { style: `font-family: ${mark.attrs.family}` },
627
+ 0
628
+ ];
629
+ },
630
+ parseDOM: [{
631
+ tag: "span[style*=\"font-family\"]",
632
+ getAttrs(dom) {
633
+ const family = dom.style.fontFamily;
634
+ return family ? { family } : false;
635
+ }
636
+ }]
637
+ } };
638
+ },
639
+ addCommands() {
640
+ return { setFontFamily: (...args) => {
641
+ const family = args[0];
642
+ return (state, dispatch) => {
643
+ const mt = state.schema.marks["fontFamily"];
644
+ if (!mt) return false;
645
+ const { from, to } = state.selection;
646
+ if (from === to) return false;
647
+ if (dispatch) {
648
+ let tr = state.tr;
649
+ tr = tr.removeMark(from, to, mt);
650
+ if (family) tr = tr.addMark(from, to, mt.create({ family }));
651
+ dispatch(tr);
652
+ }
653
+ return true;
654
+ };
655
+ } };
656
+ },
657
+ addKeyboardShortcuts() {
658
+ return {};
659
+ },
660
+ addPlugins() {
661
+ return [];
662
+ }
663
+ };
664
+ }
665
+ //#endregion
666
+ //#region node_modules/.pnpm/prosemirror-inputrules@1.5.1/node_modules/prosemirror-inputrules/dist/index.js
667
+ /**
668
+ Input rules are regular expressions describing a piece of text
669
+ that, when typed, causes something to happen. This might be
670
+ changing two dashes into an emdash, wrapping a paragraph starting
671
+ with `"> "` into a blockquote, or something entirely different.
672
+ */
673
+ var InputRule = class {
674
+ /**
675
+ Create an input rule. The rule applies when the user typed
676
+ something and the text directly in front of the cursor matches
677
+ `match`, which should end with `$`.
678
+
679
+ The `handler` can be a string, in which case the matched text, or
680
+ the first matched group in the regexp, is replaced by that
681
+ string.
682
+
683
+ Or a it can be a function, which will be called with the match
684
+ array produced by
685
+ [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
686
+ as well as the start and end of the matched range, and which can
687
+ return a [transaction](https://prosemirror.net/docs/ref/#state.Transaction) that describes the
688
+ rule's effect, or null to indicate the input was not handled.
689
+ */
690
+ constructor(match, handler, options = {}) {
691
+ this.match = match;
692
+ this.match = match;
693
+ this.handler = typeof handler == "string" ? stringHandler(handler) : handler;
694
+ this.undoable = options.undoable !== false;
695
+ this.inCode = options.inCode || false;
696
+ this.inCodeMark = options.inCodeMark !== false;
697
+ }
698
+ };
699
+ function stringHandler(string) {
700
+ return function(state, match, start, end) {
701
+ let insert = string;
702
+ if (match[1]) {
703
+ let offset = match[0].lastIndexOf(match[1]);
704
+ insert += match[0].slice(offset + match[1].length);
705
+ start += offset;
706
+ let cutOff = start - end;
707
+ if (cutOff > 0) {
708
+ insert = match[0].slice(offset - cutOff, offset) + insert;
709
+ start = end;
710
+ }
711
+ }
712
+ return state.tr.insertText(insert, start, end);
713
+ };
714
+ }
715
+ var MAX_MATCH = 500;
716
+ /**
717
+ Create an input rules plugin. When enabled, it will cause text
718
+ input that matches any of the given rules to trigger the rule's
719
+ action.
720
+ */
721
+ function inputRules({ rules }) {
722
+ let plugin = new require_dist.Plugin({
723
+ state: {
724
+ init() {
725
+ return null;
726
+ },
727
+ apply(tr, prev) {
728
+ let stored = tr.getMeta(this);
729
+ if (stored) return stored;
730
+ return tr.selectionSet || tr.docChanged ? null : prev;
731
+ }
732
+ },
733
+ props: {
734
+ handleTextInput(view, from, to, text) {
735
+ return run(view, from, to, text, rules, plugin);
736
+ },
737
+ handleDOMEvents: { compositionend: (view) => {
738
+ setTimeout(() => {
739
+ let { $cursor } = view.state.selection;
740
+ if ($cursor) run(view, $cursor.pos, $cursor.pos, "", rules, plugin);
741
+ });
742
+ } }
743
+ },
744
+ isInputRules: true
745
+ });
746
+ return plugin;
747
+ }
748
+ function run(view, from, to, text, rules, plugin) {
749
+ if (view.composing) return false;
750
+ let state = view.state, $from = state.doc.resolve(from);
751
+ let textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - MAX_MATCH), $from.parentOffset, null, "") + text;
752
+ for (let i = 0; i < rules.length; i++) {
753
+ let rule = rules[i];
754
+ if (!rule.inCodeMark && $from.marks().some((m) => m.type.spec.code)) continue;
755
+ if ($from.parent.type.spec.code) {
756
+ if (!rule.inCode) continue;
757
+ } else if (rule.inCode === "only") continue;
758
+ let match = rule.match.exec(textBefore);
759
+ if (!match || match[0].length < text.length) continue;
760
+ let startPos = from - (match[0].length - text.length);
761
+ if (!rule.inCodeMark) {
762
+ let hasMark = false;
763
+ state.doc.nodesBetween(startPos, $from.pos, (node) => {
764
+ if (node.isInline && node.marks.some((m) => m.type.spec.code)) hasMark = true;
765
+ });
766
+ if (hasMark) continue;
767
+ }
768
+ let tr = rule.handler(state, match, startPos, to);
769
+ if (!tr) continue;
770
+ if (rule.undoable) tr.setMeta(plugin, {
771
+ transform: tr,
772
+ from,
773
+ to,
774
+ text
775
+ });
776
+ view.dispatch(tr);
777
+ return true;
778
+ }
779
+ return false;
780
+ }
781
+ new InputRule(/--$/, "—", { inCodeMark: false });
782
+ new InputRule(/\.\.\.$/, "…", { inCodeMark: false });
783
+ new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(")$/, "“", { inCodeMark: false });
784
+ new InputRule(/"$/, "”", { inCodeMark: false });
785
+ new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(')$/, "‘", { inCodeMark: false });
786
+ new InputRule(/'$/, "’", { inCodeMark: false });
787
+ //#endregion
788
+ //#region src/extensions/node-extensions.ts
789
+ /** Parse text-align from DOM element style attribute */
790
+ function parseAlignFromDOM(dom) {
791
+ const match = (dom.getAttribute("style") || "").match(/text-align:\s*(left|center|right)/);
792
+ return match ? match[1] : null;
793
+ }
794
+ /** Parse indent level from DOM element (margin-left in em) */
795
+ function parseIndentFromDOM(dom) {
796
+ const match = (dom.getAttribute("style") || "").match(/margin-left:\s*([\d.]+)em/);
797
+ if (!match) return 0;
798
+ return Math.round(parseFloat(match[1]) / 2);
799
+ }
800
+ /** Build inline style string from align + indent + lineHeight attrs */
801
+ function buildBlockStyle(attrs) {
802
+ const parts = [];
803
+ const align = attrs.textAlign;
804
+ if (align && align !== "left") parts.push(`text-align: ${align}`);
805
+ const indent = attrs.indent || 0;
806
+ if (indent > 0) parts.push(`margin-left: ${indent * 2}em`);
807
+ const lineHeight = attrs.lineHeight;
808
+ if (lineHeight) parts.push(`line-height: ${lineHeight}`);
809
+ return parts.length ? parts.join("; ") : void 0;
810
+ }
811
+ /** Helper: parse line-height from DOM element */
812
+ function parseLineHeightFromDOM(dom) {
813
+ const match = (dom.getAttribute("style") || "").match(/line-height:\s*([\d.]+)/);
814
+ if (!match) return null;
815
+ return parseFloat(match[1]);
816
+ }
817
+ function HeadingExtension() {
818
+ return {
819
+ name: "heading",
820
+ type: "node",
821
+ addNodes() {
822
+ return { heading: {
823
+ group: "block",
824
+ content: "inline*",
825
+ attrs: {
826
+ level: { default: 1 },
827
+ textAlign: { default: null },
828
+ indent: { default: 0 },
829
+ lineHeight: { default: null }
830
+ },
831
+ defining: true,
832
+ toDOM(node) {
833
+ const style = buildBlockStyle(node.attrs);
834
+ const tag = `h${node.attrs.level}`;
835
+ return style ? [
836
+ tag,
837
+ { style },
838
+ 0
839
+ ] : [tag, 0];
840
+ },
841
+ parseDOM: [
842
+ {
843
+ tag: "h1",
844
+ getAttrs: (dom) => ({
845
+ level: 1,
846
+ textAlign: parseAlignFromDOM(dom),
847
+ indent: parseIndentFromDOM(dom),
848
+ lineHeight: parseLineHeightFromDOM(dom)
849
+ })
850
+ },
851
+ {
852
+ tag: "h2",
853
+ getAttrs: (dom) => ({
854
+ level: 2,
855
+ textAlign: parseAlignFromDOM(dom),
856
+ indent: parseIndentFromDOM(dom),
857
+ lineHeight: parseLineHeightFromDOM(dom)
858
+ })
859
+ },
860
+ {
861
+ tag: "h3",
862
+ getAttrs: (dom) => ({
863
+ level: 3,
864
+ textAlign: parseAlignFromDOM(dom),
865
+ indent: parseIndentFromDOM(dom),
866
+ lineHeight: parseLineHeightFromDOM(dom)
867
+ })
868
+ },
869
+ {
870
+ tag: "h4",
871
+ getAttrs: (dom) => ({
872
+ level: 4,
873
+ textAlign: parseAlignFromDOM(dom),
874
+ indent: parseIndentFromDOM(dom),
875
+ lineHeight: parseLineHeightFromDOM(dom)
876
+ })
877
+ },
878
+ {
879
+ tag: "h5",
880
+ getAttrs: (dom) => ({
881
+ level: 5,
882
+ textAlign: parseAlignFromDOM(dom),
883
+ indent: parseIndentFromDOM(dom),
884
+ lineHeight: parseLineHeightFromDOM(dom)
885
+ })
886
+ },
887
+ {
888
+ tag: "h6",
889
+ getAttrs: (dom) => ({
890
+ level: 6,
891
+ textAlign: parseAlignFromDOM(dom),
892
+ indent: parseIndentFromDOM(dom),
893
+ lineHeight: parseLineHeightFromDOM(dom)
894
+ })
895
+ }
896
+ ]
897
+ } };
898
+ },
899
+ addCommands() {
900
+ return { heading: (...args) => {
901
+ const level = args[0] ?? 1;
902
+ return (state, dispatch) => {
903
+ const headingType = state.schema.nodes["heading"];
904
+ const paraType = state.schema.nodes["paragraph"];
905
+ if (!headingType || !paraType) return false;
906
+ const { $from } = state.selection;
907
+ const parent = $from.parent;
908
+ if (parent.type === headingType && parent.attrs.level === level) return require_dist.setBlockType(paraType)(state, dispatch);
909
+ return require_dist.setBlockType(headingType, { level })(state, dispatch);
910
+ };
911
+ } };
912
+ },
913
+ addKeyboardShortcuts() {
914
+ const shortcuts = {};
915
+ for (let i = 1; i <= 6; i++) {
916
+ const level = i;
917
+ shortcuts[`Mod-Alt-${i}`] = (state, dispatch) => {
918
+ const headingType = state.schema.nodes["heading"];
919
+ const paraType = state.schema.nodes["paragraph"];
920
+ if (!headingType || !paraType) return false;
921
+ const { $from } = state.selection;
922
+ const parent = $from.parent;
923
+ if (parent.type === headingType && parent.attrs.level === level) return require_dist.setBlockType(paraType)(state, dispatch);
924
+ return require_dist.setBlockType(headingType, { level })(state, dispatch);
925
+ };
926
+ }
927
+ return shortcuts;
928
+ },
929
+ addPlugins() {
930
+ return [inputRules({ rules: [new InputRule(/^(#{1,6})\s$/, (state, match, start, end) => {
931
+ const level = match[1].length;
932
+ const nt = state.schema.nodes["heading"];
933
+ if (!nt) return null;
934
+ const tr = state.tr;
935
+ tr.delete(start, end);
936
+ const range = tr.doc.resolve(start).blockRange();
937
+ if (!range) return null;
938
+ tr.setBlockType(range.start, range.end, nt, { level });
939
+ return tr;
940
+ })] })];
941
+ }
942
+ };
943
+ }
944
+ function BlockquoteExtension() {
945
+ return {
946
+ name: "blockquote",
947
+ type: "node",
948
+ addNodes() {
949
+ return { blockquote: {
950
+ group: "block",
951
+ content: "block+",
952
+ defining: true,
953
+ toDOM() {
954
+ return ["blockquote", 0];
955
+ },
956
+ parseDOM: [{ tag: "blockquote" }]
957
+ } };
958
+ },
959
+ addCommands() {
960
+ return { blockquote: () => (state, dispatch) => {
961
+ const nt = state.schema.nodes["blockquote"];
962
+ if (!nt) return false;
963
+ return require_dist.wrapIn(nt)(state, dispatch);
964
+ } };
965
+ },
966
+ addKeyboardShortcuts() {
967
+ return { "Mod-Shift-b": (state, dispatch) => {
968
+ const nt = state.schema.nodes["blockquote"];
969
+ if (!nt) return false;
970
+ return require_dist.wrapIn(nt)(state, dispatch);
971
+ } };
972
+ }
973
+ };
974
+ }
975
+ function BulletListExtension() {
976
+ return {
977
+ name: "bullet_list",
978
+ type: "node",
979
+ addNodes() {
980
+ return {
981
+ bullet_list: {
982
+ group: "block",
983
+ content: "list_item+",
984
+ toDOM() {
985
+ return ["ul", 0];
986
+ },
987
+ parseDOM: [{ tag: "ul" }]
988
+ },
989
+ list_item: {
990
+ content: "paragraph block*",
991
+ toDOM() {
992
+ return ["li", 0];
993
+ },
994
+ parseDOM: [{ tag: "li" }]
995
+ }
996
+ };
997
+ },
998
+ addCommands() {
999
+ return { bulletList: () => (state, dispatch) => {
1000
+ const nt = state.schema.nodes["bullet_list"];
1001
+ if (!nt) return false;
1002
+ return require_dist.wrapIn(nt)(state, dispatch);
1003
+ } };
1004
+ },
1005
+ addKeyboardShortcuts() {
1006
+ return { Enter: (state, dispatch) => {
1007
+ const nt = state.schema.nodes["list_item"];
1008
+ if (!nt) return false;
1009
+ return require_dist$2.splitListItem(nt)(state, dispatch);
1010
+ } };
1011
+ }
1012
+ };
1013
+ }
1014
+ function OrderedListExtension() {
1015
+ return {
1016
+ name: "ordered_list",
1017
+ type: "node",
1018
+ addNodes() {
1019
+ return { ordered_list: {
1020
+ group: "block",
1021
+ content: "list_item+",
1022
+ attrs: { order: { default: 1 } },
1023
+ toDOM(node) {
1024
+ return node.attrs.order === 1 ? ["ol", 0] : [
1025
+ "ol",
1026
+ { start: node.attrs.order },
1027
+ 0
1028
+ ];
1029
+ },
1030
+ parseDOM: [{
1031
+ tag: "ol",
1032
+ getAttrs(dom) {
1033
+ return { order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1 };
1034
+ }
1035
+ }]
1036
+ } };
1037
+ },
1038
+ addCommands() {
1039
+ return { orderedList: () => (state, dispatch) => {
1040
+ const nt = state.schema.nodes["ordered_list"];
1041
+ if (!nt) return false;
1042
+ return require_dist.wrapIn(nt)(state, dispatch);
1043
+ } };
1044
+ },
1045
+ addKeyboardShortcuts() {
1046
+ return { Enter: (state, dispatch) => {
1047
+ const nt = state.schema.nodes["list_item"];
1048
+ if (!nt) return false;
1049
+ return require_dist$2.splitListItem(nt)(state, dispatch);
1050
+ } };
1051
+ }
1052
+ };
1053
+ }
1054
+ function TaskListExtension() {
1055
+ return {
1056
+ name: "task_list",
1057
+ type: "node",
1058
+ addNodes() {
1059
+ return {
1060
+ task_list: {
1061
+ group: "block",
1062
+ content: "task_item+",
1063
+ toDOM() {
1064
+ return [
1065
+ "ul",
1066
+ { class: "mi-task-list" },
1067
+ 0
1068
+ ];
1069
+ },
1070
+ parseDOM: [{ tag: "ul.mi-task-list" }, { tag: "ul[data-task-list]" }]
1071
+ },
1072
+ task_item: {
1073
+ content: "paragraph block*",
1074
+ attrs: { checked: { default: false } },
1075
+ toDOM(node) {
1076
+ return [
1077
+ "li",
1078
+ {
1079
+ "data-task-item": "",
1080
+ class: "mi-task-item"
1081
+ },
1082
+ ["input", {
1083
+ type: "checkbox",
1084
+ checked: node.attrs.checked ? "checked" : null,
1085
+ "data-checked": String(node.attrs.checked)
1086
+ }],
1087
+ [
1088
+ "div",
1089
+ { class: "mi-task-content" },
1090
+ 0
1091
+ ]
1092
+ ];
1093
+ },
1094
+ parseDOM: [{
1095
+ tag: "li[data-task-item]",
1096
+ getAttrs(dom) {
1097
+ return { checked: dom.querySelector("input[type=\"checkbox\"]")?.checked ?? false };
1098
+ }
1099
+ }]
1100
+ }
1101
+ };
1102
+ },
1103
+ addCommands() {
1104
+ return {
1105
+ taskList: () => (state, dispatch) => {
1106
+ const nt = state.schema.nodes["task_list"];
1107
+ if (!nt) return false;
1108
+ return require_dist.wrapIn(nt)(state, dispatch);
1109
+ },
1110
+ toggleTaskItem: () => (state, dispatch) => {
1111
+ const { $from } = state.selection;
1112
+ let taskItemPos = -1;
1113
+ let taskItemNode = null;
1114
+ state.doc.nodesBetween($from.pos, $from.pos, (node, pos) => {
1115
+ if (node.type.name === "task_item") {
1116
+ taskItemPos = pos;
1117
+ taskItemNode = node;
1118
+ return false;
1119
+ }
1120
+ });
1121
+ for (let d = $from.depth; d > 0; d--) {
1122
+ const node = $from.node(d);
1123
+ if (node.type.name === "task_item") {
1124
+ taskItemPos = $from.before(d);
1125
+ taskItemNode = node;
1126
+ break;
1127
+ }
1128
+ }
1129
+ if (taskItemPos < 0 || !taskItemNode) return false;
1130
+ if (dispatch) {
1131
+ const tr = state.tr;
1132
+ tr.setNodeMarkup(taskItemPos, null, {
1133
+ ...taskItemNode.attrs,
1134
+ checked: !taskItemNode.attrs.checked
1135
+ });
1136
+ dispatch(tr);
1137
+ }
1138
+ return true;
1139
+ }
1140
+ };
1141
+ },
1142
+ addKeyboardShortcuts() {
1143
+ return { Enter: (state, dispatch) => {
1144
+ const nt = state.schema.nodes["task_item"];
1145
+ if (!nt) return false;
1146
+ return require_dist$2.splitListItem(nt)(state, dispatch);
1147
+ } };
1148
+ },
1149
+ addPlugins() {
1150
+ return [new require_dist.Plugin({ props: { handleDOMEvents: { click(view, event) {
1151
+ const target = event.target;
1152
+ if (target.tagName === "INPUT" && target.type === "checkbox" && target.closest(".mi-task-item")) {
1153
+ event.preventDefault();
1154
+ const li = target.closest(".mi-task-item");
1155
+ if (!li) return false;
1156
+ const pos = view.posAtDOM(li, 0);
1157
+ const node = view.state.doc.nodeAt(pos);
1158
+ if (!node || node.type.name !== "task_item") return false;
1159
+ const tr = view.state.tr;
1160
+ tr.setNodeMarkup(pos, null, {
1161
+ ...node.attrs,
1162
+ checked: !node.attrs.checked
1163
+ });
1164
+ view.dispatch(tr);
1165
+ return true;
1166
+ }
1167
+ return false;
1168
+ } } } })];
1169
+ }
1170
+ };
1171
+ }
1172
+ function CodeBlockExtension() {
1173
+ return {
1174
+ name: "code_block",
1175
+ type: "node",
1176
+ addNodes() {
1177
+ return { code_block: {
1178
+ group: "block",
1179
+ content: "text*",
1180
+ marks: "",
1181
+ code: true,
1182
+ defining: true,
1183
+ toDOM() {
1184
+ return ["pre", ["code", 0]];
1185
+ },
1186
+ parseDOM: [{
1187
+ tag: "pre",
1188
+ preserveWhitespace: "full"
1189
+ }]
1190
+ } };
1191
+ },
1192
+ addCommands() {
1193
+ return { codeBlock: () => (state, dispatch) => {
1194
+ const nt = state.schema.nodes["code_block"];
1195
+ if (!nt) return false;
1196
+ return require_dist.setBlockType(nt)(state, dispatch);
1197
+ } };
1198
+ },
1199
+ addPlugins() {
1200
+ return [inputRules({ rules: [new InputRule(/^```$/, (state, _match, start, end) => {
1201
+ const nt = state.schema.nodes["code_block"];
1202
+ if (!nt) return null;
1203
+ const tr = state.tr;
1204
+ tr.delete(start, end);
1205
+ const range = tr.doc.resolve(start).blockRange();
1206
+ if (!range) return null;
1207
+ tr.setBlockType(range.start, range.end, nt);
1208
+ return tr;
1209
+ })] })];
1210
+ }
1211
+ };
1212
+ }
1213
+ function HorizontalRuleExtension() {
1214
+ return {
1215
+ name: "horizontal_rule",
1216
+ type: "node",
1217
+ addNodes() {
1218
+ return { horizontal_rule: {
1219
+ group: "block",
1220
+ toDOM() {
1221
+ return ["hr"];
1222
+ },
1223
+ parseDOM: [{ tag: "hr" }]
1224
+ } };
1225
+ },
1226
+ addCommands() {
1227
+ return { horizontalRule: () => (state, dispatch) => {
1228
+ const nt = state.schema.nodes["horizontal_rule"];
1229
+ if (!nt) return false;
1230
+ const tr = state.tr;
1231
+ tr.replaceSelectionWith(nt.create());
1232
+ if (dispatch) dispatch(tr);
1233
+ return true;
1234
+ } };
1235
+ }
1236
+ };
1237
+ }
1238
+ function HardBreakExtension() {
1239
+ return {
1240
+ name: "hard_break",
1241
+ type: "node",
1242
+ addNodes() {
1243
+ return { hard_break: {
1244
+ group: "inline",
1245
+ inline: true,
1246
+ selectable: false,
1247
+ toDOM() {
1248
+ return ["br"];
1249
+ },
1250
+ parseDOM: [{ tag: "br" }]
1251
+ } };
1252
+ },
1253
+ addKeyboardShortcuts() {
1254
+ const insertHardBreak = (state, dispatch) => {
1255
+ const nt = state.schema.nodes["hard_break"];
1256
+ if (!nt) return false;
1257
+ const tr = state.tr.replaceSelectionWith(nt.create());
1258
+ if (dispatch) dispatch(tr.scrollIntoView());
1259
+ return true;
1260
+ };
1261
+ return {
1262
+ "Mod-Enter": insertHardBreak,
1263
+ "Shift-Enter": insertHardBreak
1264
+ };
1265
+ }
1266
+ };
1267
+ }
1268
+ var MIN_IMG_SIZE = 30;
1269
+ var IMG_PRESET_SIZES = [
1270
+ 25,
1271
+ 50,
1272
+ 75,
1273
+ 100
1274
+ ];
1275
+ var ImageNodeView = class {
1276
+ constructor(node, view, getPos) {
1277
+ this.selected = false;
1278
+ this._onDocMouseDown = null;
1279
+ this.node = node;
1280
+ this.view = view;
1281
+ this.getPos = getPos;
1282
+ const { src, alt, title, width, height, align } = node.attrs;
1283
+ this.wrapper = document.createElement("span");
1284
+ this.wrapper.className = `mi-img-wrap mi-img-align-${align || "center"}`;
1285
+ this.wrapper.style.display = "inline-block";
1286
+ this.wrapper.style.position = "relative";
1287
+ this.wrapper.style.lineHeight = "0";
1288
+ this.wrapper.style.verticalAlign = "middle";
1289
+ if (width) this.wrapper.style.width = typeof width === "number" ? `${width}px` : String(width);
1290
+ this.img = document.createElement("img");
1291
+ this.img.src = src || "";
1292
+ if (alt) this.img.alt = alt;
1293
+ if (title) this.img.title = title;
1294
+ if (width) this.img.style.width = typeof width === "number" ? `${width}px` : String(width);
1295
+ if (height) this.img.style.height = typeof height === "number" ? `${height}px` : String(height);
1296
+ this.img.style.display = "block";
1297
+ this.img.style.maxWidth = "100%";
1298
+ this.img.style.height = height ? typeof height === "number" ? `${height}px` : String(height) : "auto";
1299
+ this.img.style.borderRadius = "6px";
1300
+ this.img.style.cursor = "pointer";
1301
+ this.img.draggable = false;
1302
+ this.handles = document.createElement("div");
1303
+ this.handles.style.cssText = "display:none;position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;";
1304
+ [
1305
+ "nw",
1306
+ "ne",
1307
+ "sw",
1308
+ "se"
1309
+ ].forEach((dir) => {
1310
+ const h = document.createElement("div");
1311
+ const isTop = dir.includes("n");
1312
+ const isLeft = dir.includes("w");
1313
+ h.style.cssText = `
1314
+ position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a73e8;
1315
+ border-radius:2px;pointer-events:all;cursor:${dir}-resize;z-index:10;
1316
+ top:${isTop ? "-5px" : "auto"};bottom:${!isTop ? "-5px" : "auto"};
1317
+ left:${isLeft ? "-5px" : "auto"};right:${!isLeft ? "-5px" : "auto"};
1318
+ `;
1319
+ h.addEventListener("mousedown", (e) => {
1320
+ e.preventDefault();
1321
+ e.stopPropagation();
1322
+ this.startResize(e, dir);
1323
+ });
1324
+ this.handles.appendChild(h);
1325
+ });
1326
+ this.toolbar = document.createElement("div");
1327
+ this.toolbar.style.cssText = "display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:3px 6px;box-shadow:0 2px 10px rgba(0,0,0,0.12);z-index:20;white-space:nowrap;line-height:1;";
1328
+ this.buildToolbar();
1329
+ this.wrapper.appendChild(this.img);
1330
+ this.wrapper.appendChild(this.handles);
1331
+ this.wrapper.appendChild(this.toolbar);
1332
+ this.dom = this.wrapper;
1333
+ this.wrapper.addEventListener("mousedown", (e) => {
1334
+ if (e.target === this.img || e.target === this.wrapper) e.preventDefault();
1335
+ });
1336
+ this.wrapper.addEventListener("click", (e) => {
1337
+ e.preventDefault();
1338
+ e.stopPropagation();
1339
+ this.select();
1340
+ });
1341
+ const onDocMouseDown = (e) => {
1342
+ if (!this.wrapper.contains(e.target)) this.deselect();
1343
+ };
1344
+ document.addEventListener("mousedown", onDocMouseDown);
1345
+ this._onDocMouseDown = onDocMouseDown;
1346
+ }
1347
+ buildToolbar() {
1348
+ this.toolbar.innerHTML = "";
1349
+ const sizeGroup = document.createElement("span");
1350
+ sizeGroup.style.cssText = "display:inline-flex;gap:2px;align-items:center;";
1351
+ IMG_PRESET_SIZES.forEach((pct) => {
1352
+ const btn = document.createElement("button");
1353
+ btn.textContent = `${pct}%`;
1354
+ btn.style.cssText = "padding:2px 6px;border:none;border-radius:3px;background:transparent;color:#555;cursor:pointer;font-size:11px;";
1355
+ btn.addEventListener("mousedown", (e) => {
1356
+ e.preventDefault();
1357
+ e.stopPropagation();
1358
+ });
1359
+ btn.addEventListener("click", (e) => {
1360
+ e.preventDefault();
1361
+ e.stopPropagation();
1362
+ this.applyPreset(pct);
1363
+ });
1364
+ btn.addEventListener("mouseenter", () => {
1365
+ btn.style.background = "#f0f0f0";
1366
+ });
1367
+ btn.addEventListener("mouseleave", () => {
1368
+ btn.style.background = "transparent";
1369
+ });
1370
+ sizeGroup.appendChild(btn);
1371
+ });
1372
+ const divider = document.createElement("span");
1373
+ divider.style.cssText = "display:inline-block;width:1px;height:16px;background:#e0e0e0;margin:0 4px;vertical-align:middle;";
1374
+ const alignGroup = document.createElement("span");
1375
+ alignGroup.style.cssText = "display:inline-flex;gap:2px;align-items:center;";
1376
+ const currentAlign = this.node.attrs.align || "center";
1377
+ [
1378
+ ["left", "◀"],
1379
+ ["center", "◆"],
1380
+ ["right", "▶"]
1381
+ ].forEach(([val, icon]) => {
1382
+ const btn = document.createElement("button");
1383
+ btn.textContent = icon;
1384
+ btn.style.cssText = `padding:2px 5px;border:none;border-radius:3px;background:${currentAlign === val ? "#e8f0fe" : "transparent"};color:${currentAlign === val ? "#1a73e8" : "#555"};cursor:pointer;font-size:11px;`;
1385
+ btn.addEventListener("mousedown", (e) => {
1386
+ e.preventDefault();
1387
+ e.stopPropagation();
1388
+ });
1389
+ btn.addEventListener("click", (e) => {
1390
+ e.preventDefault();
1391
+ e.stopPropagation();
1392
+ this.setAlign(val);
1393
+ });
1394
+ alignGroup.appendChild(btn);
1395
+ });
1396
+ this.toolbar.appendChild(sizeGroup);
1397
+ this.toolbar.appendChild(divider);
1398
+ this.toolbar.appendChild(alignGroup);
1399
+ }
1400
+ select() {
1401
+ if (this.selected) return;
1402
+ this.selected = true;
1403
+ this.handles.style.display = "";
1404
+ this.toolbar.style.display = "";
1405
+ this.img.style.outline = "2px solid rgba(26,115,232,0.5)";
1406
+ this.img.style.outlineOffset = "1px";
1407
+ const pos = this.getPos();
1408
+ if (pos === void 0) return;
1409
+ try {
1410
+ const tr = this.view.state.tr.setSelection(require_dist.NodeSelection.create(this.view.state.doc, pos));
1411
+ this.view.dispatch(tr);
1412
+ } catch {}
1413
+ }
1414
+ deselect() {
1415
+ if (!this.selected) return;
1416
+ this.selected = false;
1417
+ this.handles.style.display = "none";
1418
+ this.toolbar.style.display = "none";
1419
+ this.img.style.outline = "";
1420
+ this.img.style.outlineOffset = "";
1421
+ }
1422
+ startResize(e, dir) {
1423
+ const startX = e.clientX;
1424
+ const startY = e.clientY;
1425
+ const startW = this.img.offsetWidth;
1426
+ const startH = this.img.offsetHeight;
1427
+ const aspect = startW / startH;
1428
+ const onMove = (ev) => {
1429
+ let dx = ev.clientX - startX;
1430
+ let dy = ev.clientY - startY;
1431
+ if (dir.includes("w")) dx = -dx;
1432
+ if (dir.includes("n")) dy = -dy;
1433
+ const scale = Math.max(dx / startW, dy / startH);
1434
+ let newW = Math.max(MIN_IMG_SIZE, Math.round(startW * (1 + scale)));
1435
+ let newH = Math.round(newW / aspect);
1436
+ if (newH < MIN_IMG_SIZE) {
1437
+ newH = MIN_IMG_SIZE;
1438
+ newW = Math.round(newH * aspect);
1439
+ }
1440
+ this.img.style.width = `${newW}px`;
1441
+ this.img.style.height = `${newH}px`;
1442
+ this.wrapper.style.width = `${newW}px`;
1443
+ };
1444
+ const onUp = () => {
1445
+ document.removeEventListener("mousemove", onMove);
1446
+ document.removeEventListener("mouseup", onUp);
1447
+ const pos = this.getPos();
1448
+ if (pos === void 0) return;
1449
+ const cur = this.view.state.doc.nodeAt(pos);
1450
+ if (!cur || cur.type.name !== "image") return;
1451
+ const tr = this.view.state.tr.setNodeMarkup(pos, null, {
1452
+ ...cur.attrs,
1453
+ width: this.img.offsetWidth,
1454
+ height: this.img.offsetHeight
1455
+ });
1456
+ this.view.dispatch(tr);
1457
+ };
1458
+ document.addEventListener("mousemove", onMove);
1459
+ document.addEventListener("mouseup", onUp);
1460
+ }
1461
+ applyPreset(pct) {
1462
+ const pos = this.getPos();
1463
+ if (pos === void 0) return;
1464
+ const cur = this.view.state.doc.nodeAt(pos);
1465
+ if (!cur || cur.type.name !== "image") return;
1466
+ const baseW = this.img.naturalWidth || this.img.offsetWidth;
1467
+ const baseH = this.img.naturalHeight || this.img.offsetHeight;
1468
+ const newW = Math.max(MIN_IMG_SIZE, Math.round(baseW * pct / 100));
1469
+ const newH = Math.round(newW / (baseW / baseH));
1470
+ this.img.style.width = `${newW}px`;
1471
+ this.img.style.height = `${newH}px`;
1472
+ this.wrapper.style.width = `${newW}px`;
1473
+ const tr = this.view.state.tr.setNodeMarkup(pos, null, {
1474
+ ...cur.attrs,
1475
+ width: newW,
1476
+ height: newH
1477
+ });
1478
+ this.view.dispatch(tr);
1479
+ }
1480
+ setAlign(align) {
1481
+ const pos = this.getPos();
1482
+ if (pos === void 0) return;
1483
+ const cur = this.view.state.doc.nodeAt(pos);
1484
+ if (!cur || cur.type.name !== "image") return;
1485
+ this.wrapper.className = `mi-img-wrap mi-img-align-${align}`;
1486
+ this.buildToolbar();
1487
+ const tr = this.view.state.tr.setNodeMarkup(pos, null, {
1488
+ ...cur.attrs,
1489
+ align
1490
+ });
1491
+ this.view.dispatch(tr);
1492
+ }
1493
+ update(node) {
1494
+ if (node.type.name !== "image") return false;
1495
+ this.node = node;
1496
+ const { src, alt, title, width, height, align } = node.attrs;
1497
+ this.wrapper.className = `mi-img-wrap mi-img-align-${align || "center"}`;
1498
+ if (this.img.src !== src) this.img.src = src || "";
1499
+ if (alt) this.img.alt = alt;
1500
+ if (title) this.img.title = title;
1501
+ if (width) {
1502
+ const w = typeof width === "number" ? `${width}px` : String(width);
1503
+ this.img.style.width = w;
1504
+ this.wrapper.style.width = w;
1505
+ } else {
1506
+ this.img.style.width = "";
1507
+ this.wrapper.style.width = "";
1508
+ }
1509
+ if (height) this.img.style.height = typeof height === "number" ? `${height}px` : String(height);
1510
+ else this.img.style.height = "";
1511
+ this.buildToolbar();
1512
+ return true;
1513
+ }
1514
+ stopEvent() {
1515
+ return true;
1516
+ }
1517
+ ignoreMutation() {
1518
+ return true;
1519
+ }
1520
+ destroy() {
1521
+ if (this._onDocMouseDown) {
1522
+ document.removeEventListener("mousedown", this._onDocMouseDown);
1523
+ this._onDocMouseDown = null;
1524
+ }
1525
+ this.deselect();
1526
+ }
1527
+ };
1528
+ function ImageExtension() {
1529
+ return {
1530
+ name: "image",
1531
+ type: "node",
1532
+ addNodes() {
1533
+ return { image: {
1534
+ group: "inline",
1535
+ inline: true,
1536
+ attrs: {
1537
+ src: {},
1538
+ alt: { default: null },
1539
+ title: { default: null },
1540
+ width: { default: null },
1541
+ height: { default: null },
1542
+ align: { default: "center" }
1543
+ },
1544
+ selectable: true,
1545
+ draggable: true,
1546
+ toDOM(node) {
1547
+ const { src, alt, title, width, height } = node.attrs;
1548
+ const attrs = {
1549
+ src: src || "",
1550
+ draggable: "false"
1551
+ };
1552
+ if (alt) attrs.alt = alt;
1553
+ if (title) attrs.title = title;
1554
+ if (width) attrs.style = `width: ${typeof width === "number" ? `${width}px` : String(width)}; height: ${height ? typeof height === "number" ? `${height}px` : String(height) : "auto"};`;
1555
+ return ["img", attrs];
1556
+ },
1557
+ parseDOM: [{
1558
+ tag: "img[src]",
1559
+ getAttrs(dom) {
1560
+ const el = dom;
1561
+ const style = el.getAttribute("style") || "";
1562
+ const wMatch = style.match(/width:\s*(\d+)px/);
1563
+ const hMatch = style.match(/height:\s*(\d+)px/);
1564
+ return {
1565
+ src: el.getAttribute("src"),
1566
+ alt: el.getAttribute("alt"),
1567
+ title: el.getAttribute("title"),
1568
+ width: wMatch ? parseInt(wMatch[1]) : el.getAttribute("width") ? parseInt(el.getAttribute("width")) : null,
1569
+ height: hMatch ? parseInt(hMatch[1]) : el.getAttribute("height") ? parseInt(el.getAttribute("height")) : null,
1570
+ align: "center"
1571
+ };
1572
+ }
1573
+ }]
1574
+ } };
1575
+ },
1576
+ addCommands() {
1577
+ return {
1578
+ setImageSize: (...args) => {
1579
+ const width = args[0];
1580
+ const height = args[1];
1581
+ return (state, dispatch) => {
1582
+ const { $from } = state.selection;
1583
+ let imagePos = -1;
1584
+ for (let d = $from.depth; d > 0; d--) if ($from.node(d).type.name === "image") {
1585
+ imagePos = $from.before(d);
1586
+ break;
1587
+ }
1588
+ if (imagePos < 0) return false;
1589
+ const node = state.doc.nodeAt(imagePos);
1590
+ if (!node || node.type.name !== "image") return false;
1591
+ const aspect = node.attrs.width && node.attrs.height ? node.attrs.width / node.attrs.height : 1;
1592
+ const finalH = height ?? Math.round(width / aspect);
1593
+ if (dispatch) dispatch(state.tr.setNodeMarkup(imagePos, null, {
1594
+ ...node.attrs,
1595
+ width,
1596
+ height: finalH
1597
+ }));
1598
+ return true;
1599
+ };
1600
+ },
1601
+ setImageAlign: (...args) => {
1602
+ const align = args[0];
1603
+ return (state, dispatch) => {
1604
+ const { $from } = state.selection;
1605
+ let imagePos = -1;
1606
+ for (let d = $from.depth; d > 0; d--) if ($from.node(d).type.name === "image") {
1607
+ imagePos = $from.before(d);
1608
+ break;
1609
+ }
1610
+ if (imagePos < 0) return false;
1611
+ const node = state.doc.nodeAt(imagePos);
1612
+ if (!node || node.type.name !== "image") return false;
1613
+ if (dispatch) dispatch(state.tr.setNodeMarkup(imagePos, null, {
1614
+ ...node.attrs,
1615
+ align
1616
+ }));
1617
+ return true;
1618
+ };
1619
+ }
1620
+ };
1621
+ },
1622
+ addPlugins() {
1623
+ return [new require_dist.Plugin({
1624
+ key: new require_dist.PluginKey("imageNodeView"),
1625
+ props: { nodeViews: { image(node, view, getPos) {
1626
+ return new ImageNodeView(node, view, getPos);
1627
+ } } }
1628
+ })];
1629
+ }
1630
+ };
1631
+ }
1632
+ function CardExtension() {
1633
+ return {
1634
+ name: "card",
1635
+ type: "node",
1636
+ addNodes() {
1637
+ return { card: {
1638
+ group: "block",
1639
+ content: "block+",
1640
+ defining: true,
1641
+ attrs: { title: { default: "" } },
1642
+ toDOM(node) {
1643
+ return [
1644
+ "div",
1645
+ {
1646
+ class: "mi-card",
1647
+ "data-card-title": node.attrs.title || ""
1648
+ },
1649
+ [
1650
+ "div",
1651
+ { class: "mi-card-header" },
1652
+ [
1653
+ "div",
1654
+ {
1655
+ class: "mi-card-title",
1656
+ contenteditable: "false",
1657
+ "data-card-title": ""
1658
+ },
1659
+ node.attrs.title || "卡片标题"
1660
+ ]
1661
+ ],
1662
+ [
1663
+ "div",
1664
+ { class: "mi-card-body" },
1665
+ 0
1666
+ ]
1667
+ ];
1668
+ },
1669
+ parseDOM: [{
1670
+ tag: "div.mi-card",
1671
+ getAttrs(dom) {
1672
+ const el = dom;
1673
+ return { title: el.querySelector(".mi-card-title")?.textContent || el.getAttribute("data-card-title") || "" };
1674
+ }
1675
+ }]
1676
+ } };
1677
+ },
1678
+ addCommands() {
1679
+ return { card: (...args) => {
1680
+ const title = args[0] ?? "";
1681
+ return (state, dispatch) => {
1682
+ const nt = state.schema.nodes["card"];
1683
+ const paraType = state.schema.nodes["paragraph"];
1684
+ if (!nt || !paraType) return false;
1685
+ const { from, to } = state.selection;
1686
+ const para = paraType.createAndFill();
1687
+ const card = nt.create({ title }, para);
1688
+ if (dispatch) dispatch(state.tr.replaceWith(from, to, card).scrollIntoView());
1689
+ return true;
1690
+ };
1691
+ } };
1692
+ },
1693
+ addPlugins() {
1694
+ return [new require_dist.Plugin({ props: { handleDOMEvents: { click(view, event) {
1695
+ const titleEl = event.target.closest(".mi-card-title");
1696
+ if (!titleEl) return false;
1697
+ const card = titleEl.closest(".mi-card");
1698
+ if (!card) return false;
1699
+ event.preventDefault();
1700
+ const pos = view.posAtDOM(card, 0);
1701
+ const tr = view.state.tr;
1702
+ const resolved = tr.doc.resolve(pos);
1703
+ let nodePos = -1;
1704
+ for (let d = resolved.depth; d >= 0; d--) if (resolved.node(d).type.name === "card") {
1705
+ nodePos = resolved.before(d);
1706
+ break;
1707
+ }
1708
+ if (nodePos < 0) return false;
1709
+ const node = tr.doc.nodeAt(nodePos);
1710
+ if (!node || node.type.name !== "card") return false;
1711
+ const currentTitle = node.attrs.title;
1712
+ const newTitle = prompt("编辑卡片标题:", currentTitle);
1713
+ if (newTitle !== null && newTitle !== currentTitle) {
1714
+ tr.setNodeMarkup(nodePos, null, {
1715
+ ...node.attrs,
1716
+ title: newTitle
1717
+ });
1718
+ view.dispatch(tr);
1719
+ }
1720
+ return true;
1721
+ } } } })];
1722
+ }
1723
+ };
1724
+ }
1725
+ function ProductCardExtension() {
1726
+ return {
1727
+ name: "product_card",
1728
+ type: "node",
1729
+ addNodes() {
1730
+ return { product_card: {
1731
+ group: "block",
1732
+ atom: true,
1733
+ attrs: {
1734
+ productName: { default: "" },
1735
+ productImage: { default: "" },
1736
+ productPrice: { default: "" },
1737
+ productOriginalPrice: { default: "" },
1738
+ productDescription: { default: "" },
1739
+ productLink: { default: "" },
1740
+ productTags: { default: "" }
1741
+ },
1742
+ toDOM(node) {
1743
+ const a = node.attrs;
1744
+ const tags = a.productTags ? a.productTags.split(",").map((t) => t.trim()).filter(Boolean) : [];
1745
+ const imgWrap = a.productImage ? [
1746
+ "div",
1747
+ { class: "mi-product-card-img-wrap" },
1748
+ ["img", {
1749
+ src: a.productImage,
1750
+ alt: a.productName,
1751
+ class: "mi-product-card-img",
1752
+ draggable: "false"
1753
+ }]
1754
+ ] : [
1755
+ "div",
1756
+ { class: "mi-product-card-img-wrap mi-product-card-img-empty" },
1757
+ [
1758
+ "span",
1759
+ { class: "mi-product-card-img-placeholder" },
1760
+ "📦"
1761
+ ]
1762
+ ];
1763
+ const contentChildren = [[
1764
+ "div",
1765
+ { class: "mi-product-card-title" },
1766
+ a.productName || "商品名称"
1767
+ ]];
1768
+ if (a.productDescription) contentChildren.push([
1769
+ "div",
1770
+ { class: "mi-product-card-desc" },
1771
+ a.productDescription
1772
+ ]);
1773
+ if (tags.length > 0) {
1774
+ const tagElements = tags.map((tag) => [
1775
+ "span",
1776
+ { class: "mi-product-card-tag" },
1777
+ tag
1778
+ ]);
1779
+ contentChildren.push([
1780
+ "div",
1781
+ { class: "mi-product-card-tags" },
1782
+ ...tagElements
1783
+ ]);
1784
+ }
1785
+ if (a.productPrice || a.productOriginalPrice) {
1786
+ const priceElements = [];
1787
+ if (a.productPrice) priceElements.push([
1788
+ "span",
1789
+ { class: "mi-product-card-price" },
1790
+ `¥${a.productPrice}`
1791
+ ]);
1792
+ if (a.productOriginalPrice) priceElements.push([
1793
+ "span",
1794
+ { class: "mi-product-card-original-price" },
1795
+ `¥${a.productOriginalPrice}`
1796
+ ]);
1797
+ contentChildren.push([
1798
+ "div",
1799
+ { class: "mi-product-card-price-row" },
1800
+ ...priceElements
1801
+ ]);
1802
+ }
1803
+ if (a.productLink) contentChildren.push([
1804
+ "a",
1805
+ {
1806
+ class: "mi-product-card-link",
1807
+ href: a.productLink,
1808
+ target: "_blank",
1809
+ rel: "noopener noreferrer"
1810
+ },
1811
+ "查看详情 →"
1812
+ ]);
1813
+ return [
1814
+ "div",
1815
+ {
1816
+ class: "mi-product-card",
1817
+ contenteditable: "false"
1818
+ },
1819
+ [
1820
+ "div",
1821
+ { class: "mi-product-card-inner" },
1822
+ imgWrap,
1823
+ [
1824
+ "div",
1825
+ { class: "mi-product-card-content" },
1826
+ ...contentChildren
1827
+ ]
1828
+ ],
1829
+ [
1830
+ "button",
1831
+ {
1832
+ class: "mi-product-card-delete",
1833
+ title: "删除卡片"
1834
+ },
1835
+ "×"
1836
+ ]
1837
+ ];
1838
+ },
1839
+ parseDOM: [{
1840
+ tag: "div.mi-product-card",
1841
+ getAttrs(dom) {
1842
+ const el = dom;
1843
+ const get = (sel) => el.querySelector(sel)?.textContent?.trim() || "";
1844
+ const img = el.querySelector(".mi-product-card-img");
1845
+ const link = el.querySelector(".mi-product-card-link");
1846
+ const tags = Array.from(el.querySelectorAll(".mi-product-card-tag")).map((t) => t.textContent?.trim() || "").filter(Boolean).join(",");
1847
+ return {
1848
+ productName: get(".mi-product-card-title"),
1849
+ productImage: img?.getAttribute("src") || "",
1850
+ productPrice: get(".mi-product-card-price")?.replace(/^¥/, "") || "",
1851
+ productOriginalPrice: get(".mi-product-card-original-price")?.replace(/^¥/, "") || "",
1852
+ productDescription: get(".mi-product-card-desc"),
1853
+ productLink: link?.getAttribute("href") || "",
1854
+ productTags: tags
1855
+ };
1856
+ }
1857
+ }]
1858
+ } };
1859
+ },
1860
+ addCommands() {
1861
+ return { insertProductCard: (...args) => {
1862
+ const attrs = args[0];
1863
+ return (state, dispatch) => {
1864
+ const nt = state.schema.nodes["product_card"];
1865
+ if (!nt) return false;
1866
+ const { from, to } = state.selection;
1867
+ const node = nt.create({
1868
+ productName: attrs?.productName ?? "",
1869
+ productImage: attrs?.productImage ?? "",
1870
+ productPrice: attrs?.productPrice ?? "",
1871
+ productOriginalPrice: attrs?.productOriginalPrice ?? "",
1872
+ productDescription: attrs?.productDescription ?? "",
1873
+ productLink: attrs?.productLink ?? "",
1874
+ productTags: attrs?.productTags ?? ""
1875
+ });
1876
+ if (dispatch) dispatch(state.tr.replaceWith(from, to, node).scrollIntoView());
1877
+ return true;
1878
+ };
1879
+ } };
1880
+ },
1881
+ addPlugins() {
1882
+ return [new require_dist.Plugin({ props: { handleDOMEvents: {
1883
+ click(view, event) {
1884
+ const target = event.target;
1885
+ if (target.classList.contains("mi-product-card-delete")) {
1886
+ event.preventDefault();
1887
+ event.stopPropagation();
1888
+ const card = target.closest(".mi-product-card");
1889
+ if (!card || !view.dom.contains(card)) return false;
1890
+ const pos = view.posAtDOM(card, 0);
1891
+ const node = view.state.doc.nodeAt(pos);
1892
+ if (!node || node.type.name !== "product_card") return false;
1893
+ const tr = view.state.tr.delete(pos, pos + node.nodeSize);
1894
+ view.dispatch(tr);
1895
+ return true;
1896
+ }
1897
+ return false;
1898
+ },
1899
+ dblclick(view, event) {
1900
+ const target = event.target;
1901
+ if (target.classList.contains("mi-product-card-delete")) return false;
1902
+ const card = target.closest(".mi-product-card");
1903
+ if (!card || !view.dom.contains(card)) return false;
1904
+ event.preventDefault();
1905
+ const pos = view.posAtDOM(card, 0);
1906
+ const node = view.state.doc.nodeAt(pos);
1907
+ if (!node || node.type.name !== "product_card") return false;
1908
+ const a = node.attrs;
1909
+ const name = prompt("商品名称:", a.productName);
1910
+ if (name === null) return true;
1911
+ const price = prompt("商品价格:", a.productPrice);
1912
+ if (price === null) return true;
1913
+ const desc = prompt("商品描述:", a.productDescription);
1914
+ if (desc === null) return true;
1915
+ const tr = view.state.tr;
1916
+ tr.setNodeMarkup(pos, null, {
1917
+ ...node.attrs,
1918
+ productName: name,
1919
+ productPrice: price,
1920
+ productDescription: desc
1921
+ });
1922
+ view.dispatch(tr);
1923
+ return true;
1924
+ }
1925
+ } } })];
1926
+ }
1927
+ };
1928
+ }
1929
+ //#endregion
1930
+ //#region src/extensions/table-extension.ts
1931
+ function cellAttrs(attrs) {
1932
+ return {
1933
+ colspan: attrs.colspan || 1,
1934
+ rowspan: attrs.rowspan || 1,
1935
+ colwidth: attrs.colwidth || null,
1936
+ style: attrs.style || null
1937
+ };
1938
+ }
1939
+ function cellAttrsToDOM(attrs) {
1940
+ const result = {};
1941
+ if (attrs.colspan && attrs.colspan > 1) result.colspan = String(attrs.colspan);
1942
+ if (attrs.rowspan && attrs.rowspan > 1) result.rowspan = String(attrs.rowspan);
1943
+ if (attrs.colwidth && attrs.colwidth.length > 0) result.style = `width: ${attrs.colwidth[0]}px`;
1944
+ if (attrs.style) result.style = result.style ? `${result.style}; ${attrs.style}` : attrs.style;
1945
+ return result;
1946
+ }
1947
+ function parseCellAttrs(dom) {
1948
+ return {
1949
+ colspan: parseInt(dom.getAttribute("colspan") || "1", 10),
1950
+ rowspan: parseInt(dom.getAttribute("rowspan") || "1", 10),
1951
+ colwidth: dom.getAttribute("colwidth") ? dom.getAttribute("colwidth").split(",").map(Number) : null,
1952
+ style: dom.getAttribute("style") || null
1953
+ };
1954
+ }
1955
+ function tableNodes() {
1956
+ return {
1957
+ table: {
1958
+ group: "block",
1959
+ content: "table_row+",
1960
+ tableRole: "table",
1961
+ isolating: true,
1962
+ attrs: {
1963
+ class: { default: null },
1964
+ tableStyle: { default: "default" }
1965
+ },
1966
+ parseDOM: [{
1967
+ tag: "table",
1968
+ getAttrs: (dom) => ({
1969
+ class: dom.getAttribute("class"),
1970
+ tableStyle: dom.getAttribute("data-table-style") || "default"
1971
+ })
1972
+ }],
1973
+ toDOM(node) {
1974
+ const style = node.attrs.tableStyle || "default";
1975
+ const attrs = {
1976
+ class: `miyuan-table miyuan-table-${style}`,
1977
+ "data-table-style": style
1978
+ };
1979
+ if (node.attrs.class) attrs.class += ` ${node.attrs.class}`;
1980
+ return [
1981
+ "div",
1982
+ { class: "miyuan-table-wrapper" },
1983
+ [
1984
+ "table",
1985
+ attrs,
1986
+ ["tbody", 0]
1987
+ ]
1988
+ ];
1989
+ }
1990
+ },
1991
+ table_row: {
1992
+ content: "(table_cell | table_header)+",
1993
+ tableRole: "row",
1994
+ parseDOM: [{ tag: "tr" }],
1995
+ toDOM() {
1996
+ return ["tr", 0];
1997
+ }
1998
+ },
1999
+ table_cell: {
2000
+ content: "block+",
2001
+ tableRole: "cell",
2002
+ isolating: true,
2003
+ attrs: {
2004
+ colspan: { default: 1 },
2005
+ rowspan: { default: 1 },
2006
+ colwidth: { default: null },
2007
+ style: { default: null }
2008
+ },
2009
+ parseDOM: [{
2010
+ tag: "td",
2011
+ getAttrs: (dom) => parseCellAttrs(dom)
2012
+ }],
2013
+ toDOM(node) {
2014
+ return [
2015
+ "td",
2016
+ cellAttrsToDOM(cellAttrs(node.attrs)),
2017
+ 0
2018
+ ];
2019
+ }
2020
+ },
2021
+ table_header: {
2022
+ content: "block+",
2023
+ tableRole: "header_cell",
2024
+ isolating: true,
2025
+ attrs: {
2026
+ colspan: { default: 1 },
2027
+ rowspan: { default: 1 },
2028
+ colwidth: { default: null },
2029
+ style: { default: null }
2030
+ },
2031
+ parseDOM: [{
2032
+ tag: "th",
2033
+ getAttrs: (dom) => parseCellAttrs(dom)
2034
+ }],
2035
+ toDOM(node) {
2036
+ return [
2037
+ "th",
2038
+ cellAttrsToDOM(cellAttrs(node.attrs)),
2039
+ 0
2040
+ ];
2041
+ }
2042
+ }
2043
+ };
2044
+ }
2045
+ function findTable(state) {
2046
+ const { $from } = state.selection;
2047
+ for (let d = $from.depth; d > 0; d--) if ($from.node(d).type.name === "table") return {
2048
+ pos: $from.before(d),
2049
+ node: $from.node(d),
2050
+ depth: d
2051
+ };
2052
+ return null;
2053
+ }
2054
+ function findCellPosition(state) {
2055
+ const { $from } = state.selection;
2056
+ for (let d = $from.depth; d > 0; d--) {
2057
+ const node = $from.node(d);
2058
+ if (node.type.name === "table_cell" || node.type.name === "table_header") return {
2059
+ cellPos: $from.before(d),
2060
+ cellNode: node,
2061
+ rowPos: $from.before(d - 1),
2062
+ rowNode: $from.node(d - 1),
2063
+ tablePos: $from.before(d - 2),
2064
+ tableNode: $from.node(d - 2)
2065
+ };
2066
+ }
2067
+ return null;
2068
+ }
2069
+ function insertTable(rows = 3, cols = 3) {
2070
+ return (state, dispatch) => {
2071
+ const { schema } = state;
2072
+ const tableNodeType = schema.nodes.table;
2073
+ const rowType = schema.nodes.table_row;
2074
+ const cellType = schema.nodes.table_cell;
2075
+ const headerType = schema.nodes.table_header;
2076
+ const paraType = schema.nodes.paragraph;
2077
+ if (!tableNodeType || !rowType || !cellType || !headerType || !paraType) return false;
2078
+ const tableRows = [];
2079
+ const headerCells = [];
2080
+ for (let c = 0; c < cols; c++) headerCells.push(headerType.createAndFill({}, paraType.createAndFill()));
2081
+ tableRows.push(rowType.create(null, headerCells));
2082
+ for (let r = 1; r < rows; r++) {
2083
+ const cells = [];
2084
+ for (let c = 0; c < cols; c++) cells.push(cellType.createAndFill({}, paraType.createAndFill()));
2085
+ tableRows.push(rowType.create(null, cells));
2086
+ }
2087
+ const table = tableNodeType.create(null, tableRows);
2088
+ if (dispatch) {
2089
+ const { from, to } = state.selection;
2090
+ const tr = state.tr.replaceWith(from, to, table);
2091
+ const newSelection = require_dist.TextSelection.near(tr.doc.resolve(from + 2));
2092
+ tr.setSelection(newSelection);
2093
+ dispatch(tr.scrollIntoView());
2094
+ }
2095
+ return true;
2096
+ };
2097
+ }
2098
+ function addRowBefore() {
2099
+ return (state, dispatch) => {
2100
+ const cellPos = findCellPosition(state);
2101
+ if (!cellPos) return false;
2102
+ if (dispatch) {
2103
+ const { schema } = state;
2104
+ const rowType = schema.nodes.table_row;
2105
+ const cellType = schema.nodes.table_cell;
2106
+ const paraType = schema.nodes.paragraph;
2107
+ const colCount = cellPos.rowNode.childCount;
2108
+ const cells = [];
2109
+ for (let i = 0; i < colCount; i++) cells.push(cellType.createAndFill({}, paraType.createAndFill()));
2110
+ const newRow = rowType.create(null, cells);
2111
+ dispatch(state.tr.insert(cellPos.rowPos, newRow).scrollIntoView());
2112
+ }
2113
+ return true;
2114
+ };
2115
+ }
2116
+ function addRowAfter() {
2117
+ return (state, dispatch) => {
2118
+ const cellPos = findCellPosition(state);
2119
+ if (!cellPos) return false;
2120
+ if (dispatch) {
2121
+ const { schema } = state;
2122
+ const rowType = schema.nodes.table_row;
2123
+ const cellType = schema.nodes.table_cell;
2124
+ const paraType = schema.nodes.paragraph;
2125
+ const colCount = cellPos.rowNode.childCount;
2126
+ const cells = [];
2127
+ for (let i = 0; i < colCount; i++) cells.push(cellType.createAndFill({}, paraType.createAndFill()));
2128
+ const newRow = rowType.create(null, cells);
2129
+ dispatch(state.tr.insert(cellPos.rowPos + cellPos.rowNode.nodeSize, newRow).scrollIntoView());
2130
+ }
2131
+ return true;
2132
+ };
2133
+ }
2134
+ function addColumnBefore() {
2135
+ return (state, dispatch) => {
2136
+ const cellPos = findCellPosition(state);
2137
+ if (!cellPos) return false;
2138
+ if (dispatch) {
2139
+ const { schema } = state;
2140
+ const table = cellPos.tableNode;
2141
+ const colIndex = getCellColumnIndex(cellPos.rowNode, cellPos.cellPos - cellPos.rowPos - 1);
2142
+ let tr = state.tr;
2143
+ for (let i = table.childCount - 1; i >= 0; i--) {
2144
+ const row = table.child(i);
2145
+ const cellType = i === 0 ? schema.nodes.table_header : schema.nodes.table_cell;
2146
+ const paraType = schema.nodes.paragraph;
2147
+ const newCell = cellType.createAndFill({}, paraType.createAndFill());
2148
+ let insertPos = cellPos.tablePos + 1;
2149
+ for (let r = 0; r < i; r++) insertPos += table.child(r).nodeSize;
2150
+ insertPos += 1;
2151
+ for (let j = 0; j < colIndex && j < row.childCount; j++) insertPos += row.child(j).nodeSize;
2152
+ tr = tr.insert(insertPos, newCell);
2153
+ }
2154
+ dispatch(tr.scrollIntoView());
2155
+ }
2156
+ return true;
2157
+ };
2158
+ }
2159
+ function addColumnAfter() {
2160
+ return (state, dispatch) => {
2161
+ const cellPos = findCellPosition(state);
2162
+ if (!cellPos) return false;
2163
+ if (dispatch) {
2164
+ const { schema } = state;
2165
+ const table = cellPos.tableNode;
2166
+ const colIndex = getCellColumnIndex(cellPos.rowNode, cellPos.cellPos - cellPos.rowPos - 1);
2167
+ let tr = state.tr;
2168
+ for (let i = table.childCount - 1; i >= 0; i--) {
2169
+ const row = table.child(i);
2170
+ const cellType = i === 0 ? schema.nodes.table_header : schema.nodes.table_cell;
2171
+ const paraType = schema.nodes.paragraph;
2172
+ const newCell = cellType.createAndFill({}, paraType.createAndFill());
2173
+ let insertPos = cellPos.tablePos + 1;
2174
+ for (let r = 0; r < i; r++) insertPos += table.child(r).nodeSize;
2175
+ insertPos += 1;
2176
+ for (let j = 0; j <= colIndex && j < row.childCount; j++) insertPos += row.child(j).nodeSize;
2177
+ tr = tr.insert(insertPos, newCell);
2178
+ }
2179
+ dispatch(tr.scrollIntoView());
2180
+ }
2181
+ return true;
2182
+ };
2183
+ }
2184
+ function deleteRow() {
2185
+ return (state, dispatch) => {
2186
+ const cellPos = findCellPosition(state);
2187
+ if (!cellPos) return false;
2188
+ if (cellPos.tableNode.childCount === 1) return deleteTable()(state, dispatch);
2189
+ if (dispatch) dispatch(state.tr.delete(cellPos.rowPos, cellPos.rowPos + cellPos.rowNode.nodeSize).scrollIntoView());
2190
+ return true;
2191
+ };
2192
+ }
2193
+ function deleteColumn() {
2194
+ return (state, dispatch) => {
2195
+ const cellPos = findCellPosition(state);
2196
+ if (!cellPos) return false;
2197
+ if (cellPos.rowNode.childCount === 1) return deleteTable()(state, dispatch);
2198
+ if (dispatch) {
2199
+ const { schema } = state;
2200
+ const table = cellPos.tableNode;
2201
+ const colIndex = getCellColumnIndex(cellPos.rowNode, cellPos.cellPos - cellPos.rowPos - 1);
2202
+ let tr = state.tr;
2203
+ let offset = 0;
2204
+ for (let i = 0; i < table.childCount; i++) {
2205
+ const row = table.child(i);
2206
+ let cellDeletePos = cellPos.tablePos + offset + 1 + 1;
2207
+ for (let j = 0; j < colIndex && j < row.childCount; j++) cellDeletePos += row.child(j).nodeSize;
2208
+ const cellToDelete = row.child(Math.min(colIndex, row.childCount - 1));
2209
+ tr = tr.delete(cellDeletePos, cellDeletePos + cellToDelete.nodeSize);
2210
+ offset -= cellToDelete.nodeSize;
2211
+ }
2212
+ dispatch(tr.scrollIntoView());
2213
+ }
2214
+ return true;
2215
+ };
2216
+ }
2217
+ function deleteTable() {
2218
+ return (state, dispatch) => {
2219
+ const table = findTable(state);
2220
+ if (!table) return false;
2221
+ if (dispatch) dispatch(state.tr.delete(table.pos, table.pos + table.node.nodeSize).scrollIntoView());
2222
+ return true;
2223
+ };
2224
+ }
2225
+ function toggleHeaderRow() {
2226
+ return (state, dispatch) => {
2227
+ const table = findTable(state);
2228
+ if (!table) return false;
2229
+ if (dispatch) {
2230
+ const { schema } = state;
2231
+ const firstRow = table.node.child(0);
2232
+ const tr = state.tr;
2233
+ const allHeaders = Array.from({ length: firstRow.childCount }, (_, i) => firstRow.child(i).type.name).every((type) => type === "table_header");
2234
+ let offset = 0;
2235
+ for (let i = 0; i < firstRow.childCount; i++) {
2236
+ const cell = firstRow.child(i);
2237
+ const cellPos = table.pos + 1 + offset;
2238
+ const newType = allHeaders ? schema.nodes.table_cell : schema.nodes.table_header;
2239
+ tr.setNodeMarkup(cellPos, newType, cell.attrs);
2240
+ offset += cell.nodeSize;
2241
+ }
2242
+ dispatch(tr.scrollIntoView());
2243
+ }
2244
+ return true;
2245
+ };
2246
+ }
2247
+ function setTableStyle(style) {
2248
+ return (state, dispatch) => {
2249
+ const table = findTable(state);
2250
+ if (!table) return false;
2251
+ if (dispatch) dispatch(state.tr.setNodeMarkup(table.pos, null, {
2252
+ ...table.node.attrs,
2253
+ tableStyle: style
2254
+ }));
2255
+ return true;
2256
+ };
2257
+ }
2258
+ function getCellColumnIndex(row, cellOffset) {
2259
+ let offset = 0;
2260
+ for (let i = 0; i < row.childCount; i++) {
2261
+ if (offset === cellOffset) return i;
2262
+ offset += row.child(i).nodeSize;
2263
+ }
2264
+ return row.childCount - 1;
2265
+ }
2266
+ var tablePluginKey = new require_dist.PluginKey("table");
2267
+ function tablePlugin() {
2268
+ return new require_dist.Plugin({
2269
+ key: tablePluginKey,
2270
+ props: { handleKeyDown(view, event) {
2271
+ const state = view.state;
2272
+ const cellPos = findCellPosition(state);
2273
+ if (event.key === "Tab") {
2274
+ if (!cellPos) return false;
2275
+ event.preventDefault();
2276
+ const { schema } = state;
2277
+ const table = cellPos.tableNode;
2278
+ const row = cellPos.rowNode;
2279
+ const colIndex = getCellColumnIndex(row, cellPos.cellPos - cellPos.rowPos - 1);
2280
+ const rowIndex = getRowIndex(table, cellPos.rowPos - cellPos.tablePos - 1);
2281
+ let targetRow;
2282
+ let targetCol;
2283
+ if (event.shiftKey) if (colIndex > 0) {
2284
+ targetRow = rowIndex;
2285
+ targetCol = colIndex - 1;
2286
+ } else if (rowIndex > 0) {
2287
+ targetRow = rowIndex - 1;
2288
+ targetCol = table.child(rowIndex - 1).childCount - 1;
2289
+ } else return true;
2290
+ else if (colIndex < row.childCount - 1) {
2291
+ targetRow = rowIndex;
2292
+ targetCol = colIndex + 1;
2293
+ } else if (rowIndex < table.childCount - 1) {
2294
+ targetRow = rowIndex + 1;
2295
+ targetCol = 0;
2296
+ } else return addRowAfter()(state, view.dispatch);
2297
+ const targetCellPos = getCellPos(table, targetRow, targetCol, cellPos.tablePos);
2298
+ if (targetCellPos !== null) {
2299
+ const $pos = state.doc.resolve(targetCellPos + 1);
2300
+ const tr = state.tr.setSelection(require_dist.TextSelection.near($pos, 1));
2301
+ view.dispatch(tr.scrollIntoView());
2302
+ }
2303
+ return true;
2304
+ }
2305
+ if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
2306
+ if (!cellPos) return false;
2307
+ return false;
2308
+ }
2309
+ if (event.key === "Backspace") {
2310
+ if (!cellPos) return false;
2311
+ const { from, empty } = state.selection;
2312
+ if (!empty) return false;
2313
+ if (from === cellPos.cellPos + 2) {
2314
+ event.preventDefault();
2315
+ const table = cellPos.tableNode;
2316
+ table.child(0);
2317
+ const firstCellPos = cellPos.tablePos + 2;
2318
+ if (cellPos.cellPos === firstCellPos - 1) return deleteTable()(state, view.dispatch);
2319
+ const row = cellPos.rowNode;
2320
+ const colIndex = getCellColumnIndex(row, cellPos.cellPos - cellPos.rowPos - 1);
2321
+ const rowIndex = getRowIndex(table, cellPos.rowPos - cellPos.tablePos - 1);
2322
+ let prevRow;
2323
+ let prevCol;
2324
+ if (colIndex > 0) {
2325
+ prevRow = rowIndex;
2326
+ prevCol = colIndex - 1;
2327
+ } else if (rowIndex > 0) {
2328
+ prevRow = rowIndex - 1;
2329
+ prevCol = table.child(rowIndex - 1).childCount - 1;
2330
+ } else return true;
2331
+ const prevCellPos = getCellPos(table, prevRow, prevCol, cellPos.tablePos);
2332
+ if (prevCellPos !== null) {
2333
+ const $pos = state.doc.resolve(prevCellPos + 1);
2334
+ const tr = state.tr.setSelection(require_dist.TextSelection.near($pos, 1));
2335
+ view.dispatch(tr.scrollIntoView());
2336
+ }
2337
+ return true;
2338
+ }
2339
+ }
2340
+ return false;
2341
+ } }
2342
+ });
2343
+ }
2344
+ function getRowIndex(table, rowOffset) {
2345
+ let offset = 0;
2346
+ for (let i = 0; i < table.childCount; i++) {
2347
+ if (offset === rowOffset) return i;
2348
+ offset += table.child(i).nodeSize;
2349
+ }
2350
+ return table.childCount - 1;
2351
+ }
2352
+ function getCellPos(table, rowIndex, colIndex, tablePos) {
2353
+ if (rowIndex < 0 || rowIndex >= table.childCount) return null;
2354
+ const row = table.child(rowIndex);
2355
+ if (colIndex < 0 || colIndex >= row.childCount) return null;
2356
+ let offset = 1;
2357
+ for (let r = 0; r < rowIndex; r++) offset += table.child(r).nodeSize;
2358
+ let cellOffset = 1;
2359
+ for (let c = 0; c < colIndex; c++) cellOffset += row.child(c).nodeSize;
2360
+ return tablePos + offset + cellOffset;
2361
+ }
2362
+ function TableExtension() {
2363
+ return {
2364
+ name: "table",
2365
+ type: "node",
2366
+ addNodes() {
2367
+ return tableNodes();
2368
+ },
2369
+ addCommands() {
2370
+ return {
2371
+ insertTable: (...args) => (state, dispatch) => {
2372
+ return insertTable(args[0] || 3, args[1] || 3)(state, dispatch);
2373
+ },
2374
+ addRowBefore: () => addRowBefore(),
2375
+ addRowAfter: () => addRowAfter(),
2376
+ addColumnBefore: () => addColumnBefore(),
2377
+ addColumnAfter: () => addColumnAfter(),
2378
+ deleteRow: () => deleteRow(),
2379
+ deleteColumn: () => deleteColumn(),
2380
+ deleteTable: () => deleteTable(),
2381
+ toggleHeaderRow: () => toggleHeaderRow(),
2382
+ setTableStyle: (...args) => setTableStyle(args[0] || "default")
2383
+ };
2384
+ },
2385
+ addKeyboardShortcuts() {
2386
+ return { "Mod-Alt-t": insertTable(3, 3) };
2387
+ },
2388
+ addPlugins() {
2389
+ return [tablePlugin()];
2390
+ }
2391
+ };
2392
+ }
2393
+ //#endregion
2394
+ //#region src/extensions/behavior-extensions.ts
2395
+ function HistoryExtension() {
2396
+ return {
2397
+ name: "history",
2398
+ type: "extension",
2399
+ addPlugins() {
2400
+ return [require_dist$2.history()];
2401
+ },
2402
+ addCommands() {
2403
+ return {
2404
+ undo: () => require_dist$2.undo,
2405
+ redo: () => require_dist$2.redo
2406
+ };
2407
+ },
2408
+ addKeyboardShortcuts() {
2409
+ return {
2410
+ "Mod-z": require_dist$2.undo,
2411
+ "Mod-y": require_dist$2.redo,
2412
+ "Mod-Shift-z": require_dist$2.redo
2413
+ };
2414
+ }
2415
+ };
2416
+ }
2417
+ var placeholderKey = new require_dist.PluginKey("placeholder");
2418
+ function PlaceholderExtension(config) {
2419
+ const text = config?.placeholder ?? "开始输入...";
2420
+ return {
2421
+ name: "placeholder",
2422
+ type: "extension",
2423
+ addPlugins() {
2424
+ return [new require_dist.Plugin({
2425
+ key: placeholderKey,
2426
+ state: {
2427
+ init(_, state) {
2428
+ return buildDecorations(state, text);
2429
+ },
2430
+ apply(tr, oldDecorations, _oldState, newState) {
2431
+ if (tr.docChanged) return buildDecorations(newState, text);
2432
+ return oldDecorations;
2433
+ }
2434
+ },
2435
+ props: { decorations(state) {
2436
+ return this.getState(state);
2437
+ } }
2438
+ })];
2439
+ }
2440
+ };
2441
+ }
2442
+ function buildDecorations(state, text) {
2443
+ const doc = state.doc;
2444
+ if (doc.childCount !== 1) return require_dist$1.DecorationSet.empty;
2445
+ const first = doc.firstChild;
2446
+ if (!first || first.type.name !== "paragraph" || first.content.size > 0) return require_dist$1.DecorationSet.empty;
2447
+ const deco = require_dist$1.Decoration.node(0, first.nodeSize, {
2448
+ class: "mi-placeholder",
2449
+ "data-placeholder": text
2450
+ });
2451
+ return require_dist$1.DecorationSet.create(doc, [deco]);
2452
+ }
2453
+ var imageUploadKey = new require_dist.PluginKey("imageUpload");
2454
+ /** Default: convert file to base64 data URL */
2455
+ function fileToDataURL(file) {
2456
+ return new Promise((resolve, reject) => {
2457
+ const reader = new FileReader();
2458
+ reader.onload = () => resolve(reader.result);
2459
+ reader.onerror = reject;
2460
+ reader.readAsDataURL(file);
2461
+ });
2462
+ }
2463
+ function ImageUploadExtension(config) {
2464
+ const upload = config?.upload ?? fileToDataURL;
2465
+ const maxSize = config?.maxSize ?? 5 * 1024 * 1024;
2466
+ function insertImage(view, src) {
2467
+ const imageType = view.state.schema.nodes["image"];
2468
+ if (!imageType) return;
2469
+ const tr = view.state.tr.replaceSelectionWith(imageType.create({ src }));
2470
+ view.dispatch(tr.scrollIntoView());
2471
+ }
2472
+ async function handleFile(file, view) {
2473
+ if (!file.type.startsWith("image/")) return;
2474
+ if (file.size > maxSize) {
2475
+ console.warn(`Image too large: ${(file.size / 1024 / 1024).toFixed(1)}MB > ${(maxSize / 1024 / 1024).toFixed(1)}MB`);
2476
+ return;
2477
+ }
2478
+ try {
2479
+ insertImage(view, await upload(file));
2480
+ } catch (e) {
2481
+ console.error("Image upload failed:", e);
2482
+ }
2483
+ }
2484
+ return {
2485
+ name: "imageUpload",
2486
+ type: "extension",
2487
+ addCommands() {
2488
+ return { insertImage: (...args) => {
2489
+ const src = args[0];
2490
+ return (state, dispatch) => {
2491
+ const imageType = state.schema.nodes["image"];
2492
+ if (!imageType) return false;
2493
+ const tr = state.tr.replaceSelectionWith(imageType.create({ src }));
2494
+ if (dispatch) dispatch(tr.scrollIntoView());
2495
+ return true;
2496
+ };
2497
+ } };
2498
+ },
2499
+ addPlugins() {
2500
+ return [new require_dist.Plugin({
2501
+ key: imageUploadKey,
2502
+ props: {
2503
+ handlePaste(view, event) {
2504
+ const files = event.clipboardData?.files;
2505
+ if (!files || files.length === 0) return false;
2506
+ const imageFile = Array.from(files).find((f) => f.type.startsWith("image/"));
2507
+ if (!imageFile) return false;
2508
+ event.preventDefault();
2509
+ handleFile(imageFile, view);
2510
+ return true;
2511
+ },
2512
+ handleDrop(view, event) {
2513
+ const files = event.dataTransfer?.files;
2514
+ if (!files || files.length === 0) return false;
2515
+ const imageFile = Array.from(files).find((f) => f.type.startsWith("image/"));
2516
+ if (!imageFile) return false;
2517
+ event.preventDefault();
2518
+ handleFile(imageFile, view);
2519
+ return true;
2520
+ }
2521
+ }
2522
+ })];
2523
+ }
2524
+ };
2525
+ }
2526
+ //#endregion
2527
+ //#region src/extensions/line-height-extension.ts
2528
+ var DEFAULT_LINE_HEIGHTS = [
2529
+ {
2530
+ label: "单倍行距",
2531
+ value: 1
2532
+ },
2533
+ {
2534
+ label: "1.15 倍行距",
2535
+ value: 1.15
2536
+ },
2537
+ {
2538
+ label: "1.5 倍行距",
2539
+ value: 1.5
2540
+ },
2541
+ {
2542
+ label: "双倍行距",
2543
+ value: 2
2544
+ },
2545
+ {
2546
+ label: "2.5 倍行距",
2547
+ value: 2.5
2548
+ },
2549
+ {
2550
+ label: "3 倍行距",
2551
+ value: 3
2552
+ }
2553
+ ];
2554
+ function LineHeightExtension(config) {
2555
+ return {
2556
+ name: "lineHeight",
2557
+ type: "extension",
2558
+ options: { lineHeights: config?.lineHeights ?? DEFAULT_LINE_HEIGHTS },
2559
+ addCommands() {
2560
+ return { setLineHeight: (...args) => {
2561
+ const value = args[0];
2562
+ return setLineHeightCommand(value);
2563
+ } };
2564
+ }
2565
+ };
2566
+ }
2567
+ function setLineHeightCommand(value) {
2568
+ return (state, dispatch) => {
2569
+ const { from, to } = state.selection;
2570
+ let applicable = false;
2571
+ state.doc.nodesBetween(from, to, (node, pos) => {
2572
+ if (node.type.name !== "paragraph" && node.type.name !== "heading") return;
2573
+ applicable = true;
2574
+ if (dispatch) {
2575
+ const tr = state.tr;
2576
+ tr.setNodeMarkup(pos, null, {
2577
+ ...node.attrs,
2578
+ lineHeight: value
2579
+ });
2580
+ dispatch(tr);
2581
+ }
2582
+ });
2583
+ return applicable;
2584
+ };
2585
+ }
2586
+ //#endregion
2587
+ //#region src/extensions/version-history-extension.ts
2588
+ var versionHistoryKey = new require_dist.PluginKey("versionHistory");
2589
+ function generateId() {
2590
+ return `v_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
2591
+ }
2592
+ function createInitialState(versions) {
2593
+ return { versions };
2594
+ }
2595
+ function getVersions(state) {
2596
+ return versionHistoryKey.getState(state)?.versions ?? [];
2597
+ }
2598
+ function saveVersion(name) {
2599
+ return (state, dispatch) => {
2600
+ if (dispatch) dispatch(state.tr.setMeta(versionHistoryKey, {
2601
+ type: "save",
2602
+ name: name ?? `版本 ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}`
2603
+ }));
2604
+ return true;
2605
+ };
2606
+ }
2607
+ function restoreVersion(versionId) {
2608
+ return (state, dispatch) => {
2609
+ const pluginState = versionHistoryKey.getState(state);
2610
+ if (!pluginState) return false;
2611
+ const version = pluginState.versions.find((v) => v.id === versionId);
2612
+ if (!version) return false;
2613
+ if (dispatch) {
2614
+ const doc = require_dist.Node.fromJSON(state.schema, version.docJSON);
2615
+ const tr = state.tr.replaceWith(0, state.doc.content.size, doc.content);
2616
+ tr.setMeta(versionHistoryKey, { type: "restore" });
2617
+ dispatch(tr);
2618
+ }
2619
+ return true;
2620
+ };
2621
+ }
2622
+ function deleteVersion(versionId) {
2623
+ return (state, dispatch) => {
2624
+ if (dispatch) dispatch(state.tr.setMeta(versionHistoryKey, {
2625
+ type: "delete",
2626
+ versionId
2627
+ }));
2628
+ return true;
2629
+ };
2630
+ }
2631
+ function renameVersion(versionId, newName) {
2632
+ return (state, dispatch) => {
2633
+ if (dispatch) dispatch(state.tr.setMeta(versionHistoryKey, {
2634
+ type: "rename",
2635
+ versionId,
2636
+ name: newName
2637
+ }));
2638
+ return true;
2639
+ };
2640
+ }
2641
+ function VersionHistoryExtension(config) {
2642
+ const maxVersions = config?.maxVersions ?? 50;
2643
+ const autoSaveInterval = config?.autoSaveInterval ?? 0;
2644
+ const onVersionsChange = config?.onVersionsChange;
2645
+ const storage = config?.storage;
2646
+ let autoSaveTimer = null;
2647
+ let currentEditorState = null;
2648
+ let currentDispatch = null;
2649
+ return {
2650
+ name: "versionHistory",
2651
+ type: "extension",
2652
+ addCommands() {
2653
+ return {
2654
+ saveVersion: (...args) => {
2655
+ const name = args[0];
2656
+ return saveVersion(name);
2657
+ },
2658
+ restoreVersion: (...args) => {
2659
+ const versionId = args[0];
2660
+ return restoreVersion(versionId);
2661
+ },
2662
+ deleteVersion: (...args) => {
2663
+ const versionId = args[0];
2664
+ return deleteVersion(versionId);
2665
+ },
2666
+ renameVersion: (...args) => {
2667
+ const versionId = args[0];
2668
+ const newName = args[1];
2669
+ return renameVersion(versionId, newName);
2670
+ },
2671
+ listVersions: () => {
2672
+ return (state, dispatch) => {
2673
+ const versions = getVersions(state);
2674
+ if (dispatch && onVersionsChange) onVersionsChange(versions);
2675
+ return true;
2676
+ };
2677
+ }
2678
+ };
2679
+ },
2680
+ addPlugins() {
2681
+ return [new require_dist.Plugin({
2682
+ key: versionHistoryKey,
2683
+ state: {
2684
+ init() {
2685
+ const loaded = storage?.load();
2686
+ if (loaded && !(typeof loaded.then === "function")) return createInitialState(loaded);
2687
+ return createInitialState([]);
2688
+ },
2689
+ apply(tr, value, _oldState, newState) {
2690
+ const meta = tr.getMeta(versionHistoryKey);
2691
+ if (!meta) return value;
2692
+ let newVersions;
2693
+ switch (meta.type) {
2694
+ case "save":
2695
+ newVersions = [{
2696
+ id: generateId(),
2697
+ name: meta.name ?? `版本 ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}`,
2698
+ timestamp: Date.now(),
2699
+ docJSON: newState.doc.toJSON()
2700
+ }, ...value.versions];
2701
+ if (newVersions.length > maxVersions) newVersions = newVersions.slice(0, maxVersions);
2702
+ break;
2703
+ case "delete":
2704
+ newVersions = value.versions.filter((v) => v.id !== meta.versionId);
2705
+ break;
2706
+ case "rename":
2707
+ newVersions = value.versions.map((v) => v.id === meta.versionId ? {
2708
+ ...v,
2709
+ name: meta.name ?? v.name
2710
+ } : v);
2711
+ break;
2712
+ case "restore": return value;
2713
+ case "init": {
2714
+ const versions = meta.versions;
2715
+ return createInitialState(versions);
2716
+ }
2717
+ default: return value;
2718
+ }
2719
+ if (onVersionsChange) onVersionsChange(newVersions);
2720
+ if (storage) {
2721
+ const result = storage.save(newVersions);
2722
+ if (result && typeof result.then === "function") result.catch((err) => {
2723
+ console.error("[VersionHistory] Failed to save versions to storage:", err);
2724
+ });
2725
+ }
2726
+ return createInitialState(newVersions);
2727
+ }
2728
+ },
2729
+ view(editorView) {
2730
+ currentEditorState = editorView.state;
2731
+ currentDispatch = editorView.dispatch.bind(editorView);
2732
+ if (storage) {
2733
+ const loadResult = storage.load();
2734
+ if (loadResult && typeof loadResult.then === "function") loadResult.then((loaded) => {
2735
+ if (loaded && currentDispatch && currentEditorState) {
2736
+ const tr = currentEditorState.tr.setMeta(versionHistoryKey, {
2737
+ type: "init",
2738
+ versions: loaded
2739
+ });
2740
+ currentDispatch(tr);
2741
+ }
2742
+ });
2743
+ else if (loadResult) {
2744
+ const tr = editorView.state.tr.setMeta(versionHistoryKey, {
2745
+ type: "init",
2746
+ versions: loadResult
2747
+ });
2748
+ editorView.dispatch(tr);
2749
+ }
2750
+ }
2751
+ if (autoSaveInterval > 0) autoSaveTimer = setInterval(() => {
2752
+ if (currentEditorState && currentDispatch) {
2753
+ const tr = currentEditorState.tr.setMeta(versionHistoryKey, {
2754
+ type: "save",
2755
+ name: `自动保存 ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}`
2756
+ });
2757
+ currentDispatch(tr);
2758
+ }
2759
+ }, autoSaveInterval);
2760
+ return {
2761
+ update(view) {
2762
+ currentEditorState = view.state;
2763
+ currentDispatch = view.dispatch.bind(view);
2764
+ },
2765
+ destroy() {
2766
+ if (autoSaveTimer) {
2767
+ clearInterval(autoSaveTimer);
2768
+ autoSaveTimer = null;
2769
+ }
2770
+ currentEditorState = null;
2771
+ currentDispatch = null;
2772
+ }
2773
+ };
2774
+ }
2775
+ })];
2776
+ }
2777
+ };
2778
+ }
2779
+ function createLocalStorage(key = "mibao-editor-versions") {
2780
+ return {
2781
+ save(versions) {
2782
+ try {
2783
+ localStorage.setItem(key, JSON.stringify(versions));
2784
+ } catch (err) {
2785
+ console.error("[VersionHistory] localStorage save failed:", err);
2786
+ }
2787
+ },
2788
+ load() {
2789
+ try {
2790
+ const data = localStorage.getItem(key);
2791
+ return data ? JSON.parse(data) : null;
2792
+ } catch {
2793
+ return null;
2794
+ }
2795
+ }
2796
+ };
2797
+ }
2798
+ //#endregion
2799
+ //#region src/extensions/toc-node-extension.ts
2800
+ function TocNodeExtension() {
2801
+ return {
2802
+ name: "tocNode",
2803
+ type: "node",
2804
+ addNodes() {
2805
+ return { toc: {
2806
+ group: "block",
2807
+ atom: true,
2808
+ attrs: {
2809
+ items: { default: "[]" },
2810
+ title: { default: "目录" }
2811
+ },
2812
+ toDOM(node) {
2813
+ const items = JSON.parse(node.attrs.items);
2814
+ const title = node.attrs.title;
2815
+ const container = document.createElement("div");
2816
+ container.className = "mi-toc";
2817
+ container.contentEditable = "false";
2818
+ const header = document.createElement("div");
2819
+ header.className = "mi-toc-header";
2820
+ const titleSpan = document.createElement("span");
2821
+ titleSpan.className = "mi-toc-title";
2822
+ titleSpan.textContent = title;
2823
+ header.appendChild(titleSpan);
2824
+ container.appendChild(header);
2825
+ if (items.length === 0) {
2826
+ const empty = document.createElement("div");
2827
+ empty.className = "mi-toc-empty";
2828
+ empty.textContent = "暂无标题,添加标题后将自动生成目录";
2829
+ container.appendChild(empty);
2830
+ } else {
2831
+ const list = document.createElement("ul");
2832
+ list.className = "mi-toc-list";
2833
+ const minLevel = Math.min(...items.map((i) => i.level));
2834
+ items.forEach((item) => {
2835
+ const li = document.createElement("li");
2836
+ li.className = `mi-toc-item mi-toc-level-${item.level}`;
2837
+ li.style.paddingLeft = `${(item.level - minLevel) * 16}px`;
2838
+ const a = document.createElement("a");
2839
+ a.href = `#${item.id}`;
2840
+ a.className = "mi-toc-link";
2841
+ a.textContent = item.text;
2842
+ li.appendChild(a);
2843
+ list.appendChild(li);
2844
+ });
2845
+ container.appendChild(list);
2846
+ }
2847
+ const deleteBtn = document.createElement("button");
2848
+ deleteBtn.className = "mi-toc-delete";
2849
+ deleteBtn.textContent = "×";
2850
+ deleteBtn.title = "删除目录";
2851
+ container.appendChild(deleteBtn);
2852
+ return container;
2853
+ },
2854
+ parseDOM: [{
2855
+ tag: "div.mi-toc",
2856
+ getAttrs(dom) {
2857
+ const el = dom;
2858
+ const titleEl = el.querySelector(".mi-toc-title");
2859
+ return {
2860
+ items: el.getAttribute("data-items") || "[]",
2861
+ title: titleEl?.textContent || "目录"
2862
+ };
2863
+ }
2864
+ }]
2865
+ } };
2866
+ },
2867
+ addCommands() {
2868
+ return { insertTocNode: (...args) => {
2869
+ const attrs = args[0];
2870
+ return (state, dispatch) => {
2871
+ const tocType = state.schema.nodes["toc"];
2872
+ if (!tocType) return false;
2873
+ const { from, to } = state.selection;
2874
+ const node = tocType.create({
2875
+ items: attrs?.items || "[]",
2876
+ title: attrs?.title || "目录"
2877
+ });
2878
+ if (dispatch) dispatch(state.tr.replaceWith(from, to, node).scrollIntoView());
2879
+ return true;
2880
+ };
2881
+ } };
2882
+ },
2883
+ addPlugins() {
2884
+ return [new require_dist.Plugin({ props: { handleDOMEvents: { click(view, event) {
2885
+ const target = event.target;
2886
+ if (target.classList.contains("mi-toc-delete")) {
2887
+ event.preventDefault();
2888
+ event.stopPropagation();
2889
+ const toc = target.closest(".mi-toc");
2890
+ if (!toc || !view.dom.contains(toc)) return false;
2891
+ const pos = view.posAtDOM(toc, 0);
2892
+ const node = view.state.doc.nodeAt(pos);
2893
+ if (!node || node.type.name !== "toc") return false;
2894
+ const tr = view.state.tr.delete(pos, pos + node.nodeSize);
2895
+ view.dispatch(tr);
2896
+ return true;
2897
+ }
2898
+ if (target.classList.contains("mi-toc-link")) {
2899
+ event.preventDefault();
2900
+ const href = target.getAttribute("href");
2901
+ if (!href || !href.startsWith("#")) return false;
2902
+ const id = href.slice(1);
2903
+ const targetEl = document.getElementById(id);
2904
+ if (targetEl) targetEl.scrollIntoView({
2905
+ behavior: "smooth",
2906
+ block: "start"
2907
+ });
2908
+ return true;
2909
+ }
2910
+ return false;
2911
+ } } } })];
2912
+ }
2913
+ };
2914
+ }
2915
+ //#endregion
2916
+ //#region src/extensions/code-block-enhanced-extension.ts
2917
+ var codeBlockKey = new require_dist.PluginKey("codeBlockEnhanced");
2918
+ var DEFAULT_LANGUAGES = [
2919
+ "javascript",
2920
+ "typescript",
2921
+ "python",
2922
+ "java",
2923
+ "cpp",
2924
+ "c",
2925
+ "go",
2926
+ "rust",
2927
+ "html",
2928
+ "css",
2929
+ "scss",
2930
+ "json",
2931
+ "yaml",
2932
+ "markdown",
2933
+ "bash",
2934
+ "sql",
2935
+ "php",
2936
+ "ruby",
2937
+ "swift",
2938
+ "kotlin",
2939
+ "xml",
2940
+ "dockerfile",
2941
+ "plaintext"
2942
+ ];
2943
+ var CodeBlockNodeView = class {
2944
+ constructor(node, view, getPos, _decorations, languages) {
2945
+ this.node = node;
2946
+ this.view = view;
2947
+ this.getPos = getPos;
2948
+ this.languages = languages;
2949
+ const language = node.attrs.language;
2950
+ this.dom = document.createElement("div");
2951
+ this.dom.className = "mi-code-block";
2952
+ this.dom.setAttribute("data-language", language);
2953
+ const header = document.createElement("div");
2954
+ header.className = "mi-code-block-header";
2955
+ const langSelect = document.createElement("select");
2956
+ langSelect.className = "mi-code-block-lang";
2957
+ languages.forEach((lang) => {
2958
+ const option = document.createElement("option");
2959
+ option.value = lang;
2960
+ option.textContent = lang;
2961
+ if (lang === language) option.selected = true;
2962
+ langSelect.appendChild(option);
2963
+ });
2964
+ langSelect.addEventListener("mousedown", (e) => {
2965
+ e.stopPropagation();
2966
+ });
2967
+ langSelect.addEventListener("change", () => {
2968
+ const pos = this.getPos();
2969
+ if (pos === void 0) return;
2970
+ const currentNode = this.view.state.doc.nodeAt(pos);
2971
+ if (!currentNode || currentNode.type.name !== "code_block") return;
2972
+ const tr = this.view.state.tr.setNodeMarkup(pos, null, {
2973
+ ...currentNode.attrs,
2974
+ language: langSelect.value
2975
+ });
2976
+ this.view.dispatch(tr);
2977
+ });
2978
+ const copyBtn = document.createElement("button");
2979
+ copyBtn.className = "mi-code-block-copy";
2980
+ copyBtn.textContent = "复制";
2981
+ copyBtn.title = "复制代码";
2982
+ copyBtn.addEventListener("mousedown", (e) => {
2983
+ e.preventDefault();
2984
+ e.stopPropagation();
2985
+ });
2986
+ copyBtn.addEventListener("click", (e) => {
2987
+ e.preventDefault();
2988
+ e.stopPropagation();
2989
+ const pos = this.getPos();
2990
+ if (pos === void 0) return;
2991
+ const currentNode = this.view.state.doc.nodeAt(pos);
2992
+ if (!currentNode) return;
2993
+ const text = currentNode.textContent || "";
2994
+ navigator.clipboard.writeText(text).then(() => {
2995
+ copyBtn.textContent = "已复制 ✓";
2996
+ setTimeout(() => {
2997
+ copyBtn.textContent = "复制";
2998
+ }, 2e3);
2999
+ }).catch(() => {
3000
+ try {
3001
+ const textarea = document.createElement("textarea");
3002
+ textarea.value = text;
3003
+ textarea.style.cssText = "position:fixed;left:-9999px;top:-9999px;opacity:0";
3004
+ document.body.appendChild(textarea);
3005
+ textarea.select();
3006
+ document.execCommand("copy");
3007
+ document.body.removeChild(textarea);
3008
+ } catch {}
3009
+ copyBtn.textContent = "已复制 ✓";
3010
+ setTimeout(() => {
3011
+ copyBtn.textContent = "复制";
3012
+ }, 2e3);
3013
+ });
3014
+ });
3015
+ header.appendChild(langSelect);
3016
+ header.appendChild(copyBtn);
3017
+ const deleteBtn = document.createElement("button");
3018
+ deleteBtn.className = "mi-code-block-delete";
3019
+ deleteBtn.textContent = "×";
3020
+ deleteBtn.title = "删除代码块";
3021
+ deleteBtn.addEventListener("mousedown", (e) => {
3022
+ e.preventDefault();
3023
+ e.stopPropagation();
3024
+ });
3025
+ deleteBtn.addEventListener("click", (e) => {
3026
+ e.preventDefault();
3027
+ e.stopPropagation();
3028
+ const pos = this.getPos();
3029
+ if (pos === void 0) return;
3030
+ const currentNode = this.view.state.doc.nodeAt(pos);
3031
+ if (!currentNode || currentNode.type.name !== "code_block") return;
3032
+ const tr = this.view.state.tr.delete(pos, pos + currentNode.nodeSize);
3033
+ this.view.dispatch(tr);
3034
+ });
3035
+ header.appendChild(deleteBtn);
3036
+ const pre = document.createElement("pre");
3037
+ pre.className = "mi-code-block-pre";
3038
+ const codeEl = document.createElement("code");
3039
+ codeEl.className = `language-${language}`;
3040
+ codeEl.spellcheck = false;
3041
+ this.contentDOM = codeEl;
3042
+ pre.appendChild(codeEl);
3043
+ this.dom.appendChild(header);
3044
+ this.dom.appendChild(pre);
3045
+ }
3046
+ update(node) {
3047
+ if (node.type !== this.node.type) return false;
3048
+ this.node = node;
3049
+ const language = node.attrs.language;
3050
+ this.dom.setAttribute("data-language", language);
3051
+ const codeEl = this.contentDOM;
3052
+ codeEl.className = `language-${language}`;
3053
+ const select = this.dom.querySelector(".mi-code-block-lang");
3054
+ if (select && select.value !== language) select.value = language;
3055
+ return true;
3056
+ }
3057
+ stopEvent() {
3058
+ return false;
3059
+ }
3060
+ ignoreMutation() {
3061
+ return false;
3062
+ }
3063
+ };
3064
+ function CodeBlockEnhancedExtension(config) {
3065
+ const defaultLanguage = config?.defaultLanguage || "plaintext";
3066
+ const languages = config?.languages || DEFAULT_LANGUAGES;
3067
+ return {
3068
+ name: "codeBlockEnhanced",
3069
+ type: "node",
3070
+ addNodes() {
3071
+ return { code_block: {
3072
+ group: "block",
3073
+ content: "text*",
3074
+ marks: "",
3075
+ code: true,
3076
+ defining: true,
3077
+ attrs: { language: { default: defaultLanguage } },
3078
+ toDOM(node) {
3079
+ return [
3080
+ "div",
3081
+ {
3082
+ class: "mi-code-block",
3083
+ "data-language": node.attrs.language
3084
+ },
3085
+ [
3086
+ "div",
3087
+ { class: "mi-code-block-header" },
3088
+ ["select", { class: "mi-code-block-lang" }],
3089
+ [
3090
+ "button",
3091
+ { class: "mi-code-block-copy" },
3092
+ "复制"
3093
+ ],
3094
+ [
3095
+ "button",
3096
+ { class: "mi-code-block-delete" },
3097
+ "×"
3098
+ ]
3099
+ ],
3100
+ [
3101
+ "pre",
3102
+ { class: "mi-code-block-pre" },
3103
+ [
3104
+ "code",
3105
+ {
3106
+ class: `language-${node.attrs.language}`,
3107
+ spellcheck: "false"
3108
+ },
3109
+ 0
3110
+ ]
3111
+ ]
3112
+ ];
3113
+ },
3114
+ parseDOM: [{
3115
+ tag: "div.mi-code-block",
3116
+ getAttrs(dom) {
3117
+ const el = dom;
3118
+ return { language: el.querySelector(".mi-code-block-lang")?.value || el.getAttribute("data-language") || defaultLanguage };
3119
+ },
3120
+ getContent: (dom, schema) => {
3121
+ const text = dom.querySelector("code")?.textContent || "";
3122
+ return require_dist.Fragment.from(schema.text(text));
3123
+ },
3124
+ preserveWhitespace: "full"
3125
+ }]
3126
+ } };
3127
+ },
3128
+ addCommands() {
3129
+ return {
3130
+ codeBlock: (...args) => {
3131
+ const language = args[0] || defaultLanguage;
3132
+ return (state, dispatch) => {
3133
+ const nt = state.schema.nodes["code_block"];
3134
+ if (!nt) return false;
3135
+ const { $from } = state.selection;
3136
+ if ($from.parent.type === nt) {
3137
+ const paraType = state.schema.nodes["paragraph"];
3138
+ if (!paraType) return false;
3139
+ return require_dist.setBlockType(paraType)(state, dispatch);
3140
+ }
3141
+ return require_dist.setBlockType(nt, { language })(state, dispatch);
3142
+ };
3143
+ },
3144
+ setCodeBlockLanguage: (...args) => {
3145
+ const language = args[0];
3146
+ return (state, dispatch) => {
3147
+ const { $from } = state.selection;
3148
+ const nt = state.schema.nodes["code_block"];
3149
+ if (!nt) return false;
3150
+ let codeBlockPos = -1;
3151
+ for (let d = $from.depth; d > 0; d--) if ($from.node(d).type === nt) {
3152
+ codeBlockPos = $from.before(d);
3153
+ break;
3154
+ }
3155
+ if (codeBlockPos < 0) return false;
3156
+ if (dispatch) {
3157
+ const node = state.doc.nodeAt(codeBlockPos);
3158
+ if (!node) return false;
3159
+ dispatch(state.tr.setNodeMarkup(codeBlockPos, null, {
3160
+ ...node.attrs,
3161
+ language
3162
+ }));
3163
+ }
3164
+ return true;
3165
+ };
3166
+ },
3167
+ deleteCodeBlock: () => {
3168
+ return (state, dispatch) => {
3169
+ const { $from } = state.selection;
3170
+ const nt = state.schema.nodes["code_block"];
3171
+ if (!nt) return false;
3172
+ let codeBlockPos = -1;
3173
+ for (let d = $from.depth; d > 0; d--) if ($from.node(d).type === nt) {
3174
+ codeBlockPos = $from.before(d);
3175
+ break;
3176
+ }
3177
+ if (codeBlockPos < 0) return false;
3178
+ if (dispatch) {
3179
+ const node = state.doc.nodeAt(codeBlockPos);
3180
+ if (!node) return false;
3181
+ dispatch(state.tr.delete(codeBlockPos, codeBlockPos + node.nodeSize));
3182
+ }
3183
+ return true;
3184
+ };
3185
+ }
3186
+ };
3187
+ },
3188
+ addKeyboardShortcuts() {
3189
+ return {
3190
+ "Mod-Shift-c": (state, dispatch) => {
3191
+ const nt = state.schema.nodes["code_block"];
3192
+ if (!nt) return false;
3193
+ const { $from } = state.selection;
3194
+ if ($from.parent.type === nt) {
3195
+ const paraType = state.schema.nodes["paragraph"];
3196
+ if (!paraType) return false;
3197
+ return require_dist.setBlockType(paraType)(state, dispatch);
3198
+ }
3199
+ return require_dist.setBlockType(nt)(state, dispatch);
3200
+ },
3201
+ "Enter": (state, dispatch) => {
3202
+ const nt = state.schema.nodes["code_block"];
3203
+ if (!nt) return false;
3204
+ const { $from } = state.selection;
3205
+ if ($from.parent.type !== nt) return false;
3206
+ if ($from.parent.content.size > 0) return false;
3207
+ const paraType = state.schema.nodes["paragraph"];
3208
+ if (!paraType) return false;
3209
+ return require_dist.setBlockType(paraType)(state, dispatch);
3210
+ },
3211
+ "Backspace": (state, dispatch) => {
3212
+ const nt = state.schema.nodes["code_block"];
3213
+ if (!nt) return false;
3214
+ const { $from } = state.selection;
3215
+ if ($from.parent.type !== nt) return false;
3216
+ if ($from.parent.content.size > 0) return false;
3217
+ const paraType = state.schema.nodes["paragraph"];
3218
+ if (!paraType) return false;
3219
+ return require_dist.setBlockType(paraType)(state, dispatch);
3220
+ }
3221
+ };
3222
+ },
3223
+ addPlugins() {
3224
+ return [inputRules({ rules: [new InputRule(/^```(\w*)$/, (state, match, start, end) => {
3225
+ const nt = state.schema.nodes["code_block"];
3226
+ if (!nt) return null;
3227
+ const language = match[1] || defaultLanguage;
3228
+ const tr = state.tr;
3229
+ tr.delete(start, end);
3230
+ const range = tr.doc.resolve(start).blockRange();
3231
+ if (!range) return null;
3232
+ tr.setBlockType(range.start, range.end, nt, { language });
3233
+ return tr;
3234
+ })] }), new require_dist.Plugin({
3235
+ key: codeBlockKey,
3236
+ props: { nodeViews: { code_block(node, view, getPos, decorations) {
3237
+ return new CodeBlockNodeView(node, view, getPos, decorations, languages);
3238
+ } } }
3239
+ })];
3240
+ }
3241
+ };
3242
+ }
3243
+ //#endregion
3244
+ //#region src/extensions/dark-mode-extension.ts
3245
+ var darkModeKey = new require_dist.PluginKey("darkMode");
3246
+ function DarkModeExtension(config) {
3247
+ const defaultDark = config?.defaultDark ?? false;
3248
+ return {
3249
+ name: "darkMode",
3250
+ type: "extension",
3251
+ addPlugins() {
3252
+ return [new require_dist.Plugin({
3253
+ key: darkModeKey,
3254
+ state: {
3255
+ init() {
3256
+ return { isDark: defaultDark };
3257
+ },
3258
+ apply(tr, prev) {
3259
+ const meta = tr.getMeta(darkModeKey);
3260
+ if (meta?.type === "toggle") {
3261
+ const isDark = !prev.isDark;
3262
+ if (config?.onChange) setTimeout(() => config.onChange(isDark), 0);
3263
+ return { isDark };
3264
+ }
3265
+ if (meta?.type === "set") {
3266
+ const isDark = !!meta.isDark;
3267
+ if (config?.onChange) setTimeout(() => config.onChange(isDark), 0);
3268
+ return { isDark };
3269
+ }
3270
+ return prev;
3271
+ }
3272
+ },
3273
+ props: { attributes(state) {
3274
+ if (darkModeKey.getState(state)?.isDark) return {
3275
+ "data-theme": "dark",
3276
+ "class": "mi-dark-mode"
3277
+ };
3278
+ return {};
3279
+ } }
3280
+ })];
3281
+ },
3282
+ addCommands() {
3283
+ return {
3284
+ toggleDarkMode: () => (state, dispatch) => {
3285
+ if (dispatch) dispatch(state.tr.setMeta(darkModeKey, { type: "toggle" }));
3286
+ return true;
3287
+ },
3288
+ setDarkMode: (...args) => {
3289
+ const isDark = args[0];
3290
+ return (state, dispatch) => {
3291
+ if (dispatch) dispatch(state.tr.setMeta(darkModeKey, {
3292
+ type: "set",
3293
+ isDark
3294
+ }));
3295
+ return true;
3296
+ };
3297
+ }
3298
+ };
3299
+ }
3300
+ };
3301
+ }
3302
+ function isDarkMode(state) {
3303
+ return darkModeKey.getState(state)?.isDark ?? false;
3304
+ }
3305
+ //#endregion
3306
+ //#region src/extensions/html-block-extension.ts
3307
+ /**
3308
+ * HTMLBlockExtension: supports parsing and preserving arbitrary HTML block elements
3309
+ * like div, section, etc. This is useful for pasting complex HTML content from
3310
+ * external sources (e.g., 135编辑器, wangEditor) while preserving the structure.
3311
+ */
3312
+ function HTMLBlockExtension() {
3313
+ return {
3314
+ name: "html_block",
3315
+ type: "node",
3316
+ addNodes() {
3317
+ return { html_block: {
3318
+ group: "block",
3319
+ content: "block*",
3320
+ atom: false,
3321
+ defining: true,
3322
+ attrs: {
3323
+ tagName: { default: "div" },
3324
+ className: { default: "" },
3325
+ style: { default: "" },
3326
+ dataAttributes: { default: {} }
3327
+ },
3328
+ toDOM(node) {
3329
+ const { tagName, className, style, dataAttributes } = node.attrs;
3330
+ const attrs = {};
3331
+ if (className) attrs.class = className;
3332
+ if (style) attrs.style = style;
3333
+ if (dataAttributes && typeof dataAttributes === "object") {
3334
+ for (const [key, value] of Object.entries(dataAttributes)) if (value !== null && value !== void 0) attrs[key] = String(value);
3335
+ }
3336
+ return [
3337
+ tagName || "div",
3338
+ attrs,
3339
+ 0
3340
+ ];
3341
+ },
3342
+ parseDOM: [
3343
+ {
3344
+ tag: "div:not(.mi-card):not(.mi-product-card):not(.mi-code-block):not(.mi-toc):not(.mi-task-list):not(.mi-task-item)",
3345
+ getAttrs(dom) {
3346
+ return extractAttributes(dom, "div");
3347
+ }
3348
+ },
3349
+ {
3350
+ tag: "section",
3351
+ getAttrs(dom) {
3352
+ return extractAttributes(dom, "section");
3353
+ }
3354
+ },
3355
+ {
3356
+ tag: "article",
3357
+ getAttrs(dom) {
3358
+ return extractAttributes(dom, "article");
3359
+ }
3360
+ },
3361
+ {
3362
+ tag: "aside",
3363
+ getAttrs(dom) {
3364
+ return extractAttributes(dom, "aside");
3365
+ }
3366
+ },
3367
+ {
3368
+ tag: "main",
3369
+ getAttrs(dom) {
3370
+ return extractAttributes(dom, "main");
3371
+ }
3372
+ },
3373
+ {
3374
+ tag: "header",
3375
+ getAttrs(dom) {
3376
+ return extractAttributes(dom, "header");
3377
+ }
3378
+ },
3379
+ {
3380
+ tag: "footer",
3381
+ getAttrs(dom) {
3382
+ return extractAttributes(dom, "footer");
3383
+ }
3384
+ },
3385
+ {
3386
+ tag: "nav",
3387
+ getAttrs(dom) {
3388
+ return extractAttributes(dom, "nav");
3389
+ }
3390
+ }
3391
+ ]
3392
+ } };
3393
+ }
3394
+ };
3395
+ }
3396
+ /**
3397
+ * Extract attributes from a DOM element for the html_block node
3398
+ */
3399
+ function extractAttributes(el, tagName) {
3400
+ const className = el.className || "";
3401
+ const style = el.getAttribute("style") || "";
3402
+ const dataAttributes = {};
3403
+ for (const attr of Array.from(el.attributes)) if (attr.name.startsWith("data-")) dataAttributes[attr.name] = attr.value;
3404
+ return {
3405
+ tagName,
3406
+ className,
3407
+ style,
3408
+ dataAttributes
3409
+ };
3410
+ }
3411
+ //#endregion
3412
+ exports.BackgroundColorExtension = BackgroundColorExtension;
3413
+ exports.BlockquoteExtension = BlockquoteExtension;
3414
+ exports.BoldExtension = BoldExtension;
3415
+ exports.BulletListExtension = BulletListExtension;
3416
+ exports.CardExtension = CardExtension;
3417
+ exports.CodeBlockEnhancedExtension = CodeBlockEnhancedExtension;
3418
+ exports.CodeBlockExtension = CodeBlockExtension;
3419
+ exports.CodeExtension = CodeExtension;
3420
+ exports.DEFAULT_COLORS = DEFAULT_COLORS;
3421
+ exports.DEFAULT_FONT_FAMILIES = DEFAULT_FONT_FAMILIES;
3422
+ exports.DEFAULT_FONT_SIZES = DEFAULT_FONT_SIZES;
3423
+ exports.DEFAULT_LINE_HEIGHTS = DEFAULT_LINE_HEIGHTS;
3424
+ exports.DarkModeExtension = DarkModeExtension;
3425
+ exports.DocExtension = DocExtension;
3426
+ exports.FontFamilyExtension = FontFamilyExtension;
3427
+ exports.FontSizeExtension = FontSizeExtension;
3428
+ exports.HTMLBlockExtension = HTMLBlockExtension;
3429
+ exports.HardBreakExtension = HardBreakExtension;
3430
+ exports.HeadingExtension = HeadingExtension;
3431
+ exports.HistoryExtension = HistoryExtension;
3432
+ exports.HorizontalRuleExtension = HorizontalRuleExtension;
3433
+ exports.ImageExtension = ImageExtension;
3434
+ exports.ImageUploadExtension = ImageUploadExtension;
3435
+ exports.ItalicExtension = ItalicExtension;
3436
+ exports.LineHeightExtension = LineHeightExtension;
3437
+ exports.LinkExtension = LinkExtension;
3438
+ exports.OrderedListExtension = OrderedListExtension;
3439
+ exports.ParagraphExtension = ParagraphExtension;
3440
+ exports.PlaceholderExtension = PlaceholderExtension;
3441
+ exports.ProductCardExtension = ProductCardExtension;
3442
+ exports.StrikeExtension = StrikeExtension;
3443
+ exports.TableExtension = TableExtension;
3444
+ exports.TableOfContentsExtension = require_toc_extension.TableOfContentsExtension;
3445
+ exports.TaskListExtension = TaskListExtension;
3446
+ exports.TextColorExtension = TextColorExtension;
3447
+ exports.TextExtension = TextExtension;
3448
+ exports.TocNodeExtension = TocNodeExtension;
3449
+ exports.UnderlineExtension = UnderlineExtension;
3450
+ exports.VersionHistoryExtension = VersionHistoryExtension;
3451
+ exports.WordCountExtension = require_toc_extension.WordCountExtension;
3452
+ exports.createLocalStorage = createLocalStorage;
3453
+ exports.deleteVersion = deleteVersion;
3454
+ exports.getTableOfContents = require_toc_extension.getTableOfContents;
3455
+ exports.getVersions = getVersions;
3456
+ exports.getWordCountStats = require_toc_extension.getWordCountStats;
3457
+ exports.isDarkMode = isDarkMode;
3458
+ exports.renameVersion = renameVersion;
3459
+ exports.restoreVersion = restoreVersion;
3460
+ exports.saveVersion = saveVersion;
3461
+
3462
+ //# sourceMappingURL=index.cjs.js.map