@scrider/formatter 1.5.0 → 1.6.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.
package/dist/index.d.cts CHANGED
@@ -999,6 +999,25 @@ declare const tableColAlignFormat: Format<TableColAlignType>;
999
999
  */
1000
1000
  declare const blockFormat: Format<Record<string, unknown>>;
1001
1001
 
1002
+ /**
1003
+ * Code Widget embed format (Phase 8 Part 3.5)
1004
+ *
1005
+ * Delta: { insert: { codeWidget: "https://codesandbox.io/s/abc123" } }
1006
+ *
1007
+ * Value is a URL to an interactive code playground (StackBlitz, CodeSandbox,
1008
+ * Replit, CodePen, JSFiddle). Rendered as an <iframe> carrying a
1009
+ * `data-code-widget` marker so it can be told apart from a plain video iframe
1010
+ * during HTML → Delta (see videoFormat.match guard).
1011
+ *
1012
+ * Markdown: ![Widget](url)
1013
+ * HTML: <iframe data-code-widget src="<embed-url>" frameborder="0" allowfullscreen>
1014
+ *
1015
+ * The src is run through `toCodeWidgetEmbedUrl` at render time, which is
1016
+ * idempotent, so resize/float attributes and the Delta ↔ HTML round-trip stay
1017
+ * stable regardless of whether the stored value is the user URL or embed URL.
1018
+ */
1019
+ declare const codeWidgetFormat: Format<string>;
1020
+
1002
1021
  /**
1003
1022
  * Divider (Horizontal Rule) embed format
1004
1023
  *
@@ -1580,6 +1599,26 @@ declare function escapeHtml(text: string): string;
1580
1599
  * Unescape HTML entities
1581
1600
  */
1582
1601
  declare function unescapeHtml(text: string): string;
1602
+ /**
1603
+ * Convert a code-playground URL to an embeddable iframe URL (Phase 8 Part 3.5).
1604
+ *
1605
+ * Idempotent: a URL that is already in embed form is returned unchanged, so the
1606
+ * Delta → HTML → Delta round-trip is stable regardless of which form is stored.
1607
+ *
1608
+ * Provider | User URL | Embed URL
1609
+ * -------------|----------------------------------|-----------------------------------
1610
+ * StackBlitz | stackblitz.com/edit/{id} | …?embed=1
1611
+ * | stackblitz.com/github/{u}/{r} | …?embed=1
1612
+ * CodeSandbox | codesandbox.io/s/{id} | codesandbox.io/embed/{id}
1613
+ * Replit | replit.com/@{u}/{repl} | …?embed=true
1614
+ * CodePen | codepen.io/{u}/pen/{id} | codepen.io/{u}/embed/{id}
1615
+ * JSFiddle | jsfiddle.net/{u}/{id}/ | jsfiddle.net/{u}/{id}/embedded/
1616
+ *
1617
+ * Unknown hosts are returned unchanged (the marker `data-code-widget` still
1618
+ * makes them render as an iframe; auto-detection of bare URLs lives in the
1619
+ * editor layer).
1620
+ */
1621
+ declare function toCodeWidgetEmbedUrl(url: string): string;
1583
1622
 
1584
1623
  /**
1585
1624
  * GitHub-compatible slugify for heading anchor links.
@@ -1944,4 +1983,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1944
1983
  */
1945
1984
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1946
1985
 
1947
- export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1986
+ export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -999,6 +999,25 @@ declare const tableColAlignFormat: Format<TableColAlignType>;
999
999
  */
1000
1000
  declare const blockFormat: Format<Record<string, unknown>>;
1001
1001
 
