@scrider/formatter 1.8.4 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -361,7 +361,272 @@ function isTextNode(node) {
361
361
  return node.nodeType === NODE_TYPE.TEXT_NODE;
362
362
  }
363
363
 
364
+ // src/conversion/html/config.ts
365
+ var INLINE_FORMAT_TAGS = {
366
+ link: "a",
367
+ bold: "strong",
368
+ italic: "em",
369
+ underline: "u",
370
+ strike: "s",
371
+ subscript: "sub",
372
+ superscript: "sup",
373
+ code: "code",
374
+ mark: "mark",
375
+ kbd: "kbd"
376
+ };
377
+ var INLINE_FORMAT_ORDER = [
378
+ "link",
379
+ "bold",
380
+ "italic",
381
+ "underline",
382
+ "strike",
383
+ "subscript",
384
+ "superscript",
385
+ "code",
386
+ "mark",
387
+ "kbd"
388
+ ];
389
+ var INLINE_STYLE_FORMATS = {
390
+ color: "color",
391
+ background: "background-color",
392
+ font: "font-family",
393
+ size: "font-size"
394
+ };
395
+ var BLOCK_FORMAT_TAGS = {
396
+ header: (value) => `h${String(value)}`,
397
+ blockquote: "blockquote",
398
+ "code-block": "pre",
399
+ list: "li"
400
+ // Wrapped in ul/ol based on list type
401
+ };
402
+ var LIST_WRAPPER_TAGS = {
403
+ ordered: "ol",
404
+ bullet: "ul",
405
+ checked: "ul",
406
+ unchecked: "ul"
407
+ };
408
+ var EMBED_RENDERERS = {
409
+ image: (value, attrs) => {
410
+ const src = typeof value === "string" ? value : "";
411
+ const altVal = attrs?.alt;
412
+ const widthVal = attrs?.width;
413
+ const heightVal = attrs?.height;
414
+ const floatVal = attrs?.float;
415
+ const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
416
+ const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
417
+ const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
418
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
419
+ return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
420
+ },
421
+ video: (value, attrs, context) => {
422
+ const src = typeof value === "string" ? value : "";
423
+ const floatVal = attrs?.float;
424
+ const widthVal = attrs?.width;
425
+ const heightVal = attrs?.height;
426
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
427
+ const styles = [];
428
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
429
+ const w = String(widthVal);
430
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
431
+ }
432
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
433
+ const h = String(heightVal);
434
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
435
+ }
436
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
437
+ const embedSrc = toVideoEmbedUrl(src);
438
+ if (embedSrc) {
439
+ return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
440
+ }
441
+ return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
442
+ },
443
+ codeWidget: (value, attrs, context) => {
444
+ const src = typeof value === "string" ? value : "";
445
+ const floatVal = attrs?.float;
446
+ const widthVal = attrs?.width;
447
+ const heightVal = attrs?.height;
448
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
449
+ const styles = [];
450
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
451
+ const w = String(widthVal);
452
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
453
+ }
454
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
455
+ const h = String(heightVal);
456
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
457
+ }
458
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
459
+ const embedSrc = toCodeWidgetEmbedUrl(src);
460
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
461
+ },
462
+ formula: (value) => {
463
+ const latex = typeof value === "string" ? value : "";
464
+ return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
465
+ },
466
+ diagram: (value) => {
467
+ const source = typeof value === "string" ? value : "";
468
+ return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
469
+ },
470
+ drawio: (value, attrs) => {
471
+ const src = typeof value === "string" ? value : "";
472
+ const altVal = attrs?.alt;
473
+ const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
474
+ return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
475
+ },
476
+ "footnote-ref": (value) => {
477
+ const id = typeof value === "string" ? value : String(value);
478
+ return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
479
+ },
480
+ divider: () => "<hr>",
481
+ // Soft line break (Shift+Enter equivalent). Emitted with an explicit
482
+ // `data-scrider-embed` marker so that html-to-delta can distinguish this
483
+ // embed from the placeholder `<br>` that appears inside an empty
484
+ // paragraph (`<p><br></p>`) without relying solely on positional
485
+ // heuristics. See `soft-break.ts` for the format definition.
486
+ softBreak: () => "<br data-scrider-embed>"
487
+ };
488
+ var TAG_TO_INLINE_FORMAT = {
489
+ strong: { format: "bold", value: true },
490
+ b: { format: "bold", value: true },
491
+ em: { format: "italic", value: true },
492
+ i: { format: "italic", value: true },
493
+ u: { format: "underline", value: true },
494
+ ins: { format: "underline", value: true },
495
+ s: { format: "strike", value: true },
496
+ strike: { format: "strike", value: true },
497
+ del: { format: "strike", value: true },
498
+ sub: { format: "subscript", value: true },
499
+ sup: { format: "superscript", value: true },
500
+ code: { format: "code", value: true },
501
+ mark: { format: "mark", value: true },
502
+ kbd: { format: "kbd", value: true }
503
+ };
504
+ var TAG_TO_BLOCK_FORMAT = {
505
+ h1: { format: "header", value: 1 },
506
+ h2: { format: "header", value: 2 },
507
+ h3: { format: "header", value: 3 },
508
+ h4: { format: "header", value: 4 },
509
+ h5: { format: "header", value: 5 },
510
+ h6: { format: "header", value: 6 },
511
+ blockquote: { format: "blockquote", value: true },
512
+ pre: { format: "code-block", value: true }
513
+ };
514
+ var CSS_ALIGN_TO_FORMAT = {
515
+ left: "left",
516
+ center: "center",
517
+ right: "right",
518
+ justify: "justify"
519
+ };
520
+ function escapeHtml(text) {
521
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
522
+ }
523
+ function unescapeHtml(text) {
524
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&amp;/g, "&");
525
+ }
526
+ function toVideoEmbedUrl(url) {
527
+ if (url.includes("youtube.com/embed") || url.includes("player.vimeo.com") || url.includes("dailymotion.com/embed") || url.includes("video_ext.php") || url.includes("rutube.ru/play/embed")) {
528
+ return url;
529
+ }
530
+ const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
531
+ if (ytMatch) {
532
+ return `https://www.youtube.com/embed/${ytMatch[1]}`;
533
+ }
534
+ const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
535
+ if (ytShortMatch) {
536
+ return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
537
+ }
538
+ const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
539
+ if (rtMatch) {
540
+ return `https://rutube.ru/play/embed/${rtMatch[1]}`;
541
+ }
542
+ return null;
543
+ }
544
+ function fromVideoEmbedUrl(embedUrl) {
545
+ const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
546
+ if (ytMatch) {
547
+ return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
548
+ }
549
+ const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
550
+ if (rtMatch) {
551
+ return `https://rutube.ru/video/${rtMatch[1]}/`;
552
+ }
553
+ return embedUrl;
554
+ }
555
+ function splitUrl(url) {
556
+ let rest = url;
557
+ let hash = "";
558
+ const hashIdx = rest.indexOf("#");
559
+ if (hashIdx >= 0) {
560
+ hash = rest.slice(hashIdx);
561
+ rest = rest.slice(0, hashIdx);
562
+ }
563
+ let query = "";
564
+ const qIdx = rest.indexOf("?");
565
+ if (qIdx >= 0) {
566
+ query = rest.slice(qIdx);
567
+ rest = rest.slice(0, qIdx);
568
+ }
569
+ return { base: rest, query, hash };
570
+ }
571
+ function hasQueryParam(url, key) {
572
+ const { query } = splitUrl(url);
573
+ return new RegExp(`[?&]${key}=`, "i").test(query);
574
+ }
575
+ function appendQueryParam(url, key, value) {
576
+ const { base, query, hash } = splitUrl(url);
577
+ const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
578
+ return `${base}${next}${hash}`;
579
+ }
580
+ var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
581
+ function renderEmbedIframeIsolationAttrs(context, kind) {
582
+ const opts = context?.embed;
583
+ const parts = [];
584
+ if (kind === "codeWidget" && opts?.crossOriginIsolated) {
585
+ parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
586
+ }
587
+ if (opts?.credentialless) {
588
+ parts.push("credentialless");
589
+ }
590
+ return parts.length ? ` ${parts.join(" ")}` : "";
591
+ }
592
+ function toCodeWidgetEmbedUrl(url) {
593
+ const u = typeof url === "string" ? url.trim() : "";
594
+ if (!u) return "";
595
+ if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
596
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
597
+ }
598
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
599
+ if (/codesandbox\.io\/embed\//i.test(u)) return u;
600
+ return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
601
+ }
602
+ if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
603
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
604
+ }
605
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
606
+ if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
607
+ return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
608
+ }
609
+ if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
610
+ const { base, query, hash } = splitUrl(u);
611
+ if (/\/embedded(?:\/|$)/i.test(base)) return u;
612
+ const trimmed = base.replace(/\/+$/, "");
613
+ return `${trimmed}/embedded/${query}${hash}`;
614
+ }
615
+ if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
616
+ if (/trinket\.io\/embed\//i.test(u)) return u;
617
+ return u.replace(/trinket\.io\//i, "trinket.io/embed/");
618
+ }
619
+ if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
620
+ if (/onecompiler\.com\/embed\//i.test(u)) return u;
621
+ return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
622
+ }
623
+ return u;
624
+ }
625
+
364
626
  // src/schema/blocks/table.ts
