@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,1553 @@
1
+ // src/markdown.ts
2
+ import MarkdownIt from "markdown-it";
3
+ import deflistPlugin from "markdown-it-deflist";
4
+ import texmathPlugin from "markdown-it-texmath";
5
+ import { MarkdownParser, MarkdownSerializer } from "prosemirror-markdown";
6
+
7
+ // src/schema.ts
8
+ import { Schema, Fragment } from "prosemirror-model";
9
+ import katex from "katex";
10
+
11
+ // src/types.ts
12
+ var NULL_MEDIA_RESOLVER_SENTINEL = /* @__PURE__ */ Symbol("@moraya/core:null-media-resolver");
13
+
14
+ // src/schema.ts
15
+ function extractHtmlAttr(html, name) {
16
+ const re = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
17
+ const m = html.match(re);
18
+ return m ? m[1] ?? m[2] ?? m[3] ?? null : null;
19
+ }
20
+ function extractAllHtmlAttrs(html) {
21
+ const attrs = {};
22
+ const re = /([a-zA-Z_][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
23
+ let m;
24
+ while ((m = re.exec(html)) !== null) {
25
+ const name = m[1];
26
+ if (!name) continue;
27
+ attrs[name.toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
28
+ }
29
+ return attrs;
30
+ }
31
+ function showBrokenImage(container, sourceText) {
32
+ container.textContent = "";
33
+ container.className = (container.className.replace(/\bhtml-img-wrapper\b|\bimage-node\b/, "").trim() + " broken-image").trim();
34
+ const icon = document.createElement("span");
35
+ icon.className = "broken-image-icon";
36
+ container.appendChild(icon);
37
+ const code2 = document.createElement("code");
38
+ code2.className = "broken-image-src";
39
+ code2.textContent = sourceText;
40
+ container.appendChild(code2);
41
+ }
42
+ function htmlTagToStyle(openTag) {
43
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
44
+ if (!tagMatch || !tagMatch[1]) return "";
45
+ const tagName = tagMatch[1].toLowerCase();
46
+ switch (tagName) {
47
+ case "font": {
48
+ const parts = [];
49
+ const color = extractHtmlAttr(openTag, "color");
50
+ if (color) parts.push(`color: ${color}`);
51
+ const size = extractHtmlAttr(openTag, "size");
52
+ if (size) {
53
+ const sizeMap = {
54
+ "1": "0.63em",
55
+ "2": "0.82em",
56
+ "3": "1em",
57
+ "4": "1.13em",
58
+ "5": "1.5em",
59
+ "6": "2em",
60
+ "7": "3em"
61
+ };
62
+ parts.push(`font-size: ${sizeMap[size] || size}`);
63
+ }
64
+ const face = extractHtmlAttr(openTag, "face");
65
+ if (face) parts.push(`font-family: ${face}`);
66
+ return parts.join("; ");
67
+ }
68
+ case "span":
69
+ case "div":
70
+ return extractHtmlAttr(openTag, "style") || "";
71
+ default:
72
+ return "";
73
+ }
74
+ }
75
+ var documentBaseDir = "";
76
+ function isAbsoluteFilePath(src) {
77
+ if (!src) return false;
78
+ if (src.startsWith("/") && !src.startsWith("//")) return true;
79
+ if (/^[A-Z]:[\\/]/i.test(src)) return true;
80
+ return false;
81
+ }
82
+ function isRelativePath(src) {
83
+ if (!src) return false;
84
+ if (/^(https?:|data:|blob:|javascript:|vbscript:|tauri:|\/\/)/i.test(src)) return false;
85
+ if (src.startsWith("/") || /^[A-Z]:[\\/]/i.test(src)) return false;
86
+ return true;
87
+ }
88
+ function resolveRelativePath(src) {
89
+ if (!documentBaseDir) return src;
90
+ let rel = src.replace(/^\.\//, "");
91
+ const sep = documentBaseDir.includes("\\") ? "\\" : "/";
92
+ let base = documentBaseDir.endsWith(sep) ? documentBaseDir.slice(0, -1) : documentBaseDir;
93
+ while (rel.startsWith("../") || rel.startsWith("..\\")) {
94
+ rel = rel.slice(3);
95
+ const lastSep = base.lastIndexOf(sep);
96
+ if (lastSep > 0) base = base.slice(0, lastSep);
97
+ }
98
+ return `${base}${sep}${rel}`;
99
+ }
100
+ function loadLocalImageSrc(img, src, mediaResolver) {
101
+ let path;
102
+ try {
103
+ path = decodeURIComponent(src);
104
+ } catch {
105
+ path = src;
106
+ }
107
+ mediaResolver.loadLocalImage(path).then((url) => {
108
+ if (url) img.src = url;
109
+ else img.dispatchEvent(new Event("error"));
110
+ }).catch(() => {
111
+ img.dispatchEvent(new Event("error"));
112
+ });
113
+ }
114
+ function setMediaSrc(el, src, mediaResolver) {
115
+ if (isAbsoluteFilePath(src)) {
116
+ mediaResolver.loadLocalMedia(src).then((url) => {
117
+ if (!url) return;
118
+ el.src = url;
119
+ if (el instanceof HTMLMediaElement) el.load();
120
+ }).catch(() => {
121
+ });
122
+ } else if (isRelativePath(src)) {
123
+ mediaResolver.loadLocalMedia(resolveRelativePath(src)).then((url) => {
124
+ if (!url) return;
125
+ el.src = url;
126
+ if (el instanceof HTMLMediaElement) el.load();
127
+ }).catch(() => {
128
+ });
129
+ } else if (/^https?:\/\//i.test(src)) {
130
+ if (el instanceof HTMLVideoElement) {
131
+ el.src = src;
132
+ el.load();
133
+ } else {
134
+ mediaResolver.loadRemoteMedia(src).then((url) => {
135
+ if (!url) return;
136
+ el.src = url;
137
+ if (el instanceof HTMLMediaElement) el.load();
138
+ }).catch(() => {
139
+ });
140
+ }
141
+ } else {
142
+ el.src = src;
143
+ }
144
+ }
145
+ function createMediaElement(tagName, value, mediaResolver) {
146
+ const wrapper = document.createElement("span");
147
+ wrapper.dataset.type = "html-inline";
148
+ wrapper.dataset.value = value;
149
+ wrapper.className = "html-media-wrapper";
150
+ wrapper.contentEditable = "false";
151
+ const el = document.createElement(tagName);
152
+ const stopForControls = (ev) => ev.stopPropagation();
153
+ el.addEventListener("mousedown", stopForControls);
154
+ el.addEventListener("click", stopForControls);
155
+ el.addEventListener("pointerdown", stopForControls);
156
+ const openTagMatch = value.match(new RegExp(`^<${tagName}\\b[^>]*>`, "i"));
157
+ const openTag = openTagMatch ? openTagMatch[0] : "";
158
+ const attrs = extractAllHtmlAttrs(openTag);
159
+ for (const [key, val] of Object.entries(attrs)) {
160
+ if (key === "src") continue;
161
+ if (key.startsWith("on")) continue;
162
+ el.setAttribute(key, val);
163
+ }
164
+ const strippedTag = openTag.replace(/=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/g, "");
165
+ const boolAttrs = ["controls", "autoplay", "loop", "muted", "playsinline"];
166
+ for (const attr of boolAttrs) {
167
+ if (!(attr in attrs) && new RegExp(`\\b${attr}\\b`, "i").test(strippedTag)) {
168
+ el.setAttribute(attr, "");
169
+ }
170
+ }
171
+ if (tagName === "audio" && !attrs.preload) {
172
+ el.setAttribute("preload", "auto");
173
+ }
174
+ const sourceRe = /<source\b[^>]*\/?>/gi;
175
+ let srcMatch;
176
+ while ((srcMatch = sourceRe.exec(value)) !== null) {
177
+ const srcAttrs = extractAllHtmlAttrs(srcMatch[0]);
178
+ if (!srcAttrs.src) continue;
179
+ const source = document.createElement("source");
180
+ if (srcAttrs.type) source.type = srcAttrs.type;
181
+ setMediaSrc(source, srcAttrs.src, mediaResolver);
182
+ el.appendChild(source);
183
+ }
184
+ if (attrs.src) {
185
+ setMediaSrc(el, attrs.src, mediaResolver);
186
+ }
187
+ wrapper.appendChild(el);
188
+ return wrapper;
189
+ }
190
+ var doc = {
191
+ content: "block+"
192
+ };
193
+ var text = { group: "inline" };
194
+ var paragraph = {
195
+ content: "inline*",
196
+ group: "block",
197
+ parseDOM: [{ tag: "p" }],
198
+ toDOM() {
199
+ return ["p", 0];
200
+ }
201
+ };
202
+ var heading = {
203
+ attrs: {
204
+ id: { default: "" },
205
+ level: { default: 1 }
206
+ },
207
+ content: "inline*",
208
+ group: "block",
209
+ defining: true,
210
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
211
+ tag: `h${level}`,
212
+ getAttrs(dom) {
213
+ return { level, id: dom.getAttribute("id") || "" };
214
+ }
215
+ })),
216
+ toDOM(node) {
217
+ const attrs = {};
218
+ if (node.attrs.id) attrs.id = node.attrs.id;
219
+ return [`h${node.attrs.level}`, attrs, 0];
220
+ }
221
+ };
222
+ var blockquote = {
223
+ content: "block+",
224
+ group: "block",
225
+ defining: true,
226
+ parseDOM: [{ tag: "blockquote" }],
227
+ toDOM() {
228
+ return ["blockquote", 0];
229
+ }
230
+ };
231
+ var code_block = {
232
+ content: "text*",
233
+ group: "block",
234
+ marks: "",
235
+ defining: true,
236
+ code: true,
237
+ attrs: {
238
+ language: { default: "text" }
239
+ },
240
+ parseDOM: [{
241
+ tag: "pre",
242
+ preserveWhitespace: "full",
243
+ getAttrs(dom) {
244
+ return { language: dom.dataset.language || "text" };
245
+ }
246
+ }],
247
+ toDOM(node) {
248
+ return ["pre", { "data-language": node.attrs.language || void 0 }, ["code", 0]];
249
+ }
250
+ };
251
+ var horizontal_rule = {
252
+ group: "block",
253
+ parseDOM: [{ tag: "hr" }],
254
+ toDOM() {
255
+ return ["hr"];
256
+ }
257
+ };
258
+ var bullet_list = {
259
+ content: "list_item+",
260
+ group: "block",
261
+ parseDOM: [{ tag: "ul" }],
262
+ toDOM() {
263
+ return ["ul", 0];
264
+ }
265
+ };
266
+ var ordered_list = {
267
+ content: "list_item+",
268
+ group: "block",
269
+ attrs: {
270
+ order: { default: 1 }
271
+ },
272
+ parseDOM: [{
273
+ tag: "ol",
274
+ getAttrs(dom) {
275
+ return { order: dom.hasAttribute("start") ? +(dom.getAttribute("start") || 1) : 1 };
276
+ }
277
+ }],
278
+ toDOM(node) {
279
+ return node.attrs.order === 1 ? ["ol", 0] : ["ol", { start: node.attrs.order }, 0];
280
+ }
281
+ };
282
+ var list_item = {
283
+ content: "paragraph block*",
284
+ group: "listItem",
285
+ defining: true,
286
+ attrs: {
287
+ label: { default: "\u2022" },
288
+ listType: { default: "bullet" },
289
+ spread: { default: "true" },
290
+ checked: { default: null }
291
+ },
292
+ parseDOM: [
293
+ {
294
+ tag: 'li[data-item-type="task"]',
295
+ getAttrs(dom) {
296
+ return {
297
+ label: dom.dataset.label,
298
+ listType: dom.dataset.listType,
299
+ spread: dom.dataset.spread,
300
+ checked: dom.dataset.checked ? dom.dataset.checked === "true" : null
301
+ };
302
+ }
303
+ },
304
+ {
305
+ tag: "li",
306
+ getAttrs(dom) {
307
+ return {
308
+ label: dom.dataset.label || "\u2022",
309
+ listType: dom.dataset.listType || "bullet",
310
+ spread: dom.dataset.spread || "true"
311
+ };
312
+ }
313
+ }
314
+ ],
315
+ toDOM(node) {
316
+ if (node.attrs.checked != null) {
317
+ return ["li", {
318
+ "data-item-type": "task",
319
+ "data-label": node.attrs.label,
320
+ "data-list-type": node.attrs.listType,
321
+ "data-spread": node.attrs.spread,
322
+ "data-checked": String(node.attrs.checked)
323
+ }, 0];
324
+ }
325
+ return ["li", {
326
+ "data-label": node.attrs.label,
327
+ "data-list-type": node.attrs.listType,
328
+ "data-spread": node.attrs.spread
329
+ }, 0];
330
+ }
331
+ };
332
+ var hardbreak = {
333
+ inline: true,
334
+ group: "inline",
335
+ selectable: false,
336
+ attrs: {
337
+ isInline: { default: false }
338
+ },
339
+ parseDOM: [
340
+ { tag: "br" },
341
+ {
342
+ tag: 'span[data-type="hardbreak"]',
343
+ getAttrs() {
344
+ return { isInline: true };
345
+ }
346
+ }
347
+ ],
348
+ toDOM() {
349
+ return ["span", { "data-type": "hardbreak", "class": "hardbreak-marker" }, "\n"];
350
+ },
351
+ leafText() {
352
+ return "\n";
353
+ }
354
+ };
355
+ var html_block = {
356
+ content: "text*",
357
+ group: "block",
358
+ marks: "",
359
+ code: true,
360
+ defining: true,
361
+ parseDOM: [{
362
+ tag: 'div[data-type="html"]',
363
+ preserveWhitespace: "full"
364
+ }],
365
+ toDOM() {
366
+ return ["div", { "data-type": "html" }, ["pre", 0]];
367
+ }
368
+ };
369
+ var table = {
370
+ content: "table_header_row table_row+",
371
+ group: "block",
372
+ tableRole: "table",
373
+ isolating: true,
374
+ parseDOM: [{ tag: "table" }],
375
+ toDOM() {
376
+ return ["table", ["tbody", 0]];
377
+ }
378
+ };
379
+ var table_header_row = {
380
+ content: "(table_header)*",
381
+ tableRole: "row",
382
+ parseDOM: [
383
+ { tag: "tr[data-is-header]" },
384
+ {
385
+ tag: "tr",
386
+ getAttrs(dom) {
387
+ const hasHeader = dom.querySelector("th");
388
+ return hasHeader ? {} : false;
389
+ }
390
+ }
391
+ ],
392
+ toDOM() {
393
+ return ["tr", { "data-is-header": "true" }, 0];
394
+ }
395
+ };
396
+ var table_row = {
397
+ content: "(table_cell)*",
398
+ tableRole: "row",
399
+ parseDOM: [{ tag: "tr" }],
400
+ toDOM() {
401
+ return ["tr", 0];
402
+ }
403
+ };
404
+ var table_header = {
405
+ content: "paragraph+",
406
+ tableRole: "header_cell",
407
+ attrs: {
408
+ alignment: { default: "left" },
409
+ colspan: { default: 1 },
410
+ rowspan: { default: 1 },
411
+ colwidth: { default: null }
412
+ },
413
+ isolating: true,
414
+ parseDOM: [{
415
+ tag: "th",
416
+ getAttrs(dom) {
417
+ return {
418
+ alignment: dom.style.textAlign || "left",
419
+ colspan: Number(dom.getAttribute("colspan") || 1),
420
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
421
+ colwidth: null
422
+ };
423
+ }
424
+ }],
425
+ toDOM(node) {
426
+ return ["th", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
427
+ }
428
+ };
429
+ var table_cell = {
430
+ content: "paragraph+",
431
+ tableRole: "cell",
432
+ attrs: {
433
+ alignment: { default: "left" },
434
+ colspan: { default: 1 },
435
+ rowspan: { default: 1 },
436
+ colwidth: { default: null }
437
+ },
438
+ isolating: true,
439
+ parseDOM: [{
440
+ tag: "td",
441
+ getAttrs(dom) {
442
+ return {
443
+ alignment: dom.style.textAlign || "left",
444
+ colspan: Number(dom.getAttribute("colspan") || 1),
445
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
446
+ colwidth: null
447
+ };
448
+ }
449
+ }],
450
+ toDOM(node) {
451
+ return ["td", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
452
+ }
453
+ };
454
+ var math_inline = {
455
+ group: "inline",
456
+ content: "text*",
457
+ inline: true,
458
+ atom: true,
459
+ parseDOM: [{
460
+ tag: 'span[data-type="math_inline"]',
461
+ getContent(dom, schema) {
462
+ if (!(dom instanceof HTMLElement)) return Fragment.empty;
463
+ const value = dom.dataset.value ?? "";
464
+ if (!value) return Fragment.empty;
465
+ return Fragment.from(schema.text(value));
466
+ }
467
+ }],
468
+ toDOM(node) {
469
+ const code2 = node.textContent;
470
+ const dom = document.createElement("span");
471
+ dom.dataset.type = "math_inline";
472
+ dom.dataset.value = code2;
473
+ try {
474
+ katex.render(code2, dom);
475
+ } catch {
476
+ dom.textContent = code2;
477
+ dom.classList.add("math-error");
478
+ dom.setAttribute("data-math-type", "inline");
479
+ }
480
+ return dom;
481
+ }
482
+ };
483
+ var math_block = {
484
+ content: "text*",
485
+ group: "block",
486
+ marks: "",
487
+ defining: true,
488
+ atom: true,
489
+ isolating: true,
490
+ attrs: {
491
+ value: { default: "" }
492
+ },
493
+ parseDOM: [{
494
+ tag: 'div[data-type="math_block"]',
495
+ preserveWhitespace: "full",
496
+ getAttrs(dom) {
497
+ return { value: dom.dataset.value ?? "" };
498
+ }
499
+ }],
500
+ toDOM(node) {
501
+ const code2 = node.attrs.value;
502
+ const dom = document.createElement("div");
503
+ dom.dataset.type = "math_block";
504
+ dom.dataset.value = code2;
505
+ try {
506
+ katex.render(code2, dom, { displayMode: true });
507
+ } catch {
508
+ dom.textContent = code2;
509
+ dom.classList.add("math-error");
510
+ dom.setAttribute("data-math-type", "block");
511
+ }
512
+ return dom;
513
+ }
514
+ };
515
+ var defList = {
516
+ content: "(defListTerm | defListDescription)+",
517
+ group: "block",
518
+ defining: true,
519
+ parseDOM: [{ tag: "dl" }],
520
+ toDOM() {
521
+ return ["dl", { class: "definition-list" }, 0];
522
+ }
523
+ };
524
+ var defListTerm = {
525
+ content: "inline*",
526
+ group: "block",
527
+ defining: true,
528
+ parseDOM: [{ tag: "dt" }],
529
+ toDOM() {
530
+ return ["dt", 0];
531
+ }
532
+ };
533
+ var defListDescription = {
534
+ content: "block+",
535
+ group: "block",
536
+ defining: true,
537
+ parseDOM: [{ tag: "dd" }],
538
+ toDOM() {
539
+ return ["dd", 0];
540
+ }
541
+ };
542
+ var strong = {
543
+ parseDOM: [
544
+ {
545
+ tag: "b",
546
+ getAttrs(dom) {
547
+ return dom.style.fontWeight !== "normal" && null;
548
+ }
549
+ },
550
+ { tag: "strong" },
551
+ {
552
+ style: "font-weight",
553
+ getAttrs(value) {
554
+ return /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null;
555
+ }
556
+ }
557
+ ],
558
+ toDOM() {
559
+ return ["strong", 0];
560
+ }
561
+ };
562
+ var em = {
563
+ parseDOM: [
564
+ { tag: "i" },
565
+ { tag: "em" },
566
+ {
567
+ style: "font-style",
568
+ getAttrs(value) {
569
+ return value === "italic" && null;
570
+ }
571
+ }
572
+ ],
573
+ toDOM() {
574
+ return ["em", 0];
575
+ }
576
+ };
577
+ var code = {
578
+ priority: 100,
579
+ code: true,
580
+ inclusive: false,
581
+ parseDOM: [{ tag: "code" }],
582
+ toDOM() {
583
+ return ["code", 0];
584
+ }
585
+ };
586
+ var link = {
587
+ attrs: {
588
+ href: {},
589
+ title: { default: null }
590
+ },
591
+ inclusive: false,
592
+ parseDOM: [{
593
+ tag: "a[href]",
594
+ getAttrs(dom) {
595
+ return {
596
+ href: dom.getAttribute("href"),
597
+ title: dom.getAttribute("title")
598
+ };
599
+ }
600
+ }],
601
+ toDOM(mark) {
602
+ const attrs = { href: mark.attrs.href };
603
+ if (mark.attrs.title) attrs.title = mark.attrs.title;
604
+ return ["a", attrs, 0];
605
+ }
606
+ };
607
+ var strike_through = {
608
+ parseDOM: [
609
+ { tag: "del" },
610
+ { tag: "s" },
611
+ {
612
+ style: "text-decoration",
613
+ getAttrs(value) {
614
+ return value === "line-through" && null;
615
+ }
616
+ }
617
+ ],
618
+ toDOM() {
619
+ return ["del", 0];
620
+ }
621
+ };
622
+ var html_mark = {
623
+ attrs: {
624
+ openTag: { default: "" },
625
+ closeTag: { default: "" }
626
+ },
627
+ excludes: "",
628
+ // Allow nesting multiple html_marks (e.g., <font><u>text</u></font>)
629
+ parseDOM: [{
630
+ tag: '[data-type="html-mark"]',
631
+ getAttrs(dom) {
632
+ return {
633
+ openTag: dom.dataset.openTag ?? "",
634
+ closeTag: dom.dataset.closeTag ?? ""
635
+ };
636
+ }
637
+ }],
638
+ toDOM(mark) {
639
+ const openTag = mark.attrs.openTag;
640
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
641
+ const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : "span";
642
+ const attrs = {
643
+ "data-type": "html-mark",
644
+ "data-open-tag": openTag,
645
+ "data-close-tag": mark.attrs.closeTag
646
+ };
647
+ const semanticTags = ["sub", "sup", "u", "ins", "mark", "small", "big", "kbd", "abbr"];
648
+ if (semanticTags.includes(tagName)) {
649
+ return [tagName, attrs, 0];
650
+ }
651
+ const style = htmlTagToStyle(openTag);
652
+ if (style) attrs.style = style;
653
+ return ["span", attrs, 0];
654
+ }
655
+ };
656
+ function buildImageNodeSpec(mediaResolver) {
657
+ return {
658
+ inline: true,
659
+ group: "inline",
660
+ selectable: true,
661
+ draggable: true,
662
+ marks: "",
663
+ atom: true,
664
+ defining: true,
665
+ isolating: true,
666
+ attrs: {
667
+ src: { default: "" },
668
+ alt: { default: "" },
669
+ title: { default: "" }
670
+ },
671
+ parseDOM: [{
672
+ tag: "img[src]",
673
+ getAttrs(dom) {
674
+ return {
675
+ src: dom.getAttribute("src") || "",
676
+ alt: dom.getAttribute("alt") || "",
677
+ title: dom.getAttribute("title") || dom.getAttribute("alt") || ""
678
+ };
679
+ }
680
+ }],
681
+ toDOM(node) {
682
+ const container = document.createElement("span");
683
+ container.className = "image-node";
684
+ const img = document.createElement("img");
685
+ if (node.attrs.alt) img.alt = node.attrs.alt;
686
+ if (node.attrs.title) img.title = node.attrs.title;
687
+ const titleStr = node.attrs.title || "";
688
+ const widthMatch = titleStr.match(/^width=(\d+%?)$/);
689
+ const widthVal = widthMatch?.[1];
690
+ if (widthVal) {
691
+ img.style.width = widthVal.includes("%") ? widthVal : `${widthVal}px`;
692
+ img.style.maxWidth = "none";
693
+ }
694
+ img.onerror = () => {
695
+ const alt = node.attrs.alt ? `![${node.attrs.alt}]` : "![]";
696
+ const title = node.attrs.title ? ` "${node.attrs.title}"` : "";
697
+ showBrokenImage(container, `${alt}(${node.attrs.src}${title})`);
698
+ };
699
+ const src = node.attrs.src;
700
+ if (isAbsoluteFilePath(src)) {
701
+ loadLocalImageSrc(img, src, mediaResolver);
702
+ } else if (isRelativePath(src)) {
703
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
704
+ } else {
705
+ img.src = src;
706
+ }
707
+ container.appendChild(img);
708
+ return container;
709
+ }
710
+ };
711
+ }
712
+ function buildHtmlInlineNodeSpec(mediaResolver) {
713
+ return {
714
+ group: "inline",
715
+ inline: true,
716
+ atom: true,
717
+ attrs: {
718
+ value: { default: "" }
719
+ },
720
+ parseDOM: [{
721
+ tag: 'span[data-type="html-inline"]',
722
+ getAttrs(dom) {
723
+ return { value: dom.dataset.value ?? "" };
724
+ }
725
+ }],
726
+ toDOM(node) {
727
+ const value = node.attrs.value;
728
+ if (/^<img\s/i.test(value)) {
729
+ const wrapper = document.createElement("span");
730
+ wrapper.dataset.type = "html-inline";
731
+ wrapper.dataset.value = value;
732
+ wrapper.className = "html-img-wrapper";
733
+ const attrs = extractAllHtmlAttrs(value);
734
+ const src = attrs.src || "";
735
+ if (src) {
736
+ const img = document.createElement("img");
737
+ for (const [key, val] of Object.entries(attrs)) {
738
+ if (key === "src") continue;
739
+ if (key === "onerror" || key === "onload" || key.startsWith("on")) continue;
740
+ img.setAttribute(key, val);
741
+ }
742
+ img.onerror = () => {
743
+ showBrokenImage(wrapper, value);
744
+ };
745
+ if (isAbsoluteFilePath(src)) {
746
+ loadLocalImageSrc(img, src, mediaResolver);
747
+ } else if (isRelativePath(src)) {
748
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
749
+ } else {
750
+ img.src = src;
751
+ }
752
+ wrapper.appendChild(img);
753
+ } else {
754
+ showBrokenImage(wrapper, value);
755
+ }
756
+ return wrapper;
757
+ }
758
+ if (/^<video\b/i.test(value)) return createMediaElement("video", value, mediaResolver);
759
+ if (/^<audio\b/i.test(value)) return createMediaElement("audio", value, mediaResolver);
760
+ return ["span", { "data-type": "html-inline", "data-value": value }];
761
+ }
762
+ };
763
+ }
764
+ function buildNodes(mediaResolver) {
765
+ return {
766
+ doc,
767
+ text,
768
+ paragraph,
769
+ heading,
770
+ blockquote,
771
+ code_block,
772
+ horizontal_rule,
773
+ bullet_list,
774
+ ordered_list,
775
+ list_item,
776
+ image: buildImageNodeSpec(mediaResolver),
777
+ hardbreak,
778
+ html_block,
779
+ html_inline: buildHtmlInlineNodeSpec(mediaResolver),
780
+ table,
781
+ table_header_row,
782
+ table_row,
783
+ table_header,
784
+ table_cell,
785
+ math_inline,
786
+ math_block,
787
+ defList,
788
+ defListTerm,
789
+ defListDescription
790
+ };
791
+ }
792
+ var marks = {
793
+ html_mark,
794
+ strong,
795
+ em,
796
+ code,
797
+ link,
798
+ strike_through
799
+ };
800
+ var nullMediaResolver = {
801
+ [NULL_MEDIA_RESOLVER_SENTINEL]: true,
802
+ async loadLocalImage() {
803
+ return "";
804
+ },
805
+ async loadLocalMedia() {
806
+ return "";
807
+ },
808
+ async loadRemoteMedia(url) {
809
+ return url;
810
+ }
811
+ };
812
+ var defaultSchema = new Schema({
813
+ nodes: buildNodes(nullMediaResolver),
814
+ marks
815
+ });
816
+
817
+ // src/markdown.ts
818
+ var md = new MarkdownIt({
819
+ html: true,
820
+ linkify: false,
821
+ typographer: false
822
+ }).enable(["table", "strikethrough"]).use(deflistPlugin).use(texmathPlugin);
823
+ function tagPairedHtmlInline(tokens) {
824
+ const VOID_RE = /^<(?:br|hr|img|input|wbr|area|base|col|embed|link|meta|param|source|track)[\s/>]/i;
825
+ for (const token of tokens) {
826
+ if (token.type !== "inline" || !token.children) continue;
827
+ const children = token.children;
828
+ const stack = [];
829
+ for (let i = 0; i < children.length; i++) {
830
+ const child = children[i];
831
+ if (!child || child.type !== "html_inline") continue;
832
+ const content = child.content;
833
+ if (VOID_RE.test(content) || /\/>$/.test(content) || /^<!--/.test(content)) continue;
834
+ const closeMatch = content.match(/^<\/([a-zA-Z][a-zA-Z0-9]*)\s*>$/);
835
+ if (closeMatch && closeMatch[1]) {
836
+ const tagName = closeMatch[1].toLowerCase();
837
+ for (let j = stack.length - 1; j >= 0; j--) {
838
+ const entry = stack[j];
839
+ if (!entry) continue;
840
+ if (entry.tagName === tagName) {
841
+ const opener = children[entry.index];
842
+ if (opener) {
843
+ opener.meta = { ...opener.meta || {}, htmlPaired: true };
844
+ }
845
+ child.meta = { ...child.meta || {}, htmlPaired: true };
846
+ stack.splice(j, 1);
847
+ break;
848
+ }
849
+ }
850
+ continue;
851
+ }
852
+ const openMatch = content.match(/^<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>$/);
853
+ if (openMatch && openMatch[1]) {
854
+ stack.push({ tagName: openMatch[1].toLowerCase(), index: i });
855
+ }
856
+ }
857
+ }
858
+ }
859
+ function preserveBlankLines(tokens) {
860
+ function mkToken(type, tag, nesting, extra) {
861
+ return {
862
+ type,
863
+ tag,
864
+ nesting,
865
+ content: "",
866
+ children: null,
867
+ attrs: null,
868
+ info: "",
869
+ meta: null,
870
+ map: null,
871
+ block: true,
872
+ hidden: false,
873
+ level: 0,
874
+ markup: "",
875
+ ...extra
876
+ };
877
+ }
878
+ const result = [];
879
+ let lastTopBlockEndLine = 0;
880
+ for (let i = 0; i < tokens.length; i++) {
881
+ const tok = tokens[i];
882
+ if (!tok) continue;
883
+ if (tok.map && tok.level === 0 && (tok.nesting === 1 || tok.nesting === 0)) {
884
+ const startLine = tok.map[0];
885
+ const gap = startLine - lastTopBlockEndLine;
886
+ if (gap > 1 && lastTopBlockEndLine > 0) {
887
+ const extra = gap - 1;
888
+ for (let j = 0; j < extra; j++) {
889
+ result.push(
890
+ mkToken("paragraph_open", "p", 1),
891
+ mkToken("inline", "", 0, { level: 1, block: false, children: [] }),
892
+ mkToken("paragraph_close", "p", -1)
893
+ );
894
+ }
895
+ }
896
+ lastTopBlockEndLine = tok.map[1];
897
+ }
898
+ result.push(tok);
899
+ }
900
+ return result;
901
+ }
902
+ var _origMdParse = md.parse.bind(md);
903
+ md.parse = function(src, env) {
904
+ let tokens = _origMdParse(src, env);
905
+ tagPairedHtmlInline(tokens);
906
+ tokens = preserveBlankLines(tokens);
907
+ return tokens;
908
+ };
909
+ var parserTokens = {
910
+ // ── Block tokens ──
911
+ paragraph: { block: "paragraph" },
912
+ blockquote: { block: "blockquote" },
913
+ heading: {
914
+ block: "heading",
915
+ getAttrs(token) {
916
+ return { level: Number(token.tag.slice(1)) };
917
+ }
918
+ },
919
+ hr: { node: "horizontal_rule" },
920
+ bullet_list: { block: "bullet_list" },
921
+ ordered_list: {
922
+ block: "ordered_list",
923
+ getAttrs(token) {
924
+ return { order: Number(token.attrGet("start") || 1) };
925
+ }
926
+ },
927
+ list_item: {
928
+ block: "list_item",
929
+ getAttrs(_token, tokens, index) {
930
+ let checked = null;
931
+ for (let i = index + 1; i < tokens.length; i++) {
932
+ const t = tokens[i];
933
+ if (!t) continue;
934
+ if (t.type === "inline" && t.content) {
935
+ const match = t.content.match(/^\[( |x|X)\]\s?/);
936
+ if (match) {
937
+ checked = match[1] !== " ";
938
+ t.content = t.content.slice(match[0].length);
939
+ const children = t.children;
940
+ if (children && children.length > 0) {
941
+ const firstChild = children[0];
942
+ if (firstChild.type === "text") {
943
+ firstChild.content = firstChild.content.slice(match[0].length);
944
+ if (!firstChild.content) {
945
+ children.shift();
946
+ }
947
+ }
948
+ }
949
+ }
950
+ break;
951
+ }
952
+ if (t.type === "list_item_close") break;
953
+ }
954
+ return { checked };
955
+ }
956
+ },
957
+ code_block: {
958
+ block: "code_block",
959
+ getAttrs() {
960
+ return { language: "text" };
961
+ },
962
+ noCloseToken: true
963
+ },
964
+ fence: {
965
+ block: "code_block",
966
+ getAttrs(token) {
967
+ return { language: token.info.trim() || "text" };
968
+ },
969
+ noCloseToken: true
970
+ },
971
+ html_block: {
972
+ block: "html_block",
973
+ noCloseToken: true
974
+ },
975
+ html_inline: {
976
+ // markdown-it emits this token for inline HTML like <br>, <span>, <sup>,
977
+ // and HTML comments <!-- ... -->. Store raw HTML in the `value` attr.
978
+ node: "html_inline",
979
+ noCloseToken: true,
980
+ getAttrs(token) {
981
+ return { value: token.content };
982
+ }
983
+ },
984
+ // ── Table tokens ──
985
+ // NOTE: tr/th/td are NOT listed here — they are handled by custom tokenHandler
986
+ // overrides in MorayaMarkdownParser below. The `block:` spec alone can't
987
+ // handle (a) thead-row → table_header_row vs table_row dispatch, or
988
+ // (b) wrapping inline content in the required paragraph child of each cell.
989
+ table: { block: "table" },
990
+ thead: { ignore: true },
991
+ tbody: { ignore: true },
992
+ // ── Definition list tokens ──
993
+ dl: { block: "defList" },
994
+ dt: { block: "defListTerm" },
995
+ dd: { block: "defListDescription" },
996
+ // ── Math tokens (from markdown-it-texmath) ──
997
+ // Use block: spec (not node:) so token.content is added as text children,
998
+ // correctly filling math_inline's `content: 'text*'`.
999
+ math_inline: {
1000
+ block: "math_inline",
1001
+ noCloseToken: true
1002
+ },
1003
+ // markdown-it-texmath emits math_inline_double for $$...$$ in inline context.
1004
+ // Map to math_inline to prevent "Token type not supported" crash.
1005
+ math_inline_double: {
1006
+ block: "math_inline",
1007
+ noCloseToken: true
1008
+ },
1009
+ math_block: {
1010
+ node: "math_block",
1011
+ noCloseToken: true,
1012
+ getAttrs(token) {
1013
+ return { value: token.content.trim() };
1014
+ }
1015
+ },
1016
+ // ── Inline tokens ──
1017
+ image: {
1018
+ node: "image",
1019
+ getAttrs(token) {
1020
+ let src = token.attrGet("src") || "";
1021
+ try {
1022
+ src = decodeURIComponent(src);
1023
+ } catch {
1024
+ }
1025
+ return {
1026
+ src,
1027
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1028
+ alt: (token.children || []).map((c) => c.content).join("") || "",
1029
+ title: token.attrGet("title") || ""
1030
+ };
1031
+ }
1032
+ },
1033
+ hardbreak: { node: "hardbreak" },
1034
+ softbreak: { node: "hardbreak", attrs: { isInline: true } },
1035
+ // ── Mark tokens ──
1036
+ em: { mark: "em" },
1037
+ strong: { mark: "strong" },
1038
+ s: { mark: "strike_through" },
1039
+ code_inline: { mark: "code", noCloseToken: true },
1040
+ link: {
1041
+ mark: "link",
1042
+ getAttrs(token) {
1043
+ let href = token.attrGet("href") || "";
1044
+ href = href.replace(
1045
+ /%[C-F][0-9A-F](?:%[89AB][0-9A-F])+/gi,
1046
+ (m) => {
1047
+ try {
1048
+ return decodeURIComponent(m);
1049
+ } catch {
1050
+ return m;
1051
+ }
1052
+ }
1053
+ );
1054
+ return {
1055
+ href,
1056
+ title: token.attrGet("title") || null
1057
+ };
1058
+ }
1059
+ }
1060
+ };
1061
+ var MorayaMarkdownParser = class extends MarkdownParser {
1062
+ /**
1063
+ * The schema this parser instance is bound to. Captured for use in
1064
+ * tokenHandler overrides (tr_open / th_open / etc.) so they reference the
1065
+ * caller-provided schema rather than the module-level defaultSchema.
1066
+ */
1067
+ schema;
1068
+ constructor(schemaArg = defaultSchema) {
1069
+ super(schemaArg, md, parserTokens);
1070
+ this.schema = schemaArg;
1071
+ const h = (
1072
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1073
+ this.tokenHandlers
1074
+ );
1075
+ function cellAlignment(tok) {
1076
+ const style = tok.attrGet("style") || "";
1077
+ const m = style.match(/text-align:\s*(\w+)/);
1078
+ return m && m[1] ? m[1] : "left";
1079
+ }
1080
+ h["tr_open"] = (state, _tok, tokens, i) => {
1081
+ let inThead = false;
1082
+ for (let j = i - 1; j >= 0; j--) {
1083
+ if (tokens[j].type === "thead_open") {
1084
+ inThead = true;
1085
+ break;
1086
+ }
1087
+ if (tokens[j].type === "thead_close" || tokens[j].type === "tbody_open") break;
1088
+ }
1089
+ state.openNode(inThead ? schemaArg.nodes.table_header_row : schemaArg.nodes.table_row, null);
1090
+ };
1091
+ h["tr_close"] = (state) => state.closeNode();
1092
+ h["th_open"] = (state, tok) => {
1093
+ state.openNode(schemaArg.nodes.table_header, { alignment: cellAlignment(tok) });
1094
+ state.openNode(schemaArg.nodes.paragraph, null);
1095
+ };
1096
+ h["th_close"] = (state) => {
1097
+ state.closeNode();
1098
+ state.closeNode();
1099
+ };
1100
+ h["td_open"] = (state, tok) => {
1101
+ state.openNode(schemaArg.nodes.table_cell, { alignment: cellAlignment(tok) });
1102
+ state.openNode(schemaArg.nodes.paragraph, null);
1103
+ };
1104
+ h["td_close"] = (state) => {
1105
+ state.closeNode();
1106
+ state.closeNode();
1107
+ };
1108
+ const defaultLinkOpen = h["link_open"];
1109
+ const defaultLinkClose = h["link_close"];
1110
+ h["link_open"] = (state, tok, tokens, i) => {
1111
+ let hasContent = false;
1112
+ for (let j = i + 1; j < tokens.length; j++) {
1113
+ if (tokens[j].type === "link_close") break;
1114
+ if (tokens[j].type === "text" && tokens[j].content) {
1115
+ hasContent = true;
1116
+ break;
1117
+ }
1118
+ if (["image", "code_inline", "softbreak", "hardbreak", "html_inline"].includes(tokens[j].type)) {
1119
+ hasContent = true;
1120
+ break;
1121
+ }
1122
+ }
1123
+ if (!hasContent) {
1124
+ let href = tok.attrGet("href") || "";
1125
+ href = href.replace(
1126
+ /%[C-F][0-9A-F](?:%[89AB][0-9A-F])+/gi,
1127
+ (m) => {
1128
+ try {
1129
+ return decodeURIComponent(m);
1130
+ } catch {
1131
+ return m;
1132
+ }
1133
+ }
1134
+ );
1135
+ const title = tok.attrGet("title");
1136
+ let literal = `[](${href}`;
1137
+ if (title) literal += ` "${title}"`;
1138
+ literal += ")";
1139
+ state.addText(literal);
1140
+ for (let j = i + 1; j < tokens.length; j++) {
1141
+ if (tokens[j].type === "link_close") {
1142
+ tokens[j].meta = { ...tokens[j].meta || {}, skipClose: true };
1143
+ break;
1144
+ }
1145
+ }
1146
+ return;
1147
+ }
1148
+ defaultLinkOpen(state, tok, tokens, i);
1149
+ };
1150
+ h["link_close"] = (state, tok, tokens, i) => {
1151
+ if (tok.meta?.skipClose) return;
1152
+ defaultLinkClose(state, tok, tokens, i);
1153
+ };
1154
+ const defaultTextHandler = h["text"];
1155
+ h["text"] = (state, tok, toks, ii) => {
1156
+ if (tok.meta?.mediaSkip) return;
1157
+ defaultTextHandler(state, tok, toks, ii);
1158
+ };
1159
+ h["html_inline"] = (state, tok, tokens, i) => {
1160
+ if (tok.meta?.mediaSkip) return;
1161
+ const content = tok.content;
1162
+ const mediaMatch = content.match(/^<(audio|video)\b/i);
1163
+ if (mediaMatch && mediaMatch[1]) {
1164
+ const tagName = mediaMatch[1].toLowerCase();
1165
+ const closeRe = new RegExp(`^</${tagName}\\s*>$`, "i");
1166
+ let fullHtml = content;
1167
+ for (let j = i + 1; j < tokens.length; j++) {
1168
+ const t = tokens[j];
1169
+ if (t.type === "html_inline" && closeRe.test(t.content.trim())) {
1170
+ fullHtml += t.content;
1171
+ t.meta = { ...t.meta || {}, mediaSkip: true };
1172
+ break;
1173
+ }
1174
+ if (t.content) fullHtml += t.content;
1175
+ t.meta = { ...t.meta || {}, mediaSkip: true };
1176
+ }
1177
+ state.addNode(schemaArg.nodes.html_inline, { value: fullHtml });
1178
+ return;
1179
+ }
1180
+ if (tok.meta?.htmlPaired) {
1181
+ const htmlMark = schemaArg.marks.html_mark;
1182
+ if (!htmlMark) {
1183
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
1184
+ return;
1185
+ }
1186
+ if (!content.startsWith("</")) {
1187
+ const tagMatch = content.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
1188
+ const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : "";
1189
+ state.openMark(htmlMark.create({
1190
+ openTag: content,
1191
+ closeTag: `</${tagName}>`
1192
+ }));
1193
+ } else {
1194
+ state.closeMark(htmlMark);
1195
+ }
1196
+ return;
1197
+ }
1198
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
1199
+ };
1200
+ const defaultHtmlBlock = h["html_block"];
1201
+ h["html_block"] = (state, tok, tokens, i) => {
1202
+ const content = tok.content.trim();
1203
+ if (/^<img\s/i.test(content)) {
1204
+ const imgPattern = /<img\s[^>]*\/?>/gi;
1205
+ const imgs = content.match(imgPattern);
1206
+ state.openNode(schemaArg.nodes.paragraph, null);
1207
+ if (imgs && imgs.length > 0) {
1208
+ for (let j = 0; j < imgs.length; j++) {
1209
+ if (j > 0) {
1210
+ state.addNode(schemaArg.nodes.hardbreak, { isInline: true });
1211
+ }
1212
+ state.addNode(schemaArg.nodes.html_inline, { value: imgs[j] });
1213
+ }
1214
+ } else {
1215
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
1216
+ }
1217
+ state.closeNode();
1218
+ } else if (/^<(video|audio)\b/i.test(content)) {
1219
+ state.openNode(schemaArg.nodes.paragraph, null);
1220
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
1221
+ state.closeNode();
1222
+ } else {
1223
+ defaultHtmlBlock(state, tok, tokens, i);
1224
+ }
1225
+ };
1226
+ }
1227
+ };
1228
+ var defaultParser = new MorayaMarkdownParser(defaultSchema);
1229
+ var parserCache = /* @__PURE__ */ new WeakMap();
1230
+ parserCache.set(defaultSchema, defaultParser);
1231
+ function getParserFor(schema) {
1232
+ if (!schema || schema === defaultSchema) return defaultParser;
1233
+ let p = parserCache.get(schema);
1234
+ if (!p) {
1235
+ p = new MorayaMarkdownParser(schema);
1236
+ parserCache.set(schema, p);
1237
+ }
1238
+ return p;
1239
+ }
1240
+ var serializer = new MarkdownSerializer(
1241
+ {
1242
+ // ── Block nodes ──
1243
+ doc(state, node) {
1244
+ state.renderContent(node);
1245
+ },
1246
+ paragraph(state, node) {
1247
+ if (node.content.size === 0) {
1248
+ state.write("");
1249
+ } else {
1250
+ state.renderInline(node);
1251
+ }
1252
+ state.closeBlock(node);
1253
+ },
1254
+ heading(state, node) {
1255
+ state.write(`${"#".repeat(node.attrs.level)} `);
1256
+ state.renderInline(node, false);
1257
+ state.closeBlock(node);
1258
+ },
1259
+ blockquote(state, node) {
1260
+ state.wrapBlock("> ", null, node, () => state.renderContent(node));
1261
+ },
1262
+ code_block(state, node) {
1263
+ const lang = node.attrs.language || "";
1264
+ const fenceLang = lang === "text" ? "" : lang;
1265
+ state.write(`\`\`\`${fenceLang}
1266
+ `);
1267
+ state.text(node.textContent, false);
1268
+ state.ensureNewLine();
1269
+ state.write("```");
1270
+ state.closeBlock(node);
1271
+ },
1272
+ horizontal_rule(state, node) {
1273
+ state.write("---");
1274
+ state.closeBlock(node);
1275
+ },
1276
+ bullet_list(state, node) {
1277
+ state.renderList(node, " ", () => "- ");
1278
+ },
1279
+ ordered_list(state, node) {
1280
+ const start = node.attrs.order || 1;
1281
+ state.renderList(node, " ", (i) => `${start + i}. `);
1282
+ },
1283
+ list_item(state, node) {
1284
+ if (node.attrs.checked != null) {
1285
+ const checkbox = node.attrs.checked ? "[x] " : "[ ] ";
1286
+ state.write(checkbox);
1287
+ }
1288
+ state.renderContent(node);
1289
+ },
1290
+ image(state, node) {
1291
+ const alt = state.esc(node.attrs.alt || "", false);
1292
+ const src = node.attrs.src || "";
1293
+ const title = node.attrs.title;
1294
+ if (title) {
1295
+ state.write(`![${alt}](${src} "${state.esc(title, false)}")`);
1296
+ } else {
1297
+ state.write(`![${alt}](${src})`);
1298
+ }
1299
+ },
1300
+ hardbreak(state) {
1301
+ state.write(" \n");
1302
+ },
1303
+ html_block(state, node) {
1304
+ state.text(node.textContent, false);
1305
+ state.closeBlock(node);
1306
+ },
1307
+ html_inline(state, node) {
1308
+ state.text(node.attrs.value, false);
1309
+ },
1310
+ // ── Table nodes ──
1311
+ table(state, node) {
1312
+ const alignments = [];
1313
+ const headerRow = node.child(0);
1314
+ headerRow.forEach((cell) => {
1315
+ alignments.push(cell.attrs.alignment || "left");
1316
+ });
1317
+ renderTableRow(state, headerRow);
1318
+ const sep = alignments.map((a) => {
1319
+ switch (a) {
1320
+ case "center":
1321
+ return ":---:";
1322
+ case "right":
1323
+ return "---:";
1324
+ default:
1325
+ return "---";
1326
+ }
1327
+ });
1328
+ state.write(`| ${sep.join(" | ")} |`);
1329
+ state.ensureNewLine();
1330
+ for (let i = 1; i < node.childCount; i++) {
1331
+ renderTableRow(state, node.child(i));
1332
+ }
1333
+ state.closeBlock(node);
1334
+ },
1335
+ table_header_row() {
1336
+ },
1337
+ table_row() {
1338
+ },
1339
+ table_header(state, node) {
1340
+ state.renderInline(node.firstChild);
1341
+ },
1342
+ table_cell(state, node) {
1343
+ state.renderInline(node.firstChild);
1344
+ },
1345
+ // ── Math nodes ──
1346
+ math_inline(state, node) {
1347
+ state.write(`$${node.textContent}$`);
1348
+ },
1349
+ math_block(state, node) {
1350
+ state.write("$$\n");
1351
+ state.text(node.attrs.value || node.textContent, false);
1352
+ state.ensureNewLine();
1353
+ state.write("$$");
1354
+ state.closeBlock(node);
1355
+ },
1356
+ // ── Definition list nodes ──
1357
+ defList(state, node) {
1358
+ state.renderContent(node);
1359
+ },
1360
+ defListTerm(state, node) {
1361
+ state.renderInline(node);
1362
+ state.closeBlock(node);
1363
+ },
1364
+ defListDescription(state, node) {
1365
+ state.write(": ");
1366
+ state.renderContent(node);
1367
+ },
1368
+ // ── Fallback for text node (shouldn't be needed but safe) ──
1369
+ text(state, node) {
1370
+ state.text(node.text || "");
1371
+ }
1372
+ },
1373
+ {
1374
+ // ── Mark serializers ──
1375
+ strong: {
1376
+ open: "**",
1377
+ close: "**",
1378
+ mixable: true,
1379
+ expelEnclosingWhitespace: true
1380
+ },
1381
+ em: {
1382
+ open: "*",
1383
+ close: "*",
1384
+ mixable: true,
1385
+ expelEnclosingWhitespace: true
1386
+ },
1387
+ code: {
1388
+ open(_state, mark, parent, index) {
1389
+ return isPlainURL(mark, parent, index, 1) ? "" : "`";
1390
+ },
1391
+ close(_state, mark, parent, index) {
1392
+ return isPlainURL(mark, parent, index, -1) ? "" : "`";
1393
+ },
1394
+ escape: false
1395
+ },
1396
+ link: {
1397
+ open(_state, mark, parent, index) {
1398
+ return isPlainURL(mark, parent, index, 1) ? "<" : "[";
1399
+ },
1400
+ close(state, mark, parent, index) {
1401
+ const href = mark.attrs.href;
1402
+ const title = mark.attrs.title;
1403
+ if (isPlainURL(mark, parent, index, -1)) {
1404
+ return ">";
1405
+ }
1406
+ return title ? `](${href} "${state.esc(title, false)}")` : `](${href})`;
1407
+ },
1408
+ mixable: false
1409
+ },
1410
+ strike_through: {
1411
+ open: "~~",
1412
+ close: "~~",
1413
+ mixable: true,
1414
+ expelEnclosingWhitespace: true
1415
+ },
1416
+ html_mark: {
1417
+ open(_state, mark) {
1418
+ return mark.attrs.openTag;
1419
+ },
1420
+ close(_state, mark) {
1421
+ return mark.attrs.closeTag;
1422
+ }
1423
+ }
1424
+ },
1425
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1426
+ {
1427
+ hardBreakNodeName: "hardbreak",
1428
+ strict: false
1429
+ }
1430
+ );
1431
+ function renderTableRow(state, row) {
1432
+ const cells = [];
1433
+ const s = state;
1434
+ row.forEach((cell) => {
1435
+ const parts = [];
1436
+ cell.forEach((para) => {
1437
+ if (para.type.name !== "paragraph") return;
1438
+ const savedOut = s.out;
1439
+ const savedClosed = s.closed;
1440
+ s.out = "";
1441
+ s.closed = null;
1442
+ state.renderInline(para);
1443
+ const piece = s.out.replace(/\n/g, " ").trim();
1444
+ s.out = savedOut;
1445
+ s.closed = savedClosed;
1446
+ parts.push(piece);
1447
+ });
1448
+ cells.push(parts.join(" "));
1449
+ });
1450
+ state.write(`| ${cells.join(" | ")} |`);
1451
+ state.ensureNewLine();
1452
+ }
1453
+ function isPlainURL(mark, parent, index, side) {
1454
+ if (mark.attrs.title || !/^\w+:/.test(mark.attrs.href)) return false;
1455
+ const content = parent.child(index + (side < 0 ? -1 : 0));
1456
+ if (!content.isText || content.text !== mark.attrs.href || content.marks[content.marks.length - 1] !== mark) {
1457
+ return false;
1458
+ }
1459
+ if (index === (side < 0 ? 1 : parent.childCount - 1)) return true;
1460
+ const next = parent.child(index + (side < 0 ? -2 : 1));
1461
+ return !mark.isInSet(next.marks);
1462
+ }
1463
+ function normalizeMathBlocks(text2) {
1464
+ if (!text2.includes("$$")) return text2;
1465
+ const lines = text2.split("\n");
1466
+ const result = [];
1467
+ let inFence = false;
1468
+ let inMathBlock = false;
1469
+ for (let i = 0; i < lines.length; i++) {
1470
+ const line = lines[i] ?? "";
1471
+ const trimmed = line.trim();
1472
+ if (!inMathBlock && /^(`{3,}|~{3,})/.test(trimmed)) {
1473
+ inFence = !inFence;
1474
+ result.push(line);
1475
+ continue;
1476
+ }
1477
+ if (inFence) {
1478
+ result.push(line);
1479
+ continue;
1480
+ }
1481
+ if (trimmed === "$$") {
1482
+ if (!inMathBlock) {
1483
+ const last = result[result.length - 1];
1484
+ if (result.length > 0 && last !== void 0 && last.trim() !== "") {
1485
+ result.push("");
1486
+ }
1487
+ result.push(line);
1488
+ inMathBlock = true;
1489
+ } else {
1490
+ result.push(line);
1491
+ inMathBlock = false;
1492
+ const next = lines[i + 1];
1493
+ if (next !== void 0 && next.trim() !== "") {
1494
+ result.push("");
1495
+ }
1496
+ }
1497
+ } else {
1498
+ result.push(line);
1499
+ }
1500
+ }
1501
+ return result.join("\n");
1502
+ }
1503
+ function normalizeSmartQuotes(text2) {
1504
+ if (!/[“”„‟‘’‚‛]/.test(text2)) return text2;
1505
+ return text2.replace(
1506
+ /(\]\([^\n)]*\s)“([^”\n]*)”(\s*\))/g,
1507
+ (_m, pre, title, post) => `${pre}"${title}"${post}`
1508
+ ).replace(
1509
+ /(\]\([^\n)]*\s)“([^”\n]*)”(\s*\))/g,
1510
+ (_m, pre, title, post) => `${pre}"${title}"${post}`
1511
+ ).replace(
1512
+ /(\]\([^\n)]*\s)‘([^’\n]*)’(\s*\))/g,
1513
+ (_m, pre, title, post) => `${pre}'${title}'${post}`
1514
+ );
1515
+ }
1516
+ function parseMarkdown(markdown, schemaArg) {
1517
+ const p = getParserFor(schemaArg);
1518
+ try {
1519
+ return p.parse(normalizeSmartQuotes(normalizeMathBlocks(markdown)));
1520
+ } catch (err) {
1521
+ if (typeof console !== "undefined" && console.warn) {
1522
+ console.warn("[parseMarkdown] best-effort fallback for malformed input:", err);
1523
+ }
1524
+ return p.schema.topNodeType.createAndFill();
1525
+ }
1526
+ }
1527
+ var ASYNC_PARSE_THRESHOLD = 5e4;
1528
+ function parseMarkdownAsync(markdown, schemaArg) {
1529
+ const p = getParserFor(schemaArg);
1530
+ const normalized = normalizeSmartQuotes(normalizeMathBlocks(markdown));
1531
+ if (normalized.length < ASYNC_PARSE_THRESHOLD) {
1532
+ return Promise.resolve(parseMarkdown(normalized, schemaArg));
1533
+ }
1534
+ return new Promise((resolve) => setTimeout(() => {
1535
+ try {
1536
+ resolve(p.parse(normalized));
1537
+ } catch {
1538
+ resolve(p.schema.topNodeType.createAndFill());
1539
+ }
1540
+ }, 0));
1541
+ }
1542
+ function serializeMarkdown(doc2) {
1543
+ let result = serializer.serialize(doc2, { tightLists: true });
1544
+ result = result.replace(/\\\[([^\\\[\]]*)\\\]\(([^)]*)\)/g, "[$1]($2)");
1545
+ result = result.replace(/​/g, "");
1546
+ return result;
1547
+ }
1548
+ export {
1549
+ parseMarkdown,
1550
+ parseMarkdownAsync,
1551
+ serializeMarkdown
1552
+ };
1553
+ //# sourceMappingURL=markdown.js.map