1002
+ /**
1003
+ * Code Widget embed format (Phase 8 Part 3.5)
1004
+ *
1005
+ * Delta: { insert: { codeWidget: "https://codesandbox.io/s/abc123" } }
1006
+ *
1007
+ * Value is a URL to an interactive code playground (StackBlitz, CodeSandbox,
1008
+ * Replit, CodePen, JSFiddle). Rendered as an <iframe> carrying a
1009
+ * `data-code-widget` marker so it can be told apart from a plain video iframe
1010
+ * during HTML → Delta (see videoFormat.match guard).
1011
+ *
1012
+ * Markdown: ![Widget](url)
1013
+ * HTML: <iframe data-code-widget src="<embed-url>" frameborder="0" allowfullscreen>
1014
+ *
1015
+ * The src is run through `toCodeWidgetEmbedUrl` at render time, which is
1016
+ * idempotent, so resize/float attributes and the Delta ↔ HTML round-trip stay
1017
+ * stable regardless of whether the stored value is the user URL or embed URL.
1018
+ */
1019
+ declare const codeWidgetFormat: Format<string>;
1020
+
1002
1021
  /**
1003
1022
  * Divider (Horizontal Rule) embed format
1004
1023
  *
@@ -1580,6 +1599,26 @@ declare function escapeHtml(text: string): string;
1580
1599
  * Unescape HTML entities
1581
1600
  */
1582
1601
  declare function unescapeHtml(text: string): string;
1602
+ /**
1603
+ * Convert a code-playground URL to an embeddable iframe URL (Phase 8 Part 3.5).
1604
+ *
1605
+ * Idempotent: a URL that is already in embed form is returned unchanged, so the
1606
+ * Delta → HTML → Delta round-trip is stable regardless of which form is stored.
1607
+ *
1608
+ * Provider | User URL | Embed URL
1609
+ * -------------|----------------------------------|-----------------------------------
1610
+ * StackBlitz | stackblitz.com/edit/{id} | …?embed=1
1611
+ * | stackblitz.com/github/{u}/{r} | …?embed=1
1612
+ * CodeSandbox | codesandbox.io/s/{id} | codesandbox.io/embed/{id}
1613
+ * Replit | replit.com/@{u}/{repl} | …?embed=true
1614
+ * CodePen | codepen.io/{u}/pen/{id} | codepen.io/{u}/embed/{id}
1615
+ * JSFiddle | jsfiddle.net/{u}/{id}/ | jsfiddle.net/{u}/{id}/embedded/
1616
+ *
1617
+ * Unknown hosts are returned unchanged (the marker `data-code-widget` still
1618
+ * makes them render as an iframe; auto-detection of bare URLs lives in the
1619
+ * editor layer).
1620
+ */
1621
+ declare function toCodeWidgetEmbedUrl(url: string): string;
1583
1622
 
1584
1623
  /**
1585
1624
  * GitHub-compatible slugify for heading anchor links.
@@ -1944,4 +1983,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1944
1983
  */
1945
1984
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1946
1985
 
1947
- export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1986
+ export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -1897,6 +1897,25 @@ var EMBED_RENDERERS = {
1897
1897
  }
1898
1898
  return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
1899
1899
  },
