@moraya/core 0.1.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +344 -0
  2. package/LICENSE +85 -0
  3. package/README.md +82 -0
  4. package/dist/adapters/browser-media-resolver.d.ts +21 -0
  5. package/dist/adapters/browser-media-resolver.js +24 -0
  6. package/dist/adapters/browser-media-resolver.js.map +1 -0
  7. package/dist/commands.d.ts +35 -0
  8. package/dist/commands.js +976 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/doc-cache.d.ts +29 -0
  11. package/dist/doc-cache.js +50 -0
  12. package/dist/doc-cache.js.map +1 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.js +4534 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/markdown.d.ts +46 -0
  17. package/dist/markdown.js +1553 -0
  18. package/dist/markdown.js.map +1 -0
  19. package/dist/plugins/code-block-view.d.ts +52 -0
  20. package/dist/plugins/code-block-view.js +686 -0
  21. package/dist/plugins/code-block-view.js.map +1 -0
  22. package/dist/plugins/cursor-syntax.d.ts +27 -0
  23. package/dist/plugins/cursor-syntax.js +122 -0
  24. package/dist/plugins/cursor-syntax.js.map +1 -0
  25. package/dist/plugins/definition-list.d.ts +23 -0
  26. package/dist/plugins/definition-list.js +12 -0
  27. package/dist/plugins/definition-list.js.map +1 -0
  28. package/dist/plugins/editor-props-plugin.d.ts +36 -0
  29. package/dist/plugins/editor-props-plugin.js +1963 -0
  30. package/dist/plugins/editor-props-plugin.js.map +1 -0
  31. package/dist/plugins/emoji.d.ts +21 -0
  32. package/dist/plugins/emoji.js +42 -0
  33. package/dist/plugins/emoji.js.map +1 -0
  34. package/dist/plugins/enter-handler.d.ts +26 -0
  35. package/dist/plugins/enter-handler.js +193 -0
  36. package/dist/plugins/enter-handler.js.map +1 -0
  37. package/dist/plugins/highlight.d.ts +39 -0
  38. package/dist/plugins/highlight.js +283 -0
  39. package/dist/plugins/highlight.js.map +1 -0
  40. package/dist/plugins/inline-code-convert.d.ts +32 -0
  41. package/dist/plugins/inline-code-convert.js +173 -0
  42. package/dist/plugins/inline-code-convert.js.map +1 -0
  43. package/dist/plugins/link-text-plugin.d.ts +22 -0
  44. package/dist/plugins/link-text-plugin.js +194 -0
  45. package/dist/plugins/link-text-plugin.js.map +1 -0
  46. package/dist/plugins/mermaid-renderer.d.ts +24 -0
  47. package/dist/plugins/mermaid-renderer.js +80 -0
  48. package/dist/plugins/mermaid-renderer.js.map +1 -0
  49. package/dist/schema.d.ts +48 -0
  50. package/dist/schema.js +847 -0
  51. package/dist/schema.js.map +1 -0
  52. package/dist/setup.d.ts +104 -0
  53. package/dist/setup.js +4393 -0
  54. package/dist/setup.js.map +1 -0
  55. package/dist/types.d.ts +107 -0
  56. package/dist/types.js +10 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +121 -0
