@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.cjs CHANGED
@@ -491,7 +491,272 @@ function isTextNode(node) {
491
491
  return node.nodeType === NODE_TYPE.TEXT_NODE;
492
492
  }
493
493
 
494
+ // src/conversion/html/config.ts
495
+ var INLINE_FORMAT_TAGS = {
496
+ link: "a",
497
+ bold: "strong",
498
+ italic: "em",
499
+ underline: "u",
500
+ strike: "s",
501
+ subscript: "sub",
502
+ superscript: "sup",
503
+ code: "code",
504
+ mark: "mark",
505
+ kbd: "kbd"
506
+ };
507
+ var INLINE_FORMAT_ORDER = [
508
+ "link",
509
+ "bold",
510
+ "italic",
511
+ "underline",
512
+ "strike",
513
+ "subscript",
514
+ "superscript",
515
+ "code",
516
+ "mark",
517
+ "kbd"
518
+ ];
519
+ var INLINE_STYLE_FORMATS = {
520
+ color: "color",
521
+ background: "background-color",
522
+ font: "font-family",
523
+ size: "font-size"
524
+ };
525
+ var BLOCK_FORMAT_TAGS = {
526
+ header: (value) => `h${String(value)}`,
527
+ blockquote: "blockquote",
528
+ "code-block": "pre",
529
+ list: "li"
530
+ // Wrapped in ul/ol based on list type
531
+ };
532
+ var LIST_WRAPPER_TAGS = {
533
+ ordered: "ol",
534
+ bullet: "ul",
535
+ checked: "ul",
536
+ unchecked: "ul"
537
+ };
538
+ var EMBED_RENDERERS = {
539
+ image: (value, attrs) => {
540
+ const src = typeof value === "string" ? value : "";
541
+ const altVal = attrs?.alt;
542
+ const widthVal = attrs?.width;
543
+ const heightVal = attrs?.height;
544
+ const floatVal = attrs?.float;
545
+ const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
546
+ const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
547
+ const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
548
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
549
+ return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
550
+ },
551
+ video: (value, attrs, context) => {
552
+ const src = typeof value === "string" ? value : "";
553
+ const floatVal = attrs?.float;
554
+ const widthVal = attrs?.width;
555
+ const heightVal = attrs?.height;
556
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
557
+ const styles = [];
558
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
559
+ const w = String(widthVal);
560
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
561
+ }
562
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
563
+ const h = String(heightVal);
564
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
565
+ }
566
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
567
+ const embedSrc = toVideoEmbedUrl(src);
568
+ if (embedSrc) {
569
+ return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
570
+ }
571
+ return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
572
+ },
573
+ codeWidget: (value, attrs, context) => {
574
+ const src = typeof value === "string" ? value : "";
575
+ const floatVal = attrs?.float;
576
+ const widthVal = attrs?.width;
577
+ const heightVal = attrs?.height;
578
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
579
+ const styles = [];
580
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
581
+ const w = String(widthVal);
582
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
583
+ }
584
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
585
+ const h = String(heightVal);
586
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
587
+ }
588
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
589
+ const embedSrc = toCodeWidgetEmbedUrl(src);
590
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
591
+ },
592
+ formula: (value) => {
593
+ const latex = typeof value === "string" ? value : "";
594
+ return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
595
+ },
596
+ diagram: (value) => {
597
+ const source = typeof value === "string" ? value : "";
598
+ return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
599
+ },
600
+ drawio: (value, attrs) => {
601
+ const src = typeof value === "string" ? value : "";
602
+ const altVal = attrs?.alt;
603
+ const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
604
+ return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
605
+ },
606
+ "footnote-ref": (value) => {
607
+ const id = typeof value === "string" ? value : String(value);
608
+ return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
609
+ },
610
+ divider: () => "<hr>",
611
+ // Soft line break (Shift+Enter equivalent). Emitted with an explicit
612
+ // `data-scrider-embed` marker so that html-to-delta can distinguish this
613
+ // embed from the placeholder `<br>` that appears inside an empty
614
+ // paragraph (`<p><br></p>`) without relying solely on positional
615
+ // heuristics. See `soft-break.ts` for the format definition.
616
+ softBreak: () => "<br data-scrider-embed>"
617
+ };
618
+ var TAG_TO_INLINE_FORMAT = {
619
+ strong: { format: "bold", value: true },
620
+ b: { format: "bold", value: true },
621
+ em: { format: "italic", value: true },
622
+ i: { format: "italic", value: true },
623
+ u: { format: "underline", value: true },
624
+ ins: { format: "underline", value: true },
625
+ s: { format: "strike", value: true },
626
+ strike: { format: "strike", value: true },
627
+ del: { format: "strike", value: true },
628
+ sub: { format: "subscript", value: true },
629
+ sup: { format: "superscript", value: true },
630
+ code: { format: "code", value: true },
631
+ mark: { format: "mark", value: true },
632
+ kbd: { format: "kbd", value: true }
633
+ };
634
+ var TAG_TO_BLOCK_FORMAT = {
635
+ h1: { format: "header", value: 1 },
636
+ h2: { format: "header", value: 2 },
637
+ h3: { format: "header", value: 3 },
638
+ h4: { format: "header", value: 4 },
639
+ h5: { format: "header", value: 5 },
640
+ h6: { format: "header", value: 6 },
641
+ blockquote: { format: "blockquote", value: true },
642
+ pre: { format: "code-block", value: true }
643
+ };
644
+ var CSS_ALIGN_TO_FORMAT = {
645
+ left: "left",
646
+ center: "center",
647
+ right: "right",
648
+ justify: "justify"
649
+ };
650
+ function escapeHtml(text) {
651
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
652
+ }
653
+ function unescapeHtml(text) {
654
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&amp;/g, "&");
655
+ }
656
+ function toVideoEmbedUrl(url) {
657
+ 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")) {
658
+ return url;
659
+ }
660
+ const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
661
+ if (ytMatch) {
662
+ return `https://www.youtube.com/embed/${ytMatch[1]}`;
663
+ }
664
+ const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
665
+ if (ytShortMatch) {
666
+ return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
667
+ }
668
+ const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
669
+ if (rtMatch) {
670
+ return `https://rutube.ru/play/embed/${rtMatch[1]}`;
671
+ }
672
+ return null;
673
+ }
674
+ function fromVideoEmbedUrl(embedUrl) {
675
+ const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
676
+ if (ytMatch) {
677
+ return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
678
+ }
679
+ const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
680
+ if (rtMatch) {
681
+ return `https://rutube.ru/video/${rtMatch[1]}/`;
682
+ }
683
+ return embedUrl;
684
+ }
685
+ function splitUrl(url) {
686
+ let rest = url;
687
+ let hash = "";
688
+ const hashIdx = rest.indexOf("#");
689
+ if (hashIdx >= 0) {
690
+ hash = rest.slice(hashIdx);
691
+ rest = rest.slice(0, hashIdx);
692
+ }
693
+ let query = "";
694
+ const qIdx = rest.indexOf("?");
695
+ if (qIdx >= 0) {
696
+ query = rest.slice(qIdx);
697
+ rest = rest.slice(0, qIdx);
698
+ }
699
+ return { base: rest, query, hash };
700
+ }
701
+ function hasQueryParam(url, key) {
702
+ const { query } = splitUrl(url);
703
+ return new RegExp(`[?&]${key}=`, "i").test(query);
704
+ }
705
+ function appendQueryParam(url, key, value) {
706
+ const { base, query, hash } = splitUrl(url);
707
+ const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
708
+ return `${base}${next}${hash}`;
709
+ }
710
+ var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
711
+ function renderEmbedIframeIsolationAttrs(context, kind) {
712
+ const opts = context?.embed;
713
+ const parts = [];
714
+ if (kind === "codeWidget" && opts?.crossOriginIsolated) {
715
+ parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
716
+ }
717
+ if (opts?.credentialless) {
718
+ parts.push("credentialless");
719
+ }
720
+ return parts.length ? ` ${parts.join(" ")}` : "";
721
+ }
722
+ function toCodeWidgetEmbedUrl(url) {
723
+ const u = typeof url === "string" ? url.trim() : "";
724
+ if (!u) return "";
725
+ if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
726
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
727
+ }
728
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
729
+ if (/codesandbox\.io\/embed\//i.test(u)) return u;
730
+ return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
731
+ }
732
+ if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
733
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
734
+ }
735
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
736
+ if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
737
+ return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
738
+ }
739
+ if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
740
+ const { base, query, hash } = splitUrl(u);
741
+ if (/\/embedded(?:\/|$)/i.test(base)) return u;
742
+ const trimmed = base.replace(/\/+$/, "");
743
+ return `${trimmed}/embedded/${query}${hash}`;
744
+ }
745
+ if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
746
+ if (/trinket\.io\/embed\//i.test(u)) return u;
747
+ return u.replace(/trinket\.io\//i, "trinket.io/embed/");
748
+ }
749
+ if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
750
+ if (/onecompiler\.com\/embed\//i.test(u)) return u;
751
+ return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
752
+ }
753
+ return u;
754
+ }
755
+
494
756
  // src/schema/blocks/table.ts