627
+ var EXT_TABLE_HOST_ATTR = "data-scrider-ext-table";
628
+ var EXT_TABLE_HOST_CLASS = "scrider-ext-table-host";
629
+ var VALID_TABLE_BLOCK_FLOATS = ["left", "center", "right"];
365
630
  var VALID_CELL_HORIZONTAL_ALIGNS = [
366
631
  "left",
367
632
  "center",
@@ -549,11 +814,57 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
549
814
  html += `${ind(2)}</tr>${nl}`;
550
815
  return html;
551
816
  }
817
+ function parseTableBlockFloat(value) {
818
+ if (value === "left" || value === "center" || value === "right") return value;
819
+ return void 0;
820
+ }
821
+ function extractHostBlockWidthPx(host) {
822
+ const style = host.getAttribute("style") || "";
823
+ const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
824
+ if (!widthMatch?.[1]) return void 0;
825
+ const n = parseFloat(widthMatch[1]);
826
+ if (!Number.isFinite(n) || n <= 0) return void 0;
827
+ return Math.round(n);
828
+ }
829
+ function enrichTableBlockFromHost(data, host) {
830
+ const float = parseTableBlockFloat(host.getAttribute("data-float"));
831
+ const width = extractHostBlockWidthPx(host);
832
+ if (float == null && width == null) return data;
833
+ const next = { ...data };
834
+ if (float != null) next.float = float;
835
+ if (width != null) next.width = width;
836
+ return next;
837
+ }
838
+ function tableNeedsHostWrapper(data) {
839
+ return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
840
+ }
841
+ function renderTableHostWrapperOpen(data, pretty) {
842
+ const nl = pretty ? "\n" : "";
843
+ const ind = pretty ? " " : "";
844
+ const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
845
+ if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
846
+ const styleParts = [];
847
+ if (data.width != null && data.width > 0) {
848
+ styleParts.push(`width: ${Math.round(data.width)}px`);
849
+ styleParts.push("max-width: 100%");
850
+ }
851
+ if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
852
+ return `<div ${attrs.join(" ")}>${nl}${ind}`;
853
+ }
854
+ function renderTableHostWrapperClose(pretty) {
855
+ return pretty ? "</div>\n" : "</div>";
856
+ }
552
857
  function isGfmCompatible(data) {
553
858
  if (data.colWidths && data.colWidths.some((w) => w > 0)) {
554
859
  return false;
555
860
  }
556
- if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
861
+ if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
862
+ return false;
863
+ }
864
+ if (data.width != null && data.width > 0) {
865
+ return false;
866
+ }
867
+ if (data.float != null) {
557
868
  return false;
558
869
  }