@@ -0,0 +1,976 @@
1
+ // src/commands.ts
2
+ import {
3
+ toggleMark,
4
+ setBlockType,
5
+ wrapIn,
6
+ lift
7
+ } from "prosemirror-commands";
8
+ import { wrapInList, liftListItem } from "prosemirror-schema-list";
9
+
10
+ // src/schema.ts
11
+ import { Schema, Fragment } from "prosemirror-model";
12
+ import katex from "katex";
13
+
14
+ // src/types.ts
15
+ var NULL_MEDIA_RESOLVER_SENTINEL = /* @__PURE__ */ Symbol("@moraya/core:null-media-resolver");
16
+
17
+ // src/schema.ts
18
+ function extractHtmlAttr(html, name) {
19
+ const re = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
20
+ const m = html.match(re);
21
+ return m ? m[1] ?? m[2] ?? m[3] ?? null : null;
22
+ }
23
+ function extractAllHtmlAttrs(html) {
24
+ const attrs = {};
25
+ const re = /([a-zA-Z_][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
26
+ let m;
27
+ while ((m = re.exec(html)) !== null) {
28
+ const name = m[1];
29
+ if (!name) continue;
30
+ attrs[name.toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
31
+ }
32
+ return attrs;
33
+ }
34
+ function showBrokenImage(container, sourceText) {
35
+ container.textContent = "";
36
+ container.className = (container.className.replace(/\bhtml-img-wrapper\b|\bimage-node\b/, "").trim() + " broken-image").trim();
37
+ const icon = document.createElement("span");
38
+ icon.className = "broken-image-icon";
39
+ container.appendChild(icon);
40
+ const code2 = document.createElement("code");
41
+ code2.className = "broken-image-src";
42
+ code2.textContent = sourceText;
43
+ container.appendChild(code2);
44
+ }
45
+ function htmlTagToStyle(openTag) {
46
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
47
+ if (!tagMatch || !tagMatch[1]) return "";
48
+ const tagName = tagMatch[1].toLowerCase();
49
+ switch (tagName) {
50
+ case "font": {
51
+ const parts = [];
52
+ const color = extractHtmlAttr(openTag, "color");
53
+ if (color) parts.push(`color: ${color}`);
54
+ const size = extractHtmlAttr(openTag, "size");
55
+ if (size) {
56
+ const sizeMap = {
57
+ "1": "0.63em",
58
+ "2": "0.82em",
59
+ "3": "1em",
60
+ "4": "1.13em",
61
+ "5": "1.5em",
62
+ "6": "2em",
63
+ "7": "3em"
64
+ };
65
+ parts.push(`font-size: ${sizeMap[size] || size}`);
66
+ }
67
+ const face = extractHtmlAttr(openTag, "face");
68
+ if (face) parts.push(`font-family: ${face}`);
69
+ return parts.join("; ");
70
+ }
71
+ case "span":
72
+ case "div":
73
+ return extractHtmlAttr(openTag, "style") || "";
74
+ default:
75
+ return "";
76
+ }
77
+ }
78
+ var documentBaseDir = "";
79
+ function isAbsoluteFilePath(src) {
80
+ if (!src) return false;
81
+ if (src.startsWith("/") && !src.startsWith("//")) return true;
82
+ if (/^[A-Z]:[\\/]/i.test(src)) return true;
83
+ return false;
84
+ }
85
+ function isRelativePath(src) {
86
+ if (!src) return false;
87
+ if (/^(https?:|data:|blob:|javascript:|vbscript:|tauri:|\/\/)/i.test(src)) return false;
88
+ if (src.startsWith("/") || /^[A-Z]:[\\/]/i.test(src)) return false;
89
+ return true;
90
+ }
91
+ function resolveRelativePath(src) {
92
+ if (!documentBaseDir) return src;
93
+ let rel = src.replace(/^\.\//, "");
94
+ const sep = documentBaseDir.includes("\\") ? "\\" : "/";
95
+ let base = documentBaseDir.endsWith(sep) ? documentBaseDir.slice(0, -1) : documentBaseDir;
96
+ while (rel.startsWith("../") || rel.startsWith("..\\")) {
97
+ rel = rel.slice(3);
98
+ const lastSep = base.lastIndexOf(sep);
99
+ if (lastSep > 0) base = base.slice(0, lastSep);
100
+ }
101
+ return `${base}${sep}${rel}`;
102
+ }
103
+ function loadLocalImageSrc(img, src, mediaResolver) {
104
+ let path;
105
+ try {
106
+ path = decodeURIComponent(src);
107
+ } catch {
108
+ path = src;
109
+ }
110
+ mediaResolver.loadLocalImage(path).then((url) => {
111
+ if (url) img.src = url;
112
+ else img.dispatchEvent(new Event("error"));
113
+ }).catch(() => {
114
+ img.dispatchEvent(new Event("error"));
115
+ });
116
+ }
117
+ function setMediaSrc(el, src, mediaResolver) {
118
+ if (isAbsoluteFilePath(src)) {
119
+ mediaResolver.loadLocalMedia(src).then((url) => {
120
+ if (!url) return;
121
+ el.src = url;
122
+ if (el instanceof HTMLMediaElement) el.load();
123
+ }).catch(() => {
124
+ });
125
+ } else if (isRelativePath(src)) {
126
+ mediaResolver.loadLocalMedia(resolveRelativePath(src)).then((url) => {
127
+ if (!url) return;
128
+ el.src = url;
129
+ if (el instanceof HTMLMediaElement) el.load();
130
+ }).catch(() => {
131
+ });
132
+ } else if (/^https?:\/\//i.test(src)) {
133
+ if (el instanceof HTMLVideoElement) {
134
+ el.src = src;
135
+ el.load();
136
+ } else {
137
+ mediaResolver.loadRemoteMedia(src).then((url) => {
138
+ if (!url) return;
139
+ el.src = url;
140
+ if (el instanceof HTMLMediaElement) el.load();
141
+ }).catch(() => {
142
+ });
143
+ }
144
+ } else {
145
+ el.src = src;
146
+ }
147
+ }
148
+ function createMediaElement(tagName, value, mediaResolver) {
149
+ const wrapper = document.createElement("span");
150
+ wrapper.dataset.type = "html-inline";
151
+ wrapper.dataset.value = value;
152
+ wrapper.className = "html-media-wrapper";
153
+ wrapper.contentEditable = "false";
154
+ const el = document.createElement(tagName);
155
+ const stopForControls = (ev) => ev.stopPropagation();
156
+ el.addEventListener("mousedown", stopForControls);
157
+ el.addEventListener("click", stopForControls);
158
+ el.addEventListener("pointerdown", stopForControls);
159
+ const openTagMatch = value.match(new RegExp(`^<${tagName}\\b[^>]*>`, "i"));
160
+ const openTag = openTagMatch ? openTagMatch[0] : "";
161
+ const attrs = extractAllHtmlAttrs(openTag);
162
+ for (const [key, val] of Object.entries(attrs)) {
163
+ if (key === "src") continue;
164
+ if (key.startsWith("on")) continue;
165
+ el.setAttribute(key, val);
166
+ }
167
+ const strippedTag = openTag.replace(/=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/g, "");
168
+ const boolAttrs = ["controls", "autoplay", "loop", "muted", "playsinline"];
169
+ for (const attr of boolAttrs) {
170
+ if (!(attr in attrs) && new RegExp(`\\b${attr}\\b`, "i").test(strippedTag)) {
171
+ el.setAttribute(attr, "");
172
+ }
173
+ }
174
+ if (tagName === "audio" && !attrs.preload) {
175
+ el.setAttribute("preload", "auto");
176
+ }
177
+ const sourceRe = /<source\b[^>]*\/?>/gi;
178
+ let srcMatch;
179
+ while ((srcMatch = sourceRe.exec(value)) !== null) {
180
+ const srcAttrs = extractAllHtmlAttrs(srcMatch[0]);
181
+ if (!srcAttrs.src) continue;
182
+ const source = document.createElement("source");
183
+ if (srcAttrs.type) source.type = srcAttrs.type;
184
+ setMediaSrc(source, srcAttrs.src, mediaResolver);
185
+ el.appendChild(source);
186
+ }
187
+ if (attrs.src) {
188
+ setMediaSrc(el, attrs.src, mediaResolver);
189
+ }
190
+ wrapper.appendChild(el);
191
+ return wrapper;
192
+ }
193
+ var doc = {
194
+ content: "block+"
195
+ };
196
+ var text = { group: "inline" };
197
+ var paragraph = {
198
+ content: "inline*",
199
+ group: "block",
200
+ parseDOM: [{ tag: "p" }],
201
+ toDOM() {
202
+ return ["p", 0];
203
+ }
204
+ };
205
+ var heading = {
206
+ attrs: {
207
+ id: { default: "" },
208
+ level: { default: 1 }
209
+ },
210
+ content: "inline*",
211
+ group: "block",
212
+ defining: true,
213
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
214
+ tag: `h${level}`,
215
+ getAttrs(dom) {
216
+ return { level, id: dom.getAttribute("id") || "" };
217
+ }
218
+ })),
219
+ toDOM(node) {
220
+ const attrs = {};
221
+ if (node.attrs.id) attrs.id = node.attrs.id;
222
+ return [`h${node.attrs.level}`, attrs, 0];
223
+ }
224
+ };
225
+ var blockquote = {
226
+ content: "block+",
227
+ group: "block",
228
+ defining: true,
229
+ parseDOM: [{ tag: "blockquote" }],
230
+ toDOM() {
231
+ return ["blockquote", 0];
232
+ }
233
+ };
234
+ var code_block = {
235
+ content: "text*",
236
+ group: "block",
237
+ marks: "",
238
+ defining: true,
239
+ code: true,
240
+ attrs: {
241
+ language: { default: "text" }
242
+ },
243
+ parseDOM: [{
244
+ tag: "pre",
245
+ preserveWhitespace: "full",
246
+ getAttrs(dom) {
247
+ return { language: dom.dataset.language || "text" };
248
+ }
249
+ }],
250
+ toDOM(node) {
251
+ return ["pre", { "data-language": node.attrs.language || void 0 }, ["code", 0]];
252
+ }
253
+ };
254
+ var horizontal_rule = {
255
+ group: "block",
256
+ parseDOM: [{ tag: "hr" }],
257
+ toDOM() {
258
+ return ["hr"];
259
+ }
260
+ };
261
+ var bullet_list = {
262
+ content: "list_item+",
263
+ group: "block",
264
+ parseDOM: [{ tag: "ul" }],
265
+ toDOM() {
266
+ return ["ul", 0];
267
+ }
268
+ };
269
+ var ordered_list = {
270
+ content: "list_item+",
271
+ group: "block",
272
+ attrs: {
273
+ order: { default: 1 }
274
+ },
275
+ parseDOM: [{
276
+ tag: "ol",
277
+ getAttrs(dom) {
278
+ return { order: dom.hasAttribute("start") ? +(dom.getAttribute("start") || 1) : 1 };
279
+ }
280
+ }],
281
+ toDOM(node) {
282
+ return node.attrs.order === 1 ? ["ol", 0] : ["ol", { start: node.attrs.order }, 0];
283
+ }
284
+ };
285
+ var list_item = {
286
+ content: "paragraph block*",
287
+ group: "listItem",
288
+ defining: true,
289
+ attrs: {
290
+ label: { default: "\u2022" },
291
+ listType: { default: "bullet" },
292
+ spread: { default: "true" },
293
+ checked: { default: null }
294
+ },
295
+ parseDOM: [
296
+ {
297
+ tag: 'li[data-item-type="task"]',
298
+ getAttrs(dom) {
299
+ return {
300
+ label: dom.dataset.label,
301
+ listType: dom.dataset.listType,
302
+ spread: dom.dataset.spread,
303
+ checked: dom.dataset.checked ? dom.dataset.checked === "true" : null
304
+ };
305
+ }
306
+ },
307
+ {
308
+ tag: "li",
309
+ getAttrs(dom) {
310
+ return {
311
+ label: dom.dataset.label || "\u2022",
312
+ listType: dom.dataset.listType || "bullet",
313
+ spread: dom.dataset.spread || "true"
314
+ };
315
+ }
316
+ }
317
+ ],
318
+ toDOM(node) {
319
+ if (node.attrs.checked != null) {
320
+ return ["li", {
321
+ "data-item-type": "task",
322
+ "data-label": node.attrs.label,
323
+ "data-list-type": node.attrs.listType,
324
+ "data-spread": node.attrs.spread,
325
+ "data-checked": String(node.attrs.checked)
326
+ }, 0];
327
+ }
328
+ return ["li", {
329
+ "data-label": node.attrs.label,
330
+ "data-list-type": node.attrs.listType,
331
+ "data-spread": node.attrs.spread
332
+ }, 0];
333
+ }
334
+ };
335
+ var hardbreak = {
336
+ inline: true,
337
+ group: "inline",
338
+ selectable: false,
339
+ attrs: {
340
+ isInline: { default: false }
341
+ },
342
+ parseDOM: [
343
+ { tag: "br" },
344
+ {
345
+ tag: 'span[data-type="hardbreak"]',
346
+ getAttrs() {
347
+ return { isInline: true };
348
+ }
349
+ }
350
+ ],
351
+ toDOM() {
352
+ return ["span", { "data-type": "hardbreak", "class": "hardbreak-marker" }, "\n"];
353
+ },
354
+ leafText() {
355
+ return "\n";
356
+ }
357
+ };
358
+ var html_block = {
359
+ content: "text*",
360
+ group: "block",
361
+ marks: "",
362
+ code: true,
363
+ defining: true,
364
+ parseDOM: [{
365
+ tag: 'div[data-type="html"]',
366
+ preserveWhitespace: "full"
367
+ }],
368
+ toDOM() {
369
+ return ["div", { "data-type": "html" }, ["pre", 0]];
370
+ }
371
+ };
372
+ var table = {
373
+ content: "table_header_row table_row+",
374
+ group: "block",
375
+ tableRole: "table",
376
+ isolating: true,
377
+ parseDOM: [{ tag: "table" }],
378
+ toDOM() {
379
+ return ["table", ["tbody", 0]];
380
+ }
381
+ };
382
+ var table_header_row = {
383
+ content: "(table_header)*",
384
+ tableRole: "row",
385
+ parseDOM: [
386
+ { tag: "tr[data-is-header]" },
387
+ {
388
+ tag: "tr",
389
+ getAttrs(dom) {
390
+ const hasHeader = dom.querySelector("th");
391
+ return hasHeader ? {} : false;
392
+ }
393
+ }
394
+ ],
395
+ toDOM() {
396
+ return ["tr", { "data-is-header": "true" }, 0];
397
+ }
398
+ };
399
+ var table_row = {
400
+ content: "(table_cell)*",
401
+ tableRole: "row",
402
+ parseDOM: [{ tag: "tr" }],
403
+ toDOM() {
404
+ return ["tr", 0];
405
+ }
406
+ };
407
+ var table_header = {
408
+ content: "paragraph+",
409
+ tableRole: "header_cell",
410
+ attrs: {
411
+ alignment: { default: "left" },
412
+ colspan: { default: 1 },
413
+ rowspan: { default: 1 },
414
+ colwidth: { default: null }
415
+ },
416
+ isolating: true,
417
+ parseDOM: [{
418
+ tag: "th",
419
+ getAttrs(dom) {
420
+ return {
421
+ alignment: dom.style.textAlign || "left",
422
+ colspan: Number(dom.getAttribute("colspan") || 1),
423
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
424
+ colwidth: null
425
+ };
426
+ }
427
+ }],
428
+ toDOM(node) {
429
+ return ["th", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
430
+ }
431
+ };
432
+ var table_cell = {
433
+ content: "paragraph+",
434
+ tableRole: "cell",
435
+ attrs: {
436
+ alignment: { default: "left" },
437
+ colspan: { default: 1 },
438
+ rowspan: { default: 1 },
439
+ colwidth: { default: null }
440
+ },
441
+ isolating: true,
442
+ parseDOM: [{
443
+ tag: "td",
444
+ getAttrs(dom) {
445
+ return {
446
+ alignment: dom.style.textAlign || "left",
447
+ colspan: Number(dom.getAttribute("colspan") || 1),
448
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
449
+ colwidth: null
450
+ };
451
+ }
452
+ }],
453
+ toDOM(node) {
454
+ return ["td", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
455
+ }
456
+ };
457
+ var math_inline = {
458
+ group: "inline",
459
+ content: "text*",
460
+ inline: true,
461
+ atom: true,
462
+ parseDOM: [{
463
+ tag: 'span[data-type="math_inline"]',
464
+ getContent(dom, schema2) {
465
+ if (!(dom instanceof HTMLElement)) return Fragment.empty;
466
+ const value = dom.dataset.value ?? "";
467
+ if (!value) return Fragment.empty;
468
+ return Fragment.from(schema2.text(value));
469
+ }
470
+ }],
471
+ toDOM(node) {
472
+ const code2 = node.textContent;
473
+ const dom = document.createElement("span");
474
+ dom.dataset.type = "math_inline";
475
+ dom.dataset.value = code2;
476
+ try {
477
+ katex.render(code2, dom);
478
+ } catch {
479
+ dom.textContent = code2;
480
+ dom.classList.add("math-error");
481
+ dom.setAttribute("data-math-type", "inline");
482
+ }
483
+ return dom;
484
+ }
485
+ };
486
+ var math_block = {
487
+ content: "text*",
488
+ group: "block",
489
+ marks: "",
490
+ defining: true,
491
+ atom: true,
492
+ isolating: true,
493
+ attrs: {
494
+ value: { default: "" }
495
+ },
496
+ parseDOM: [{
497
+ tag: 'div[data-type="math_block"]',
498
+ preserveWhitespace: "full",
499
+ getAttrs(dom) {
500
+ return { value: dom.dataset.value ?? "" };
501
+ }
502
+ }],
503
+ toDOM(node) {
504
+ const code2 = node.attrs.value;
505
+ const dom = document.createElement("div");
506
+ dom.dataset.type = "math_block";
507
+ dom.dataset.value = code2;
508
+ try {
509
+ katex.render(code2, dom, { displayMode: true });
510
+ } catch {
511
+ dom.textContent = code2;
512
+ dom.classList.add("math-error");
513
+ dom.setAttribute("data-math-type", "block");
514
+ }
515
+ return dom;
516
+ }
517
+ };
518
+ var defList = {
519
+ content: "(defListTerm | defListDescription)+",
520
+ group: "block",
521
+ defining: true,
522
+ parseDOM: [{ tag: "dl" }],
523
+ toDOM() {
524
+ return ["dl", { class: "definition-list" }, 0];
525
+ }
526
+ };
527
+ var defListTerm = {
528
+ content: "inline*",
529
+ group: "block",
530
+ defining: true,
531
+ parseDOM: [{ tag: "dt" }],
532
+ toDOM() {
533
+ return ["dt", 0];
534
+ }
535
+ };
536
+ var defListDescription = {
537
+ content: "block+",
538
+ group: "block",
539
+ defining: true,
540
+ parseDOM: [{ tag: "dd" }],
541
+ toDOM() {
542
+ return ["dd", 0];
543
+ }
544
+ };
545
+ var strong = {
546
+ parseDOM: [
547
+ {
548
+ tag: "b",
549
+ getAttrs(dom) {
550
+ return dom.style.fontWeight !== "normal" && null;
551
+ }
552
+ },
553
+ { tag: "strong" },
554
+ {
555
+ style: "font-weight",
556
+ getAttrs(value) {
557
+ return /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null;
558
+ }
559
+ }
560
+ ],
561
+ toDOM() {
562
+ return ["strong", 0];
563
+ }
564
+ };
565
+ var em = {
566
+ parseDOM: [
567
+ { tag: "i" },
568
+ { tag: "em" },
569
+ {
570
+ style: "font-style",
571
+ getAttrs(value) {
572
+ return value === "italic" && null;
573
+ }
574
+ }
575
+ ],
576
+ toDOM() {
577
+ return ["em", 0];
578
+ }
579
+ };
580
+ var code = {
581
+ priority: 100,
582
+ code: true,
583
+ inclusive: false,
584
+ parseDOM: [{ tag: "code" }],
585
+ toDOM() {
586
+ return ["code", 0];
587
+ }
588
+ };
589
+ var link = {
590
+ attrs: {
591
+ href: {},
592
+ title: { default: null }
593
+ },
594
+ inclusive: false,
595
+ parseDOM: [{
596
+ tag: "a[href]",
597
+ getAttrs(dom) {
598
+ return {
599
+ href: dom.getAttribute("href"),
600
+ title: dom.getAttribute("title")
601
+ };
602
+ }
603
+ }],
604
+ toDOM(mark) {
605
+ const attrs = { href: mark.attrs.href };
606
+ if (mark.attrs.title) attrs.title = mark.attrs.title;
607
+ return ["a", attrs, 0];
608
+ }
609
+ };
610
+ var strike_through = {
611
+ parseDOM: [
612
+ { tag: "del" },
613
+ { tag: "s" },
614
+ {
615
+ style: "text-decoration",
616
+ getAttrs(value) {
617
+ return value === "line-through" && null;
618
+ }
619
+ }
620
+ ],
621
+ toDOM() {
622
+ return ["del", 0];
623
+ }
624
+ };
625
+ var html_mark = {
626
+ attrs: {
627
+ openTag: { default: "" },
628
+ closeTag: { default: "" }
629
+ },
630
+ excludes: "",
631
+ // Allow nesting multiple html_marks (e.g., <font><u>text</u></font>)
632
+ parseDOM: [{
633
+ tag: '[data-type="html-mark"]',
634
+ getAttrs(dom) {
635
+ return {
636
+ openTag: dom.dataset.openTag ?? "",
637
+ closeTag: dom.dataset.closeTag ?? ""
638
+ };
639
+ }
640
+ }],
641
+ toDOM(mark) {
642
+ const openTag = mark.attrs.openTag;
643
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
644
+ const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : "span";
645
+ const attrs = {
646
+ "data-type": "html-mark",
647
+ "data-open-tag": openTag,
648
+ "data-close-tag": mark.attrs.closeTag
649
+ };
650
+ const semanticTags = ["sub", "sup", "u", "ins", "mark", "small", "big", "kbd", "abbr"];
651
+ if (semanticTags.includes(tagName)) {
652
+ return [tagName, attrs, 0];
653
+ }
654
+ const style = htmlTagToStyle(openTag);
655
+ if (style) attrs.style = style;
656
+ return ["span", attrs, 0];
657
+ }
658
+ };
659
+ function buildImageNodeSpec(mediaResolver) {
660
+ return {
661
+ inline: true,
662
+ group: "inline",
663
+ selectable: true,
664
+ draggable: true,
665
+ marks: "",
666
+ atom: true,
667
+ defining: true,
668
+ isolating: true,
669
+ attrs: {
670
+ src: { default: "" },
671
+ alt: { default: "" },
672
+ title: { default: "" }
673
+ },
674
+ parseDOM: [{
675
+ tag: "img[src]",
676
+ getAttrs(dom) {
677
+ return {
678
+ src: dom.getAttribute("src") || "",
679
+ alt: dom.getAttribute("alt") || "",
680
+ title: dom.getAttribute("title") || dom.getAttribute("alt") || ""
681
+ };
682
+ }
683
+ }],
684
+ toDOM(node) {
685
+ const container = document.createElement("span");
686
+ container.className = "image-node";
687
+ const img = document.createElement("img");
688
+ if (node.attrs.alt) img.alt = node.attrs.alt;
689
+ if (node.attrs.title) img.title = node.attrs.title;
690
+ const titleStr = node.attrs.title || "";
691
+ const widthMatch = titleStr.match(/^width=(\d+%?)$/);
692
+ const widthVal = widthMatch?.[1];
693
+ if (widthVal) {
694
+ img.style.width = widthVal.includes("%") ? widthVal : `${widthVal}px`;
695
+ img.style.maxWidth = "none";
696
+ }
697
+ img.onerror = () => {
698
+ const alt = node.attrs.alt ? `![${node.attrs.alt}]` : "![]";
699
+ const title = node.attrs.title ? ` "${node.attrs.title}"` : "";
700
+ showBrokenImage(container, `${alt}(${node.attrs.src}${title})`);
701
+ };
702
+ const src = node.attrs.src;
703
+ if (isAbsoluteFilePath(src)) {
704
+ loadLocalImageSrc(img, src, mediaResolver);
705
+ } else if (isRelativePath(src)) {
706
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
707
+ } else {
708
+ img.src = src;
709
+ }
710
+ container.appendChild(img);
711
+ return container;
712
+ }
713
+ };
714
+ }
715
+ function buildHtmlInlineNodeSpec(mediaResolver) {
716
+ return {
717
+ group: "inline",
718
+ inline: true,
719
+ atom: true,
720
+ attrs: {
721
+ value: { default: "" }
722
+ },
723
+ parseDOM: [{
724
+ tag: 'span[data-type="html-inline"]',
725
+ getAttrs(dom) {
726
+ return { value: dom.dataset.value ?? "" };
727
+ }
728
+ }],
729
+ toDOM(node) {
730
+ const value = node.attrs.value;
731
+ if (/^<img\s/i.test(value)) {
732
+ const wrapper = document.createElement("span");
733
+ wrapper.dataset.type = "html-inline";
734
+ wrapper.dataset.value = value;
735
+ wrapper.className = "html-img-wrapper";
736
+ const attrs = extractAllHtmlAttrs(value);
737
+ const src = attrs.src || "";
738
+ if (src) {
739
+ const img = document.createElement("img");
740
+ for (const [key, val] of Object.entries(attrs)) {
741
+ if (key === "src") continue;
742
+ if (key === "onerror" || key === "onload" || key.startsWith("on")) continue;
743
+ img.setAttribute(key, val);
744
+ }
745
+ img.onerror = () => {
746
+ showBrokenImage(wrapper, value);
747
+ };
748
+ if (isAbsoluteFilePath(src)) {
749
+ loadLocalImageSrc(img, src, mediaResolver);
750
+ } else if (isRelativePath(src)) {
751
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
752
+ } else {
753
+ img.src = src;
754
+ }
755
+ wrapper.appendChild(img);
756
+ } else {
757
+ showBrokenImage(wrapper, value);
758
+ }
759
+ return wrapper;
760
+ }
761
+ if (/^<video\b/i.test(value)) return createMediaElement("video", value, mediaResolver);
762
+ if (/^<audio\b/i.test(value)) return createMediaElement("audio", value, mediaResolver);
763
+ return ["span", { "data-type": "html-inline", "data-value": value }];
764
+ }
765
+ };
766
+ }
767
+ function buildNodes(mediaResolver) {
768
+ return {
769
+ doc,
770
+ text,
771
+ paragraph,
772
+ heading,
773
+ blockquote,
774
+ code_block,
775
+ horizontal_rule,
776
+ bullet_list,
777
+ ordered_list,
778
+ list_item,
779
+ image: buildImageNodeSpec(mediaResolver),
780
+ hardbreak,
781
+ html_block,
782
+ html_inline: buildHtmlInlineNodeSpec(mediaResolver),
783
+ table,
784
+ table_header_row,
785
+ table_row,
786
+ table_header,
787
+ table_cell,
788
+ math_inline,
789
+ math_block,
790
+ defList,
791
+ defListTerm,
792
+ defListDescription
793
+ };
794
+ }
795
+ var marks = {
796
+ html_mark,
797
+ strong,
798
+ em,
799
+ code,
800
+ link,
801
+ strike_through
802
+ };
803
+ var nullMediaResolver = {
804
+ [NULL_MEDIA_RESOLVER_SENTINEL]: true,
805
+ async loadLocalImage() {
806
+ return "";
807
+ },
808
+ async loadLocalMedia() {
809
+ return "";
810
+ },
811
+ async loadRemoteMedia(url) {
812
+ return url;
813
+ }
814
+ };
815
+ var defaultSchema = new Schema({
816
+ nodes: buildNodes(nullMediaResolver),
817
+ marks
818
+ });
819
+
820
+ // src/commands.ts
821
+ var schema = defaultSchema;
822
+ function markType(name) {
823
+ const m = schema.marks[name];
824
+ if (!m) throw new Error(`[@moraya/core] mark "${name}" not in schema`);
825
+ return m;
826
+ }
827
+ function nodeType(name) {
828
+ const n = schema.nodes[name];
829
+ if (!n) throw new Error(`[@moraya/core] node "${name}" not in schema`);
830
+ return n;
831
+ }
832
+ var toggleBold = (state, dispatch) => toggleMark(markType("strong"))(state, dispatch);
833
+ var toggleItalic = (state, dispatch) => toggleMark(markType("em"))(state, dispatch);
834
+ var toggleStrikethrough = (state, dispatch) => toggleMark(markType("strike_through"))(state, dispatch);
835
+ var toggleCode = (state, dispatch) => toggleMark(markType("code"))(state, dispatch);
836
+ function setHeading(level) {
837
+ return (state, dispatch) => setBlockType(nodeType("heading"), { level })(state, dispatch);
838
+ }
839
+ var toggleBlockquote = (state, dispatch) => {
840
+ const $from = state.selection.$from;
841
+ for (let d = $from.depth; d > 0; d--) {
842
+ if ($from.node(d).type.name === "blockquote") {
843
+ return lift(state, dispatch);
844
+ }
845
+ }
846
+ return wrapIn(nodeType("blockquote"))(state, dispatch);
847
+ };
848
+ var toggleOrderedList = (state, dispatch) => wrapInList(nodeType("ordered_list"))(state, dispatch);
849
+ var toggleBulletList = (state, dispatch) => wrapInList(nodeType("bullet_list"))(state, dispatch);
850
+ function makeToggleList(typeName) {
851
+ return (state, dispatch, view) => {
852
+ const listType = state.schema.nodes[typeName];
853
+ const listItemType = state.schema.nodes.list_item;
854
+ if (!listType || !listItemType) return false;
855
+ const { $from } = state.selection;
856
+ for (let d = $from.depth; d >= 0; d--) {
857
+ if ($from.node(d).type === listType) {
858
+ return liftListItem(listItemType)(state, dispatch, view);
859
+ }
860
+ }
861
+ return wrapInList(listType)(state, dispatch, view);
862
+ };
863
+ }
864
+ var wrapInBulletList = makeToggleList("bullet_list");
865
+ var wrapInOrderedList = makeToggleList("ordered_list");
866
+ var wrapInTaskList = (state, dispatch) => {
867
+ const bulletListType = state.schema.nodes.bullet_list;
868
+ const listItemType = state.schema.nodes.list_item;
869
+ if (!bulletListType || !listItemType) return false;
870
+ if (!wrapInList(bulletListType)(state)) return false;
871
+ if (!dispatch) return true;
872
+ let listTr;
873
+ wrapInList(bulletListType)(state, (tr) => {
874
+ listTr = tr;
875
+ });
876
+ if (!listTr) return false;
877
+ const { from, to } = listTr.selection;
878
+ const updates = [];
879
+ listTr.doc.nodesBetween(
880
+ Math.max(0, from - 200),
881
+ Math.min(listTr.doc.content.size, to + 200),
882
+ (node, pos) => {
883
+ if (node.type === listItemType && node.attrs.checked === null) {
884
+ updates.push({ pos, attrs: { ...node.attrs, checked: false } });
885
+ }
886
+ }
887
+ );
888
+ for (let i = updates.length - 1; i >= 0; i--) {
889
+ const u = updates[i];
890
+ listTr.setNodeMarkup(u.pos, void 0, u.attrs);
891
+ }
892
+ dispatch(listTr.scrollIntoView());
893
+ return true;
894
+ };
895
+ var toggleCodeBlock = (state, dispatch) => {
896
+ const cb = nodeType("code_block");
897
+ if (state.selection.$from.parent.type === cb) {
898
+ return setBlockType(nodeType("paragraph"))(state, dispatch);
899
+ }
900
+ return setBlockType(cb)(state, dispatch);
901
+ };
902
+ var insertHorizontalRule = (state, dispatch) => {
903
+ if (dispatch) {
904
+ dispatch(state.tr.replaceSelectionWith(nodeType("horizontal_rule").create()));
905
+ }
906
+ return true;
907
+ };
908
+ var insertTable = (state, dispatch) => {
909
+ const tableType = schema.nodes.table;
910
+ if (!tableType) {
911
+ if (dispatch) {
912
+ const text2 = "\n\n| Col 1 | Col 2 | Col 3 |\n|---|---|---|\n| | | |\n| | | |\n\n";
913
+ dispatch(state.tr.insertText(text2));
914
+ }
915
+ return true;
916
+ }
917
+ return false;
918
+ };
919
+ var insertMathBlock = (state, dispatch) => {
920
+ const mathBlock = schema.nodes.math_block;
921
+ if (!mathBlock) {
922
+ if (dispatch) {
923
+ dispatch(state.tr.insertText("\n$$\n\\\n$$\n"));
924
+ }
925
+ return true;
926
+ }
927
+ if (dispatch) {
928
+ const node = mathBlock.create({ value: "" });
929
+ dispatch(state.tr.replaceSelectionWith(node));
930
+ }
931
+ return true;
932
+ };
933
+ function toggleLink(href) {
934
+ return (state, dispatch) => {
935
+ const link2 = markType("link");
936
+ const { from, to } = state.selection;
937
+ if (state.doc.rangeHasMark(from, to, link2)) {
938
+ if (dispatch) dispatch(state.tr.removeMark(from, to, link2));
939
+ return true;
940
+ }
941
+ if (!href) return false;
942
+ if (dispatch) {
943
+ dispatch(state.tr.addMark(from, to, link2.create({ href })));
944
+ }
945
+ return true;
946
+ };
947
+ }
948
+ function insertImage(src, alt) {
949
+ return (state, dispatch) => {
950
+ const img = nodeType("image").create({ src, alt: alt ?? null });
951
+ if (dispatch) {
952
+ dispatch(state.tr.replaceSelectionWith(img));
953
+ }
954
+ return true;
955
+ };
956
+ }
957
+ export {
958
+ insertHorizontalRule,
959
+ insertImage,
960
+ insertMathBlock,
961
+ insertTable,
962
+ setHeading,
963
+ toggleBlockquote,
964
+ toggleBold,
965
+ toggleBulletList,
966
+ toggleCode,
967
+ toggleCodeBlock,
968
+ toggleItalic,
969
+ toggleLink,
970
+ toggleOrderedList,
971
+ toggleStrikethrough,
972
+ wrapInBulletList,
973
+ wrapInOrderedList,
974
+ wrapInTaskList
975
+ };
976
+ //# sourceMappingURL=commands.js.map