757
+ var EXT_TABLE_HOST_ATTR = "data-scrider-ext-table";
758
+ var EXT_TABLE_HOST_CLASS = "scrider-ext-table-host";
759
+ var VALID_TABLE_BLOCK_FLOATS = ["left", "center", "right"];
495
760
  var VALID_CELL_HORIZONTAL_ALIGNS = [
496
761
  "left",
497
762
  "center",
@@ -679,11 +944,57 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
679
944
  html += `${ind(2)}</tr>${nl}`;
680
945
  return html;
681
946
  }
947
+ function parseTableBlockFloat(value) {
948
+ if (value === "left" || value === "center" || value === "right") return value;
949
+ return void 0;
950
+ }
951
+ function extractHostBlockWidthPx(host) {
952
+ const style = host.getAttribute("style") || "";
953
+ const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
954
+ if (!widthMatch?.[1]) return void 0;
955
+ const n = parseFloat(widthMatch[1]);
956
+ if (!Number.isFinite(n) || n <= 0) return void 0;
957
+ return Math.round(n);
958
+ }
959
+ function enrichTableBlockFromHost(data, host) {
960
+ const float = parseTableBlockFloat(host.getAttribute("data-float"));
961
+ const width = extractHostBlockWidthPx(host);
962
+ if (float == null && width == null) return data;
963
+ const next = { ...data };
964
+ if (float != null) next.float = float;
965
+ if (width != null) next.width = width;
966
+ return next;
967
+ }
968
+ function tableNeedsHostWrapper(data) {
969
+ return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
970
+ }
971
+ function renderTableHostWrapperOpen(data, pretty) {
972
+ const nl = pretty ? "\n" : "";
973
+ const ind = pretty ? " " : "";
974
+ const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
975
+ if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
976
+ const styleParts = [];
977
+ if (data.width != null && data.width > 0) {
978
+ styleParts.push(`width: ${Math.round(data.width)}px`);
979
+ styleParts.push("max-width: 100%");
980
+ }
981
+ if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
982
+ return `<div ${attrs.join(" ")}>${nl}${ind}`;
983
+ }
984
+ function renderTableHostWrapperClose(pretty) {
985
+ return pretty ? "</div>\n" : "</div>";
986
+ }
682
987
  function isGfmCompatible(data) {
683
988
  if (data.colWidths && data.colWidths.some((w) => w > 0)) {
684
989
  return false;
685
990
  }
686
- if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
991
+ if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
992
+ return false;
993
+ }
994
+ if (data.width != null && data.width > 0) {
995
+ return false;
996
+ }
997
+ if (data.float != null) {
687
998
  return false;
688
999
  }
