@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
package/dist/schema.js ADDED
@@ -0,0 +1,847 @@
1
+ // src/schema.ts
2
+ import { Schema, Fragment } from "prosemirror-model";
3
+ import katex from "katex";
4
+
5
+ // src/types.ts
6
+ var NULL_MEDIA_RESOLVER_SENTINEL = /* @__PURE__ */ Symbol("@moraya/core:null-media-resolver");
7
+ function isNullMediaResolver(r) {
8
+ return r[NULL_MEDIA_RESOLVER_SENTINEL] === true;
9
+ }
10
+
11
+ // src/schema.ts
12
+ function extractHtmlAttr(html, name) {
13
+ const re = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
14
+ const m = html.match(re);
15
+ return m ? m[1] ?? m[2] ?? m[3] ?? null : null;
16
+ }
17
+ function extractAllHtmlAttrs(html) {
18
+ const attrs = {};
19
+ const re = /([a-zA-Z_][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
20
+ let m;
21
+ while ((m = re.exec(html)) !== null) {
22
+ const name = m[1];
23
+ if (!name) continue;
24
+ attrs[name.toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
25
+ }
26
+ return attrs;
27
+ }
28
+ function showBrokenImage(container, sourceText) {
29
+ container.textContent = "";
30
+ container.className = (container.className.replace(/\bhtml-img-wrapper\b|\bimage-node\b/, "").trim() + " broken-image").trim();
31
+ const icon = document.createElement("span");
32
+ icon.className = "broken-image-icon";
33
+ container.appendChild(icon);
34
+ const code2 = document.createElement("code");
35
+ code2.className = "broken-image-src";
36
+ code2.textContent = sourceText;
37
+ container.appendChild(code2);
38
+ }
39
+ function htmlTagToStyle(openTag) {
40
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
41
+ if (!tagMatch || !tagMatch[1]) return "";
42
+ const tagName = tagMatch[1].toLowerCase();
43
+ switch (tagName) {
44
+ case "font": {
45
+ const parts = [];
46
+ const color = extractHtmlAttr(openTag, "color");
47
+ if (color) parts.push(`color: ${color}`);
48
+ const size = extractHtmlAttr(openTag, "size");
49
+ if (size) {
50
+ const sizeMap = {
51
+ "1": "0.63em",
52
+ "2": "0.82em",
53
+ "3": "1em",
54
+ "4": "1.13em",
55
+ "5": "1.5em",
56
+ "6": "2em",
57
+ "7": "3em"
58
+ };
59
+ parts.push(`font-size: ${sizeMap[size] || size}`);
60
+ }
61
+ const face = extractHtmlAttr(openTag, "face");
62
+ if (face) parts.push(`font-family: ${face}`);
63
+ return parts.join("; ");
64
+ }
65
+ case "span":
66
+ case "div":
67
+ return extractHtmlAttr(openTag, "style") || "";
68
+ default:
69
+ return "";
70
+ }
71
+ }
72
+ var documentBaseDir = "";
73
+ function setDocumentBaseDir(dir) {
74
+ documentBaseDir = dir;
75
+ }
76
+ function getDocumentBaseDir() {
77
+ return documentBaseDir;
78
+ }
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, schema) {
465
+ if (!(dom instanceof HTMLElement)) return Fragment.empty;
466
+ const value = dom.dataset.value ?? "";
467
+ if (!value) return Fragment.empty;
468
+ return Fragment.from(schema.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
+ var schemaCache = /* @__PURE__ */ new WeakMap();
820
+ function createSchema(config) {
821
+ if (!config || typeof config !== "object") {
822
+ throw new TypeError("@moraya/core: createSchema() requires a config object with a MediaResolver");
823
+ }
824
+ if (!config.mediaResolver) {
825
+ throw new TypeError("@moraya/core: createSchema() requires a MediaResolver");
826
+ }
827
+ if (isNullMediaResolver(config.mediaResolver)) {
828
+ throw new TypeError(
829
+ "@moraya/core: do not pass nullMediaResolver to createSchema(). That instance is reserved for parseMarkdown/serializeMarkdown internal use only. Provide a real MediaResolver implementation (e.g. BrowserMediaResolver from '@moraya/core/adapters/browser-media-resolver')."
830
+ );
831
+ }
832
+ const cached = schemaCache.get(config.mediaResolver);
833
+ if (cached) return cached;
834
+ const schema = new Schema({
835
+ nodes: buildNodes(config.mediaResolver),
836
+ marks
837
+ });
838
+ schemaCache.set(config.mediaResolver, schema);
839
+ return schema;
840
+ }
841
+ export {
842
+ createSchema,
843
+ defaultSchema,
844
+ getDocumentBaseDir,
845
+ setDocumentBaseDir
846
+ };
847
+ //# sourceMappingURL=schema.js.map