559
870
  for (const cell of Object.values(data.cells)) {
@@ -908,6 +1219,12 @@ var tableBlockHandler = {
908
1219
  if (typeof h !== "number" || h < 0) return false;
909
1220
  }
910
1221
  }
1222
+ if (data.width !== void 0) {
1223
+ if (typeof data.width !== "number" || data.width <= 0) return false;
1224
+ }
1225
+ if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
1226
+ return false;
1227
+ }
911
1228
  if (data.colAligns !== void 0) {
912
1229
  if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
913
1230
  return false;
@@ -959,6 +1276,9 @@ var tableBlockHandler = {
959
1276
  html += `${ind(1)}</tbody>${nl}`;
960
1277
  }
961
1278
  html += `</table>`;
1279
+ if (tableNeedsHostWrapper(data)) {
1280
+ return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
1281
+ }
962
1282
  return html;
963
1283
  },
964
1284
  fromHtml(element, context) {
@@ -1764,544 +2084,282 @@ var boldFormat = {
1764
2084
  return value === true;
1765
2085
  }
1766
2086
  };
1767
-
1768
- // src/schema/formats/inline/code.ts
1769
- var codeFormat = {
1770
- name: "code",
1771
- scope: "inline",
1772
- validate(value) {
1773
- return value === true;
1774
- }
1775
- };
1776
-
1777
- // src/schema/formats/inline/color.ts
1778
- var colorFormat = {
1779
- name: "color",
1780
- scope: "inline",
1781
- normalize(value) {
1782
- return toHexColor(value);
1783
- },
1784
- validate(value) {
1785
- return typeof value === "string" && isValidColor(value);
1786
- }
1787
- };
1788
-
1789
- // src/schema/formats/inline/font.ts
1790
- var fontFormat = {
1791
- name: "font",
1792
- scope: "inline",
1793
- validate(value) {
1794
- return typeof value === "string" && value.length > 0;
1795
- }
1796
- };
1797
-
1798
- // src/schema/formats/inline/italic.ts
1799
- var italicFormat = {
1800
- name: "italic",
1801
- scope: "inline",
1802
- validate(value) {
1803
- return value === true;
1804
- }
1805
- };
1806
-
1807
- // src/schema/formats/inline/kbd.ts
1808
- var kbdFormat = {
1809
- name: "kbd",
1810
- scope: "inline",
1811
- validate(value) {
1812
- return value === true;
1813
- }
1814
- };
1815
-
1816
- // src/schema/formats/inline/link.ts
1817
- var linkFormat = {
1818
- name: "link",
1819
- scope: "inline",
1820
- normalize(value) {
1821
- return value.trim();
1822
- },
1823
- validate(value) {
1824
- if (typeof value !== "string" || value.length === 0) {
1825
- return false;
1826
- }
1827
- const trimmed = value.trim();
1828
- if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
1829
- return true;
1830
- }
1831
- if (trimmed.startsWith("//")) {
1832
- return true;
1833
- }
1834
- if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
1835
- return true;
1836
- }
1837
- try {
1838
- const url = new URL(trimmed);
1839
- return url.protocol === "http:" || url.protocol === "https:";
1840
- } catch {
1841
- return false;
1842
- }
1843
- }
1844
- };
1845
-
1846
- // src/schema/formats/inline/mark.ts
1847
- var markFormat = {
1848
- name: "mark",
1849
- scope: "inline",
1850
- validate(value) {
1851
- return value === true;
1852
- }
1853
- };
1854
-
1855
- // src/schema/formats/inline/size.ts
1856
- var sizeFormat = {
1857
- name: "size",
1858
- scope: "inline",
1859
- validate(value) {
1860
- return typeof value === "string" && value.length > 0;
1861
- }
1862
- };
1863
-
1864
- // src/schema/formats/inline/strike.ts
1865
- var strikeFormat = {
1866
- name: "strike",
1867
- scope: "inline",
1868
- validate(value) {
1869
- return value === true;
1870
- }
1871
- };
1872
-
1873
- // src/schema/formats/inline/subscript.ts
1874
- var subscriptFormat = {
1875
- name: "subscript",
1876
- scope: "inline",
1877
- validate(value) {
1878
- return value === true;
1879
- }
1880
- };
1881
-
1882
- // src/schema/formats/inline/superscript.ts
1883
- var superscriptFormat = {
1884
- name: "superscript",
1885
- scope: "inline",
1886
- validate(value) {
1887
- return value === true;
1888
- }
1889
- };
1890
-
1891
- // src/schema/formats/inline/underline.ts
1892
- var underlineFormat = {
1893
- name: "underline",
1894
- scope: "inline",
1895
- validate(value) {
1896
- return value === true;
1897
- }
1898
- };
1899
-
1900
- // src/schema/formats/block/align.ts
1901
- var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
1902
- var alignFormat = {
1903
- name: "align",
1904
- scope: "block",
1905
- normalize(value) {
1906
- return value.toLowerCase();
1907
- },
1908
- validate(value) {
1909
- return VALID_ALIGN_TYPES.includes(value);
1910
- }
1911
- };
1912
-
1913
- // src/schema/formats/block/blockquote.ts
1914
- var blockquoteFormat = {
1915
- name: "blockquote",
1916
- scope: "block",
1917
- validate(value) {
1918
- return value === true;
1919
- }
1920
- };
1921
-
1922
- // src/schema/formats/block/code-block.ts
1923
- var codeBlockFormat = {
1924
- name: "code-block",
1925
- scope: "block",
1926
- normalize(value) {
1927
- if (typeof value === "string") {
1928
- return value.toLowerCase().trim();
1929
- }
1930
- return value;
1931
- },
1932
- validate(value) {
1933
- if (value === true) {
1934
- return true;
1935
- }
1936
- if (typeof value === "string" && value.length > 0) {
1937
- return true;
1938
- }
1939
- return false;
1940
- }
1941
- };
1942
-
1943
- // src/schema/formats/block/header.ts
1944
- var headerFormat = {
1945
- name: "header",
1946
- scope: "block",
1947
- normalize(value) {
1948
- return Math.max(1, Math.min(6, Math.floor(value)));
1949
- },
1950
- validate(value) {
1951
- return Number.isInteger(value) && value >= 1 && value <= 6;
1952
- }
1953
- };
1954
-
1955
- // src/schema/formats/block/header-id.ts
1956
- var headerIdFormat = {
1957
- name: "header-id",
1958
- scope: "block",
1959
- normalize(value) {
1960
- return String(value).trim().toLowerCase();
1961
- },
1962
- validate(value) {
1963
- if (typeof value !== "string") return false;
1964
- const trimmed = value.trim();
1965
- return trimmed.length > 0 && !/\s/.test(trimmed);
1966
- }
1967
- };
1968
-
1969
- // src/schema/formats/block/indent.ts
1970
- var MAX_INDENT = 8;
1971
- var indentFormat = {
1972
- name: "indent",
1973
- scope: "block",
1974
- normalize(value) {
1975
- return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
1976
- },
1977
- validate(value) {
1978
- return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
1979
- }
1980
- };
1981
-
1982
- // src/schema/formats/block/list.ts
1983
- var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
1984
- var listFormat = {
1985
- name: "list",
1986
- scope: "block",
1987
- normalize(value) {
1988
- return value.toLowerCase();
1989
- },
1990
- validate(value) {
1991
- return VALID_LIST_TYPES.includes(value);
1992
- }
1993
- };
1994
-
1995
- // src/schema/formats/block/table-row.ts
1996
- var tableRowFormat = {
1997
- name: "table-row",
1998
- scope: "block",
1999
- validate(value) {
2000
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
2001
- }
2002
- };
2003
-
2004
- // src/schema/formats/block/table-col.ts
2005
- var tableColFormat = {
2006
- name: "table-col",
2007
- scope: "block",
2008
- validate(value) {
2009
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
2010
- }
2011
- };
2012
-
2013
- // src/schema/formats/block/table-header.ts
2014
- var tableHeaderFormat = {
2015
- name: "table-header",
2016
- scope: "block",
2087
+
2088
+ // src/schema/formats/inline/code.ts
2089
+ var codeFormat = {
2090
+ name: "code",
2091
+ scope: "inline",
2017
2092
  validate(value) {
2018
2093
  return value === true;
2019
2094
  }
2020
2095
  };
2021
2096
 
2022
- // src/schema/formats/block/table-col-align.ts
2023
- var VALID_ALIGNS = ["left", "center", "right"];
2024
- var tableColAlignFormat = {
2025
- name: "table-col-align",
2026
- scope: "block",
2097
+ // src/schema/formats/inline/color.ts
2098
+ var colorFormat = {
2099
+ name: "color",
2100
+ scope: "inline",
2027
2101
  normalize(value) {
2028
- return value.toLowerCase();
2102
+ return toHexColor(value);
2029
2103
  },
2030
2104
  validate(value) {
2031
- return VALID_ALIGNS.includes(value);
2105
+ return typeof value === "string" && isValidColor(value);
2032
2106
  }
2033
2107
  };
2034
2108
 
2035
- // src/schema/formats/embed/block.ts
2036
- var blockFormat = {
2037
- name: "block",
2038
- scope: "embed",
2109
+ // src/schema/formats/inline/font.ts
2110
+ var fontFormat = {
2111
+ name: "font",
2112
+ scope: "inline",
2039
2113
  validate(value) {
2040
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
2114
+ return typeof value === "string" && value.length > 0;
2041
2115
  }
2042
2116
  };
2043
2117
 
2044
- // src/conversion/html/config.ts
2045
- var INLINE_FORMAT_TAGS = {
2046
- link: "a",
2047
- bold: "strong",
2048
- italic: "em",
2049
- underline: "u",
2050
- strike: "s",
2051
- subscript: "sub",
2052
- superscript: "sup",
2053
- code: "code",
2054
- mark: "mark",
2055
- kbd: "kbd"
2056
- };
2057
- var INLINE_FORMAT_ORDER = [
2058
- "link",
2059
- "bold",
2060
- "italic",
2061
- "underline",
2062
- "strike",
2063
- "subscript",
2064
- "superscript",
2065
- "code",
2066
- "mark",
2067
- "kbd"
2068
- ];
2069
- var INLINE_STYLE_FORMATS = {
2070
- color: "color",
2071
- background: "background-color",
2072
- font: "font-family",
2073
- size: "font-size"
2074
- };
2075
- var BLOCK_FORMAT_TAGS = {
2076
- header: (value) => `h${String(value)}`,
2077
- blockquote: "blockquote",
2078
- "code-block": "pre",
2079
- list: "li"
2080
- // Wrapped in ul/ol based on list type
2118
+ // src/schema/formats/inline/italic.ts
2119
+ var italicFormat = {
2120
+ name: "italic",
2121
+ scope: "inline",
2122
+ validate(value) {
2123
+ return value === true;
2124
+ }
2081
2125
  };
2082
- var LIST_WRAPPER_TAGS = {
2083
- ordered: "ol",
2084
- bullet: "ul",
2085
- checked: "ul",
2086
- unchecked: "ul"
2126
+
2127
+ // src/schema/formats/inline/kbd.ts
2128
+ var kbdFormat = {
2129
+ name: "kbd",
2130
+ scope: "inline",
2131
+ validate(value) {
2132
+ return value === true;
2133
+ }
2087
2134
  };
2088
- var EMBED_RENDERERS = {
2089
- image: (value, attrs) => {
2090
- const src = typeof value === "string" ? value : "";
2091
- const altVal = attrs?.alt;
2092
- const widthVal = attrs?.width;
2093
- const heightVal = attrs?.height;
2094
- const floatVal = attrs?.float;
2095
- const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
2096
- const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
2097
- const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
2098
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2099
- return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
2135
+
2136
+ // src/schema/formats/inline/link.ts
2137
+ var linkFormat = {
2138
+ name: "link",
2139
+ scope: "inline",
2140
+ normalize(value) {
2141
+ return value.trim();
2100
2142
  },
2101
- video: (value, attrs, context) => {
2102
- const src = typeof value === "string" ? value : "";
2103
- const floatVal = attrs?.float;
2104
- const widthVal = attrs?.width;
2105
- const heightVal = attrs?.height;
2106
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2107
- const styles = [];
2108
- if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2109
- const w = String(widthVal);
2110
- if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2143
+ validate(value) {
2144
+ if (typeof value !== "string" || value.length === 0) {
2145
+ return false;
2111
2146
  }
2112
- if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2113
- const h = String(heightVal);
2114
- if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2147
+ const trimmed = value.trim();
2148
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2149
+ return true;
2115
2150
  }
2116
- const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2117
- const embedSrc = toVideoEmbedUrl(src);
2118
- if (embedSrc) {
2119
- return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
2151
+ if (trimmed.startsWith("//")) {
2152
+ return true;
2120
2153
  }
2121
- return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
2122
- },
2123
- codeWidget: (value, attrs, context) => {
2124
- const src = typeof value === "string" ? value : "";
2125
- const floatVal = attrs?.float;
2126
- const widthVal = attrs?.width;
2127
- const heightVal = attrs?.height;
2128
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2129
- const styles = [];
2130
- if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2131
- const w = String(widthVal);
2132
- if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2154
+ if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
2155
+ return true;
2133
2156
  }
2134
- if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2135
- const h = String(heightVal);
2136
- if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2157
+ try {
2158
+ const url = new URL(trimmed);
2159
+ return url.protocol === "http:" || url.protocol === "https:";
2160
+ } catch {
2161
+ return false;
2137
2162
  }
2138
- const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2139
- const embedSrc = toCodeWidgetEmbedUrl(src);
2140
- return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
2141
- },
2142
- formula: (value) => {
2143
- const latex = typeof value === "string" ? value : "";
2144
- return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
2145
- },
2146
- diagram: (value) => {
2147
- const source = typeof value === "string" ? value : "";
2148
- return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
2149
- },
2150
- drawio: (value, attrs) => {
2151
- const src = typeof value === "string" ? value : "";
2152
- const altVal = attrs?.alt;
2153
- const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
2154
- return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
2155
- },
2156
- "footnote-ref": (value) => {
2157
- const id = typeof value === "string" ? value : String(value);
2158
- return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
2159
- },
2160
- divider: () => "<hr>",
2161
- // Soft line break (Shift+Enter equivalent). Emitted with an explicit
2162
- // `data-scrider-embed` marker so that html-to-delta can distinguish this
2163
- // embed from the placeholder `<br>` that appears inside an empty
2164
- // paragraph (`<p><br></p>`) without relying solely on positional
2165
- // heuristics. See `soft-break.ts` for the format definition.
2166
- softBreak: () => "<br data-scrider-embed>"
2167
- };
2168
- var TAG_TO_INLINE_FORMAT = {
2169
- strong: { format: "bold", value: true },
2170
- b: { format: "bold", value: true },
2171
- em: { format: "italic", value: true },
2172
- i: { format: "italic", value: true },
2173
- u: { format: "underline", value: true },
2174
- ins: { format: "underline", value: true },
2175
- s: { format: "strike", value: true },
2176
- strike: { format: "strike", value: true },
2177
- del: { format: "strike", value: true },
2178
- sub: { format: "subscript", value: true },
2179
- sup: { format: "superscript", value: true },
2180
- code: { format: "code", value: true },
2181
- mark: { format: "mark", value: true },
2182
- kbd: { format: "kbd", value: true }
2183
- };
2184
- var TAG_TO_BLOCK_FORMAT = {
2185
- h1: { format: "header", value: 1 },
2186
- h2: { format: "header", value: 2 },
2187
- h3: { format: "header", value: 3 },
2188
- h4: { format: "header", value: 4 },
2189
- h5: { format: "header", value: 5 },
2190
- h6: { format: "header", value: 6 },
2191
- blockquote: { format: "blockquote", value: true },
2192
- pre: { format: "code-block", value: true }
2163
+ }
2193
2164
  };
2194
- var CSS_ALIGN_TO_FORMAT = {
2195
- left: "left",
2196
- center: "center",
2197
- right: "right",
2198
- justify: "justify"
2165
+
2166
+ // src/schema/formats/inline/mark.ts
2167
+ var markFormat = {
2168
+ name: "mark",
2169
+ scope: "inline",
2170
+ validate(value) {
2171
+ return value === true;
2172
+ }
2199
2173
  };
2200
- function escapeHtml(text) {
2201
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2202
- }
2203
- function unescapeHtml(text) {
2204
- return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&amp;/g, "&");
2205
- }
2206
- function toVideoEmbedUrl(url) {
2207
- if (url.includes("youtube.com/embed") || url.includes("player.vimeo.com") || url.includes("dailymotion.com/embed") || url.includes("video_ext.php") || url.includes("rutube.ru/play/embed")) {
2208
- return url;
2174
+
2175
+ // src/schema/formats/inline/size.ts
2176
+ var sizeFormat = {
2177
+ name: "size",
2178
+ scope: "inline",
2179
+ validate(value) {
2180
+ return typeof value === "string" && value.length > 0;
2209
2181
  }
2210
- const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
2211
- if (ytMatch) {
2212
- return `https://www.youtube.com/embed/${ytMatch[1]}`;
2182
+ };
2183
+
2184
+ // src/schema/formats/inline/strike.ts
2185
+ var strikeFormat = {
2186
+ name: "strike",
2187
+ scope: "inline",
2188
+ validate(value) {
2189
+ return value === true;
2213
2190
  }
2214
- const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
2215
- if (ytShortMatch) {
2216
- return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
2191
+ };
2192
+
2193
+ // src/schema/formats/inline/subscript.ts
2194
+ var subscriptFormat = {
2195
+ name: "subscript",
2196
+ scope: "inline",
2197
+ validate(value) {
2198
+ return value === true;
2217
2199
  }
2218
- const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
2219
- if (rtMatch) {
2220
- return `https://rutube.ru/play/embed/${rtMatch[1]}`;
2200
+ };
2201
+
2202
+ // src/schema/formats/inline/superscript.ts
2203
+ var superscriptFormat = {
2204
+ name: "superscript",
2205
+ scope: "inline",
2206
+ validate(value) {
2207
+ return value === true;
2221
2208
  }
2222
- return null;
2223
- }
2224
- function fromVideoEmbedUrl(embedUrl) {
2225
- const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
2226
- if (ytMatch) {
2227
- return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
2209
+ };
2210
+
2211
+ // src/schema/formats/inline/underline.ts
2212
+ var underlineFormat = {
2213
+ name: "underline",
2214
+ scope: "inline",
2215
+ validate(value) {
2216
+ return value === true;
2228
2217
  }
2229
- const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
2230
- if (rtMatch) {
2231
- return `https://rutube.ru/video/${rtMatch[1]}/`;
2218
+ };
2219
+
2220
+ // src/schema/formats/block/align.ts
2221
+ var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
2222
+ var alignFormat = {
2223
+ name: "align",
2224
+ scope: "block",
2225
+ normalize(value) {
2226
+ return value.toLowerCase();
2227
+ },
2228
+ validate(value) {
2229
+ return VALID_ALIGN_TYPES.includes(value);
2232
2230
  }
2233
- return embedUrl;
2234
- }
2235
- function splitUrl(url) {
2236
- let rest = url;
2237
- let hash = "";
2238
- const hashIdx = rest.indexOf("#");
2239
- if (hashIdx >= 0) {
2240
- hash = rest.slice(hashIdx);
2241
- rest = rest.slice(0, hashIdx);
2231
+ };
2232
+
2233
+ // src/schema/formats/block/blockquote.ts
2234
+ var blockquoteFormat = {
2235
+ name: "blockquote",
2236
+ scope: "block",
2237
+ validate(value) {
2238
+ return value === true;
2242
2239
  }
2243
- let query = "";
2244
- const qIdx = rest.indexOf("?");
2245
- if (qIdx >= 0) {
2246
- query = rest.slice(qIdx);
2247
- rest = rest.slice(0, qIdx);
2240
+ };
2241
+
2242
+ // src/schema/formats/block/code-block.ts
2243
+ var codeBlockFormat = {
2244
+ name: "code-block",
2245
+ scope: "block",
2246
+ normalize(value) {
2247
+ if (typeof value === "string") {
2248
+ return value.toLowerCase().trim();
2249
+ }
2250
+ return value;
2251
+ },
2252
+ validate(value) {
2253
+ if (value === true) {
2254
+ return true;
2255
+ }
2256
+ if (typeof value === "string" && value.length > 0) {
2257
+ return true;
2258
+ }
2259
+ return false;
2248
2260
  }
2249
- return { base: rest, query, hash };
2250
- }
2251
- function hasQueryParam(url, key) {
2252
- const { query } = splitUrl(url);
2253
- return new RegExp(`[?&]${key}=`, "i").test(query);
2254
- }
2255
- function appendQueryParam(url, key, value) {
2256
- const { base, query, hash } = splitUrl(url);
2257
- const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
2258
- return `${base}${next}${hash}`;
2259
- }
2260
- var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
2261
- function renderEmbedIframeIsolationAttrs(context, kind) {
2262
- const opts = context?.embed;
2263
- const parts = [];
2264
- if (kind === "codeWidget" && opts?.crossOriginIsolated) {
2265
- parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
2261
+ };
2262
+
2263
+ // src/schema/formats/block/header.ts
2264
+ var headerFormat = {
2265
+ name: "header",
2266
+ scope: "block",
2267
+ normalize(value) {
2268
+ return Math.max(1, Math.min(6, Math.floor(value)));
2269
+ },
2270
+ validate(value) {
2271
+ return Number.isInteger(value) && value >= 1 && value <= 6;
2266
2272
  }
2267
- if (opts?.credentialless) {
2268
- parts.push("credentialless");
2273
+ };
2274
+
2275
+ // src/schema/formats/block/header-id.ts
2276
+ var headerIdFormat = {
2277
+ name: "header-id",
2278
+ scope: "block",
2279
+ normalize(value) {
2280
+ return String(value).trim().toLowerCase();
2281
+ },
2282
+ validate(value) {
2283
+ if (typeof value !== "string") return false;
2284
+ const trimmed = value.trim();
2285
+ return trimmed.length > 0 && !/\s/.test(trimmed);
2269
2286
  }
2270
- return parts.length ? ` ${parts.join(" ")}` : "";
2271
- }
2272
- function toCodeWidgetEmbedUrl(url) {
2273
- const u = typeof url === "string" ? url.trim() : "";
2274
- if (!u) return "";
2275
- if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
2276
- return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
2287
+ };
2288
+
2289
+ // src/schema/formats/block/indent.ts
2290
+ var MAX_INDENT = 8;
2291
+ var indentFormat = {
2292
+ name: "indent",
2293
+ scope: "block",
2294
+ normalize(value) {
2295
+ return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
2296
+ },
2297
+ validate(value) {
2298
+ return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
2277
2299
  }
2278
- if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
2279
- if (/codesandbox\.io\/embed\//i.test(u)) return u;
2280
- return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
2300
+ };
2301
+
2302
+ // src/schema/formats/block/list.ts
2303
+ var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
2304
+ var listFormat = {
2305
+ name: "list",
2306
+ scope: "block",
2307
+ normalize(value) {
2308
+ return value.toLowerCase();
2309
+ },
2310
+ validate(value) {
2311
+ return VALID_LIST_TYPES.includes(value);
2281
2312
  }
2282
- if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
2283
- return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
2313
+ };
2314
+
2315
+ // src/schema/formats/block/table-row.ts
2316
+ var tableRowFormat = {
2317
+ name: "table-row",
2318
+ scope: "block",
2319
+ validate(value) {
2320
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2284
2321
  }
2285
- if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
2286
- if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
2287
- return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
2322
+ };
2323
+
2324
+ // src/schema/formats/block/table-col.ts
2325
+ var tableColFormat = {
2326
+ name: "table-col",
2327
+ scope: "block",
2328
+ validate(value) {
2329
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2288
2330
  }
2289
- if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
2290
- const { base, query, hash } = splitUrl(u);
2291
- if (/\/embedded(?:\/|$)/i.test(base)) return u;
2292
- const trimmed = base.replace(/\/+$/, "");
2293
- return `${trimmed}/embedded/${query}${hash}`;
2331
+ };
2332
+
2333
+ // src/schema/formats/block/table-header.ts
2334
+ var tableHeaderFormat = {
2335
+ name: "table-header",
2336
+ scope: "block",
2337
+ validate(value) {
2338
+ return value === true;
2294
2339
  }
2295
- if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
2296
- if (/trinket\.io\/embed\//i.test(u)) return u;
2297
- return u.replace(/trinket\.io\//i, "trinket.io/embed/");
2340
+ };
2341
+
2342
+ // src/schema/formats/block/table-col-align.ts
2343
+ var VALID_ALIGNS = ["left", "center", "right"];
2344
+ var tableColAlignFormat = {
2345
+ name: "table-col-align",
2346
+ scope: "block",
2347
+ normalize(value) {
2348
+ return value.toLowerCase();
2349
+ },
2350
+ validate(value) {
2351
+ return VALID_ALIGNS.includes(value);
2298
2352
  }
2299
- if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
2300
- if (/onecompiler\.com\/embed\//i.test(u)) return u;
2301
- return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
2353
+ };
2354
+
2355
+ // src/schema/formats/embed/block.ts
2356
+ var blockFormat = {
2357
+ name: "block",
2358
+ scope: "embed",
2359
+ validate(value) {
2360
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
2302
2361
  }
2303
- return u;
2304
- }
2362
+ };
2305
2363
 
2306
2364
  // src/schema/formats/embed/codeWidget.ts
2307
2365
  var codeWidgetFormat = {
@@ -3856,6 +3914,10 @@ function htmlToDelta(html, options = {}) {
3856
3914
  processBoxElement(node);
3857
3915
  return;
3858
3916
  }
3917
+ if (node.hasAttribute("data-scrider-ext-table")) {
3918
+ processExtTableHostElement(node);
3919
+ return;
3920
+ }
3859
3921
  }
3860
3922
  if (tagName === "p" || tagName === "div") {
3861
3923
  processDefaultBlock(node);
@@ -4199,27 +4261,43 @@ function htmlToDelta(html, options = {}) {
4199
4261
  }
4200
4262
  processChildren(element);
4201
4263
  }
4202
- function processTableElement(table) {
4203
- const tableHandler = options.blockHandlers?.get("table");
4204
- if (tableHandler) {
4205
- const blockContext = {
4206
- registry: void 0,
4207
- // Registry not needed for fromHtml parsing
4208
- parseElement: (el) => {
4209
- const innerHtml = el.innerHTML ?? "";
4210
- if (!innerHtml) return [{ insert: "\n" }];
4211
- return htmlToDelta(innerHtml, options).ops;
4212
- }
4213
- };
4214
- const data = tableHandler.fromHtml(table, blockContext);
4215
- if (data) {
4216
- flushText();
4217
- delta.insert({ block: data });
4218
- delta.insert("\n");
4219
- atLineStart = true;
4220
- return;
4264
+ function processExtTableHostElement(host) {
4265
+ let table = null;
4266
+ const children2 = host.childNodes;
4267
+ for (let i = 0; i < children2.length; i++) {
4268
+ const child = children2[i];
4269
+ if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
4270
+ table = child;
4271
+ break;
4221
4272
  }
4222
4273
  }
4274
+ if (table && insertExtendedTableBlock(table, host)) return;
4275
+ processDefaultBlock(host);
4276
+ }
4277
+ function makeTableBlockParseContext() {
4278
+ return {
4279
+ registry: void 0,
4280
+ parseElement: (el) => {
4281
+ const innerHtml = el.innerHTML ?? "";
4282
+ if (!innerHtml) return [{ insert: "\n" }];
4283
+ return htmlToDelta(innerHtml, options).ops;
4284
+ }
4285
+ };
4286
+ }
4287
+ function insertExtendedTableBlock(table, host) {
4288
+ const tableHandler = options.blockHandlers?.get("table");
4289
+ if (!tableHandler) return false;
4290
+ let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
4291
+ if (!data) return false;
4292
+ if (host) data = enrichTableBlockFromHost(data, host);
4293
+ flushText();
4294
+ delta.insert({ block: data });
4295
+ delta.insert("\n");
4296
+ atLineStart = true;
4297
+ return true;
4298
+ }
4299
+ function processTableElement(table) {
4300
+ if (insertExtendedTableBlock(table, null)) return;
4223
4301
  let rowIdx = 0;
4224
4302
  const tableChildren = table.childNodes;
4225
4303
  for (let i = 0; i < tableChildren.length; i++) {