689
1000
  for (const cell of Object.values(data.cells)) {
@@ -1038,6 +1349,12 @@ var tableBlockHandler = {
1038
1349
  if (typeof h !== "number" || h < 0) return false;
1039
1350
  }
1040
1351
  }
1352
+ if (data.width !== void 0) {
1353
+ if (typeof data.width !== "number" || data.width <= 0) return false;
1354
+ }
1355
+ if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
1356
+ return false;
1357
+ }
1041
1358
  if (data.colAligns !== void 0) {
1042
1359
  if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
1043
1360
  return false;
@@ -1089,6 +1406,9 @@ var tableBlockHandler = {
1089
1406
  html += `${ind(1)}</tbody>${nl}`;
1090
1407
  }
1091
1408
  html += `</table>`;
1409
+ if (tableNeedsHostWrapper(data)) {
1410
+ return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
1411
+ }
1092
1412
  return html;
1093
1413
  },
1094
1414
  fromHtml(element, context) {
@@ -1894,544 +2214,282 @@ var boldFormat = {
1894
2214
  return value === true;
1895
2215
  }
1896
2216
  };
1897
-
1898
- // src/schema/formats/inline/code.ts
1899
- var codeFormat = {
1900
- name: "code",
1901
- scope: "inline",
1902
- validate(value) {
1903
- return value === true;
1904
- }
1905
- };
1906
-
1907
- // src/schema/formats/inline/color.ts
1908
- var colorFormat = {
1909
- name: "color",
1910
- scope: "inline",
1911
- normalize(value) {
1912
- return toHexColor(value);
1913
- },
1914
- validate(value) {
1915
- return typeof value === "string" && isValidColor(value);
1916
- }
1917
- };
1918
-
1919
- // src/schema/formats/inline/font.ts
1920
- var fontFormat = {
1921
- name: "font",
1922
- scope: "inline",
1923
- validate(value) {
1924
- return typeof value === "string" && value.length > 0;
1925
- }
1926
- };
1927
-
1928
- // src/schema/formats/inline/italic.ts
1929
- var italicFormat = {
1930
- name: "italic",
1931
- scope: "inline",
1932
- validate(value) {
1933
- return value === true;
1934
- }
1935
- };
1936
-
1937
- // src/schema/formats/inline/kbd.ts
1938
- var kbdFormat = {
1939
- name: "kbd",
1940
- scope: "inline",
1941
- validate(value) {
1942
- return value === true;
1943
- }
1944
- };
1945
-
1946
- // src/schema/formats/inline/link.ts
1947
- var linkFormat = {
1948
- name: "link",
1949
- scope: "inline",
1950
- normalize(value) {
1951
- return value.trim();
1952
- },
1953
- validate(value) {
1954
- if (typeof value !== "string" || value.length === 0) {
1955
- return false;
1956
- }
1957
- const trimmed = value.trim();
1958
- if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
1959
- return true;
1960
- }
1961
- if (trimmed.startsWith("//")) {
1962
- return true;
1963
- }
1964
- if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
1965
- return true;
1966
- }
1967
- try {
1968
- const url = new URL(trimmed);
1969
- return url.protocol === "http:" || url.protocol === "https:";
1970
- } catch {
1971
- return false;
1972
- }
1973
- }
1974
- };
1975
-
1976
- // src/schema/formats/inline/mark.ts
1977
- var markFormat = {
1978
- name: "mark",
1979
- scope: "inline",
1980
- validate(value) {
1981
- return value === true;
1982
- }
1983
- };
1984
-
1985
- // src/schema/formats/inline/size.ts
1986
- var sizeFormat = {
1987
- name: "size",
1988
- scope: "inline",
1989
- validate(value) {
1990
- return typeof value === "string" && value.length > 0;
1991
- }
1992
- };
1993
-
1994
- // src/schema/formats/inline/strike.ts
1995
- var strikeFormat = {
1996
- name: "strike",
1997
- scope: "inline",
1998
- validate(value) {
1999
- return value === true;
2000
- }
2001
- };
2002
-
2003
- // src/schema/formats/inline/subscript.ts
2004
- var subscriptFormat = {
2005
- name: "subscript",
2006
- scope: "inline",
2007
- validate(value) {
2008
- return value === true;
2009
- }
2010
- };
2011
-
2012
- // src/schema/formats/inline/superscript.ts
2013
- var superscriptFormat = {
2014
- name: "superscript",
2015
- scope: "inline",
2016
- validate(value) {
2017
- return value === true;
2018
- }
2019
- };
2020
-
2021
- // src/schema/formats/inline/underline.ts
2022
- var underlineFormat = {
2023
- name: "underline",
2024
- scope: "inline",
2025
- validate(value) {
2026
- return value === true;
2027
- }
2028
- };
2029
-
2030
- // src/schema/formats/block/align.ts
2031
- var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
2032
- var alignFormat = {
2033
- name: "align",
2034
- scope: "block",
2035
- normalize(value) {
2036
- return value.toLowerCase();
2037
- },
2038
- validate(value) {
2039
- return VALID_ALIGN_TYPES.includes(value);
2040
- }
2041
- };
2042
-
2043
- // src/schema/formats/block/blockquote.ts
2044
- var blockquoteFormat = {
2045
- name: "blockquote",
2046
- scope: "block",
2047
- validate(value) {
2048
- return value === true;
2049
- }
2050
- };
2051
-
2052
- // src/schema/formats/block/code-block.ts
2053
- var codeBlockFormat = {
2054
- name: "code-block",
2055
- scope: "block",
2056
- normalize(value) {
2057
- if (typeof value === "string") {
2058
- return value.toLowerCase().trim();
2059
- }
2060
- return value;
2061
- },
2062
- validate(value) {
2063
- if (value === true) {
2064
- return true;
2065
- }
2066
- if (typeof value === "string" && value.length > 0) {
2067
- return true;
2068
- }
2069
- return false;
2070
- }
2071
- };
2072
-
2073
- // src/schema/formats/block/header.ts
2074
- var headerFormat = {
2075
- name: "header",
2076
- scope: "block",
2077
- normalize(value) {
2078
- return Math.max(1, Math.min(6, Math.floor(value)));
2079
- },
2080
- validate(value) {
2081
- return Number.isInteger(value) && value >= 1 && value <= 6;
2082
- }
2083
- };
2084
-
2085
- // src/schema/formats/block/header-id.ts
2086
- var headerIdFormat = {
2087
- name: "header-id",
2088
- scope: "block",
2089
- normalize(value) {
2090
- return String(value).trim().toLowerCase();
2091
- },
2092
- validate(value) {
2093
- if (typeof value !== "string") return false;
2094
- const trimmed = value.trim();
2095
- return trimmed.length > 0 && !/\s/.test(trimmed);
2096
- }
2097
- };
2098
-
2099
- // src/schema/formats/block/indent.ts
2100
- var MAX_INDENT = 8;
2101
- var indentFormat = {
2102
- name: "indent",
2103
- scope: "block",
2104
- normalize(value) {
2105
- return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
2106
- },
2107
- validate(value) {
2108
- return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
2109
- }
2110
- };
2111
-
2112
- // src/schema/formats/block/list.ts
2113
- var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
2114
- var listFormat = {
2115
- name: "list",
2116
- scope: "block",
2117
- normalize(value) {
2118
- return value.toLowerCase();
2119
- },
2120
- validate(value) {
2121
- return VALID_LIST_TYPES.includes(value);
2122
- }
2123
- };
2124
-
2125
- // src/schema/formats/block/table-row.ts
2126
- var tableRowFormat = {
2127
- name: "table-row",
2128
- scope: "block",
2129
- validate(value) {
2130
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
2131
- }
2132
- };
2133
-
2134
- // src/schema/formats/block/table-col.ts
2135
- var tableColFormat = {
2136
- name: "table-col",
2137
- scope: "block",
2138
- validate(value) {
2139
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
2140
- }
2141
- };
2142
-
2143
- // src/schema/formats/block/table-header.ts
2144
- var tableHeaderFormat = {
2145
- name: "table-header",
2146
- scope: "block",
2217
+
2218
+ // src/schema/formats/inline/code.ts
2219
+ var codeFormat = {
2220
+ name: "code",
2221
+ scope: "inline",
2147
2222
  validate(value) {
2148
2223
  return value === true;
2149
2224
  }
2150
2225
  };
2151
2226
 
2152
- // src/schema/formats/block/table-col-align.ts
2153
- var VALID_ALIGNS = ["left", "center", "right"];
2154
- var tableColAlignFormat = {
2155
- name: "table-col-align",
2156
- scope: "block",
2227
+ // src/schema/formats/inline/color.ts
2228
+ var colorFormat = {
2229
+ name: "color",
2230
+ scope: "inline",
2157
2231
  normalize(value) {
2158
- return value.toLowerCase();
2232
+ return toHexColor(value);
2159
2233
  },
2160
2234
  validate(value) {
2161
- return VALID_ALIGNS.includes(value);
2235
+ return typeof value === "string" && isValidColor(value);
2162
2236
  }
2163
2237
  };
2164
2238
 
2165
- // src/schema/formats/embed/block.ts
2166
- var blockFormat = {
2167
- name: "block",
2168
- scope: "embed",
2239
+ // src/schema/formats/inline/font.ts
2240
+ var fontFormat = {
2241
+ name: "font",
2242
+ scope: "inline",
2169
2243
  validate(value) {
2170
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
2244
+ return typeof value === "string" && value.length > 0;
2171
2245
  }
2172
2246
  };
2173
2247
 
2174
- // src/conversion/html/config.ts
2175
- var INLINE_FORMAT_TAGS = {
2176
- link: "a",
2177
- bold: "strong",
2178
- italic: "em",
2179
- underline: "u",
2180
- strike: "s",
2181
- subscript: "sub",
2182
- superscript: "sup",
2183
- code: "code",
2184
- mark: "mark",
2185
- kbd: "kbd"
2186
- };
2187
- var INLINE_FORMAT_ORDER = [
2188
- "link",
2189
- "bold",
2190
- "italic",
2191
- "underline",
2192
- "strike",
2193
- "subscript",
2194
- "superscript",
2195
- "code",
2196
- "mark",
2197
- "kbd"
2198
- ];
2199
- var INLINE_STYLE_FORMATS = {
2200
- color: "color",
2201
- background: "background-color",
2202
- font: "font-family",
2203
- size: "font-size"
2204
- };
2205
- var BLOCK_FORMAT_TAGS = {
2206
- header: (value) => `h${String(value)}`,
2207
- blockquote: "blockquote",
2208
- "code-block": "pre",
2209
- list: "li"
2210
- // Wrapped in ul/ol based on list type
2248
+ // src/schema/formats/inline/italic.ts
2249
+ var italicFormat = {
2250
+ name: "italic",
2251
+ scope: "inline",
2252
+ validate(value) {
2253
+ return value === true;
2254
+ }
2211
2255
  };
2212
- var LIST_WRAPPER_TAGS = {
2213
- ordered: "ol",
2214
- bullet: "ul",
2215
- checked: "ul",
2216
- unchecked: "ul"
2256
+
2257
+ // src/schema/formats/inline/kbd.ts
2258
+ var kbdFormat = {
2259
+ name: "kbd",
2260
+ scope: "inline",
2261
+ validate(value) {
2262
+ return value === true;
2263
+ }
2217
2264
  };
2218
- var EMBED_RENDERERS = {
2219
- image: (value, attrs) => {
2220
- const src = typeof value === "string" ? value : "";
2221
- const altVal = attrs?.alt;
2222
- const widthVal = attrs?.width;
2223
- const heightVal = attrs?.height;
2224
- const floatVal = attrs?.float;
2225
- const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
2226
- const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
2227
- const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
2228
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2229
- return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
2265
+
2266
+ // src/schema/formats/inline/link.ts
2267
+ var linkFormat = {
2268
+ name: "link",
2269
+ scope: "inline",
2270
+ normalize(value) {
2271
+ return value.trim();
2230
2272
  },
2231
- video: (value, attrs, context) => {
2232
- const src = typeof value === "string" ? value : "";
2233
- const floatVal = attrs?.float;
2234
- const widthVal = attrs?.width;
2235
- const heightVal = attrs?.height;
2236
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2237
- const styles = [];
2238
- if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2239
- const w = String(widthVal);
2240
- if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2273
+ validate(value) {
2274
+ if (typeof value !== "string" || value.length === 0) {
2275
+ return false;
2241
2276
  }
2242
- if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2243
- const h = String(heightVal);
2244
- if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2277
+ const trimmed = value.trim();
2278
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2279
+ return true;
2245
2280
  }
2246
- const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2247
- const embedSrc = toVideoEmbedUrl(src);
2248
- if (embedSrc) {
2249
- return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
2281
+ if (trimmed.startsWith("//")) {
2282
+ return true;
2250
2283
  }
2251
- return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
2252
- },
2253
- codeWidget: (value, attrs, context) => {
2254
- const src = typeof value === "string" ? value : "";
2255
- const floatVal = attrs?.float;
2256
- const widthVal = attrs?.width;
2257
- const heightVal = attrs?.height;
2258
- const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2259
- const styles = [];
2260
- if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2261
- const w = String(widthVal);
2262
- if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2284
+ if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
2285
+ return true;
2263
2286
  }
2264
- if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2265
- const h = String(heightVal);
2266
- if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2287
+ try {
2288
+ const url = new URL(trimmed);
2289
+ return url.protocol === "http:" || url.protocol === "https:";
2290
+ } catch {
2291
+ return false;
2267
2292
  }
2268
- const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2269
- const embedSrc = toCodeWidgetEmbedUrl(src);
2270
- return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
2271
- },
2272
- formula: (value) => {
2273
- const latex = typeof value === "string" ? value : "";
2274
- return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
2275
- },
2276
- diagram: (value) => {
2277
- const source = typeof value === "string" ? value : "";
2278
- return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
2279
- },
2280
- drawio: (value, attrs) => {
2281
- const src = typeof value === "string" ? value : "";
2282
- const altVal = attrs?.alt;
2283
- const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
2284
- return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
2285
- },
2286
- "footnote-ref": (value) => {
2287
- const id = typeof value === "string" ? value : String(value);
2288
- return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
2289
- },
2290
- divider: () => "<hr>",
2291
- // Soft line break (Shift+Enter equivalent). Emitted with an explicit
2292
- // `data-scrider-embed` marker so that html-to-delta can distinguish this
2293
- // embed from the placeholder `<br>` that appears inside an empty
2294
- // paragraph (`<p><br></p>`) without relying solely on positional
2295
- // heuristics. See `soft-break.ts` for the format definition.
2296
- softBreak: () => "<br data-scrider-embed>"
2297
- };
2298
- var TAG_TO_INLINE_FORMAT = {
2299
- strong: { format: "bold", value: true },
2300
- b: { format: "bold", value: true },
2301
- em: { format: "italic", value: true },
2302
- i: { format: "italic", value: true },
2303
- u: { format: "underline", value: true },
2304
- ins: { format: "underline", value: true },
2305
- s: { format: "strike", value: true },
2306
- strike: { format: "strike", value: true },
2307
- del: { format: "strike", value: true },
2308
- sub: { format: "subscript", value: true },
2309
- sup: { format: "superscript", value: true },
2310
- code: { format: "code", value: true },
2311
- mark: { format: "mark", value: true },
2312
- kbd: { format: "kbd", value: true }
2313
- };
2314
- var TAG_TO_BLOCK_FORMAT = {
2315
- h1: { format: "header", value: 1 },
2316
- h2: { format: "header", value: 2 },
2317
- h3: { format: "header", value: 3 },
2318
- h4: { format: "header", value: 4 },
2319
- h5: { format: "header", value: 5 },
2320
- h6: { format: "header", value: 6 },
2321
- blockquote: { format: "blockquote", value: true },
2322
- pre: { format: "code-block", value: true }
2293
+ }
2323
2294
  };
2324
- var CSS_ALIGN_TO_FORMAT = {
2325
- left: "left",
2326
- center: "center",
2327
- right: "right",
2328
- justify: "justify"
2295
+
2296
+ // src/schema/formats/inline/mark.ts
2297
+ var markFormat = {
2298
+ name: "mark",
2299
+ scope: "inline",
2300
+ validate(value) {
2301
+ return value === true;
2302
+ }
2329
2303
  };
2330
- function escapeHtml(text) {
2331
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2332
- }
2333
- function unescapeHtml(text) {
2334
- return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&amp;/g, "&");
2335
- }
2336
- function toVideoEmbedUrl(url) {
2337
- 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")) {
2338
- return url;
2304
+
2305
+ // src/schema/formats/inline/size.ts
2306
+ var sizeFormat = {
2307
+ name: "size",
2308
+ scope: "inline",
2309
+ validate(value) {
2310
+ return typeof value === "string" && value.length > 0;
2339
2311
  }
2340
- const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
2341
- if (ytMatch) {
2342
- return `https://www.youtube.com/embed/${ytMatch[1]}`;
2312
+ };
2313
+
2314
+ // src/schema/formats/inline/strike.ts
2315
+ var strikeFormat = {
2316
+ name: "strike",
2317
+ scope: "inline",
2318
+ validate(value) {
2319
+ return value === true;
2343
2320
  }
2344
- const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
2345
- if (ytShortMatch) {
2346
- return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
2321
+ };
2322
+
2323
+ // src/schema/formats/inline/subscript.ts
2324
+ var subscriptFormat = {
2325
+ name: "subscript",
2326
+ scope: "inline",
2327
+ validate(value) {
2328
+ return value === true;
2347
2329
  }
2348
- const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
2349
- if (rtMatch) {
2350
- return `https://rutube.ru/play/embed/${rtMatch[1]}`;
2330
+ };
2331
+
2332
+ // src/schema/formats/inline/superscript.ts
2333
+ var superscriptFormat = {
2334
+ name: "superscript",
2335
+ scope: "inline",
2336
+ validate(value) {
2337
+ return value === true;
2351
2338
  }
2352
- return null;
2353
- }
2354
- function fromVideoEmbedUrl(embedUrl) {
2355
- const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
2356
- if (ytMatch) {
2357
- return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
2339
+ };
2340
+
2341
+ // src/schema/formats/inline/underline.ts
2342
+ var underlineFormat = {
2343
+ name: "underline",
2344
+ scope: "inline",
2345
+ validate(value) {
2346
+ return value === true;
2358
2347
  }
2359
- const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
2360
- if (rtMatch) {
2361
- return `https://rutube.ru/video/${rtMatch[1]}/`;
2348
+ };
2349
+
2350
+ // src/schema/formats/block/align.ts
2351
+ var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
2352
+ var alignFormat = {
2353
+ name: "align",
2354
+ scope: "block",
2355
+ normalize(value) {
2356
+ return value.toLowerCase();
2357
+ },
2358
+ validate(value) {
2359
+ return VALID_ALIGN_TYPES.includes(value);
2362
2360
  }
2363
- return embedUrl;
2364
- }
2365
- function splitUrl(url) {
2366
- let rest = url;
2367
- let hash = "";
2368
- const hashIdx = rest.indexOf("#");
2369
- if (hashIdx >= 0) {
2370
- hash = rest.slice(hashIdx);
2371
- rest = rest.slice(0, hashIdx);
2361
+ };
2362
+
2363
+ // src/schema/formats/block/blockquote.ts
2364
+ var blockquoteFormat = {
2365
+ name: "blockquote",
2366
+ scope: "block",
2367
+ validate(value) {
2368
+ return value === true;
2372
2369
  }
2373
- let query = "";
2374
- const qIdx = rest.indexOf("?");
2375
- if (qIdx >= 0) {
2376
- query = rest.slice(qIdx);
2377
- rest = rest.slice(0, qIdx);
2370
+ };
2371
+
2372
+ // src/schema/formats/block/code-block.ts
2373
+ var codeBlockFormat = {
2374
+ name: "code-block",
2375
+ scope: "block",
2376
+ normalize(value) {
2377
+ if (typeof value === "string") {
2378
+ return value.toLowerCase().trim();
2379
+ }
2380
+ return value;
2381
+ },
2382
+ validate(value) {
2383
+ if (value === true) {
2384
+ return true;
2385
+ }
2386
+ if (typeof value === "string" && value.length > 0) {
2387
+ return true;
2388
+ }
2389
+ return false;
2378
2390
  }
2379
- return { base: rest, query, hash };
2380
- }
2381
- function hasQueryParam(url, key) {
2382
- const { query } = splitUrl(url);
2383
- return new RegExp(`[?&]${key}=`, "i").test(query);
2384
- }
2385
- function appendQueryParam(url, key, value) {
2386
- const { base, query, hash } = splitUrl(url);
2387
- const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
2388
- return `${base}${next}${hash}`;
2389
- }
2390
- var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
2391
- function renderEmbedIframeIsolationAttrs(context, kind) {
2392
- const opts = context?.embed;
2393
- const parts = [];
2394
- if (kind === "codeWidget" && opts?.crossOriginIsolated) {
2395
- parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
2391
+ };
2392
+
2393
+ // src/schema/formats/block/header.ts
2394
+ var headerFormat = {
2395
+ name: "header",
2396
+ scope: "block",
2397
+ normalize(value) {
2398
+ return Math.max(1, Math.min(6, Math.floor(value)));
2399
+ },
2400
+ validate(value) {
2401
+ return Number.isInteger(value) && value >= 1 && value <= 6;
2396
2402
  }
2397
- if (opts?.credentialless) {
2398
- parts.push("credentialless");
2403
+ };
2404
+
2405
+ // src/schema/formats/block/header-id.ts
2406
+ var headerIdFormat = {
2407
+ name: "header-id",
2408
+ scope: "block",
2409
+ normalize(value) {
2410
+ return String(value).trim().toLowerCase();
2411
+ },
2412
+ validate(value) {
2413
+ if (typeof value !== "string") return false;
2414
+ const trimmed = value.trim();
2415
+ return trimmed.length > 0 && !/\s/.test(trimmed);
2399
2416
  }
2400
- return parts.length ? ` ${parts.join(" ")}` : "";
2401
- }
2402
- function toCodeWidgetEmbedUrl(url) {
2403
- const u = typeof url === "string" ? url.trim() : "";
2404
- if (!u) return "";
2405
- if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
2406
- return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
2417
+ };
2418
+
2419
+ // src/schema/formats/block/indent.ts
2420
+ var MAX_INDENT = 8;
2421
+ var indentFormat = {
2422
+ name: "indent",
2423
+ scope: "block",
2424
+ normalize(value) {
2425
+ return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
2426
+ },
2427
+ validate(value) {
2428
+ return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
2407
2429
  }
2408
- if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
2409
- if (/codesandbox\.io\/embed\//i.test(u)) return u;
2410
- return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
2430
+ };
2431
+
2432
+ // src/schema/formats/block/list.ts
2433
+ var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
2434
+ var listFormat = {
2435
+ name: "list",
2436
+ scope: "block",
2437
+ normalize(value) {
2438
+ return value.toLowerCase();
2439
+ },
2440
+ validate(value) {
2441
+ return VALID_LIST_TYPES.includes(value);
2411
2442
  }
2412
- if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
2413
- return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
2443
+ };
2444
+
2445
+ // src/schema/formats/block/table-row.ts
2446
+ var tableRowFormat = {
2447
+ name: "table-row",
2448
+ scope: "block",
2449
+ validate(value) {
2450
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2414
2451
  }
2415
- if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
2416
- if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
2417
- return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
2452
+ };
2453
+
2454
+ // src/schema/formats/block/table-col.ts
2455
+ var tableColFormat = {
2456
+ name: "table-col",
2457
+ scope: "block",
2458
+ validate(value) {
2459
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2418
2460
  }
2419
- if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
2420
- const { base, query, hash } = splitUrl(u);
2421
- if (/\/embedded(?:\/|$)/i.test(base)) return u;
2422
- const trimmed = base.replace(/\/+$/, "");
2423
- return `${trimmed}/embedded/${query}${hash}`;
2461
+ };
2462
+
2463
+ // src/schema/formats/block/table-header.ts
2464
+ var tableHeaderFormat = {
2465
+ name: "table-header",
2466
+ scope: "block",
2467
+ validate(value) {
2468
+ return value === true;
2424
2469
  }
2425
- if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
2426
- if (/trinket\.io\/embed\//i.test(u)) return u;
2427
- return u.replace(/trinket\.io\//i, "trinket.io/embed/");
2470
+ };
2471
+
2472
+ // src/schema/formats/block/table-col-align.ts
2473
+ var VALID_ALIGNS = ["left", "center", "right"];
2474
+ var tableColAlignFormat = {
2475
+ name: "table-col-align",
2476
+ scope: "block",
2477
+ normalize(value) {
2478
+ return value.toLowerCase();
2479
+ },
2480
+ validate(value) {
2481
+ return VALID_ALIGNS.includes(value);
2428
2482
  }
2429
- if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
2430
- if (/onecompiler\.com\/embed\//i.test(u)) return u;
2431
- return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
2483
+ };
2484
+
2485
+ // src/schema/formats/embed/block.ts
2486
+ var blockFormat = {
2487
+ name: "block",
2488
+ scope: "embed",
2489
+ validate(value) {
2490
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
2432
2491
  }
2433
- return u;
2434
- }
2492
+ };
2435
2493
 
2436
2494
  // src/schema/formats/embed/codeWidget.ts
2437
2495
  var codeWidgetFormat = {
@@ -3986,6 +4044,10 @@ function htmlToDelta(html, options = {}) {
3986
4044
  processBoxElement(node);
3987
4045
  return;
3988
4046
  }
4047
+ if (node.hasAttribute("data-scrider-ext-table")) {
4048
+ processExtTableHostElement(node);
4049
+ return;
4050
+ }
3989
4051
  }
3990
4052
  if (tagName === "p" || tagName === "div") {
3991
4053
  processDefaultBlock(node);
@@ -4329,27 +4391,43 @@ function htmlToDelta(html, options = {}) {
4329
4391
  }
4330
4392
  processChildren(element);
4331
4393
  }
4332
- function processTableElement(table) {
4333
- const tableHandler = options.blockHandlers?.get("table");
4334
- if (tableHandler) {
4335
- const blockContext = {
4336
- registry: void 0,
4337
- // Registry not needed for fromHtml parsing
4338
- parseElement: (el) => {
4339
- const innerHtml = el.innerHTML ?? "";
4340
- if (!innerHtml) return [{ insert: "\n" }];
4341
- return htmlToDelta(innerHtml, options).ops;
4342
- }
4343
- };
4344
- const data = tableHandler.fromHtml(table, blockContext);
4345
- if (data) {
4346
- flushText();
4347
- delta.insert({ block: data });
4348
- delta.insert("\n");
4349
- atLineStart = true;
4350
- return;
4394
+ function processExtTableHostElement(host) {
4395
+ let table = null;
4396
+ const children2 = host.childNodes;
4397
+ for (let i = 0; i < children2.length; i++) {
4398
+ const child = children2[i];
4399
+ if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
4400
+ table = child;
4401
+ break;
4351
4402
  }
4352
4403
  }
4404
+ if (table && insertExtendedTableBlock(table, host)) return;
4405
+ processDefaultBlock(host);
4406
+ }
4407
+ function makeTableBlockParseContext() {
4408
+ return {
4409
+ registry: void 0,
4410
+ parseElement: (el) => {
4411
+ const innerHtml = el.innerHTML ?? "";
4412
+ if (!innerHtml) return [{ insert: "\n" }];
4413
+ return htmlToDelta(innerHtml, options).ops;
4414
+ }
4415
+ };
4416
+ }
4417
+ function insertExtendedTableBlock(table, host) {
4418
+ const tableHandler = options.blockHandlers?.get("table");
4419
+ if (!tableHandler) return false;
4420
+ let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
4421
+ if (!data) return false;
4422
+ if (host) data = enrichTableBlockFromHost(data, host);
4423
+ flushText();
4424
+ delta.insert({ block: data });
4425
+ delta.insert("\n");
4426
+ atLineStart = true;
4427
+ return true;
4428
+ }
4429
+ function processTableElement(table) {
4430
+ if (insertExtendedTableBlock(table, null)) return;
4353
4431
  let rowIdx = 0;
4354
4432
  const tableChildren = table.childNodes;
4355
4433
  for (let i = 0; i < tableChildren.length; i++) {