1900
+ codeWidget: (value, attrs) => {
1901
+ const src = typeof value === "string" ? value : "";
1902
+ const floatVal = attrs?.float;
1903
+ const widthVal = attrs?.width;
1904
+ const heightVal = attrs?.height;
1905
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
1906
+ const styles = [];
1907
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
1908
+ const w = String(widthVal);
1909
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
1910
+ }
1911
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
1912
+ const h = String(heightVal);
1913
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
1914
+ }
1915
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
1916
+ const embedSrc = toCodeWidgetEmbedUrl(src);
1917
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${float}${style}></iframe>`;
1918
+ },
1900
1919
  formula: (value) => {
1901
1920
  const latex = typeof value === "string" ? value : "";
1902
1921
  return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
@@ -1990,6 +2009,124 @@ function fromVideoEmbedUrl(embedUrl) {
1990
2009
  }
1991
2010
  return embedUrl;
1992
2011
  }
2012
+ function splitUrl(url) {
2013
+ let rest = url;
2014
+ let hash = "";
2015
+ const hashIdx = rest.indexOf("#");
2016
+ if (hashIdx >= 0) {
2017
+ hash = rest.slice(hashIdx);
2018
+ rest = rest.slice(0, hashIdx);
2019
+ }
2020
+ let query = "";
2021
+ const qIdx = rest.indexOf("?");
2022
+ if (qIdx >= 0) {
2023
+ query = rest.slice(qIdx);
2024
+ rest = rest.slice(0, qIdx);
2025
+ }
2026
+ return { base: rest, query, hash };
2027
+ }
2028
+ function hasQueryParam(url, key) {
2029
+ const { query } = splitUrl(url);
2030
+ return new RegExp(`[?&]${key}=`, "i").test(query);
2031
+ }
2032
+ function appendQueryParam(url, key, value) {
2033
+ const { base, query, hash } = splitUrl(url);
2034
+ const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
2035
+ return `${base}${next}${hash}`;
2036
+ }
2037
+ function toCodeWidgetEmbedUrl(url) {
2038
+ const u = typeof url === "string" ? url.trim() : "";
2039
+ if (!u) return "";
2040
+ if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
2041
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
2042
+ }
2043
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
2044
+ if (/codesandbox\.io\/embed\//i.test(u)) return u;
2045
+ return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
2046
+ }
2047
+ if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
2048
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
2049
+ }
2050
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
2051
+ if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
2052
+ return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
2053
+ }
2054
+ if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
2055
+ const { base, query, hash } = splitUrl(u);
2056
+ if (/\/embedded(?:\/|$)/i.test(base)) return u;
2057
+ const trimmed = base.replace(/\/+$/, "");
2058
+ return `${trimmed}/embedded/${query}${hash}`;
2059
+ }
2060
+ return u;
2061
+ }
2062
+
2063
+ // src/schema/formats/embed/codeWidget.ts
2064
+ var codeWidgetFormat = {
2065
+ name: "codeWidget",
2066
+ scope: "embed",
2067
+ normalize(value) {
2068
+ return typeof value === "string" ? value.trim() : value;
2069
+ },
2070
+ validate(value) {
2071
+ if (typeof value !== "string" || value.length === 0) {
2072
+ return false;
2073
+ }
2074
+ const trimmed = value.trim();
2075
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2076
+ return true;
2077
+ }
2078
+ if (trimmed.startsWith("//")) {
2079
+ return true;
2080
+ }
2081
+ try {
2082
+ const url = new URL(trimmed);
2083
+ return url.protocol === "http:" || url.protocol === "https:";
2084
+ } catch {
2085
+ return false;
2086
+ }
2087
+ },
2088
+ render(value, attributes) {
2089
+ const src = typeof value === "string" ? value : "";
2090
+ const floatVal = attributes?.float;
2091
+ const widthVal = attributes?.width;
2092
+ const heightVal = attributes?.height;
2093
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2094
+ const styles = [];
2095
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2096
+ const w = String(widthVal);
2097
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2098
+ }
2099
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2100
+ const h = String(heightVal);
2101
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2102
+ }
2103
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2104
+ const embedSrc = toCodeWidgetEmbedUrl(src);
2105
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${float}${style}></iframe>`;
2106
+ },
2107
+ match(element) {
2108
+ if (element.tagName.toLowerCase() !== "iframe") return null;
2109
+ if (element.getAttribute("data-code-widget") === null) return null;
2110
+ const src = element.getAttribute("src");
2111
+ if (!src) return null;
2112
+ const attrs = {};
2113
+ const float = element.getAttribute("data-float");
2114
+ const styleAttr = element.getAttribute("style") || "";
2115
+ if (float) attrs.float = float;
2116
+ const widthMatch = styleAttr.match(/(?:^|;\s*)width:\s*([^;]+)/);
2117
+ if (widthMatch?.[1]) attrs.width = widthMatch[1].trim().replace(/px$/, "");
2118
+ const heightMatch = styleAttr.match(/(?:^|;\s*)height:\s*([^;]+)/);
2119
+ if (heightMatch?.[1]) attrs.height = heightMatch[1].trim().replace(/px$/, "");
2120
+ if (Object.keys(attrs).length > 0) {
2121
+ return { value: src, attributes: attrs };
2122
+ }
2123
+ return { value: src };
2124
+ },
2125
+ toMarkdown(value) {
2126
+ const src = typeof value === "string" ? value : "";
2127
+ return `![Widget](${src})`;
2128
+ }
2129
+ };
1993
2130
 
1994
2131
  // src/schema/formats/embed/divider.ts
