@thescaffold/editor-addons 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,4111 @@
1
+ // src/base/commands/index.ts
2
+ function CommandsAddon() {
3
+ return {
4
+ id: "x/commands",
5
+ dependencies: ["x/schema"],
6
+ impl: CommandsAddonImpl
7
+ };
8
+ }
9
+ var CommandsAddonImpl = {
10
+ id: "x/commands",
11
+ init(ctx) {
12
+ ctx.commands.register({
13
+ id: "selectAll",
14
+ label: "Select All",
15
+ shortcut: "Mod-a",
16
+ isEnabled: () => true,
17
+ isActive: () => false,
18
+ execute: () => true
19
+ });
20
+ return {};
21
+ }
22
+ };
23
+
24
+ // src/base/history/index.ts
25
+ function HistoryAddon() {
26
+ return { id: "x/history", impl: HistoryAddonImpl };
27
+ }
28
+ var HistoryAddonImpl = {
29
+ id: "x/history",
30
+ init(ctx) {
31
+ const ctxImpl = ctx;
32
+ ctx.commands.register({
33
+ id: "undo",
34
+ label: "Undo",
35
+ icon: "undo",
36
+ shortcut: "Mod-z",
37
+ isEnabled: () => true,
38
+ isActive: () => false,
39
+ execute: () => ctxImpl.dispatchPmCommand("undo")
40
+ });
41
+ ctx.commands.register({
42
+ id: "redo",
43
+ label: "Redo",
44
+ icon: "redo",
45
+ shortcut: "Mod-Shift-z",
46
+ isEnabled: () => true,
47
+ isActive: () => false,
48
+ execute: () => ctxImpl.dispatchPmCommand("redo")
49
+ });
50
+ return {};
51
+ }
52
+ };
53
+
54
+ // src/base/input-rules/index.ts
55
+ function InputRulesAddon() {
56
+ return {
57
+ id: "x/input-rules",
58
+ dependencies: ["x/schema"],
59
+ impl: InputRulesAddonImpl
60
+ };
61
+ }
62
+ var InputRulesAddonImpl = {
63
+ id: "x/input-rules",
64
+ init(_ctx) {
65
+ return {};
66
+ }
67
+ };
68
+
69
+ // src/base/keymap/index.ts
70
+ function KeymapAddon(config) {
71
+ return {
72
+ id: "x/keymap",
73
+ config,
74
+ dependencies: ["x/commands"],
75
+ impl: KeymapAddonImpl
76
+ };
77
+ }
78
+ var IS_MAC = typeof navigator !== "undefined" && /Mac|iP(hone|ad|od)/.test(navigator.platform);
79
+ function normalizeShortcut(shortcut) {
80
+ return shortcut.split("-").map((part) => part.trim().toLowerCase()).filter(Boolean).sort((a, b) => {
81
+ const order = ["mod", "meta", "ctrl", "alt", "shift"];
82
+ const ai = order.indexOf(a);
83
+ const bi = order.indexOf(b);
84
+ if (ai === -1 && bi === -1) return 0;
85
+ if (ai === -1) return 1;
86
+ if (bi === -1) return -1;
87
+ return ai - bi;
88
+ }).join("-");
89
+ }
90
+ function eventToShortcut(e) {
91
+ const parts = [];
92
+ if (e.metaKey || e.ctrlKey) parts.push("mod");
93
+ if (e.altKey) parts.push("alt");
94
+ if (e.shiftKey) parts.push("shift");
95
+ let key = e.key;
96
+ if (key.length === 1) key = key.toLowerCase();
97
+ if (key === " ") key = "space";
98
+ parts.push(key.toLowerCase());
99
+ return parts.join("-");
100
+ }
101
+ var KeymapAddonImpl = {
102
+ id: "x/keymap",
103
+ init(ctx, config = {}) {
104
+ const disabled = new Set(config.disabled ?? []);
105
+ const overrides = config.overrides ?? {};
106
+ const handleKeydown = (e) => {
107
+ const shortcut = eventToShortcut(e);
108
+ for (const cmd of ctx.commands.getAll()) {
109
+ if (disabled.has(cmd.id)) continue;
110
+ const cmdShortcut = overrides[cmd.id] ?? cmd.shortcut;
111
+ if (!cmdShortcut) continue;
112
+ if (normalizeShortcut(cmdShortcut) === shortcut) {
113
+ if (cmd.isEnabled?.(ctx) === false) continue;
114
+ e.preventDefault();
115
+ cmd.execute(ctx);
116
+ return;
117
+ }
118
+ }
119
+ };
120
+ const content = ctx.getSlot("content");
121
+ const target = content ?? document;
122
+ target.addEventListener("keydown", handleKeydown, true);
123
+ return {
124
+ destroy() {
125
+ target.removeEventListener(
126
+ "keydown",
127
+ handleKeydown,
128
+ true
129
+ );
130
+ }
131
+ };
132
+ }
133
+ };
134
+
135
+ // src/base/schema/index.ts
136
+ function SchemaAddon(config) {
137
+ return { id: "x/schema", config, impl: SchemaAddonImpl };
138
+ }
139
+ var SchemaAddonImpl = {
140
+ id: "x/schema",
141
+ initSchema(ctx, _config = {}) {
142
+ ctx.addNode({
143
+ name: "doc",
144
+ content: "block+"
145
+ });
146
+ ctx.addNode({
147
+ name: "paragraph",
148
+ group: "block",
149
+ content: "inline*",
150
+ attrs: {
151
+ align: { default: null },
152
+ indent: { default: 0 },
153
+ lineHeight: { default: null }
154
+ },
155
+ parseDOM: [
156
+ {
157
+ tag: "p",
158
+ getAttrs: (el) => {
159
+ const e = el;
160
+ return {
161
+ align: e.style.textAlign || null,
162
+ indent: parseInt(e.dataset["indent"] ?? "0", 10) || 0,
163
+ lineHeight: e.style.lineHeight || null
164
+ };
165
+ }
166
+ }
167
+ ],
168
+ toDOM: (node) => {
169
+ const style = [];
170
+ if (node.attrs.align) style.push(`text-align:${node.attrs.align}`);
171
+ if (node.attrs.lineHeight)
172
+ style.push(`line-height:${node.attrs.lineHeight}`);
173
+ const attrs = {};
174
+ if (style.length > 0) attrs["style"] = style.join(";");
175
+ if (node.attrs.indent > 0)
176
+ attrs["data-indent"] = String(node.attrs.indent);
177
+ return ["p", attrs, 0];
178
+ }
179
+ });
180
+ ctx.addNode({
181
+ name: "text",
182
+ group: "inline"
183
+ });
184
+ ctx.addNode({
185
+ name: "hardBreak",
186
+ group: "inline",
187
+ inline: true,
188
+ selectable: false,
189
+ parseDOM: [{ tag: "br" }],
190
+ toDOM: () => ["br"]
191
+ });
192
+ },
193
+ init(_ctx, _config = {}) {
194
+ return {};
195
+ }
196
+ };
197
+
198
+ // src/marks/index.ts
199
+ function BoldAddon() {
200
+ return { id: "x/bold", dependencies: ["x/schema"], impl: BoldAddonImpl };
201
+ }
202
+ var BoldAddonImpl = {
203
+ id: "x/bold",
204
+ initSchema(ctx) {
205
+ ctx.addMark({
206
+ name: "bold",
207
+ parseDOM: [
208
+ { tag: "strong" },
209
+ { tag: "b" },
210
+ { style: "font-weight=bold" }
211
+ ],
212
+ toDOM: () => ["strong", 0]
213
+ });
214
+ },
215
+ init(ctx) {
216
+ ctx.commands.register({
217
+ id: "bold",
218
+ label: "Bold",
219
+ icon: "bold",
220
+ shortcut: "Mod-b",
221
+ isEnabled: (c) => c.selection.canHaveMark("bold"),
222
+ isActive: (c) => c.selection.hasMark("bold"),
223
+ execute: (c) => {
224
+ c.addMark("bold");
225
+ return true;
226
+ }
227
+ });
228
+ return {};
229
+ }
230
+ };
231
+ function ItalicAddon() {
232
+ return { id: "x/italic", dependencies: ["x/schema"], impl: ItalicAddonImpl };
233
+ }
234
+ var ItalicAddonImpl = {
235
+ id: "x/italic",
236
+ initSchema(ctx) {
237
+ ctx.addMark({
238
+ name: "italic",
239
+ parseDOM: [{ tag: "em" }, { tag: "i" }, { style: "font-style=italic" }],
240
+ toDOM: () => ["em", 0]
241
+ });
242
+ },
243
+ init(ctx) {
244
+ ctx.commands.register({
245
+ id: "italic",
246
+ label: "Italic",
247
+ icon: "italic",
248
+ shortcut: "Mod-i",
249
+ isEnabled: (c) => c.selection.canHaveMark("italic"),
250
+ isActive: (c) => c.selection.hasMark("italic"),
251
+ execute: (c) => {
252
+ c.addMark("italic");
253
+ return true;
254
+ }
255
+ });
256
+ return {};
257
+ }
258
+ };
259
+ function UnderlineAddon() {
260
+ return {
261
+ id: "x/underline",
262
+ dependencies: ["x/schema"],
263
+ impl: UnderlineAddonImpl
264
+ };
265
+ }
266
+ var UnderlineAddonImpl = {
267
+ id: "x/underline",
268
+ initSchema(ctx) {
269
+ ctx.addMark({
270
+ name: "underline",
271
+ parseDOM: [{ tag: "u" }, { style: "text-decoration=underline" }],
272
+ toDOM: () => ["u", 0]
273
+ });
274
+ },
275
+ init(ctx) {
276
+ ctx.commands.register({
277
+ id: "underline",
278
+ label: "Underline",
279
+ icon: "underline",
280
+ shortcut: "Mod-u",
281
+ isEnabled: (c) => c.selection.canHaveMark("underline"),
282
+ isActive: (c) => c.selection.hasMark("underline"),
283
+ execute: (c) => {
284
+ c.addMark("underline");
285
+ return true;
286
+ }
287
+ });
288
+ return {};
289
+ }
290
+ };
291
+ function StrikethroughAddon() {
292
+ return {
293
+ id: "x/strikethrough",
294
+ dependencies: ["x/schema"],
295
+ impl: StrikethroughAddonImpl
296
+ };
297
+ }
298
+ var StrikethroughAddonImpl = {
299
+ id: "x/strikethrough",
300
+ initSchema(ctx) {
301
+ ctx.addMark({
302
+ name: "strikethrough",
303
+ parseDOM: [{ tag: "s" }, { tag: "del" }, { tag: "strike" }],
304
+ toDOM: () => ["s", 0]
305
+ });
306
+ },
307
+ init(ctx) {
308
+ ctx.commands.register({
309
+ id: "strikethrough",
310
+ label: "Strikethrough",
311
+ icon: "strikethrough",
312
+ isEnabled: (c) => c.selection.canHaveMark("strikethrough"),
313
+ isActive: (c) => c.selection.hasMark("strikethrough"),
314
+ execute: (c) => {
315
+ c.addMark("strikethrough");
316
+ return true;
317
+ }
318
+ });
319
+ return {};
320
+ }
321
+ };
322
+ function InlineCodeAddon() {
323
+ return {
324
+ id: "x/inline-code",
325
+ dependencies: ["x/schema"],
326
+ impl: InlineCodeAddonImpl
327
+ };
328
+ }
329
+ var InlineCodeAddonImpl = {
330
+ id: "x/inline-code",
331
+ initSchema(ctx) {
332
+ ctx.addMark({
333
+ name: "code",
334
+ parseDOM: [{ tag: "code" }],
335
+ toDOM: () => ["code", 0]
336
+ });
337
+ },
338
+ init(ctx) {
339
+ ctx.commands.register({
340
+ id: "inlineCode",
341
+ label: "Inline Code",
342
+ icon: "code",
343
+ isEnabled: (c) => c.selection.canHaveMark("code"),
344
+ isActive: (c) => c.selection.hasMark("code"),
345
+ execute: (c) => {
346
+ c.addMark("code");
347
+ return true;
348
+ }
349
+ });
350
+ return {};
351
+ }
352
+ };
353
+ function SubscriptAddon() {
354
+ return {
355
+ id: "x/subscript",
356
+ dependencies: ["x/schema"],
357
+ impl: SubscriptAddonImpl
358
+ };
359
+ }
360
+ var SubscriptAddonImpl = {
361
+ id: "x/subscript",
362
+ initSchema(ctx) {
363
+ ctx.addMark({
364
+ name: "subscript",
365
+ excludes: "superscript",
366
+ parseDOM: [{ tag: "sub" }],
367
+ toDOM: () => ["sub", 0]
368
+ });
369
+ },
370
+ init(ctx) {
371
+ ctx.commands.register({
372
+ id: "subscript",
373
+ label: "Subscript",
374
+ icon: "subscript",
375
+ isEnabled: (c) => c.selection.canHaveMark("subscript"),
376
+ isActive: (c) => c.selection.hasMark("subscript"),
377
+ execute: (c) => {
378
+ c.addMark("subscript");
379
+ return true;
380
+ }
381
+ });
382
+ return {};
383
+ }
384
+ };
385
+ function SuperscriptAddon() {
386
+ return {
387
+ id: "x/superscript",
388
+ dependencies: ["x/schema"],
389
+ impl: SuperscriptAddonImpl
390
+ };
391
+ }
392
+ var SuperscriptAddonImpl = {
393
+ id: "x/superscript",
394
+ initSchema(ctx) {
395
+ ctx.addMark({
396
+ name: "superscript",
397
+ excludes: "subscript",
398
+ parseDOM: [{ tag: "sup" }],
399
+ toDOM: () => ["sup", 0]
400
+ });
401
+ },
402
+ init(ctx) {
403
+ ctx.commands.register({
404
+ id: "superscript",
405
+ label: "Superscript",
406
+ icon: "superscript",
407
+ isEnabled: (c) => c.selection.canHaveMark("superscript"),
408
+ isActive: (c) => c.selection.hasMark("superscript"),
409
+ execute: (c) => {
410
+ c.addMark("superscript");
411
+ return true;
412
+ }
413
+ });
414
+ return {};
415
+ }
416
+ };
417
+ function HighlightAddon(config) {
418
+ return {
419
+ id: "x/highlight",
420
+ config,
421
+ dependencies: ["x/schema"],
422
+ impl: HighlightAddonImpl
423
+ };
424
+ }
425
+ var HighlightAddonImpl = {
426
+ id: "x/highlight",
427
+ initSchema(ctx) {
428
+ ctx.addMark({
429
+ name: "highlight",
430
+ attrs: { color: { default: "#ffff00" } },
431
+ parseDOM: [{ tag: "mark" }],
432
+ toDOM: (mark) => [
433
+ "mark",
434
+ { style: `background:${mark.attrs.color}` },
435
+ 0
436
+ ]
437
+ });
438
+ },
439
+ init(ctx, config = {}) {
440
+ ctx.commands.register({
441
+ id: "highlight",
442
+ label: "Highlight",
443
+ icon: "highlight",
444
+ isEnabled: (c) => c.selection.canHaveMark("highlight"),
445
+ isActive: (c) => c.selection.hasMark("highlight"),
446
+ execute: (c) => {
447
+ c.addMark("highlight", { color: config.defaultColor ?? "#ffff00" });
448
+ return true;
449
+ }
450
+ });
451
+ return {};
452
+ }
453
+ };
454
+ function TextColorAddon(config) {
455
+ return {
456
+ id: "x/text-color",
457
+ config,
458
+ dependencies: ["x/schema"],
459
+ impl: TextColorAddonImpl
460
+ };
461
+ }
462
+ var TextColorAddonImpl = {
463
+ id: "x/text-color",
464
+ initSchema(ctx) {
465
+ ctx.addMark({
466
+ name: "textColor",
467
+ attrs: { color: { default: null } },
468
+ parseDOM: [{ style: "color", getAttrs: (v) => ({ color: v }) }],
469
+ toDOM: (mark) => [
470
+ "span",
471
+ { style: `color:${mark.attrs.color}` },
472
+ 0
473
+ ]
474
+ });
475
+ },
476
+ init(ctx, config = {}) {
477
+ ctx.commands.register({
478
+ id: "textColor",
479
+ label: "Text Color",
480
+ icon: "text-color",
481
+ isEnabled: (c) => c.selection.canHaveMark("textColor"),
482
+ isActive: (c) => c.selection.hasMark("textColor"),
483
+ execute: (c) => {
484
+ c.addMark("textColor", { color: config.defaultColor ?? "#000000" });
485
+ return true;
486
+ }
487
+ });
488
+ ctx.commands.register({
489
+ id: "clearTextColor",
490
+ label: "Clear Text Color",
491
+ isEnabled: (c) => c.selection.hasMark("textColor"),
492
+ isActive: () => false,
493
+ execute: (c) => {
494
+ c.removeMark("textColor");
495
+ return true;
496
+ }
497
+ });
498
+ return {};
499
+ }
500
+ };
501
+ function FontFamilyAddon(config) {
502
+ return {
503
+ id: "x/font-family",
504
+ config,
505
+ dependencies: ["x/schema"],
506
+ impl: FontFamilyAddonImpl
507
+ };
508
+ }
509
+ var FontFamilyAddonImpl = {
510
+ id: "x/font-family",
511
+ initSchema(ctx) {
512
+ ctx.addMark({
513
+ name: "fontFamily",
514
+ attrs: { family: { default: null } },
515
+ parseDOM: [
516
+ { style: "font-family", getAttrs: (v) => ({ family: v }) }
517
+ ],
518
+ toDOM: (mark) => [
519
+ "span",
520
+ { style: `font-family:${mark.attrs.family}` },
521
+ 0
522
+ ]
523
+ });
524
+ },
525
+ init(ctx) {
526
+ ctx.commands.register({
527
+ id: "fontFamily",
528
+ label: "Font Family",
529
+ icon: "font-family",
530
+ isEnabled: () => true,
531
+ isActive: () => false,
532
+ execute: (c, args) => {
533
+ const family = args?.family;
534
+ if (!family) return false;
535
+ c.addMark("fontFamily", { family });
536
+ return true;
537
+ }
538
+ });
539
+ ctx.commands.register({
540
+ id: "clearFontFamily",
541
+ label: "Clear Font Family",
542
+ isEnabled: (c) => c.selection.hasMark("fontFamily"),
543
+ isActive: () => false,
544
+ execute: (c) => {
545
+ c.removeMark("fontFamily");
546
+ return true;
547
+ }
548
+ });
549
+ return {};
550
+ }
551
+ };
552
+ function FontSizeAddon(config) {
553
+ return {
554
+ id: "x/font-size",
555
+ config,
556
+ dependencies: ["x/schema"],
557
+ impl: FontSizeAddonImpl
558
+ };
559
+ }
560
+ var FontSizeAddonImpl = {
561
+ id: "x/font-size",
562
+ initSchema(ctx) {
563
+ ctx.addMark({
564
+ name: "fontSize",
565
+ attrs: { size: { default: null } },
566
+ parseDOM: [
567
+ { style: "font-size", getAttrs: (v) => ({ size: v }) }
568
+ ],
569
+ toDOM: (mark) => [
570
+ "span",
571
+ { style: `font-size:${mark.attrs.size}` },
572
+ 0
573
+ ]
574
+ });
575
+ },
576
+ init(ctx) {
577
+ ctx.commands.register({
578
+ id: "fontSize",
579
+ label: "Font Size",
580
+ isEnabled: () => true,
581
+ isActive: () => false,
582
+ execute: (c, args) => {
583
+ const size = args?.size;
584
+ if (!size) return false;
585
+ c.addMark("fontSize", { size });
586
+ return true;
587
+ }
588
+ });
589
+ ctx.commands.register({
590
+ id: "increaseFontSize",
591
+ label: "Increase Font Size",
592
+ isEnabled: () => true,
593
+ isActive: () => false,
594
+ execute: () => true
595
+ });
596
+ ctx.commands.register({
597
+ id: "decreaseFontSize",
598
+ label: "Decrease Font Size",
599
+ isEnabled: () => true,
600
+ isActive: () => false,
601
+ execute: () => true
602
+ });
603
+ ctx.commands.register({
604
+ id: "clearFontSize",
605
+ label: "Clear Font Size",
606
+ isEnabled: (c) => c.selection.hasMark("fontSize"),
607
+ isActive: () => false,
608
+ execute: (c) => {
609
+ c.removeMark("fontSize");
610
+ return true;
611
+ }
612
+ });
613
+ return {};
614
+ }
615
+ };
616
+ function ClearFormattingAddon() {
617
+ return {
618
+ id: "x/clear-formatting",
619
+ dependencies: ["x/commands"],
620
+ impl: ClearFormattingAddonImpl
621
+ };
622
+ }
623
+ var ClearFormattingAddonImpl = {
624
+ id: "x/clear-formatting",
625
+ init(ctx) {
626
+ ctx.commands.register({
627
+ id: "clearFormatting",
628
+ label: "Clear Formatting",
629
+ icon: "clear-formatting",
630
+ shortcut: "Mod-\\",
631
+ isEnabled: () => true,
632
+ isActive: () => false,
633
+ execute: (c) => {
634
+ for (const m of c.schema.getMarks()) c.removeMark(m.name);
635
+ return true;
636
+ }
637
+ });
638
+ return {};
639
+ }
640
+ };
641
+
642
+ // src/formatting/index.ts
643
+ function AlignmentAddon() {
644
+ return {
645
+ id: "x/alignment",
646
+ dependencies: ["x/schema"],
647
+ impl: AlignmentAddonImpl
648
+ };
649
+ }
650
+ var AlignmentAddonImpl = {
651
+ id: "x/alignment",
652
+ init(ctx) {
653
+ const setAttrs = (c, patch) => c.setBlockAttrs(patch);
654
+ for (const align of ["left", "center", "right", "justify"]) {
655
+ const id = `align${align.charAt(0).toUpperCase()}${align.slice(1)}`;
656
+ ctx.commands.register({
657
+ id,
658
+ label: `Align ${align.charAt(0).toUpperCase()}${align.slice(1)}`,
659
+ icon: `align-${align}`,
660
+ isEnabled: () => true,
661
+ isActive: () => false,
662
+ execute: (c) => setAttrs(c, { align })
663
+ });
664
+ }
665
+ return {};
666
+ }
667
+ };
668
+ function LineHeightAddon(config) {
669
+ return {
670
+ id: "x/line-height",
671
+ config,
672
+ dependencies: ["x/schema"],
673
+ impl: LineHeightAddonImpl
674
+ };
675
+ }
676
+ var LineHeightAddonImpl = {
677
+ id: "x/line-height",
678
+ init(ctx) {
679
+ ctx.commands.register({
680
+ id: "lineHeight",
681
+ label: "Line Height",
682
+ isEnabled: () => true,
683
+ isActive: () => false,
684
+ execute: (c, args) => {
685
+ const value = args?.value;
686
+ if (value == null) return false;
687
+ return c.setBlockAttrs({ lineHeight: String(value) });
688
+ }
689
+ });
690
+ return {};
691
+ }
692
+ };
693
+ function ParagraphSpacingAddon() {
694
+ return {
695
+ id: "x/paragraph-spacing",
696
+ dependencies: ["x/schema"],
697
+ impl: ParagraphSpacingAddonImpl
698
+ };
699
+ }
700
+ var ParagraphSpacingAddonImpl = {
701
+ id: "x/paragraph-spacing",
702
+ init(ctx) {
703
+ ctx.commands.register({
704
+ id: "paragraphSpacing",
705
+ label: "Paragraph Spacing",
706
+ isEnabled: () => true,
707
+ isActive: () => false,
708
+ execute: (c, args) => {
709
+ const value = args?.value;
710
+ if (value == null) return false;
711
+ return c.setBlockAttrs({ paragraphSpacing: String(value) });
712
+ }
713
+ });
714
+ return {};
715
+ }
716
+ };
717
+ function IndentAddon() {
718
+ return { id: "x/indent", dependencies: ["x/schema"], impl: IndentAddonImpl };
719
+ }
720
+ var IndentAddonImpl = {
721
+ id: "x/indent",
722
+ init(ctx) {
723
+ const bump = (c, delta) => {
724
+ const view = c._prosemirror;
725
+ if (!view) return false;
726
+ const { from, to } = view.state.selection;
727
+ let current = 0;
728
+ view.state.doc.nodeAt(from)?.attrs;
729
+ const node = view.state.doc.nodeAt(Math.max(0, from - 1));
730
+ if (node && typeof node.attrs?.["indent"] === "number")
731
+ current = node.attrs["indent"];
732
+ const next = Math.max(0, Math.min(10, current + delta));
733
+ return c.setBlockAttrs({ indent: next });
734
+ };
735
+ ctx.commands.register({
736
+ id: "indent",
737
+ label: "Indent",
738
+ icon: "indent",
739
+ shortcut: "Tab",
740
+ isEnabled: () => true,
741
+ isActive: () => false,
742
+ execute: (c) => bump(c, 1)
743
+ });
744
+ ctx.commands.register({
745
+ id: "outdent",
746
+ label: "Outdent",
747
+ icon: "outdent",
748
+ shortcut: "Shift-Tab",
749
+ isEnabled: () => true,
750
+ isActive: () => false,
751
+ execute: (c) => bump(c, -1)
752
+ });
753
+ return {};
754
+ }
755
+ };
756
+
757
+ // src/blocks/index.ts
758
+ function HeadingsAddon() {
759
+ return {
760
+ id: "x/headings",
761
+ dependencies: ["x/schema"],
762
+ impl: HeadingsAddonImpl
763
+ };
764
+ }
765
+ var HeadingsAddonImpl = {
766
+ id: "x/headings",
767
+ initSchema(ctx) {
768
+ ctx.addNode({
769
+ name: "heading",
770
+ group: "block",
771
+ content: "inline*",
772
+ attrs: {
773
+ level: { default: 1 },
774
+ align: { default: null },
775
+ indent: { default: 0 },
776
+ lineHeight: { default: null }
777
+ },
778
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
779
+ tag: `h${level}`,
780
+ getAttrs: () => ({ level })
781
+ })),
782
+ toDOM: (node) => {
783
+ const style = [];
784
+ if (node.attrs.align) style.push(`text-align:${node.attrs.align}`);
785
+ if (node.attrs.lineHeight)
786
+ style.push(`line-height:${node.attrs.lineHeight}`);
787
+ const attrs = {};
788
+ if (style.length > 0) attrs["style"] = style.join(";");
789
+ if (node.attrs.indent > 0)
790
+ attrs["data-indent"] = String(node.attrs.indent);
791
+ return [`h${node.attrs.level}`, attrs, 0];
792
+ }
793
+ });
794
+ },
795
+ init(ctx) {
796
+ const ctxImpl = ctx;
797
+ for (let level = 1; level <= 6; level++) {
798
+ ctx.commands.register({
799
+ id: `heading${level}`,
800
+ label: `Heading ${level}`,
801
+ icon: `h${level}`,
802
+ shortcut: `Mod-Alt-${level}`,
803
+ isEnabled: () => true,
804
+ isActive: () => false,
805
+ execute: () => ctxImpl.setBlockType("heading", { level })
806
+ });
807
+ }
808
+ ctx.commands.register({
809
+ id: "paragraph",
810
+ label: "Paragraph",
811
+ shortcut: "Mod-Alt-0",
812
+ isEnabled: () => true,
813
+ isActive: () => false,
814
+ execute: () => ctxImpl.setBlockType("paragraph")
815
+ });
816
+ return {};
817
+ }
818
+ };
819
+ function BlockquoteAddon() {
820
+ return {
821
+ id: "x/blockquote",
822
+ dependencies: ["x/schema"],
823
+ impl: BlockquoteAddonImpl
824
+ };
825
+ }
826
+ var BlockquoteAddonImpl = {
827
+ id: "x/blockquote",
828
+ initSchema(ctx) {
829
+ ctx.addNode({
830
+ name: "blockquote",
831
+ group: "block",
832
+ content: "block+",
833
+ parseDOM: [{ tag: "blockquote" }],
834
+ toDOM: () => ["blockquote", 0]
835
+ });
836
+ },
837
+ init(ctx) {
838
+ const ctxImpl = ctx;
839
+ ctx.commands.register({
840
+ id: "blockquote",
841
+ label: "Blockquote",
842
+ icon: "blockquote",
843
+ shortcut: "Mod-Shift-.",
844
+ isEnabled: () => true,
845
+ isActive: () => false,
846
+ execute: () => ctxImpl.wrapInBlock("blockquote") || ctxImpl.liftBlock()
847
+ });
848
+ return {};
849
+ }
850
+ };
851
+ function DividerAddon() {
852
+ return {
853
+ id: "x/divider",
854
+ dependencies: ["x/schema"],
855
+ impl: DividerAddonImpl
856
+ };
857
+ }
858
+ var DividerAddonImpl = {
859
+ id: "x/divider",
860
+ initSchema(ctx) {
861
+ ctx.addNode({
862
+ name: "horizontalRule",
863
+ group: "block",
864
+ parseDOM: [{ tag: "hr" }],
865
+ toDOM: () => ["hr"]
866
+ });
867
+ },
868
+ init(ctx) {
869
+ ctx.commands.register({
870
+ id: "insertDivider",
871
+ label: "Divider",
872
+ icon: "divider",
873
+ isEnabled: () => true,
874
+ isActive: () => false,
875
+ execute: (c) => {
876
+ c.insertNode("horizontalRule");
877
+ return true;
878
+ }
879
+ });
880
+ return {};
881
+ }
882
+ };
883
+ function PageBreakAddon() {
884
+ return {
885
+ id: "x/page-break",
886
+ dependencies: ["x/schema"],
887
+ impl: PageBreakAddonImpl
888
+ };
889
+ }
890
+ var PageBreakAddonImpl = {
891
+ id: "x/page-break",
892
+ initSchema(ctx) {
893
+ ctx.addNode({
894
+ name: "pageBreak",
895
+ group: "block",
896
+ parseDOM: [{ tag: "div[data-page-break]" }],
897
+ toDOM: () => [
898
+ "div",
899
+ { "data-page-break": "true", style: "page-break-after:always" }
900
+ ]
901
+ });
902
+ },
903
+ init(ctx) {
904
+ ctx.commands.register({
905
+ id: "insertPageBreak",
906
+ label: "Page Break",
907
+ icon: "page-break",
908
+ isEnabled: () => true,
909
+ isActive: () => false,
910
+ execute: (c) => {
911
+ c.insertNode("pageBreak");
912
+ return true;
913
+ }
914
+ });
915
+ return {};
916
+ }
917
+ };
918
+ function CalloutAddon(config) {
919
+ return {
920
+ id: "x/callout",
921
+ config,
922
+ dependencies: ["x/schema"],
923
+ impl: CalloutAddonImpl
924
+ };
925
+ }
926
+ var CalloutAddonImpl = {
927
+ id: "x/callout",
928
+ initSchema(ctx) {
929
+ ctx.addNode({
930
+ name: "callout",
931
+ group: "block",
932
+ content: "block+",
933
+ attrs: { type: { default: "info" } },
934
+ parseDOM: [
935
+ {
936
+ tag: "div[data-callout]",
937
+ getAttrs: (el) => ({
938
+ type: el.getAttribute("data-callout")
939
+ })
940
+ }
941
+ ],
942
+ toDOM: (node) => [
943
+ "div",
944
+ {
945
+ "data-callout": node.attrs.type,
946
+ class: `xe-callout xe-callout--${node.attrs.type}`
947
+ },
948
+ 0
949
+ ]
950
+ });
951
+ },
952
+ init(ctx, config = {}) {
953
+ const types = config.types ?? [
954
+ "info",
955
+ "success",
956
+ "warning",
957
+ "error",
958
+ "tip",
959
+ "note"
960
+ ];
961
+ const defaultType = types[0] ?? "info";
962
+ ctx.commands.register({
963
+ id: "insertCallout",
964
+ label: "Callout",
965
+ icon: "callout",
966
+ isEnabled: () => true,
967
+ isActive: () => false,
968
+ execute: (c, args) => {
969
+ const type = args?.type ?? defaultType;
970
+ c.insertNode("callout", { type }, { type: "paragraph" });
971
+ return true;
972
+ }
973
+ });
974
+ for (const type of types) {
975
+ ctx.commands.register({
976
+ id: `insertCallout_${type}`,
977
+ label: `Callout: ${type}`,
978
+ icon: `callout-${type}`,
979
+ isEnabled: () => true,
980
+ isActive: () => false,
981
+ execute: (c) => {
982
+ c.insertNode("callout", { type }, { type: "paragraph" });
983
+ return true;
984
+ }
985
+ });
986
+ }
987
+ return {};
988
+ }
989
+ };
990
+ function DetailsAddon() {
991
+ return {
992
+ id: "x/details",
993
+ dependencies: ["x/schema"],
994
+ impl: DetailsAddonImpl
995
+ };
996
+ }
997
+ var DetailsAddonImpl = {
998
+ id: "x/details",
999
+ initSchema(ctx) {
1000
+ ctx.addNode({
1001
+ name: "details",
1002
+ group: "block",
1003
+ content: "summary block+",
1004
+ parseDOM: [{ tag: "details" }],
1005
+ toDOM: () => ["details", 0]
1006
+ });
1007
+ ctx.addNode({
1008
+ name: "summary",
1009
+ content: "inline*",
1010
+ parseDOM: [{ tag: "summary" }],
1011
+ toDOM: () => ["summary", 0]
1012
+ });
1013
+ },
1014
+ init(ctx) {
1015
+ ctx.commands.register({
1016
+ id: "insertDetails",
1017
+ label: "Details / Toggle",
1018
+ icon: "details",
1019
+ isEnabled: () => true,
1020
+ isActive: () => false,
1021
+ execute: (c) => {
1022
+ c.insertNode("details", void 0, [
1023
+ { type: "summary" },
1024
+ { type: "paragraph" }
1025
+ ]);
1026
+ return true;
1027
+ }
1028
+ });
1029
+ return {};
1030
+ }
1031
+ };
1032
+ function ColumnsAddon(config) {
1033
+ return {
1034
+ id: "x/columns",
1035
+ config,
1036
+ dependencies: ["x/schema"],
1037
+ impl: ColumnsAddonImpl
1038
+ };
1039
+ }
1040
+ var ColumnsAddonImpl = {
1041
+ id: "x/columns",
1042
+ initSchema(ctx) {
1043
+ ctx.addNode({
1044
+ name: "columns",
1045
+ group: "block",
1046
+ content: "column+",
1047
+ attrs: { ratio: { default: "1:1" } },
1048
+ parseDOM: [{ tag: "div[data-columns]" }],
1049
+ toDOM: () => ["div", { "data-columns": "true", class: "xe-columns" }, 0]
1050
+ });
1051
+ ctx.addNode({
1052
+ name: "column",
1053
+ content: "block+",
1054
+ parseDOM: [{ tag: "div[data-column]" }],
1055
+ toDOM: () => ["div", { "data-column": "true", class: "xe-column" }, 0]
1056
+ });
1057
+ },
1058
+ init(ctx, config = {}) {
1059
+ ctx.commands.register({
1060
+ id: "insertColumns",
1061
+ label: "Columns",
1062
+ icon: "columns",
1063
+ isEnabled: () => true,
1064
+ isActive: () => false,
1065
+ execute: (c) => {
1066
+ c.insertNode("columns", void 0, [
1067
+ { type: "column", content: [{ type: "paragraph" }] },
1068
+ { type: "column", content: [{ type: "paragraph" }] }
1069
+ ]);
1070
+ return true;
1071
+ }
1072
+ });
1073
+ ctx.commands.register({
1074
+ id: "addColumn",
1075
+ label: "Add Column",
1076
+ isEnabled: () => true,
1077
+ isActive: () => false,
1078
+ execute: () => true
1079
+ });
1080
+ ctx.commands.register({
1081
+ id: "removeColumn",
1082
+ label: "Remove Column",
1083
+ isEnabled: () => true,
1084
+ isActive: () => false,
1085
+ execute: () => true
1086
+ });
1087
+ ctx.commands.register({
1088
+ id: "setColumnRatio",
1089
+ label: "Set Column Ratio",
1090
+ isEnabled: () => true,
1091
+ isActive: () => false,
1092
+ execute: () => true
1093
+ });
1094
+ return {};
1095
+ }
1096
+ };
1097
+ function CodeBlockAddon(config) {
1098
+ return {
1099
+ id: "x/code-block",
1100
+ config,
1101
+ dependencies: ["x/schema"],
1102
+ impl: CodeBlockAddonImpl
1103
+ };
1104
+ }
1105
+ var CodeBlockAddonImpl = {
1106
+ id: "x/code-block",
1107
+ initSchema(ctx) {
1108
+ ctx.addNode({
1109
+ name: "codeBlock",
1110
+ group: "block",
1111
+ content: "text*",
1112
+ attrs: { language: { default: "" } },
1113
+ parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
1114
+ toDOM: () => ["pre", ["code", 0]]
1115
+ });
1116
+ },
1117
+ init(ctx) {
1118
+ const ctxImpl = ctx;
1119
+ ctx.commands.register({
1120
+ id: "insertCodeBlock",
1121
+ label: "Code Block",
1122
+ icon: "code-block",
1123
+ shortcut: "Mod-Alt-c",
1124
+ isEnabled: () => true,
1125
+ isActive: () => false,
1126
+ execute: () => ctxImpl.setBlockType("codeBlock")
1127
+ });
1128
+ ctx.commands.register({
1129
+ id: "copyCodeBlock",
1130
+ label: "Copy Code",
1131
+ isEnabled: () => true,
1132
+ isActive: () => false,
1133
+ execute: (c) => {
1134
+ const text = c.getContent("text");
1135
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
1136
+ navigator.clipboard.writeText(text).catch(() => {
1137
+ });
1138
+ }
1139
+ return true;
1140
+ }
1141
+ });
1142
+ return {};
1143
+ }
1144
+ };
1145
+
1146
+ // src/lists/index.ts
1147
+ function BulletListAddon() {
1148
+ return {
1149
+ id: "x/bullet-list",
1150
+ dependencies: ["x/schema"],
1151
+ impl: BulletListAddonImpl
1152
+ };
1153
+ }
1154
+ var BulletListAddonImpl = {
1155
+ id: "x/bullet-list",
1156
+ initSchema(ctx) {
1157
+ ctx.addNode({
1158
+ name: "bulletList",
1159
+ group: "block",
1160
+ content: "listItem+",
1161
+ parseDOM: [{ tag: "ul" }],
1162
+ toDOM: () => ["ul", 0]
1163
+ });
1164
+ ctx.addNode({
1165
+ name: "listItem",
1166
+ content: "paragraph block*",
1167
+ parseDOM: [{ tag: "li" }],
1168
+ toDOM: () => ["li", 0]
1169
+ });
1170
+ },
1171
+ init(ctx) {
1172
+ const ctxImpl = ctx;
1173
+ ctx.commands.register({
1174
+ id: "bulletList",
1175
+ label: "Bullet List",
1176
+ icon: "bullet-list",
1177
+ shortcut: "Mod-Shift-8",
1178
+ isEnabled: () => true,
1179
+ isActive: () => false,
1180
+ execute: () => ctxImpl.wrapInBlock("bulletList") || ctxImpl.liftBlock()
1181
+ });
1182
+ return {};
1183
+ }
1184
+ };
1185
+ function OrderedListAddon() {
1186
+ return {
1187
+ id: "x/ordered-list",
1188
+ dependencies: ["x/schema"],
1189
+ impl: OrderedListAddonImpl
1190
+ };
1191
+ }
1192
+ var OrderedListAddonImpl = {
1193
+ id: "x/ordered-list",
1194
+ initSchema(ctx) {
1195
+ ctx.addNode({
1196
+ name: "orderedList",
1197
+ group: "block",
1198
+ content: "listItem+",
1199
+ attrs: { order: { default: 1 } },
1200
+ parseDOM: [
1201
+ {
1202
+ tag: "ol",
1203
+ getAttrs: (el) => ({
1204
+ order: parseInt(el.getAttribute("start") ?? "1", 10)
1205
+ })
1206
+ }
1207
+ ],
1208
+ toDOM: (node) => node.attrs.order === 1 ? ["ol", 0] : ["ol", { start: node.attrs.order }, 0]
1209
+ });
1210
+ },
1211
+ init(ctx) {
1212
+ const ctxImpl = ctx;
1213
+ ctx.commands.register({
1214
+ id: "orderedList",
1215
+ label: "Ordered List",
1216
+ icon: "ordered-list",
1217
+ shortcut: "Mod-Shift-7",
1218
+ isEnabled: () => true,
1219
+ isActive: () => false,
1220
+ execute: () => ctxImpl.wrapInBlock("orderedList") || ctxImpl.liftBlock()
1221
+ });
1222
+ return {};
1223
+ }
1224
+ };
1225
+ function TaskListAddon() {
1226
+ return {
1227
+ id: "x/task-list",
1228
+ dependencies: ["x/schema"],
1229
+ impl: TaskListAddonImpl
1230
+ };
1231
+ }
1232
+ var TaskListAddonImpl = {
1233
+ id: "x/task-list",
1234
+ initSchema(ctx) {
1235
+ ctx.addNode({
1236
+ name: "taskList",
1237
+ group: "block",
1238
+ content: "taskItem+",
1239
+ parseDOM: [{ tag: "ul[data-task-list]" }],
1240
+ toDOM: () => ["ul", { "data-task-list": "true" }, 0]
1241
+ });
1242
+ ctx.addNode({
1243
+ name: "taskItem",
1244
+ content: "paragraph block*",
1245
+ attrs: { checked: { default: false } },
1246
+ parseDOM: [{ tag: "li[data-task-item]" }],
1247
+ toDOM: (node) => [
1248
+ "li",
1249
+ { "data-task-item": "true", "data-checked": node.attrs.checked },
1250
+ 0
1251
+ ]
1252
+ });
1253
+ },
1254
+ init(ctx) {
1255
+ const ctxImpl = ctx;
1256
+ ctx.commands.register({
1257
+ id: "taskList",
1258
+ label: "Task List",
1259
+ icon: "task-list",
1260
+ shortcut: "Mod-Shift-9",
1261
+ isEnabled: () => true,
1262
+ isActive: () => false,
1263
+ execute: () => ctxImpl.wrapInBlock("taskList") || ctxImpl.liftBlock()
1264
+ });
1265
+ return {};
1266
+ }
1267
+ };
1268
+ function ToggleListAddon() {
1269
+ return {
1270
+ id: "x/toggle-list",
1271
+ dependencies: ["x/schema"],
1272
+ impl: ToggleListAddonImpl
1273
+ };
1274
+ }
1275
+ var ToggleListAddonImpl = {
1276
+ id: "x/toggle-list",
1277
+ initSchema(ctx) {
1278
+ ctx.addNode({
1279
+ name: "toggleList",
1280
+ group: "block",
1281
+ content: "toggleItem+",
1282
+ parseDOM: [{ tag: "ul[data-toggle-list]" }],
1283
+ toDOM: () => ["ul", { "data-toggle-list": "true" }, 0]
1284
+ });
1285
+ ctx.addNode({
1286
+ name: "toggleItem",
1287
+ content: "paragraph block*",
1288
+ attrs: { open: { default: true } },
1289
+ parseDOM: [{ tag: "li[data-toggle-item]" }],
1290
+ toDOM: () => ["li", { "data-toggle-item": "true" }, 0]
1291
+ });
1292
+ },
1293
+ init(ctx) {
1294
+ ctx.commands.register({
1295
+ id: "toggleList",
1296
+ label: "Toggle List",
1297
+ icon: "toggle-list",
1298
+ isEnabled: () => true,
1299
+ isActive: () => false,
1300
+ execute: (c) => {
1301
+ c.insertNode("toggleList", void 0, [
1302
+ { type: "toggleItem", content: [{ type: "paragraph" }] }
1303
+ ]);
1304
+ return true;
1305
+ }
1306
+ });
1307
+ return {};
1308
+ }
1309
+ };
1310
+ function ListsAddon() {
1311
+ return [
1312
+ BulletListAddon(),
1313
+ OrderedListAddon(),
1314
+ TaskListAddon(),
1315
+ ToggleListAddon()
1316
+ ];
1317
+ }
1318
+
1319
+ // src/tables/index.ts
1320
+ function TableAddon(config) {
1321
+ return {
1322
+ id: "x/table",
1323
+ config,
1324
+ dependencies: ["x/schema"],
1325
+ impl: TableAddonImpl
1326
+ };
1327
+ }
1328
+ var _tablesCache = null;
1329
+ var _tablesPromise = null;
1330
+ async function loadTables() {
1331
+ if (_tablesCache) return _tablesCache;
1332
+ if (!_tablesPromise) {
1333
+ _tablesPromise = import("prosemirror-tables").then((mod) => {
1334
+ _tablesCache = mod;
1335
+ return _tablesCache;
1336
+ });
1337
+ }
1338
+ return _tablesPromise;
1339
+ }
1340
+ function getView(ctx) {
1341
+ return ctx._prosemirror ?? null;
1342
+ }
1343
+ var TableAddonImpl = {
1344
+ id: "x/table",
1345
+ initSchema(ctx) {
1346
+ ctx.addNode({
1347
+ name: "table",
1348
+ group: "block",
1349
+ content: "tableRow+",
1350
+ tableRole: "table",
1351
+ isolating: true,
1352
+ parseDOM: [{ tag: "table" }],
1353
+ toDOM: () => ["table", ["tbody", 0]]
1354
+ });
1355
+ ctx.addNode({
1356
+ name: "tableRow",
1357
+ content: "(tableHeader | tableCell)+",
1358
+ tableRole: "row",
1359
+ parseDOM: [{ tag: "tr" }],
1360
+ toDOM: () => ["tr", 0]
1361
+ });
1362
+ ctx.addNode({
1363
+ name: "tableHeader",
1364
+ content: "block+",
1365
+ tableRole: "header_cell",
1366
+ isolating: true,
1367
+ attrs: {
1368
+ colspan: { default: 1 },
1369
+ rowspan: { default: 1 },
1370
+ colwidth: { default: null },
1371
+ align: { default: null },
1372
+ background: { default: null }
1373
+ },
1374
+ parseDOM: [
1375
+ {
1376
+ tag: "th",
1377
+ getAttrs: (el) => readCellAttrs(el)
1378
+ }
1379
+ ],
1380
+ toDOM: (node) => [
1381
+ "th",
1382
+ cellDomAttrs(node.attrs),
1383
+ 0
1384
+ ]
1385
+ });
1386
+ ctx.addNode({
1387
+ name: "tableCell",
1388
+ content: "block+",
1389
+ tableRole: "cell",
1390
+ isolating: true,
1391
+ attrs: {
1392
+ colspan: { default: 1 },
1393
+ rowspan: { default: 1 },
1394
+ colwidth: { default: null },
1395
+ align: { default: null },
1396
+ background: { default: null }
1397
+ },
1398
+ parseDOM: [
1399
+ {
1400
+ tag: "td",
1401
+ getAttrs: (el) => readCellAttrs(el)
1402
+ }
1403
+ ],
1404
+ toDOM: (node) => [
1405
+ "td",
1406
+ cellDomAttrs(node.attrs),
1407
+ 0
1408
+ ]
1409
+ });
1410
+ },
1411
+ init(ctx, config) {
1412
+ void loadTables();
1413
+ ctx.commands.register({
1414
+ id: "insertTable",
1415
+ label: "Insert Table",
1416
+ icon: "table",
1417
+ isEnabled: () => true,
1418
+ isActive: () => false,
1419
+ execute: (c, args) => {
1420
+ const a = args ?? {};
1421
+ const maxCols = config?.maxCols ?? 20;
1422
+ const maxRows = config?.maxRows ?? 100;
1423
+ const rows = clamp(a.rows ?? 3, 1, maxRows);
1424
+ const cols = clamp(a.cols ?? 3, 1, maxCols);
1425
+ const header = a.withHeader ?? true;
1426
+ const headerRow = header ? [
1427
+ {
1428
+ type: "tableRow",
1429
+ content: Array.from({ length: cols }, () => ({
1430
+ type: "tableHeader",
1431
+ content: [{ type: "paragraph" }]
1432
+ }))
1433
+ }
1434
+ ] : [];
1435
+ const bodyRows = Array.from(
1436
+ { length: header ? Math.max(0, rows - 1) : rows },
1437
+ () => ({
1438
+ type: "tableRow",
1439
+ content: Array.from({ length: cols }, () => ({
1440
+ type: "tableCell",
1441
+ content: [{ type: "paragraph" }]
1442
+ }))
1443
+ })
1444
+ );
1445
+ c.insertNode("table", void 0, [
1446
+ ...headerRow,
1447
+ ...bodyRows
1448
+ ]);
1449
+ return true;
1450
+ }
1451
+ });
1452
+ registerTableCommand(
1453
+ ctx,
1454
+ "addRowBefore",
1455
+ "Add Row Before",
1456
+ (t) => t.addRowBefore
1457
+ );
1458
+ registerTableCommand(
1459
+ ctx,
1460
+ "addRowAfter",
1461
+ "Add Row After",
1462
+ (t) => t.addRowAfter
1463
+ );
1464
+ registerTableCommand(ctx, "deleteRow", "Delete Row", (t) => t.deleteRow);
1465
+ registerTableCommand(
1466
+ ctx,
1467
+ "addColumnBefore",
1468
+ "Add Column Before",
1469
+ (t) => t.addColumnBefore
1470
+ );
1471
+ registerTableCommand(
1472
+ ctx,
1473
+ "addColumnAfter",
1474
+ "Add Column After",
1475
+ (t) => t.addColumnAfter
1476
+ );
1477
+ registerTableCommand(
1478
+ ctx,
1479
+ "deleteColumn",
1480
+ "Delete Column",
1481
+ (t) => t.deleteColumn
1482
+ );
1483
+ registerTableCommand(
1484
+ ctx,
1485
+ "addColBefore",
1486
+ "Add Column Before",
1487
+ (t) => t.addColumnBefore
1488
+ );
1489
+ registerTableCommand(
1490
+ ctx,
1491
+ "addColAfter",
1492
+ "Add Column After",
1493
+ (t) => t.addColumnAfter
1494
+ );
1495
+ registerTableCommand(
1496
+ ctx,
1497
+ "deleteCol",
1498
+ "Delete Column",
1499
+ (t) => t.deleteColumn
1500
+ );
1501
+ registerTableCommand(ctx, "mergeCells", "Merge Cells", (t) => t.mergeCells);
1502
+ registerTableCommand(ctx, "splitCell", "Split Cell", (t) => t.splitCell);
1503
+ registerTableCommand(
1504
+ ctx,
1505
+ "toggleHeaderRow",
1506
+ "Toggle Header Row",
1507
+ (t) => t.toggleHeaderRow
1508
+ );
1509
+ registerTableCommand(
1510
+ ctx,
1511
+ "toggleHeaderCol",
1512
+ "Toggle Header Column",
1513
+ (t) => t.toggleHeaderColumn
1514
+ );
1515
+ registerTableCommand(
1516
+ ctx,
1517
+ "toggleHeaderColumn",
1518
+ "Toggle Header Column",
1519
+ (t) => t.toggleHeaderColumn
1520
+ );
1521
+ registerTableCommand(
1522
+ ctx,
1523
+ "goToNextCell",
1524
+ "Next Cell",
1525
+ (t) => t.goToNextCell(1)
1526
+ );
1527
+ registerTableCommand(
1528
+ ctx,
1529
+ "goToPrevCell",
1530
+ "Previous Cell",
1531
+ (t) => t.goToNextCell(-1)
1532
+ );
1533
+ ctx.commands.register({
1534
+ id: "setCellAlignment",
1535
+ label: "Set Cell Alignment",
1536
+ isEnabled: () => true,
1537
+ isActive: () => false,
1538
+ execute: (c, args) => {
1539
+ const align = args?.align ?? null;
1540
+ return setCellAttr(c, "align", align);
1541
+ }
1542
+ });
1543
+ ctx.commands.register({
1544
+ id: "setCellBackground",
1545
+ label: "Set Cell Background",
1546
+ isEnabled: () => true,
1547
+ isActive: () => false,
1548
+ execute: (c, args) => {
1549
+ const bg = args?.background ?? args?.color ?? null;
1550
+ return setCellAttr(c, "background", bg);
1551
+ }
1552
+ });
1553
+ ctx.commands.register({
1554
+ id: "deleteTable",
1555
+ label: "Delete Table",
1556
+ icon: "table-delete",
1557
+ isEnabled: () => true,
1558
+ isActive: () => false,
1559
+ execute: (c) => {
1560
+ const view = getView(c);
1561
+ if (!view) return false;
1562
+ if (_tablesCache) {
1563
+ const ok = _tablesCache.deleteTable(view.state, view.dispatch);
1564
+ if (ok) return true;
1565
+ }
1566
+ const found = findTableNode(view);
1567
+ if (!found) return false;
1568
+ const tr = view.state.tr.delete(found.from, found.from + found.size);
1569
+ view.dispatch(tr);
1570
+ return true;
1571
+ }
1572
+ });
1573
+ return {};
1574
+ }
1575
+ };
1576
+ function clamp(n, min, max) {
1577
+ return Math.max(min, Math.min(max, n));
1578
+ }
1579
+ function readCellAttrs(el) {
1580
+ const colspan = parseInt(el.getAttribute("colspan") ?? "1", 10);
1581
+ const rowspan = parseInt(el.getAttribute("rowspan") ?? "1", 10);
1582
+ const widthAttr = el.getAttribute("data-colwidth");
1583
+ let colwidth = null;
1584
+ if (widthAttr) {
1585
+ const widths = widthAttr.split(",").map((s) => parseInt(s, 10));
1586
+ if (widths.every((w) => !isNaN(w)) && widths.length === colspan) {
1587
+ colwidth = widths;
1588
+ }
1589
+ }
1590
+ const align = el.style.textAlign || el.getAttribute("data-align") || null;
1591
+ const background = el.style.backgroundColor || el.getAttribute("data-background") || null;
1592
+ return { colspan, rowspan, colwidth, align, background };
1593
+ }
1594
+ function cellDomAttrs(attrs) {
1595
+ const out = {};
1596
+ const colspan = attrs["colspan"] ?? 1;
1597
+ const rowspan = attrs["rowspan"] ?? 1;
1598
+ if (colspan !== 1) out["colspan"] = String(colspan);
1599
+ if (rowspan !== 1) out["rowspan"] = String(rowspan);
1600
+ const colwidth = attrs["colwidth"];
1601
+ if (colwidth) out["data-colwidth"] = colwidth.join(",");
1602
+ const align = attrs["align"];
1603
+ const background = attrs["background"];
1604
+ const styles = [];
1605
+ if (align) styles.push(`text-align:${align}`);
1606
+ if (background) styles.push(`background-color:${background}`);
1607
+ if (styles.length > 0) out["style"] = styles.join(";");
1608
+ if (align) out["data-align"] = align;
1609
+ if (background) out["data-background"] = background;
1610
+ return out;
1611
+ }
1612
+ function registerTableCommand(ctx, id, label, pick) {
1613
+ ctx.commands.register({
1614
+ id,
1615
+ label,
1616
+ isEnabled: () => true,
1617
+ isActive: () => false,
1618
+ execute: (c) => {
1619
+ const view = getView(c);
1620
+ if (!view) return false;
1621
+ if (_tablesCache) {
1622
+ const cmd = pick(_tablesCache);
1623
+ if (!cmd) return false;
1624
+ return cmd(view.state, view.dispatch);
1625
+ }
1626
+ void loadTables();
1627
+ return false;
1628
+ }
1629
+ });
1630
+ }
1631
+ function setCellAttr(ctx, name, value) {
1632
+ const view = getView(ctx);
1633
+ if (!view) return false;
1634
+ if (!_tablesCache) {
1635
+ void loadTables();
1636
+ return false;
1637
+ }
1638
+ return _tablesCache.setCellAttr(name, value)(view.state, view.dispatch);
1639
+ }
1640
+ function findTableNode(view) {
1641
+ let result = null;
1642
+ view.state.doc.descendants((node, pos) => {
1643
+ if (!result && node.type.name === "table") {
1644
+ result = { from: pos, size: node.nodeSize };
1645
+ }
1646
+ });
1647
+ return result;
1648
+ }
1649
+
1650
+ // src/media/index.ts
1651
+ function ImageAddon(config) {
1652
+ return {
1653
+ id: "x/image",
1654
+ config,
1655
+ dependencies: ["x/schema"],
1656
+ impl: ImageAddonImpl
1657
+ };
1658
+ }
1659
+ var ImageAddonImpl = {
1660
+ id: "x/image",
1661
+ initSchema(ctx) {
1662
+ ctx.addNode({
1663
+ name: "image",
1664
+ group: "block",
1665
+ atom: true,
1666
+ draggable: true,
1667
+ attrs: {
1668
+ src: {},
1669
+ alt: { default: "" },
1670
+ title: { default: null },
1671
+ width: { default: null },
1672
+ align: { default: "center" }
1673
+ },
1674
+ parseDOM: [
1675
+ {
1676
+ tag: "img[src]",
1677
+ getAttrs: (el) => ({
1678
+ src: el.getAttribute("src"),
1679
+ alt: el.getAttribute("alt"),
1680
+ title: el.getAttribute("title")
1681
+ })
1682
+ }
1683
+ ],
1684
+ toDOM: (node) => ["img", node.attrs]
1685
+ });
1686
+ },
1687
+ init(ctx) {
1688
+ ctx.commands.register({
1689
+ id: "insertImage",
1690
+ label: "Image",
1691
+ icon: "image",
1692
+ isEnabled: () => true,
1693
+ isActive: () => false,
1694
+ execute: (c, args) => {
1695
+ const a = args ?? {};
1696
+ if (!a.src) return false;
1697
+ c.insertNode("image", {
1698
+ src: a.src,
1699
+ alt: a.alt ?? "",
1700
+ title: a.title ?? null,
1701
+ width: a.width ?? null,
1702
+ align: a.align ?? "center"
1703
+ });
1704
+ return true;
1705
+ }
1706
+ });
1707
+ ctx.commands.register({
1708
+ id: "setImageAlt",
1709
+ label: "Set Image Alt",
1710
+ isEnabled: () => true,
1711
+ isActive: () => false,
1712
+ execute: () => true
1713
+ });
1714
+ ctx.commands.register({
1715
+ id: "setImageSize",
1716
+ label: "Set Image Size",
1717
+ isEnabled: () => true,
1718
+ isActive: () => false,
1719
+ execute: () => true
1720
+ });
1721
+ ctx.commands.register({
1722
+ id: "alignImageLeft",
1723
+ label: "Align Image Left",
1724
+ isEnabled: () => true,
1725
+ isActive: () => false,
1726
+ execute: () => true
1727
+ });
1728
+ ctx.commands.register({
1729
+ id: "alignImageCenter",
1730
+ label: "Align Image Center",
1731
+ isEnabled: () => true,
1732
+ isActive: () => false,
1733
+ execute: () => true
1734
+ });
1735
+ ctx.commands.register({
1736
+ id: "alignImageRight",
1737
+ label: "Align Image Right",
1738
+ isEnabled: () => true,
1739
+ isActive: () => false,
1740
+ execute: () => true
1741
+ });
1742
+ ctx.commands.register({
1743
+ id: "alignImageFull",
1744
+ label: "Align Image Full",
1745
+ isEnabled: () => true,
1746
+ isActive: () => false,
1747
+ execute: () => true
1748
+ });
1749
+ return {};
1750
+ }
1751
+ };
1752
+ function VideoAddon(config) {
1753
+ return {
1754
+ id: "x/video",
1755
+ config,
1756
+ dependencies: ["x/schema"],
1757
+ impl: VideoAddonImpl
1758
+ };
1759
+ }
1760
+ var VideoAddonImpl = {
1761
+ id: "x/video",
1762
+ initSchema(ctx) {
1763
+ ctx.addNode({
1764
+ name: "video",
1765
+ group: "block",
1766
+ atom: true,
1767
+ attrs: {
1768
+ src: {},
1769
+ provider: { default: "upload" },
1770
+ poster: { default: null },
1771
+ width: { default: null }
1772
+ },
1773
+ parseDOM: [
1774
+ {
1775
+ tag: "video[src]",
1776
+ getAttrs: (el) => ({ src: el.getAttribute("src") })
1777
+ }
1778
+ ],
1779
+ toDOM: (node) => [
1780
+ "video",
1781
+ { src: node.attrs["src"], controls: "true" }
1782
+ ]
1783
+ });
1784
+ },
1785
+ init(ctx) {
1786
+ ctx.commands.register({
1787
+ id: "insertVideo",
1788
+ label: "Video",
1789
+ icon: "video",
1790
+ isEnabled: () => true,
1791
+ isActive: () => false,
1792
+ execute: (c, args) => {
1793
+ const a = args ?? {};
1794
+ if (!a.src) return false;
1795
+ c.insertNode("video", {
1796
+ src: a.src,
1797
+ provider: a.provider ?? "upload",
1798
+ poster: a.poster ?? null,
1799
+ width: a.width ?? null
1800
+ });
1801
+ return true;
1802
+ }
1803
+ });
1804
+ return {};
1805
+ }
1806
+ };
1807
+ function AudioAddon(config) {
1808
+ return {
1809
+ id: "x/audio",
1810
+ config,
1811
+ dependencies: ["x/schema"],
1812
+ impl: AudioAddonImpl
1813
+ };
1814
+ }
1815
+ var AudioAddonImpl = {
1816
+ id: "x/audio",
1817
+ initSchema(ctx) {
1818
+ ctx.addNode({
1819
+ name: "audio",
1820
+ group: "block",
1821
+ atom: true,
1822
+ attrs: { src: {}, title: { default: "" } },
1823
+ parseDOM: [
1824
+ {
1825
+ tag: "audio[src]",
1826
+ getAttrs: (el) => ({ src: el.getAttribute("src") })
1827
+ }
1828
+ ],
1829
+ toDOM: (node) => [
1830
+ "audio",
1831
+ { src: node.attrs["src"], controls: "true" }
1832
+ ]
1833
+ });
1834
+ },
1835
+ init(ctx) {
1836
+ ctx.commands.register({
1837
+ id: "insertAudio",
1838
+ label: "Audio",
1839
+ icon: "audio",
1840
+ isEnabled: () => true,
1841
+ isActive: () => false,
1842
+ execute: (c, args) => {
1843
+ const a = args ?? {};
1844
+ if (!a.src) return false;
1845
+ c.insertNode("audio", { src: a.src, title: a.title ?? "" });
1846
+ return true;
1847
+ }
1848
+ });
1849
+ return {};
1850
+ }
1851
+ };
1852
+ function FileAddon(config) {
1853
+ return {
1854
+ id: "x/file",
1855
+ config,
1856
+ dependencies: ["x/schema"],
1857
+ impl: FileAddonImpl
1858
+ };
1859
+ }
1860
+ var FileAddonImpl = {
1861
+ id: "x/file",
1862
+ initSchema(ctx) {
1863
+ ctx.addNode({
1864
+ name: "file",
1865
+ group: "block",
1866
+ atom: true,
1867
+ attrs: {
1868
+ src: {},
1869
+ name: {},
1870
+ size: { default: 0 },
1871
+ mimeType: { default: "" }
1872
+ },
1873
+ parseDOM: [{ tag: "a[data-file]" }],
1874
+ toDOM: (node) => [
1875
+ "a",
1876
+ {
1877
+ href: node.attrs["src"],
1878
+ download: node.attrs["name"],
1879
+ "data-file": "true"
1880
+ },
1881
+ String(node.attrs["name"])
1882
+ ]
1883
+ });
1884
+ },
1885
+ init(ctx) {
1886
+ ctx.commands.register({
1887
+ id: "insertFile",
1888
+ label: "File",
1889
+ icon: "file",
1890
+ isEnabled: () => true,
1891
+ isActive: () => false,
1892
+ execute: (c, args) => {
1893
+ const a = args ?? {};
1894
+ const src = a.src ?? a.url;
1895
+ if (!src) return false;
1896
+ c.insertNode("file", {
1897
+ src,
1898
+ name: a.name ?? "file",
1899
+ size: a.size ?? 0,
1900
+ mimeType: a.mimeType ?? ""
1901
+ });
1902
+ return true;
1903
+ }
1904
+ });
1905
+ return {};
1906
+ }
1907
+ };
1908
+ function EmbedAddon(config) {
1909
+ return {
1910
+ id: "x/embed",
1911
+ config,
1912
+ dependencies: ["x/schema"],
1913
+ impl: EmbedAddonImpl
1914
+ };
1915
+ }
1916
+ var EmbedAddonImpl = {
1917
+ id: "x/embed",
1918
+ initSchema(ctx) {
1919
+ ctx.addNode({
1920
+ name: "embed",
1921
+ group: "block",
1922
+ atom: true,
1923
+ attrs: {
1924
+ url: {},
1925
+ provider: { default: "" },
1926
+ width: { default: null },
1927
+ height: { default: null }
1928
+ },
1929
+ parseDOM: [{ tag: "iframe[data-embed]" }],
1930
+ toDOM: (node) => [
1931
+ "iframe",
1932
+ { src: node.attrs["url"], "data-embed": "true", class: "xe-embed" }
1933
+ ]
1934
+ });
1935
+ },
1936
+ init(ctx) {
1937
+ ctx.commands.register({
1938
+ id: "insertEmbed",
1939
+ label: "Embed",
1940
+ icon: "embed",
1941
+ isEnabled: () => true,
1942
+ isActive: () => false,
1943
+ execute: (c, args) => {
1944
+ const a = args ?? {};
1945
+ if (!a.url) return false;
1946
+ c.insertNode("embed", {
1947
+ url: a.url,
1948
+ provider: a.provider ?? "",
1949
+ width: a.width ?? null,
1950
+ height: a.height ?? null
1951
+ });
1952
+ return true;
1953
+ }
1954
+ });
1955
+ return {};
1956
+ }
1957
+ };
1958
+ function BookmarkAddon(config) {
1959
+ return {
1960
+ id: "x/bookmark",
1961
+ config,
1962
+ dependencies: ["x/schema"],
1963
+ impl: BookmarkAddonImpl
1964
+ };
1965
+ }
1966
+ var BookmarkAddonImpl = {
1967
+ id: "x/bookmark",
1968
+ initSchema(ctx) {
1969
+ ctx.addNode({
1970
+ name: "bookmark",
1971
+ group: "block",
1972
+ atom: true,
1973
+ attrs: {
1974
+ url: {},
1975
+ title: { default: "" },
1976
+ description: { default: "" },
1977
+ image: { default: null },
1978
+ favicon: { default: null }
1979
+ },
1980
+ parseDOM: [{ tag: "div[data-bookmark]" }],
1981
+ toDOM: () => ["div", { "data-bookmark": "true", class: "xe-bookmark" }]
1982
+ });
1983
+ },
1984
+ init(ctx) {
1985
+ ctx.commands.register({
1986
+ id: "insertBookmark",
1987
+ label: "Bookmark",
1988
+ icon: "bookmark",
1989
+ isEnabled: () => true,
1990
+ isActive: () => false,
1991
+ execute: (c, args) => {
1992
+ const a = args ?? {};
1993
+ if (!a.url) return false;
1994
+ c.insertNode("bookmark", {
1995
+ url: a.url,
1996
+ title: a.title ?? "",
1997
+ description: a.description ?? "",
1998
+ image: a.image ?? null,
1999
+ favicon: a.favicon ?? null
2000
+ });
2001
+ return true;
2002
+ }
2003
+ });
2004
+ return {};
2005
+ }
2006
+ };
2007
+
2008
+ // src/inline/index.ts
2009
+ function LinkAddon(config) {
2010
+ return {
2011
+ id: "x/link",
2012
+ config,
2013
+ dependencies: ["x/schema"],
2014
+ impl: LinkAddonImpl
2015
+ };
2016
+ }
2017
+ var LinkAddonImpl = {
2018
+ id: "x/link",
2019
+ initSchema(ctx) {
2020
+ ctx.addMark({
2021
+ name: "link",
2022
+ attrs: {
2023
+ href: {},
2024
+ title: { default: null },
2025
+ target: { default: "_blank" }
2026
+ },
2027
+ inclusive: false,
2028
+ parseDOM: [
2029
+ {
2030
+ tag: "a[href]",
2031
+ getAttrs: (el) => ({
2032
+ href: el.getAttribute("href"),
2033
+ title: el.getAttribute("title"),
2034
+ target: el.getAttribute("target")
2035
+ })
2036
+ }
2037
+ ],
2038
+ toDOM: (mark) => ["a", mark.attrs, 0]
2039
+ });
2040
+ },
2041
+ init(ctx, config = {}) {
2042
+ ctx.commands.register({
2043
+ id: "insertLink",
2044
+ label: "Insert Link",
2045
+ icon: "link",
2046
+ shortcut: "Mod-k",
2047
+ isEnabled: () => true,
2048
+ isActive: (c) => c.selection.hasMark("link"),
2049
+ execute: (c, args) => {
2050
+ const a = args ?? {};
2051
+ let url = a.href;
2052
+ if (!url) {
2053
+ if (typeof window === "undefined") return false;
2054
+ const prompted = window.prompt("Enter URL", "https://");
2055
+ if (!prompted) return false;
2056
+ url = prompted;
2057
+ }
2058
+ if (config.validate && !config.validate(url)) return false;
2059
+ c.addMark("link", {
2060
+ href: url,
2061
+ title: a.title ?? null,
2062
+ target: a.target ?? config.defaultTarget ?? "_blank"
2063
+ });
2064
+ return true;
2065
+ }
2066
+ });
2067
+ ctx.commands.register({
2068
+ id: "editLink",
2069
+ label: "Edit Link",
2070
+ isEnabled: (c) => c.selection.hasMark("link"),
2071
+ isActive: () => false,
2072
+ execute: (c) => {
2073
+ if (typeof window === "undefined") return false;
2074
+ const url = window.prompt("Edit URL", "https://");
2075
+ if (!url) return false;
2076
+ c.addMark("link", {
2077
+ href: url,
2078
+ target: config.defaultTarget ?? "_blank"
2079
+ });
2080
+ return true;
2081
+ }
2082
+ });
2083
+ ctx.commands.register({
2084
+ id: "removeLink",
2085
+ label: "Remove Link",
2086
+ isEnabled: (c) => c.selection.hasMark("link"),
2087
+ isActive: () => false,
2088
+ execute: (c) => {
2089
+ c.removeMark("link");
2090
+ return true;
2091
+ }
2092
+ });
2093
+ ctx.commands.register({
2094
+ id: "openLink",
2095
+ label: "Open Link",
2096
+ isEnabled: (c) => c.selection.hasMark("link"),
2097
+ isActive: () => false,
2098
+ execute: () => true
2099
+ });
2100
+ return {};
2101
+ }
2102
+ };
2103
+ function MentionAddon(config) {
2104
+ return {
2105
+ id: "x/mention",
2106
+ config,
2107
+ dependencies: ["x/schema"],
2108
+ impl: MentionAddonImpl
2109
+ };
2110
+ }
2111
+ var MentionAddonImpl = {
2112
+ id: "x/mention",
2113
+ initSchema(ctx) {
2114
+ ctx.addNode({
2115
+ name: "mention",
2116
+ group: "inline",
2117
+ inline: true,
2118
+ atom: true,
2119
+ attrs: { id: {}, label: {}, type: { default: "user" } },
2120
+ parseDOM: [{ tag: "span[data-mention]" }],
2121
+ toDOM: (node) => [
2122
+ "span",
2123
+ {
2124
+ "data-mention": node.attrs["type"],
2125
+ "data-id": node.attrs["id"],
2126
+ class: "xe-mention"
2127
+ },
2128
+ `@${node.attrs["label"]}`
2129
+ ]
2130
+ });
2131
+ },
2132
+ init(ctx) {
2133
+ ctx.commands.register({
2134
+ id: "insertMention",
2135
+ label: "Mention",
2136
+ icon: "mention",
2137
+ isEnabled: () => true,
2138
+ isActive: () => false,
2139
+ execute: (c, args) => {
2140
+ const a = args ?? {};
2141
+ if (!a.id || !a.label) return false;
2142
+ c.insertNode("mention", {
2143
+ id: a.id,
2144
+ label: a.label,
2145
+ type: a.type ?? "user"
2146
+ });
2147
+ return true;
2148
+ }
2149
+ });
2150
+ return {};
2151
+ }
2152
+ };
2153
+ function EmojiAddon(config) {
2154
+ return {
2155
+ id: "x/emoji",
2156
+ config,
2157
+ dependencies: ["x/schema"],
2158
+ impl: EmojiAddonImpl
2159
+ };
2160
+ }
2161
+ var EmojiAddonImpl = {
2162
+ id: "x/emoji",
2163
+ initSchema(ctx) {
2164
+ ctx.addNode({
2165
+ name: "emoji",
2166
+ group: "inline",
2167
+ inline: true,
2168
+ atom: true,
2169
+ attrs: { id: {}, native: {} },
2170
+ parseDOM: [{ tag: "span[data-emoji]" }],
2171
+ toDOM: (node) => [
2172
+ "span",
2173
+ { "data-emoji": node.attrs["id"], class: "xe-emoji" },
2174
+ node.attrs["native"]
2175
+ ]
2176
+ });
2177
+ },
2178
+ init(ctx) {
2179
+ ctx.commands.register({
2180
+ id: "insertEmoji",
2181
+ label: "Emoji",
2182
+ icon: "emoji",
2183
+ isEnabled: () => true,
2184
+ isActive: () => false,
2185
+ execute: (c, args) => {
2186
+ const a = args ?? {};
2187
+ const id = a.id ?? a.shortcode ?? "smile";
2188
+ const native = a.native ?? "\u{1F642}";
2189
+ c.insertNode("emoji", { id, native });
2190
+ return true;
2191
+ }
2192
+ });
2193
+ return {};
2194
+ }
2195
+ };
2196
+ function EquationAddon(config) {
2197
+ return {
2198
+ id: "x/equation",
2199
+ config,
2200
+ dependencies: ["x/schema"],
2201
+ impl: EquationAddonImpl
2202
+ };
2203
+ }
2204
+ var EquationAddonImpl = {
2205
+ id: "x/equation",
2206
+ initSchema(ctx) {
2207
+ ctx.addMark({
2208
+ name: "equation",
2209
+ parseDOM: [{ tag: "span[data-equation]" }],
2210
+ toDOM: () => [
2211
+ "span",
2212
+ { "data-equation": "true", class: "xe-equation-inline" },
2213
+ 0
2214
+ ]
2215
+ });
2216
+ ctx.addNode({
2217
+ name: "equationBlock",
2218
+ group: "block",
2219
+ atom: true,
2220
+ attrs: { src: {} },
2221
+ parseDOM: [{ tag: "div[data-equation-block]" }],
2222
+ toDOM: () => [
2223
+ "div",
2224
+ { "data-equation-block": "true", class: "xe-equation-block" }
2225
+ ]
2226
+ });
2227
+ },
2228
+ init(ctx) {
2229
+ ctx.commands.register({
2230
+ id: "insertEquationBlock",
2231
+ label: "Equation Block",
2232
+ icon: "equation-block",
2233
+ isEnabled: () => true,
2234
+ isActive: () => false,
2235
+ execute: (c, args) => {
2236
+ const src = args?.latex ?? args?.src ?? "";
2237
+ c.insertNode("equationBlock", { src });
2238
+ return true;
2239
+ }
2240
+ });
2241
+ ctx.commands.register({
2242
+ id: "insertEquation",
2243
+ label: "Equation",
2244
+ icon: "equation",
2245
+ isEnabled: () => true,
2246
+ isActive: () => false,
2247
+ execute: (c, args) => c.execCommand("insertEquationBlock", args)
2248
+ });
2249
+ ctx.commands.register({
2250
+ id: "insertInlineEquation",
2251
+ label: "Inline Equation",
2252
+ icon: "equation",
2253
+ isEnabled: () => true,
2254
+ isActive: () => false,
2255
+ execute: (c) => {
2256
+ c.addMark("equation");
2257
+ return true;
2258
+ }
2259
+ });
2260
+ ctx.commands.register({
2261
+ id: "editEquation",
2262
+ label: "Edit Equation",
2263
+ isEnabled: () => true,
2264
+ isActive: () => false,
2265
+ execute: () => true
2266
+ });
2267
+ return {};
2268
+ }
2269
+ };
2270
+ function FootnoteAddon() {
2271
+ return {
2272
+ id: "x/footnote",
2273
+ dependencies: ["x/schema"],
2274
+ impl: FootnoteAddonImpl
2275
+ };
2276
+ }
2277
+ var FootnoteAddonImpl = {
2278
+ id: "x/footnote",
2279
+ initSchema(ctx) {
2280
+ ctx.addNode({
2281
+ name: "footnoteRef",
2282
+ group: "inline",
2283
+ inline: true,
2284
+ atom: true,
2285
+ attrs: { id: {}, index: { default: 1 } },
2286
+ parseDOM: [{ tag: "sup[data-footnote-ref]" }],
2287
+ toDOM: (node) => [
2288
+ "sup",
2289
+ { "data-footnote-ref": node.attrs["id"], class: "xe-footnote-ref" },
2290
+ `[${node.attrs["index"]}]`
2291
+ ]
2292
+ });
2293
+ ctx.addNode({
2294
+ name: "footnote",
2295
+ group: "block",
2296
+ content: "block+",
2297
+ attrs: { id: {} },
2298
+ parseDOM: [{ tag: "div[data-footnote]" }],
2299
+ toDOM: () => [
2300
+ "div",
2301
+ { "data-footnote": "true", class: "xe-footnote" },
2302
+ 0
2303
+ ]
2304
+ });
2305
+ },
2306
+ init(ctx) {
2307
+ let _footnoteCounter = 0;
2308
+ ctx.commands.register({
2309
+ id: "insertFootnote",
2310
+ label: "Footnote",
2311
+ icon: "footnote",
2312
+ isEnabled: () => true,
2313
+ isActive: () => false,
2314
+ execute: (c, args) => {
2315
+ const a = args ?? {};
2316
+ const id = a.id ?? `fn-${++_footnoteCounter}`;
2317
+ c.insertNode("footnote", { id }, [
2318
+ {
2319
+ type: "paragraph",
2320
+ content: a.text ? [{ type: "text", text: a.text }] : []
2321
+ }
2322
+ ]);
2323
+ return true;
2324
+ }
2325
+ });
2326
+ ctx.commands.register({
2327
+ id: "editFootnote",
2328
+ label: "Edit Footnote",
2329
+ isEnabled: () => true,
2330
+ isActive: () => false,
2331
+ execute: () => true
2332
+ });
2333
+ return {};
2334
+ }
2335
+ };
2336
+ function SmartChipAddon(config) {
2337
+ return {
2338
+ id: "x/smart-chip",
2339
+ config,
2340
+ dependencies: ["x/mention"],
2341
+ impl: SmartChipAddonImpl
2342
+ };
2343
+ }
2344
+ var SmartChipAddonImpl = {
2345
+ id: "x/smart-chip",
2346
+ initSchema(ctx) {
2347
+ ctx.addNode({
2348
+ name: "smartChip",
2349
+ group: "inline",
2350
+ inline: true,
2351
+ atom: true,
2352
+ attrs: {
2353
+ kind: { default: "generic" },
2354
+ value: { default: "" },
2355
+ label: { default: null }
2356
+ },
2357
+ parseDOM: [{ tag: "span[data-smart-chip]" }],
2358
+ toDOM: (node) => [
2359
+ "span",
2360
+ {
2361
+ "data-smart-chip": node.attrs["kind"],
2362
+ "data-value": node.attrs["value"],
2363
+ class: "xe-smart-chip"
2364
+ },
2365
+ String(node.attrs["label"] ?? node.attrs["value"])
2366
+ ]
2367
+ });
2368
+ },
2369
+ init(ctx) {
2370
+ ctx.commands.register({
2371
+ id: "insertSmartChip",
2372
+ label: "Smart Chip",
2373
+ icon: "smart-chip",
2374
+ isEnabled: () => true,
2375
+ isActive: () => false,
2376
+ execute: (c, args) => {
2377
+ const a = args ?? {};
2378
+ c.insertNode("smartChip", {
2379
+ kind: a.kind ?? "generic",
2380
+ value: a.value ?? "",
2381
+ label: a.label ?? null
2382
+ });
2383
+ return true;
2384
+ }
2385
+ });
2386
+ return {};
2387
+ }
2388
+ };
2389
+
2390
+ // src/ui-shell/command-group.ts
2391
+ function createCommandGroup(ctx, opts) {
2392
+ const mode = opts.mode ?? "inline";
2393
+ const density = opts.density ?? (mode === "popover" ? "compact" : "comfortable");
2394
+ const orientation = opts.orientation ?? "horizontal";
2395
+ const showLabels = opts.showLabels ?? false;
2396
+ const showTooltips = opts.showTooltips ?? true;
2397
+ let loading = opts.loading ?? /* @__PURE__ */ new Set();
2398
+ const el = document.createElement("div");
2399
+ el.className = `xe-cgroup xe-cgroup--${mode} xe-cgroup--${density} xe-cgroup--${orientation}`;
2400
+ el.setAttribute("role", "group");
2401
+ el.setAttribute("aria-orientation", orientation);
2402
+ if (opts.label) el.setAttribute("aria-label", opts.label);
2403
+ const entries = [];
2404
+ for (const cmdId of opts.commands) {
2405
+ const cmd = ctx.commands.get(cmdId);
2406
+ if (!cmd) continue;
2407
+ const btn = document.createElement("button");
2408
+ btn.type = "button";
2409
+ btn.className = "xe-cgroup__btn";
2410
+ btn.dataset["cmdId"] = cmdId;
2411
+ btn.setAttribute("aria-label", cmd.label);
2412
+ btn.tabIndex = -1;
2413
+ if (cmd.icon) {
2414
+ const svg = ctx.icons.get(cmd.icon);
2415
+ const iconWrap = document.createElement("span");
2416
+ iconWrap.className = "xe-cgroup__icon";
2417
+ iconWrap.innerHTML = svg ?? cmd.icon;
2418
+ btn.appendChild(iconWrap);
2419
+ }
2420
+ if (showLabels) {
2421
+ const label = document.createElement("span");
2422
+ label.className = "xe-cgroup__label";
2423
+ label.textContent = cmd.label;
2424
+ btn.appendChild(label);
2425
+ }
2426
+ btn.addEventListener("click", (ev) => {
2427
+ ev.preventDefault();
2428
+ if (btn.disabled) return;
2429
+ if (loading.has(cmdId)) return;
2430
+ if (opts.onActivate) opts.onActivate(cmdId);
2431
+ else ctx.execCommand(cmdId);
2432
+ });
2433
+ btn.addEventListener("mousedown", (ev) => {
2434
+ ev.preventDefault();
2435
+ });
2436
+ el.appendChild(btn);
2437
+ entries.push({ cmdId, cmd, btn });
2438
+ }
2439
+ let activeIndex = entries.findIndex(
2440
+ (e) => !(e.cmd.isEnabled?.(ctx) === false)
2441
+ );
2442
+ if (activeIndex < 0 && entries.length > 0) activeIndex = 0;
2443
+ if (activeIndex >= 0) entries[activeIndex].btn.tabIndex = 0;
2444
+ const setActive = (next, focus) => {
2445
+ if (next < 0 || next >= entries.length) return;
2446
+ if (entries[next].btn.disabled) return;
2447
+ if (activeIndex >= 0 && activeIndex < entries.length) {
2448
+ entries[activeIndex].btn.tabIndex = -1;
2449
+ }
2450
+ activeIndex = next;
2451
+ entries[activeIndex].btn.tabIndex = 0;
2452
+ if (focus) entries[activeIndex].btn.focus();
2453
+ };
2454
+ const findNext = (from, dir) => {
2455
+ if (entries.length === 0) return -1;
2456
+ for (let i = 1; i <= entries.length; i++) {
2457
+ const idx = (from + dir * i + entries.length * i) % entries.length;
2458
+ if (!entries[idx].btn.disabled) return idx;
2459
+ }
2460
+ return -1;
2461
+ };
2462
+ const onKeyDown = (ev) => {
2463
+ const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
2464
+ const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
2465
+ if (ev.key === nextKey) {
2466
+ ev.preventDefault();
2467
+ const n = findNext(activeIndex, 1);
2468
+ if (n >= 0) setActive(n, true);
2469
+ } else if (ev.key === prevKey) {
2470
+ ev.preventDefault();
2471
+ const n = findNext(activeIndex, -1);
2472
+ if (n >= 0) setActive(n, true);
2473
+ } else if (ev.key === "Home") {
2474
+ ev.preventDefault();
2475
+ for (let i = 0; i < entries.length; i++) {
2476
+ if (!entries[i].btn.disabled) {
2477
+ setActive(i, true);
2478
+ break;
2479
+ }
2480
+ }
2481
+ } else if (ev.key === "End") {
2482
+ ev.preventDefault();
2483
+ for (let i = entries.length - 1; i >= 0; i--) {
2484
+ if (!entries[i].btn.disabled) {
2485
+ setActive(i, true);
2486
+ break;
2487
+ }
2488
+ }
2489
+ }
2490
+ };
2491
+ el.addEventListener("keydown", onKeyDown);
2492
+ const onFocusIn = (ev) => {
2493
+ const target = ev.target;
2494
+ if (!target) return;
2495
+ const idx = entries.findIndex((e) => e.btn === target);
2496
+ if (idx >= 0 && idx !== activeIndex) setActive(idx, false);
2497
+ };
2498
+ el.addEventListener("focusin", onFocusIn);
2499
+ const update = () => {
2500
+ for (const entry of entries) {
2501
+ const { cmd, btn, cmdId } = entry;
2502
+ const enabled = cmd.isEnabled?.(ctx) ?? true;
2503
+ const active = cmd.isActive?.(ctx) ?? false;
2504
+ const isLoading = loading.has(cmdId);
2505
+ btn.disabled = !enabled || isLoading;
2506
+ btn.setAttribute("aria-disabled", String(!enabled));
2507
+ btn.setAttribute("aria-pressed", String(active));
2508
+ if (isLoading) btn.setAttribute("aria-busy", "true");
2509
+ else btn.removeAttribute("aria-busy");
2510
+ btn.classList.toggle("xe-cgroup__btn--active", active);
2511
+ btn.classList.toggle("xe-cgroup__btn--disabled", !enabled);
2512
+ btn.classList.toggle("xe-cgroup__btn--loading", isLoading);
2513
+ const tooltipText = showTooltips && cmd.label ? cmd.shortcut ? `${cmd.label} (${cmd.shortcut})` : cmd.label : "";
2514
+ if (tooltipText) btn.title = tooltipText;
2515
+ else btn.removeAttribute("title");
2516
+ const hasSpinner = btn.querySelector(".xe-cgroup__spinner");
2517
+ if (isLoading && !hasSpinner) {
2518
+ const spinner = document.createElement("span");
2519
+ spinner.className = "xe-cgroup__spinner";
2520
+ spinner.setAttribute("aria-hidden", "true");
2521
+ btn.appendChild(spinner);
2522
+ } else if (!isLoading && hasSpinner) {
2523
+ hasSpinner.remove();
2524
+ }
2525
+ }
2526
+ if (activeIndex >= 0 && activeIndex < entries.length && entries[activeIndex].btn.disabled) {
2527
+ const n = findNext(activeIndex, 1);
2528
+ if (n >= 0) setActive(n, false);
2529
+ }
2530
+ };
2531
+ update();
2532
+ const focusFirst = () => {
2533
+ const n = findNext(-1, 1);
2534
+ if (n >= 0) setActive(n, true);
2535
+ };
2536
+ const focusLast = () => {
2537
+ const n = findNext(entries.length, -1);
2538
+ if (n >= 0) setActive(n, true);
2539
+ };
2540
+ const setLoading = (next) => {
2541
+ loading = next;
2542
+ update();
2543
+ };
2544
+ const destroy = () => {
2545
+ el.removeEventListener("keydown", onKeyDown);
2546
+ el.removeEventListener("focusin", onFocusIn);
2547
+ el.remove();
2548
+ };
2549
+ return { el, update, focusFirst, focusLast, setLoading, destroy };
2550
+ }
2551
+
2552
+ // src/ui-shell/index.ts
2553
+ function ToolbarAddon(config) {
2554
+ return {
2555
+ id: "x/toolbar",
2556
+ config,
2557
+ dependencies: ["x/commands"],
2558
+ impl: ToolbarAddonImpl
2559
+ };
2560
+ }
2561
+ var ToolbarAddonImpl = {
2562
+ id: "x/toolbar",
2563
+ init(ctx, config = {}) {
2564
+ const container = ctx.getSlot("toolbar");
2565
+ if (!container) return {};
2566
+ const toolbar = document.createElement("div");
2567
+ toolbar.className = "xe-toolbar";
2568
+ toolbar.setAttribute("role", "toolbar");
2569
+ container.appendChild(toolbar);
2570
+ let groupHandles = [];
2571
+ let lastGroupsKey = "";
2572
+ const render = () => {
2573
+ const groups = config.groups ?? [
2574
+ { commands: ctx.commands.getAll().map((c) => c.id) }
2575
+ ];
2576
+ const groupsKey = JSON.stringify(
2577
+ groups.map((g) => [g.label ?? "", g.commands])
2578
+ );
2579
+ if (groupsKey === lastGroupsKey) {
2580
+ for (const h of groupHandles) h.update();
2581
+ return;
2582
+ }
2583
+ lastGroupsKey = groupsKey;
2584
+ for (const h of groupHandles) h.destroy();
2585
+ groupHandles = [];
2586
+ toolbar.innerHTML = "";
2587
+ for (const group of groups) {
2588
+ const handle = createCommandGroup(ctx, {
2589
+ commands: group.commands,
2590
+ label: group.label,
2591
+ mode: "inline",
2592
+ density: "comfortable",
2593
+ showLabels: config.showLabels,
2594
+ showTooltips: config.showTooltips
2595
+ });
2596
+ toolbar.appendChild(handle.el);
2597
+ groupHandles.push(handle);
2598
+ }
2599
+ };
2600
+ render();
2601
+ const unsub = ctx.on("selection:change", () => {
2602
+ for (const h of groupHandles) h.update();
2603
+ });
2604
+ const unsub2 = ctx.on("doc:change", render);
2605
+ return {
2606
+ destroy() {
2607
+ unsub();
2608
+ unsub2();
2609
+ for (const h of groupHandles) h.destroy();
2610
+ groupHandles = [];
2611
+ toolbar.remove();
2612
+ }
2613
+ };
2614
+ }
2615
+ };
2616
+ function BubbleMenuAddon(config) {
2617
+ return {
2618
+ id: "x/bubble-menu",
2619
+ config,
2620
+ dependencies: ["x/commands"],
2621
+ impl: BubbleMenuAddonImpl
2622
+ };
2623
+ }
2624
+ var DEFAULT_BUBBLE_COMMANDS = [
2625
+ "bold",
2626
+ "italic",
2627
+ "underline",
2628
+ "strikethrough",
2629
+ "inlineCode",
2630
+ "insertLink"
2631
+ ];
2632
+ var BubbleMenuAddonImpl = {
2633
+ id: "x/bubble-menu",
2634
+ init(ctx, config = {}) {
2635
+ const menu = document.createElement("div");
2636
+ menu.className = "xe-bubble-menu";
2637
+ menu.style.display = "none";
2638
+ menu.style.position = "absolute";
2639
+ ctx.getSlot("overlay")?.appendChild(menu);
2640
+ const commands = (config.commands ?? DEFAULT_BUBBLE_COMMANDS).filter(
2641
+ (id) => !!ctx.commands.get(id)
2642
+ );
2643
+ const group = createCommandGroup(ctx, {
2644
+ commands,
2645
+ mode: "popover",
2646
+ density: "compact",
2647
+ showTooltips: true,
2648
+ label: "Selection actions"
2649
+ });
2650
+ menu.appendChild(group.el);
2651
+ const unsub = ctx.on("selection:change", () => {
2652
+ if (ctx.selection.empty) {
2653
+ menu.style.display = "none";
2654
+ } else {
2655
+ menu.style.display = "flex";
2656
+ group.update();
2657
+ }
2658
+ });
2659
+ const unsub2 = ctx.on("doc:change", () => group.update());
2660
+ return {
2661
+ destroy() {
2662
+ unsub();
2663
+ unsub2();
2664
+ group.destroy();
2665
+ menu.remove();
2666
+ }
2667
+ };
2668
+ }
2669
+ };
2670
+ function SlashMenuAddon(config) {
2671
+ return {
2672
+ id: "x/slash-menu",
2673
+ config,
2674
+ dependencies: ["x/commands"],
2675
+ impl: SlashMenuAddonImpl
2676
+ };
2677
+ }
2678
+ var SlashMenuAddonImpl = {
2679
+ id: "x/slash-menu",
2680
+ init(ctx, config = {}) {
2681
+ const content = ctx.getSlot("content");
2682
+ let menu = null;
2683
+ let groupHandles = [];
2684
+ const close = () => {
2685
+ for (const h of groupHandles) h.destroy();
2686
+ groupHandles = [];
2687
+ if (menu) {
2688
+ menu.remove();
2689
+ menu = null;
2690
+ }
2691
+ };
2692
+ const buildGroups = () => {
2693
+ if (config.groups && config.groups.length > 0) return config.groups;
2694
+ const all = ctx.commands.getAll().slice(0, config.maxVisible ?? 30).map((c) => c.id);
2695
+ return [{ label: "Commands", commands: all }];
2696
+ };
2697
+ const open = () => {
2698
+ close();
2699
+ menu = document.createElement("div");
2700
+ menu.className = "xe-slash-menu";
2701
+ menu.setAttribute("data-xe-slash", "true");
2702
+ const groups = buildGroups();
2703
+ for (const grp of groups) {
2704
+ if (grp.label) {
2705
+ const header = document.createElement("div");
2706
+ header.className = "xe-slash-menu__label";
2707
+ header.textContent = grp.label;
2708
+ menu.appendChild(header);
2709
+ }
2710
+ const handle = createCommandGroup(ctx, {
2711
+ commands: grp.commands,
2712
+ label: grp.label,
2713
+ mode: "popover",
2714
+ density: "compact",
2715
+ orientation: "vertical",
2716
+ showLabels: true,
2717
+ showTooltips: false,
2718
+ onActivate: (cmdId) => {
2719
+ ctx.execCommand(cmdId);
2720
+ close();
2721
+ }
2722
+ });
2723
+ menu.appendChild(handle.el);
2724
+ groupHandles.push(handle);
2725
+ }
2726
+ const host = ctx.getSlot("overlay") ?? content ?? document.body;
2727
+ host.appendChild(menu);
2728
+ menu.addEventListener("keydown", (ev) => {
2729
+ if (ev.key === "Escape") {
2730
+ ev.preventDefault();
2731
+ close();
2732
+ }
2733
+ });
2734
+ groupHandles[0]?.focusFirst();
2735
+ };
2736
+ const onKeydown = (e) => {
2737
+ if (e.key === "/") {
2738
+ setTimeout(open, 0);
2739
+ } else if (e.key === "Escape" && menu) {
2740
+ e.preventDefault();
2741
+ close();
2742
+ }
2743
+ };
2744
+ const target = content ?? document;
2745
+ target.addEventListener("keydown", onKeydown);
2746
+ document.addEventListener(
2747
+ "click",
2748
+ (ev) => {
2749
+ if (menu && !menu.contains(ev.target)) close();
2750
+ },
2751
+ true
2752
+ );
2753
+ return {
2754
+ destroy() {
2755
+ close();
2756
+ target.removeEventListener("keydown", onKeydown);
2757
+ }
2758
+ };
2759
+ }
2760
+ };
2761
+ function CommandPaletteAddon(config) {
2762
+ return {
2763
+ id: "x/command-palette",
2764
+ config,
2765
+ dependencies: ["x/commands"],
2766
+ impl: CommandPaletteAddonImpl
2767
+ };
2768
+ }
2769
+ var CommandPaletteAddonImpl = {
2770
+ id: "x/command-palette",
2771
+ init(ctx, _config = {}) {
2772
+ let palette = null;
2773
+ const close = () => {
2774
+ if (palette) {
2775
+ palette.remove();
2776
+ palette = null;
2777
+ }
2778
+ };
2779
+ const open = () => {
2780
+ close();
2781
+ palette = document.createElement("div");
2782
+ palette.className = "xe-command-palette";
2783
+ palette.setAttribute("data-xe-palette", "true");
2784
+ palette.style.cssText = "position:fixed; top:20%; left:50%; transform:translateX(-50%); background:var(--xe-bg,#fff); border:1px solid var(--xe-border,#ddd); border-radius:8px; padding:10px; width:420px; max-height:60vh; overflow:auto; box-shadow:0 12px 48px rgba(0,0,0,0.2); z-index:1000;";
2785
+ const input = document.createElement("input");
2786
+ input.type = "text";
2787
+ input.placeholder = "Search commands\u2026";
2788
+ input.style.cssText = "width:100%; padding:8px; border:1px solid var(--xe-border,#ddd); border-radius:4px; box-sizing:border-box;";
2789
+ const list = document.createElement("div");
2790
+ list.className = "xe-command-palette-list";
2791
+ list.style.cssText = "margin-top:8px; max-height:50vh; overflow:auto;";
2792
+ const renderList = (q = "") => {
2793
+ list.innerHTML = "";
2794
+ const ql = q.toLowerCase();
2795
+ for (const cmd of ctx.commands.getAll()) {
2796
+ if (ql && !cmd.label.toLowerCase().includes(ql) && !cmd.id.toLowerCase().includes(ql))
2797
+ continue;
2798
+ const row = document.createElement("button");
2799
+ row.type = "button";
2800
+ row.textContent = cmd.label;
2801
+ row.style.cssText = "display:block; width:100%; text-align:left; padding:6px 8px; border:0; background:transparent; cursor:pointer;";
2802
+ row.addEventListener("mousedown", (ev) => {
2803
+ ev.preventDefault();
2804
+ ctx.execCommand(cmd.id);
2805
+ close();
2806
+ });
2807
+ list.appendChild(row);
2808
+ }
2809
+ };
2810
+ input.addEventListener("input", () => renderList(input.value));
2811
+ input.addEventListener("keydown", (ev) => {
2812
+ if (ev.key === "Escape") close();
2813
+ });
2814
+ palette.appendChild(input);
2815
+ palette.appendChild(list);
2816
+ renderList();
2817
+ document.body.appendChild(palette);
2818
+ input.focus();
2819
+ };
2820
+ const handleKeydown = (e) => {
2821
+ const mod = e.metaKey || e.ctrlKey;
2822
+ if (mod && (e.key === "k" || e.key === "K")) {
2823
+ e.preventDefault();
2824
+ ctx.emit("command-palette:open");
2825
+ open();
2826
+ }
2827
+ };
2828
+ document.addEventListener("keydown", handleKeydown);
2829
+ return {
2830
+ destroy() {
2831
+ close();
2832
+ document.removeEventListener("keydown", handleKeydown);
2833
+ }
2834
+ };
2835
+ }
2836
+ };
2837
+ function BlockHandlesAddon(config) {
2838
+ return { id: "x/block-handles", config, impl: BlockHandlesAddonImpl };
2839
+ }
2840
+ var BlockHandlesAddonImpl = {
2841
+ id: "x/block-handles",
2842
+ init(_ctx) {
2843
+ return {};
2844
+ }
2845
+ };
2846
+ function ContextMenuAddon() {
2847
+ return {
2848
+ id: "x/context-menu",
2849
+ dependencies: ["x/commands"],
2850
+ impl: ContextMenuAddonImpl
2851
+ };
2852
+ }
2853
+ var ContextMenuAddonImpl = {
2854
+ id: "x/context-menu",
2855
+ init(ctx) {
2856
+ const content = ctx.getSlot("content");
2857
+ const handleContextMenu = (e) => {
2858
+ e.preventDefault();
2859
+ ctx.emit("context-menu:open", { x: e.clientX, y: e.clientY });
2860
+ };
2861
+ content?.addEventListener("contextmenu", handleContextMenu);
2862
+ return {
2863
+ destroy() {
2864
+ content?.removeEventListener("contextmenu", handleContextMenu);
2865
+ }
2866
+ };
2867
+ }
2868
+ };
2869
+ function StatusBarAddon(config) {
2870
+ return { id: "x/status-bar", config, impl: StatusBarAddonImpl };
2871
+ }
2872
+ var StatusBarAddonImpl = {
2873
+ id: "x/status-bar",
2874
+ init(ctx, config = {}) {
2875
+ const container = ctx.getSlot("statusbar");
2876
+ if (!container) return {};
2877
+ const slots = config.slots ?? ["wordCount", "charCount"];
2878
+ const bar = document.createElement("div");
2879
+ bar.className = "xe-status-bar";
2880
+ bar.style.display = "flex";
2881
+ bar.style.gap = "12px";
2882
+ bar.style.padding = "4px 8px";
2883
+ container.appendChild(bar);
2884
+ const slotEls = /* @__PURE__ */ new Map();
2885
+ for (const slot of slots) {
2886
+ const el = document.createElement("span");
2887
+ el.dataset["slot"] = slot;
2888
+ el.className = `xe-status-${slot}`;
2889
+ bar.appendChild(el);
2890
+ slotEls.set(slot, el);
2891
+ }
2892
+ let words = 0, chars = 0, peerCount = 0, readingMinutes = 0;
2893
+ const render = () => {
2894
+ for (const [slot, el] of slotEls) {
2895
+ switch (slot) {
2896
+ case "wordCount":
2897
+ el.textContent = `Words: ${words}`;
2898
+ break;
2899
+ case "charCount":
2900
+ el.textContent = `Chars: ${chars}`;
2901
+ break;
2902
+ case "readingTime":
2903
+ el.textContent = `${readingMinutes}m read`;
2904
+ break;
2905
+ case "peers":
2906
+ el.textContent = peerCount > 0 ? `${peerCount} peer${peerCount === 1 ? "" : "s"}` : "Solo";
2907
+ break;
2908
+ case "cursorPos":
2909
+ el.textContent = `Pos: ${ctx.selection.from}`;
2910
+ break;
2911
+ case "saveStatus":
2912
+ el.textContent = "Saved";
2913
+ break;
2914
+ }
2915
+ }
2916
+ };
2917
+ render();
2918
+ const unsubs = [
2919
+ ctx.on("count:update", ((payload) => {
2920
+ const p = payload ?? {};
2921
+ if (typeof p.words === "number") words = p.words;
2922
+ if (typeof p.chars === "number") chars = p.chars;
2923
+ render();
2924
+ })),
2925
+ ctx.on("readingTime:update", ((payload) => {
2926
+ const p = payload ?? {};
2927
+ if (typeof p.minutes === "number") readingMinutes = p.minutes;
2928
+ render();
2929
+ })),
2930
+ ctx.on("collab:awareness", ((payload) => {
2931
+ const p = payload ?? {};
2932
+ if (typeof p.count === "number") peerCount = p.count;
2933
+ render();
2934
+ })),
2935
+ ctx.on("collab:sync", ((payload) => {
2936
+ const p = payload ?? {};
2937
+ if (p.state === "disconnected") {
2938
+ peerCount = 0;
2939
+ render();
2940
+ }
2941
+ })),
2942
+ ctx.on("selection:change", render),
2943
+ ctx.on("auto-save:saved", () => {
2944
+ const el = slotEls.get("saveStatus");
2945
+ if (el) {
2946
+ el.textContent = "Saved \u2713";
2947
+ setTimeout(() => {
2948
+ el.textContent = "Saved";
2949
+ }, 1500);
2950
+ }
2951
+ })
2952
+ ];
2953
+ return {
2954
+ destroy() {
2955
+ for (const u of unsubs) u();
2956
+ bar.remove();
2957
+ }
2958
+ };
2959
+ }
2960
+ };
2961
+
2962
+ // src/structure/index.ts
2963
+ function TableOfContentsAddon(config) {
2964
+ return {
2965
+ id: "x/toc",
2966
+ config,
2967
+ dependencies: ["x/headings"],
2968
+ impl: TableOfContentsAddonImpl
2969
+ };
2970
+ }
2971
+ var TableOfContentsAddonImpl = {
2972
+ id: "x/toc",
2973
+ initSchema(ctx) {
2974
+ ctx.addNode({
2975
+ name: "tableOfContents",
2976
+ group: "block",
2977
+ atom: true,
2978
+ parseDOM: [{ tag: "nav[data-toc]" }],
2979
+ toDOM: () => ["nav", { "data-toc": "true", class: "xe-toc" }]
2980
+ });
2981
+ },
2982
+ init(ctx) {
2983
+ ctx.commands.register({
2984
+ id: "insertTableOfContents",
2985
+ label: "Table of Contents",
2986
+ icon: "toc",
2987
+ isEnabled: () => true,
2988
+ isActive: () => false,
2989
+ execute: (c) => {
2990
+ c.insertNode("tableOfContents");
2991
+ return true;
2992
+ }
2993
+ });
2994
+ ctx.commands.register({
2995
+ id: "toggleOutlinePanel",
2996
+ label: "Toggle Outline Panel",
2997
+ isEnabled: () => true,
2998
+ isActive: () => false,
2999
+ execute: () => true
3000
+ });
3001
+ return {};
3002
+ }
3003
+ };
3004
+ function OutlineAddon() {
3005
+ return {
3006
+ id: "x/outline",
3007
+ dependencies: ["x/headings"],
3008
+ impl: OutlineAddonImpl
3009
+ };
3010
+ }
3011
+ var OutlineAddonImpl = {
3012
+ id: "x/outline",
3013
+ init(_ctx) {
3014
+ return {};
3015
+ }
3016
+ };
3017
+ function CoverAddon(config) {
3018
+ return { id: "x/cover", config, impl: CoverAddonImpl };
3019
+ }
3020
+ var CoverAddonImpl = {
3021
+ id: "x/cover",
3022
+ init(ctx) {
3023
+ ctx.commands.register({
3024
+ id: "setCover",
3025
+ label: "Set Cover",
3026
+ isEnabled: () => true,
3027
+ isActive: () => false,
3028
+ execute: () => true
3029
+ });
3030
+ ctx.commands.register({
3031
+ id: "removeCover",
3032
+ label: "Remove Cover",
3033
+ isEnabled: () => true,
3034
+ isActive: () => false,
3035
+ execute: () => true
3036
+ });
3037
+ ctx.commands.register({
3038
+ id: "repositionCover",
3039
+ label: "Reposition Cover",
3040
+ isEnabled: () => true,
3041
+ isActive: () => false,
3042
+ execute: () => true
3043
+ });
3044
+ return {};
3045
+ }
3046
+ };
3047
+ function BreadcrumbAddon(config) {
3048
+ return { id: "x/breadcrumb", config, impl: BreadcrumbAddonImpl };
3049
+ }
3050
+ var BreadcrumbAddonImpl = {
3051
+ id: "x/breadcrumb",
3052
+ init(_ctx) {
3053
+ return {};
3054
+ }
3055
+ };
3056
+ function TemplatesAddon() {
3057
+ return {
3058
+ id: "x/templates",
3059
+ dependencies: ["x/content"],
3060
+ impl: TemplatesAddonImpl
3061
+ };
3062
+ }
3063
+ var TemplatesAddonImpl = {
3064
+ id: "x/templates",
3065
+ init(ctx) {
3066
+ ctx.commands.register({
3067
+ id: "insertTemplate",
3068
+ label: "Insert Template",
3069
+ icon: "template",
3070
+ isEnabled: () => true,
3071
+ isActive: () => false,
3072
+ execute: () => true
3073
+ });
3074
+ return {};
3075
+ }
3076
+ };
3077
+ function PageSetupAddon(config) {
3078
+ return { id: "x/page-setup", config, impl: PageSetupAddonImpl };
3079
+ }
3080
+ var PageSetupAddonImpl = {
3081
+ id: "x/page-setup",
3082
+ init(ctx) {
3083
+ ctx.commands.register({
3084
+ id: "pageSetup",
3085
+ label: "Page Setup",
3086
+ isEnabled: () => true,
3087
+ isActive: () => false,
3088
+ execute: () => true
3089
+ });
3090
+ return {};
3091
+ }
3092
+ };
3093
+
3094
+ // src/export/index.ts
3095
+ function ContentAddon() {
3096
+ return { id: "x/content", impl: ContentAddonImpl };
3097
+ }
3098
+ function downloadString(filename, content, mimeType) {
3099
+ if (typeof document === "undefined" || typeof URL === "undefined") return;
3100
+ const blob = new Blob([content], { type: mimeType });
3101
+ const url = URL.createObjectURL(blob);
3102
+ const a = document.createElement("a");
3103
+ a.href = url;
3104
+ a.download = filename;
3105
+ a.style.display = "none";
3106
+ document.body.appendChild(a);
3107
+ a.click();
3108
+ setTimeout(() => {
3109
+ a.remove();
3110
+ URL.revokeObjectURL(url);
3111
+ }, 0);
3112
+ }
3113
+ var ContentAddonImpl = {
3114
+ id: "x/content",
3115
+ init(ctx) {
3116
+ ctx.commands.register({
3117
+ id: "exportJson",
3118
+ label: "Export as JSON",
3119
+ isEnabled: () => true,
3120
+ isActive: () => false,
3121
+ execute: (c) => {
3122
+ downloadString(
3123
+ "document.json",
3124
+ c.getContent("json"),
3125
+ "application/json"
3126
+ );
3127
+ return true;
3128
+ }
3129
+ });
3130
+ ctx.commands.register({
3131
+ id: "exportHtml",
3132
+ label: "Export as HTML",
3133
+ isEnabled: () => true,
3134
+ isActive: () => false,
3135
+ execute: (c) => {
3136
+ downloadString("document.html", c.getContent("html"), "text/html");
3137
+ return true;
3138
+ }
3139
+ });
3140
+ ctx.commands.register({
3141
+ id: "exportMarkdown",
3142
+ label: "Export as Markdown",
3143
+ isEnabled: () => true,
3144
+ isActive: () => false,
3145
+ execute: (c) => {
3146
+ downloadString(
3147
+ "document.md",
3148
+ c.getContent("markdown"),
3149
+ "text/markdown"
3150
+ );
3151
+ return true;
3152
+ }
3153
+ });
3154
+ ctx.commands.register({
3155
+ id: "exportText",
3156
+ label: "Export as Text",
3157
+ isEnabled: () => true,
3158
+ isActive: () => false,
3159
+ execute: (c) => {
3160
+ downloadString("document.txt", c.getContent("text"), "text/plain");
3161
+ return true;
3162
+ }
3163
+ });
3164
+ return {};
3165
+ }
3166
+ };
3167
+ function DocxAddon() {
3168
+ return { id: "x/docx", dependencies: ["x/content"], impl: DocxAddonImpl };
3169
+ }
3170
+ var DocxAddonImpl = {
3171
+ id: "x/docx",
3172
+ init(ctx) {
3173
+ ctx.commands.register({
3174
+ id: "exportDocx",
3175
+ label: "Export as DOCX",
3176
+ icon: "docx",
3177
+ isEnabled: () => true,
3178
+ isActive: () => false,
3179
+ execute: () => true
3180
+ });
3181
+ ctx.commands.register({
3182
+ id: "importDocx",
3183
+ label: "Import DOCX",
3184
+ isEnabled: () => true,
3185
+ isActive: () => false,
3186
+ execute: () => true
3187
+ });
3188
+ return {};
3189
+ }
3190
+ };
3191
+ function PdfAddon() {
3192
+ return { id: "x/pdf", dependencies: ["x/content"], impl: PdfAddonImpl };
3193
+ }
3194
+ var PdfAddonImpl = {
3195
+ id: "x/pdf",
3196
+ init(ctx) {
3197
+ ctx.commands.register({
3198
+ id: "exportPdf",
3199
+ label: "Export as PDF",
3200
+ icon: "pdf",
3201
+ isEnabled: () => true,
3202
+ isActive: () => false,
3203
+ execute: () => true
3204
+ });
3205
+ return {};
3206
+ }
3207
+ };
3208
+ function PrintAddon() {
3209
+ return { id: "x/print", impl: PrintAddonImpl };
3210
+ }
3211
+ var PrintAddonImpl = {
3212
+ id: "x/print",
3213
+ init(ctx) {
3214
+ ctx.commands.register({
3215
+ id: "print",
3216
+ label: "Print",
3217
+ icon: "print",
3218
+ shortcut: "Mod-p",
3219
+ isEnabled: () => true,
3220
+ isActive: () => false,
3221
+ execute: () => {
3222
+ window.print();
3223
+ return true;
3224
+ }
3225
+ });
3226
+ return {};
3227
+ }
3228
+ };
3229
+
3230
+ // src/tools/index.ts
3231
+ function SearchAddon() {
3232
+ return { id: "x/search", impl: SearchAddonImpl };
3233
+ }
3234
+ var SearchAddonImpl = {
3235
+ id: "x/search",
3236
+ init(ctx) {
3237
+ ctx.commands.register({
3238
+ id: "findReplace",
3239
+ label: "Find & Replace",
3240
+ icon: "search",
3241
+ shortcut: "Mod-f",
3242
+ isEnabled: () => true,
3243
+ isActive: () => false,
3244
+ execute: () => true
3245
+ });
3246
+ ctx.commands.register({
3247
+ id: "findNext",
3248
+ label: "Find Next",
3249
+ shortcut: "F3",
3250
+ isEnabled: () => true,
3251
+ isActive: () => false,
3252
+ execute: () => true
3253
+ });
3254
+ ctx.commands.register({
3255
+ id: "findPrevious",
3256
+ label: "Find Previous",
3257
+ shortcut: "Shift-F3",
3258
+ isEnabled: () => true,
3259
+ isActive: () => false,
3260
+ execute: () => true
3261
+ });
3262
+ return {};
3263
+ }
3264
+ };
3265
+ function SpellCheckAddon() {
3266
+ return { id: "x/spell-check", impl: SpellCheckAddonImpl };
3267
+ }
3268
+ var SpellCheckAddonImpl = {
3269
+ id: "x/spell-check",
3270
+ init(_ctx) {
3271
+ return {};
3272
+ }
3273
+ };
3274
+ function GrammarAddon() {
3275
+ return {
3276
+ id: "x/grammar",
3277
+ dependencies: ["x/spell-check"],
3278
+ impl: GrammarAddonImpl
3279
+ };
3280
+ }
3281
+ var GrammarAddonImpl = {
3282
+ id: "x/grammar",
3283
+ init(_ctx) {
3284
+ return {};
3285
+ }
3286
+ };
3287
+ function AutoSaveAddon(config) {
3288
+ return {
3289
+ id: "x/auto-save",
3290
+ config,
3291
+ dependencies: ["x/content"],
3292
+ impl: AutoSaveAddonImpl
3293
+ };
3294
+ }
3295
+ var AutoSaveAddonImpl = {
3296
+ id: "x/auto-save",
3297
+ init(ctx, config) {
3298
+ const interval = config.interval ?? 5e3;
3299
+ let timer = null;
3300
+ let dirty = false;
3301
+ const schedSave = () => {
3302
+ dirty = true;
3303
+ if (timer) clearTimeout(timer);
3304
+ timer = setTimeout(async () => {
3305
+ if (!dirty) return;
3306
+ dirty = false;
3307
+ const content = ctx.getContent(config.format ?? "json");
3308
+ await config.save(content, config.format ?? "json").catch(() => {
3309
+ });
3310
+ ctx.emit("auto-save:saved");
3311
+ }, interval);
3312
+ };
3313
+ const unsub = ctx.on("doc:change", schedSave);
3314
+ return {
3315
+ destroy() {
3316
+ unsub();
3317
+ if (timer) clearTimeout(timer);
3318
+ }
3319
+ };
3320
+ }
3321
+ };
3322
+ function ReadOnlyAddon(config) {
3323
+ return { id: "x/read-only", config, impl: ReadOnlyAddonImpl };
3324
+ }
3325
+ var ReadOnlyAddonImpl = {
3326
+ id: "x/read-only",
3327
+ init(ctx, config = {}) {
3328
+ let readOnly = config.readOnly ?? false;
3329
+ const ctxImpl = ctx;
3330
+ if (readOnly) ctxImpl.setEditable(false);
3331
+ ctx.commands.register({
3332
+ id: "toggleReadOnly",
3333
+ label: "Toggle Read Only",
3334
+ isEnabled: () => true,
3335
+ isActive: () => readOnly,
3336
+ execute: () => {
3337
+ readOnly = !readOnly;
3338
+ ctxImpl.setEditable(!readOnly);
3339
+ ctx.emit("read-only:change", { readOnly });
3340
+ return true;
3341
+ }
3342
+ });
3343
+ return {};
3344
+ }
3345
+ };
3346
+
3347
+ // src/collab/index.ts
3348
+ function CollabAddon(config) {
3349
+ return { id: "x/collab", config, impl: CollabAddonImpl };
3350
+ }
3351
+ var CollabAddonImpl = {
3352
+ id: "x/collab",
3353
+ init(ctx, _config) {
3354
+ const binder = ctx._collabBinder;
3355
+ if (!binder?.awareness) return {};
3356
+ const aw = binder.awareness;
3357
+ const emitPeerCount = () => {
3358
+ const states = aw.getStates();
3359
+ const others = Math.max(0, states.size - 1);
3360
+ ctx.emit("collab:awareness", { count: others });
3361
+ };
3362
+ const handler = () => emitPeerCount();
3363
+ aw.on("update", handler);
3364
+ emitPeerCount();
3365
+ return {
3366
+ destroy() {
3367
+ aw.off("update", handler);
3368
+ }
3369
+ };
3370
+ }
3371
+ };
3372
+ function AwarenessAddon(config) {
3373
+ return {
3374
+ id: "x/awareness",
3375
+ config,
3376
+ dependencies: ["x/collab"],
3377
+ impl: AwarenessAddonImpl
3378
+ };
3379
+ }
3380
+ var AwarenessAddonImpl = {
3381
+ id: "x/awareness",
3382
+ init(ctx, config = {}) {
3383
+ const binder = ctx._collabBinder;
3384
+ const aw = binder?.awareness;
3385
+ if (!aw) return {};
3386
+ const palette = [
3387
+ "#e74c3c",
3388
+ "#3498db",
3389
+ "#2ecc71",
3390
+ "#f39c12",
3391
+ "#9b59b6",
3392
+ "#1abc9c"
3393
+ ];
3394
+ const identity = config.identity ?? {
3395
+ id: typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `user-${Math.random().toString(36).slice(2, 10)}`,
3396
+ name: "Anonymous",
3397
+ color: palette[Math.floor(Math.random() * palette.length)]
3398
+ };
3399
+ aw.setLocalStateField("user", identity);
3400
+ if (config.schemaVersion) {
3401
+ aw.setLocalStateField("schemaVersion", config.schemaVersion);
3402
+ }
3403
+ return {
3404
+ destroy() {
3405
+ try {
3406
+ aw.setLocalState(null);
3407
+ } catch {
3408
+ }
3409
+ }
3410
+ };
3411
+ }
3412
+ };
3413
+ function PresenceAddon() {
3414
+ return {
3415
+ id: "x/presence",
3416
+ dependencies: ["x/collab", "x/awareness"],
3417
+ impl: PresenceAddonImpl
3418
+ };
3419
+ }
3420
+ var PresenceAddonImpl = {
3421
+ id: "x/presence",
3422
+ init(_ctx) {
3423
+ return {};
3424
+ }
3425
+ };
3426
+ function CommentsAddon(config) {
3427
+ return { id: "x/comments", config, impl: CommentsAddonImpl };
3428
+ }
3429
+ var CommentsAddonImpl = {
3430
+ id: "x/comments",
3431
+ init(ctx) {
3432
+ ctx.commands.register({
3433
+ id: "addComment",
3434
+ label: "Add Comment",
3435
+ icon: "comment",
3436
+ shortcut: "Mod-Alt-m",
3437
+ isEnabled: () => true,
3438
+ isActive: () => false,
3439
+ execute: () => true
3440
+ });
3441
+ ctx.commands.register({
3442
+ id: "resolveComment",
3443
+ label: "Resolve Comment",
3444
+ isEnabled: () => true,
3445
+ isActive: () => false,
3446
+ execute: () => true
3447
+ });
3448
+ ctx.commands.register({
3449
+ id: "toggleComments",
3450
+ label: "Toggle Comments Panel",
3451
+ isEnabled: () => true,
3452
+ isActive: () => false,
3453
+ execute: () => true
3454
+ });
3455
+ return {};
3456
+ }
3457
+ };
3458
+ function SuggestionsAddon() {
3459
+ return {
3460
+ id: "x/suggestions",
3461
+ dependencies: ["x/schema", "x/commands"],
3462
+ impl: SuggestionsAddonImpl
3463
+ };
3464
+ }
3465
+ var SuggestionsAddonImpl = {
3466
+ id: "x/suggestions",
3467
+ init(ctx) {
3468
+ ctx.commands.register({
3469
+ id: "toggleSuggestions",
3470
+ label: "Toggle Tracked Changes",
3471
+ isEnabled: () => true,
3472
+ isActive: () => false,
3473
+ execute: () => true
3474
+ });
3475
+ ctx.commands.register({
3476
+ id: "acceptSuggestion",
3477
+ label: "Accept Change",
3478
+ isEnabled: () => true,
3479
+ isActive: () => false,
3480
+ execute: () => true
3481
+ });
3482
+ ctx.commands.register({
3483
+ id: "rejectSuggestion",
3484
+ label: "Reject Change",
3485
+ isEnabled: () => true,
3486
+ isActive: () => false,
3487
+ execute: () => true
3488
+ });
3489
+ ctx.commands.register({
3490
+ id: "acceptAllSuggestions",
3491
+ label: "Accept All Changes",
3492
+ isEnabled: () => true,
3493
+ isActive: () => false,
3494
+ execute: () => true
3495
+ });
3496
+ ctx.commands.register({
3497
+ id: "rejectAllSuggestions",
3498
+ label: "Reject All Changes",
3499
+ isEnabled: () => true,
3500
+ isActive: () => false,
3501
+ execute: () => true
3502
+ });
3503
+ return {};
3504
+ }
3505
+ };
3506
+ function VersionAddon() {
3507
+ return {
3508
+ id: "x/version",
3509
+ dependencies: ["x/content"],
3510
+ impl: VersionAddonImpl
3511
+ };
3512
+ }
3513
+ var VersionAddonImpl = {
3514
+ id: "x/version",
3515
+ init(ctx) {
3516
+ ctx.commands.register({
3517
+ id: "createSnapshot",
3518
+ label: "Create Snapshot",
3519
+ isEnabled: () => true,
3520
+ isActive: () => false,
3521
+ execute: () => true
3522
+ });
3523
+ ctx.commands.register({
3524
+ id: "restoreSnapshot",
3525
+ label: "Restore Snapshot",
3526
+ isEnabled: () => true,
3527
+ isActive: () => false,
3528
+ execute: () => true
3529
+ });
3530
+ ctx.commands.register({
3531
+ id: "viewHistory",
3532
+ label: "View History",
3533
+ isEnabled: () => true,
3534
+ isActive: () => false,
3535
+ execute: () => true
3536
+ });
3537
+ return {};
3538
+ }
3539
+ };
3540
+
3541
+ // src/code/index.ts
3542
+ function CodeAddon(config) {
3543
+ return { id: "x/code", config, impl: CodeAddonImpl };
3544
+ }
3545
+ var CodeAddonImpl = {
3546
+ id: "x/code",
3547
+ init(_ctx) {
3548
+ return {};
3549
+ }
3550
+ };
3551
+ function LineNumbersAddon() {
3552
+ return {
3553
+ id: "x/line-numbers",
3554
+ dependencies: ["x/code"],
3555
+ impl: LineNumbersAddonImpl
3556
+ };
3557
+ }
3558
+ var LineNumbersAddonImpl = {
3559
+ id: "x/line-numbers",
3560
+ init(_ctx) {
3561
+ return {};
3562
+ }
3563
+ };
3564
+ function CodeFoldingAddon() {
3565
+ return {
3566
+ id: "x/code-folding",
3567
+ dependencies: ["x/code"],
3568
+ impl: CodeFoldingAddonImpl
3569
+ };
3570
+ }
3571
+ var CodeFoldingAddonImpl = {
3572
+ id: "x/code-folding",
3573
+ init(ctx) {
3574
+ ctx.commands.register({
3575
+ id: "foldCode",
3576
+ label: "Fold Code",
3577
+ isEnabled: () => true,
3578
+ isActive: () => false,
3579
+ execute: () => true
3580
+ });
3581
+ ctx.commands.register({
3582
+ id: "unfoldCode",
3583
+ label: "Unfold Code",
3584
+ isEnabled: () => true,
3585
+ isActive: () => false,
3586
+ execute: () => true
3587
+ });
3588
+ ctx.commands.register({
3589
+ id: "foldAll",
3590
+ label: "Fold All",
3591
+ isEnabled: () => true,
3592
+ isActive: () => false,
3593
+ execute: () => true
3594
+ });
3595
+ ctx.commands.register({
3596
+ id: "unfoldAll",
3597
+ label: "Unfold All",
3598
+ isEnabled: () => true,
3599
+ isActive: () => false,
3600
+ execute: () => true
3601
+ });
3602
+ return {};
3603
+ }
3604
+ };
3605
+ function BracketMatchingAddon() {
3606
+ return {
3607
+ id: "x/bracket-matching",
3608
+ dependencies: ["x/code"],
3609
+ impl: BracketMatchingAddonImpl
3610
+ };
3611
+ }
3612
+ var BracketMatchingAddonImpl = {
3613
+ id: "x/bracket-matching",
3614
+ init(_ctx) {
3615
+ return {};
3616
+ }
3617
+ };
3618
+ function CodeAutoCompleteAddon(config) {
3619
+ return {
3620
+ id: "x/code-autocomplete",
3621
+ config,
3622
+ dependencies: ["x/code"],
3623
+ impl: CodeAutoCompleteAddonImpl
3624
+ };
3625
+ }
3626
+ var CodeAutoCompleteAddonImpl = {
3627
+ id: "x/code-autocomplete",
3628
+ init(_ctx) {
3629
+ return {};
3630
+ }
3631
+ };
3632
+ function CodeLintAddon(config) {
3633
+ return {
3634
+ id: "x/code-lint",
3635
+ config,
3636
+ dependencies: ["x/code"],
3637
+ impl: CodeLintAddonImpl
3638
+ };
3639
+ }
3640
+ var CodeLintAddonImpl = {
3641
+ id: "x/code-lint",
3642
+ init(_ctx) {
3643
+ return {};
3644
+ }
3645
+ };
3646
+ function DiffAddon(config) {
3647
+ return {
3648
+ id: "x/diff",
3649
+ config,
3650
+ dependencies: ["x/code"],
3651
+ impl: DiffAddonImpl
3652
+ };
3653
+ }
3654
+ var DiffAddonImpl = {
3655
+ id: "x/diff",
3656
+ init(ctx) {
3657
+ ctx.commands.register({
3658
+ id: "toggleDiff",
3659
+ label: "Toggle Diff",
3660
+ isEnabled: () => true,
3661
+ isActive: () => false,
3662
+ execute: () => true
3663
+ });
3664
+ return {};
3665
+ }
3666
+ };
3667
+ function MiniMapAddon() {
3668
+ return { id: "x/minimap", dependencies: ["x/code"], impl: MiniMapAddonImpl };
3669
+ }
3670
+ var MiniMapAddonImpl = {
3671
+ id: "x/minimap",
3672
+ init(_ctx) {
3673
+ return {};
3674
+ }
3675
+ };
3676
+
3677
+ // src/utility/index.ts
3678
+ function PlaceholderAddon(config) {
3679
+ return { id: "x/placeholder", config, impl: PlaceholderAddonImpl };
3680
+ }
3681
+ var PlaceholderAddonImpl = {
3682
+ id: "x/placeholder",
3683
+ init(ctx, config) {
3684
+ const container = ctx.getSlot("content");
3685
+ if (!container) return {};
3686
+ const el = document.createElement("div");
3687
+ el.className = "xe-placeholder";
3688
+ el.textContent = config.text;
3689
+ el.style.pointerEvents = "none";
3690
+ el.style.position = "absolute";
3691
+ el.style.color = "var(--xe-fg)";
3692
+ el.style.opacity = "0.4";
3693
+ container.style.position = "relative";
3694
+ container.appendChild(el);
3695
+ const update = () => {
3696
+ const isEmpty = ctx.getContent("text").trim() === "";
3697
+ el.style.display = isEmpty ? "block" : "none";
3698
+ };
3699
+ update();
3700
+ const unsub = ctx.on("doc:change", update);
3701
+ return {
3702
+ destroy() {
3703
+ unsub();
3704
+ el.remove();
3705
+ }
3706
+ };
3707
+ }
3708
+ };
3709
+ function CountAddon(config) {
3710
+ return { id: "x/count", config, impl: CountAddonImpl };
3711
+ }
3712
+ var CountAddonImpl = {
3713
+ id: "x/count",
3714
+ init(ctx, config = {}) {
3715
+ const update = () => {
3716
+ const text = ctx.getContent("text");
3717
+ const words = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
3718
+ const chars = text.length;
3719
+ ctx.emit("count:update", { words, chars });
3720
+ };
3721
+ update();
3722
+ const unsub = ctx.on("doc:change", update);
3723
+ return { destroy: unsub };
3724
+ }
3725
+ };
3726
+ function ReadingTimeAddon() {
3727
+ return { id: "x/reading-time", impl: ReadingTimeAddonImpl };
3728
+ }
3729
+ var ReadingTimeAddonImpl = {
3730
+ id: "x/reading-time",
3731
+ init(ctx) {
3732
+ const update = () => {
3733
+ const text = ctx.getContent("text");
3734
+ const words = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
3735
+ const minutes = Math.ceil(words / 200);
3736
+ ctx.emit("readingTime:update", { minutes, words });
3737
+ };
3738
+ const unsub = ctx.on("doc:change", update);
3739
+ return { destroy: unsub };
3740
+ }
3741
+ };
3742
+ function FocusModeAddon(config) {
3743
+ return { id: "x/focus-mode", config, impl: FocusModeAddonImpl };
3744
+ }
3745
+ var FocusModeAddonImpl = {
3746
+ id: "x/focus-mode",
3747
+ init(ctx) {
3748
+ let active = false;
3749
+ ctx.commands.register({
3750
+ id: "toggleFocusMode",
3751
+ label: "Toggle Focus Mode",
3752
+ icon: "focus",
3753
+ isEnabled: () => true,
3754
+ isActive: () => active,
3755
+ execute: () => {
3756
+ active = !active;
3757
+ ctx.emit("focus-mode:change", { active });
3758
+ return true;
3759
+ }
3760
+ });
3761
+ return {};
3762
+ }
3763
+ };
3764
+ function FullScreenAddon() {
3765
+ return { id: "x/fullscreen", impl: FullScreenAddonImpl };
3766
+ }
3767
+ var FullScreenAddonImpl = {
3768
+ id: "x/fullscreen",
3769
+ init(ctx) {
3770
+ ctx.commands.register({
3771
+ id: "toggleFullScreen",
3772
+ label: "Toggle Full Screen",
3773
+ icon: "fullscreen",
3774
+ shortcut: "F11",
3775
+ isEnabled: () => true,
3776
+ isActive: () => !!document.fullscreenElement,
3777
+ execute: () => {
3778
+ if (document.fullscreenElement) {
3779
+ document.exitFullscreen?.();
3780
+ } else {
3781
+ ctx.shadowRoot?.host?.requestFullscreen?.();
3782
+ }
3783
+ return true;
3784
+ }
3785
+ });
3786
+ return {};
3787
+ }
3788
+ };
3789
+ function AccessibilityAddon() {
3790
+ return { id: "x/accessibility", impl: AccessibilityAddonImpl };
3791
+ }
3792
+ var AccessibilityAddonImpl = {
3793
+ id: "x/accessibility",
3794
+ init(ctx) {
3795
+ const content = ctx.getSlot("content");
3796
+ if (content) {
3797
+ content.setAttribute("role", "textbox");
3798
+ content.setAttribute("aria-multiline", "true");
3799
+ }
3800
+ const toolbar = ctx.getSlot("toolbar");
3801
+ if (toolbar) {
3802
+ toolbar.setAttribute("role", "toolbar");
3803
+ }
3804
+ return {};
3805
+ }
3806
+ };
3807
+ function MarkdownAddon(config) {
3808
+ return {
3809
+ id: "x/markdown",
3810
+ config,
3811
+ dependencies: ["x/schema"],
3812
+ impl: MarkdownAddonImpl
3813
+ };
3814
+ }
3815
+ var MarkdownAddonImpl = {
3816
+ id: "x/markdown",
3817
+ init(ctx) {
3818
+ ctx.commands.register({
3819
+ id: "toggleMarkdownMode",
3820
+ label: "Toggle Markdown Mode",
3821
+ isEnabled: () => true,
3822
+ isActive: () => false,
3823
+ execute: () => true
3824
+ });
3825
+ ctx.commands.register({
3826
+ id: "toggleMarkdownPreview",
3827
+ label: "Toggle Markdown Preview",
3828
+ isEnabled: () => true,
3829
+ isActive: () => false,
3830
+ execute: () => true
3831
+ });
3832
+ return {};
3833
+ }
3834
+ };
3835
+
3836
+ // src/index.ts
3837
+ var ADDON_IMPLEMENTATIONS = /* @__PURE__ */ new Map([
3838
+ [SchemaAddonImpl.id, SchemaAddonImpl],
3839
+ [CommandsAddonImpl.id, CommandsAddonImpl],
3840
+ [KeymapAddonImpl.id, KeymapAddonImpl],
3841
+ [HistoryAddonImpl.id, HistoryAddonImpl],
3842
+ [InputRulesAddonImpl.id, InputRulesAddonImpl],
3843
+ [BoldAddonImpl.id, BoldAddonImpl],
3844
+ [ItalicAddonImpl.id, ItalicAddonImpl],
3845
+ [UnderlineAddonImpl.id, UnderlineAddonImpl],
3846
+ [StrikethroughAddonImpl.id, StrikethroughAddonImpl],
3847
+ [InlineCodeAddonImpl.id, InlineCodeAddonImpl],
3848
+ [SubscriptAddonImpl.id, SubscriptAddonImpl],
3849
+ [SuperscriptAddonImpl.id, SuperscriptAddonImpl],
3850
+ [HighlightAddonImpl.id, HighlightAddonImpl],
3851
+ [TextColorAddonImpl.id, TextColorAddonImpl],
3852
+ [FontFamilyAddonImpl.id, FontFamilyAddonImpl],
3853
+ [FontSizeAddonImpl.id, FontSizeAddonImpl],
3854
+ [ClearFormattingAddonImpl.id, ClearFormattingAddonImpl],
3855
+ [AlignmentAddonImpl.id, AlignmentAddonImpl],
3856
+ [LineHeightAddonImpl.id, LineHeightAddonImpl],
3857
+ [ParagraphSpacingAddonImpl.id, ParagraphSpacingAddonImpl],
3858
+ [IndentAddonImpl.id, IndentAddonImpl],
3859
+ [HeadingsAddonImpl.id, HeadingsAddonImpl],
3860
+ [BlockquoteAddonImpl.id, BlockquoteAddonImpl],
3861
+ [DividerAddonImpl.id, DividerAddonImpl],
3862
+ [PageBreakAddonImpl.id, PageBreakAddonImpl],
3863
+ [CalloutAddonImpl.id, CalloutAddonImpl],
3864
+ [DetailsAddonImpl.id, DetailsAddonImpl],
3865
+ [ColumnsAddonImpl.id, ColumnsAddonImpl],
3866
+ [CodeBlockAddonImpl.id, CodeBlockAddonImpl],
3867
+ [BulletListAddonImpl.id, BulletListAddonImpl],
3868
+ [OrderedListAddonImpl.id, OrderedListAddonImpl],
3869
+ [TaskListAddonImpl.id, TaskListAddonImpl],
3870
+ [ToggleListAddonImpl.id, ToggleListAddonImpl],
3871
+ [TableAddonImpl.id, TableAddonImpl],
3872
+ [ImageAddonImpl.id, ImageAddonImpl],
3873
+ [VideoAddonImpl.id, VideoAddonImpl],
3874
+ [AudioAddonImpl.id, AudioAddonImpl],
3875
+ [FileAddonImpl.id, FileAddonImpl],
3876
+ [EmbedAddonImpl.id, EmbedAddonImpl],
3877
+ [BookmarkAddonImpl.id, BookmarkAddonImpl],
3878
+ [LinkAddonImpl.id, LinkAddonImpl],
3879
+ [MentionAddonImpl.id, MentionAddonImpl],
3880
+ [EmojiAddonImpl.id, EmojiAddonImpl],
3881
+ [EquationAddonImpl.id, EquationAddonImpl],
3882
+ [FootnoteAddonImpl.id, FootnoteAddonImpl],
3883
+ [SmartChipAddonImpl.id, SmartChipAddonImpl],
3884
+ [ToolbarAddonImpl.id, ToolbarAddonImpl],
3885
+ [BubbleMenuAddonImpl.id, BubbleMenuAddonImpl],
3886
+ [SlashMenuAddonImpl.id, SlashMenuAddonImpl],
3887
+ [CommandPaletteAddonImpl.id, CommandPaletteAddonImpl],
3888
+ [BlockHandlesAddonImpl.id, BlockHandlesAddonImpl],
3889
+ [ContextMenuAddonImpl.id, ContextMenuAddonImpl],
3890
+ [StatusBarAddonImpl.id, StatusBarAddonImpl],
3891
+ [TableOfContentsAddonImpl.id, TableOfContentsAddonImpl],
3892
+ [OutlineAddonImpl.id, OutlineAddonImpl],
3893
+ [CoverAddonImpl.id, CoverAddonImpl],
3894
+ [BreadcrumbAddonImpl.id, BreadcrumbAddonImpl],
3895
+ [TemplatesAddonImpl.id, TemplatesAddonImpl],
3896
+ [PageSetupAddonImpl.id, PageSetupAddonImpl],
3897
+ [ContentAddonImpl.id, ContentAddonImpl],
3898
+ [DocxAddonImpl.id, DocxAddonImpl],
3899
+ [PdfAddonImpl.id, PdfAddonImpl],
3900
+ [PrintAddonImpl.id, PrintAddonImpl],
3901
+ [SearchAddonImpl.id, SearchAddonImpl],
3902
+ [SpellCheckAddonImpl.id, SpellCheckAddonImpl],
3903
+ [GrammarAddonImpl.id, GrammarAddonImpl],
3904
+ [AutoSaveAddonImpl.id, AutoSaveAddonImpl],
3905
+ [ReadOnlyAddonImpl.id, ReadOnlyAddonImpl],
3906
+ [CollabAddonImpl.id, CollabAddonImpl],
3907
+ [AwarenessAddonImpl.id, AwarenessAddonImpl],
3908
+ [PresenceAddonImpl.id, PresenceAddonImpl],
3909
+ [CommentsAddonImpl.id, CommentsAddonImpl],
3910
+ [SuggestionsAddonImpl.id, SuggestionsAddonImpl],
3911
+ [VersionAddonImpl.id, VersionAddonImpl],
3912
+ [CodeAddonImpl.id, CodeAddonImpl],
3913
+ [LineNumbersAddonImpl.id, LineNumbersAddonImpl],
3914
+ [CodeFoldingAddonImpl.id, CodeFoldingAddonImpl],
3915
+ [BracketMatchingAddonImpl.id, BracketMatchingAddonImpl],
3916
+ [CodeAutoCompleteAddonImpl.id, CodeAutoCompleteAddonImpl],
3917
+ [CodeLintAddonImpl.id, CodeLintAddonImpl],
3918
+ [DiffAddonImpl.id, DiffAddonImpl],
3919
+ [MiniMapAddonImpl.id, MiniMapAddonImpl],
3920
+ [PlaceholderAddonImpl.id, PlaceholderAddonImpl],
3921
+ [CountAddonImpl.id, CountAddonImpl],
3922
+ [ReadingTimeAddonImpl.id, ReadingTimeAddonImpl],
3923
+ [FocusModeAddonImpl.id, FocusModeAddonImpl],
3924
+ [FullScreenAddonImpl.id, FullScreenAddonImpl],
3925
+ [AccessibilityAddonImpl.id, AccessibilityAddonImpl],
3926
+ [MarkdownAddonImpl.id, MarkdownAddonImpl]
3927
+ ]);
3928
+ export {
3929
+ ADDON_IMPLEMENTATIONS,
3930
+ AccessibilityAddon,
3931
+ AccessibilityAddonImpl,
3932
+ AlignmentAddon,
3933
+ AlignmentAddonImpl,
3934
+ AudioAddon,
3935
+ AudioAddonImpl,
3936
+ AutoSaveAddon,
3937
+ AutoSaveAddonImpl,
3938
+ AwarenessAddon,
3939
+ AwarenessAddonImpl,
3940
+ BlockHandlesAddon,
3941
+ BlockHandlesAddonImpl,
3942
+ BlockquoteAddon,
3943
+ BlockquoteAddonImpl,
3944
+ BoldAddon,
3945
+ BoldAddonImpl,
3946
+ BookmarkAddon,
3947
+ BookmarkAddonImpl,
3948
+ BracketMatchingAddon,
3949
+ BracketMatchingAddonImpl,
3950
+ BreadcrumbAddon,
3951
+ BreadcrumbAddonImpl,
3952
+ BubbleMenuAddon,
3953
+ BubbleMenuAddonImpl,
3954
+ BulletListAddon,
3955
+ BulletListAddonImpl,
3956
+ CalloutAddon,
3957
+ CalloutAddonImpl,
3958
+ ClearFormattingAddon,
3959
+ ClearFormattingAddonImpl,
3960
+ CodeAddon,
3961
+ CodeAddonImpl,
3962
+ CodeAutoCompleteAddon,
3963
+ CodeAutoCompleteAddonImpl,
3964
+ CodeBlockAddon,
3965
+ CodeBlockAddonImpl,
3966
+ CodeFoldingAddon,
3967
+ CodeFoldingAddonImpl,
3968
+ CodeLintAddon,
3969
+ CodeLintAddonImpl,
3970
+ CollabAddon,
3971
+ CollabAddonImpl,
3972
+ ColumnsAddon,
3973
+ ColumnsAddonImpl,
3974
+ CommandPaletteAddon,
3975
+ CommandPaletteAddonImpl,
3976
+ CommandsAddon,
3977
+ CommandsAddonImpl,
3978
+ CommentsAddon,
3979
+ CommentsAddonImpl,
3980
+ ContentAddon,
3981
+ ContentAddonImpl,
3982
+ ContextMenuAddon,
3983
+ ContextMenuAddonImpl,
3984
+ CountAddon,
3985
+ CountAddonImpl,
3986
+ CoverAddon,
3987
+ CoverAddonImpl,
3988
+ DetailsAddon,
3989
+ DetailsAddonImpl,
3990
+ DiffAddon,
3991
+ DiffAddonImpl,
3992
+ DividerAddon,
3993
+ DividerAddonImpl,
3994
+ DocxAddon,
3995
+ DocxAddonImpl,
3996
+ EmbedAddon,
3997
+ EmbedAddonImpl,
3998
+ EmojiAddon,
3999
+ EmojiAddonImpl,
4000
+ EquationAddon,
4001
+ EquationAddonImpl,
4002
+ FileAddon,
4003
+ FileAddonImpl,
4004
+ FocusModeAddon,
4005
+ FocusModeAddonImpl,
4006
+ FontFamilyAddon,
4007
+ FontFamilyAddonImpl,
4008
+ FontSizeAddon,
4009
+ FontSizeAddonImpl,
4010
+ FootnoteAddon,
4011
+ FootnoteAddonImpl,
4012
+ FullScreenAddon,
4013
+ FullScreenAddonImpl,
4014
+ GrammarAddon,
4015
+ GrammarAddonImpl,
4016
+ HeadingsAddon,
4017
+ HeadingsAddonImpl,
4018
+ HighlightAddon,
4019
+ HighlightAddonImpl,
4020
+ HistoryAddon,
4021
+ HistoryAddonImpl,
4022
+ ImageAddon,
4023
+ ImageAddonImpl,
4024
+ IndentAddon,
4025
+ IndentAddonImpl,
4026
+ InlineCodeAddon,
4027
+ InlineCodeAddonImpl,
4028
+ InputRulesAddon,
4029
+ InputRulesAddonImpl,
4030
+ ItalicAddon,
4031
+ ItalicAddonImpl,
4032
+ KeymapAddon,
4033
+ KeymapAddonImpl,
4034
+ LineHeightAddon,
4035
+ LineHeightAddonImpl,
4036
+ LineNumbersAddon,
4037
+ LineNumbersAddonImpl,
4038
+ LinkAddon,
4039
+ LinkAddonImpl,
4040
+ ListsAddon,
4041
+ MarkdownAddon,
4042
+ MarkdownAddonImpl,
4043
+ MentionAddon,
4044
+ MentionAddonImpl,
4045
+ MiniMapAddon,
4046
+ MiniMapAddonImpl,
4047
+ OrderedListAddon,
4048
+ OrderedListAddonImpl,
4049
+ OutlineAddon,
4050
+ OutlineAddonImpl,
4051
+ PageBreakAddon,
4052
+ PageBreakAddonImpl,
4053
+ PageSetupAddon,
4054
+ PageSetupAddonImpl,
4055
+ ParagraphSpacingAddon,
4056
+ ParagraphSpacingAddonImpl,
4057
+ PdfAddon,
4058
+ PdfAddonImpl,
4059
+ PlaceholderAddon,
4060
+ PlaceholderAddonImpl,
4061
+ PresenceAddon,
4062
+ PresenceAddonImpl,
4063
+ PrintAddon,
4064
+ PrintAddonImpl,
4065
+ ReadOnlyAddon,
4066
+ ReadOnlyAddonImpl,
4067
+ ReadingTimeAddon,
4068
+ ReadingTimeAddonImpl,
4069
+ SchemaAddon,
4070
+ SchemaAddonImpl,
4071
+ SearchAddon,
4072
+ SearchAddonImpl,
4073
+ SlashMenuAddon,
4074
+ SlashMenuAddonImpl,
4075
+ SmartChipAddon,
4076
+ SmartChipAddonImpl,
4077
+ SpellCheckAddon,
4078
+ SpellCheckAddonImpl,
4079
+ StatusBarAddon,
4080
+ StatusBarAddonImpl,
4081
+ StrikethroughAddon,
4082
+ StrikethroughAddonImpl,
4083
+ SubscriptAddon,
4084
+ SubscriptAddonImpl,
4085
+ SuggestionsAddon,
4086
+ SuggestionsAddonImpl,
4087
+ SuperscriptAddon,
4088
+ SuperscriptAddonImpl,
4089
+ TableAddon,
4090
+ TableAddonImpl,
4091
+ TableOfContentsAddon,
4092
+ TableOfContentsAddonImpl,
4093
+ TaskListAddon,
4094
+ TaskListAddonImpl,
4095
+ TemplatesAddon,
4096
+ TemplatesAddonImpl,
4097
+ TextColorAddon,
4098
+ TextColorAddonImpl,
4099
+ ToggleListAddon,
4100
+ ToggleListAddonImpl,
4101
+ ToolbarAddon,
4102
+ ToolbarAddonImpl,
4103
+ UnderlineAddon,
4104
+ UnderlineAddonImpl,
4105
+ VersionAddon,
4106
+ VersionAddonImpl,
4107
+ VideoAddon,
4108
+ VideoAddonImpl,
4109
+ createCommandGroup
4110
+ };
4111
+ //# sourceMappingURL=index.mjs.map