1995
2132
  var dividerFormat = {
@@ -2234,6 +2371,7 @@ var videoFormat = {
2234
2371
  return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
2235
2372
  },
2236
2373
  match(element) {
2374
+ if (element.getAttribute("data-code-widget") !== null) return null;
2237
2375
  const tagName = element.tagName.toLowerCase();
2238
2376
  if (tagName !== "video" && tagName !== "iframe") return null;
2239
2377
  const src = element.getAttribute("src");
@@ -2286,6 +2424,7 @@ var defaultBlockFormats = [
2286
2424
  var defaultEmbedFormats = [
2287
2425
  imageFormat,
2288
2426
  videoFormat,
2427
+ codeWidgetFormat,
2289
2428
  formulaFormat,
2290
2429
  dividerFormat,
2291
2430
  softBreakFormat,
@@ -3628,6 +3767,10 @@ function htmlToDelta(html, options = {}) {
3628
3767
  const heightMatch = style.match(/(?:^|;\s*)height:\s*([^;]+)/);
3629
3768
  if (heightMatch?.[1]) attrs.height = heightMatch[1].trim().replace(/px$/, "");
3630
3769
  const embedAttrs = Object.keys(attrs).length > 0 ? attrs : void 0;
3770
+ if (element.getAttribute("data-code-widget") !== null) {
3771
+ context.pushEmbed({ codeWidget: src }, embedAttrs);
3772
+ return;
3773
+ }
3631
3774
  context.pushEmbed({ video: fromVideoEmbedUrl(src) }, embedAttrs);
3632
3775
  }
3633
3776
  function processFootnotesSection(section) {
@@ -4485,6 +4628,28 @@ function renderEmbed2(embed, attributes, customRenderers, useLatexDelimiters = f
4485
4628
  }
4486
4629
  return `![Video](${src})`;
4487
4630
  }
4631
+ if (embedType === "codeWidget") {
4632
+ const src = typeof embedValue === "string" ? embedValue : "";
4633
+ const hasFloat = attributes?.float != null && typeof attributes.float === "string" && attributes.float !== "none";
4634
+ const hasWidth = attributes?.width != null;
4635
+ const hasHeight = attributes?.height != null;
4636
+ if (hasFloat || hasWidth || hasHeight) {
4637
+ const floatAttr = hasFloat ? ` data-float="${escapeHtml(String(attributes.float))}"` : "";
4638
+ const styles = [];
4639
+ if (hasWidth) {
4640
+ const w = typeof attributes.width === "string" || typeof attributes.width === "number" ? String(attributes.width) : "";
4641
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
4642
+ }
4643
+ if (hasHeight) {
4644
+ const h = typeof attributes.height === "string" || typeof attributes.height === "number" ? String(attributes.height) : "";
4645
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
4646
+ }
4647
+ const styleAttr = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
4648
+ const embedSrc = toCodeWidgetEmbedUrl(src);
4649
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${floatAttr}${styleAttr}></iframe>`;
4650
+ }
4651
+ return `![Widget](${src})`;
4652
+ }
4488
4653
  if (embedType === "formula") {
4489
4654
  const latex = typeof embedValue === "string" ? embedValue : "";
4490
4655
  return useLatexDelimiters ? `\\(${latex}\\)` : `$${latex}$`;
@@ -4951,6 +5116,10 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4951
5116
  context.pushEmbed({ video: url });
4952
5117
  return;
4953
5118
  }
5119
+ if (alt.toLowerCase() === "widget") {
5120
+ context.pushEmbed({ codeWidget: url });
5121
+ return;
5122
+ }
4954
5123
  const attrs = {};
4955
5124
  if (node.alt) attrs.alt = node.alt;
4956
5125
  if (url.toLowerCase().endsWith(".drawio")) {
@@ -5317,6 +5486,7 @@ export {
5317
5486
  cloneDelta,
5318
5487
  codeBlockFormat,
5319
5488
  codeFormat,
5489
+ codeWidgetFormat,
5320
5490
  colorFormat,
5321
5491
  columnsBlockHandler,
5322
5492
  createDefaultBlockHandlers,
@@ -5380,6 +5550,7 @@ export {
5380
5550
  tableColFormat,
5381
5551
  tableHeaderFormat,
5382
5552
  tableRowFormat,
5553
+ toCodeWidgetEmbedUrl,
5383
5554
  toHexColor,
5384
5555
  underlineFormat,
5385
5556
  unescapeHtml,