@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.cjs CHANGED
@@ -60,6 +60,7 @@ __export(index_exports, {
60
60
  cloneDelta: () => cloneDelta,
61
61
  codeBlockFormat: () => codeBlockFormat,
62
62
  codeFormat: () => codeFormat,
63
+ codeWidgetFormat: () => codeWidgetFormat,
63
64
  colorFormat: () => colorFormat,
64
65
  columnsBlockHandler: () => columnsBlockHandler,
65
66
  createDefaultBlockHandlers: () => createDefaultBlockHandlers,
@@ -123,6 +124,7 @@ __export(index_exports, {
123
124
  tableColFormat: () => tableColFormat,
124
125
  tableHeaderFormat: () => tableHeaderFormat,
125
126
  tableRowFormat: () => tableRowFormat,
127
+ toCodeWidgetEmbedUrl: () => toCodeWidgetEmbedUrl,
126
128
  toHexColor: () => toHexColor,
127
129
  underlineFormat: () => underlineFormat,
128
130
  unescapeHtml: () => unescapeHtml,
@@ -2021,6 +2023,25 @@ var EMBED_RENDERERS = {
2021
2023
  }
2022
2024
  return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
2023
2025
  },
2026
+ codeWidget: (value, attrs) => {
2027
+ const src = typeof value === "string" ? value : "";
2028
+ const floatVal = attrs?.float;
2029
+ const widthVal = attrs?.width;
2030
+ const heightVal = attrs?.height;
2031
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2032
+ const styles = [];
2033
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2034
+ const w = String(widthVal);
2035
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2036
+ }
2037
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2038
+ const h = String(heightVal);
2039
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2040
+ }
2041
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2042
+ const embedSrc = toCodeWidgetEmbedUrl(src);
2043
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${float}${style}></iframe>`;
2044
+ },
2024
2045
  formula: (value) => {
2025
2046
  const latex = typeof value === "string" ? value : "";
2026
2047
  return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
@@ -2114,6 +2135,124 @@ function fromVideoEmbedUrl(embedUrl) {
2114
2135
  }
2115
2136
  return embedUrl;
2116
2137
  }
2138
+ function splitUrl(url) {
2139
+ let rest = url;
2140
+ let hash = "";
2141
+ const hashIdx = rest.indexOf("#");
2142
+ if (hashIdx >= 0) {
2143
+ hash = rest.slice(hashIdx);
2144
+ rest = rest.slice(0, hashIdx);
2145
+ }
2146
+ let query = "";
2147
+ const qIdx = rest.indexOf("?");
2148
+ if (qIdx >= 0) {
2149
+ query = rest.slice(qIdx);
2150
+ rest = rest.slice(0, qIdx);
2151
+ }
2152
+ return { base: rest, query, hash };
2153
+ }
2154
+ function hasQueryParam(url, key) {
2155
+ const { query } = splitUrl(url);
2156
+ return new RegExp(`[?&]${key}=`, "i").test(query);
2157
+ }
2158
+ function appendQueryParam(url, key, value) {
2159
+ const { base, query, hash } = splitUrl(url);
2160
+ const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
2161
+ return `${base}${next}${hash}`;
2162
+ }
2163
+ function toCodeWidgetEmbedUrl(url) {
2164
+ const u = typeof url === "string" ? url.trim() : "";
2165
+ if (!u) return "";
2166
+ if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
2167
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
2168
+ }
2169
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
2170
+ if (/codesandbox\.io\/embed\//i.test(u)) return u;
2171
+ return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
2172
+ }
2173
+ if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
2174
+ return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
2175
+ }
2176
+ if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
2177
+ if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
2178
+ return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
2179
+ }
2180
+ if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
2181
+ const { base, query, hash } = splitUrl(u);
2182
+ if (/\/embedded(?:\/|$)/i.test(base)) return u;
2183
+ const trimmed = base.replace(/\/+$/, "");
2184
+ return `${trimmed}/embedded/${query}${hash}`;
2185
+ }
2186
+ return u;
2187
+ }
2188
+
2189
+ // src/schema/formats/embed/codeWidget.ts
2190
+ var codeWidgetFormat = {
2191
+ name: "codeWidget",
2192
+ scope: "embed",
2193
+ normalize(value) {
2194
+ return typeof value === "string" ? value.trim() : value;
2195
+ },
2196
+ validate(value) {
2197
+ if (typeof value !== "string" || value.length === 0) {
2198
+ return false;
2199
+ }
2200
+ const trimmed = value.trim();
2201
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
2202
+ return true;
2203
+ }
2204
+ if (trimmed.startsWith("//")) {
2205
+ return true;
2206
+ }
2207
+ try {
2208
+ const url = new URL(trimmed);
2209
+ return url.protocol === "http:" || url.protocol === "https:";
2210
+ } catch {
2211
+ return false;
2212
+ }
2213
+ },
2214
+ render(value, attributes) {
2215
+ const src = typeof value === "string" ? value : "";
2216
+ const floatVal = attributes?.float;
2217
+ const widthVal = attributes?.width;
2218
+ const heightVal = attributes?.height;
2219
+ const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
2220
+ const styles = [];
2221
+ if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
2222
+ const w = String(widthVal);
2223
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
2224
+ }
2225
+ if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
2226
+ const h = String(heightVal);
2227
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
2228
+ }
2229
+ const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
2230
+ const embedSrc = toCodeWidgetEmbedUrl(src);
2231
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${float}${style}></iframe>`;
2232
+ },
2233
+ match(element) {
2234
+ if (element.tagName.toLowerCase() !== "iframe") return null;
2235
+ if (element.getAttribute("data-code-widget") === null) return null;
2236
+ const src = element.getAttribute("src");
2237
+ if (!src) return null;
2238
+ const attrs = {};
2239
+ const float = element.getAttribute("data-float");
2240
+ const styleAttr = element.getAttribute("style") || "";
2241
+ if (float) attrs.float = float;
2242
+ const widthMatch = styleAttr.match(/(?:^|;\s*)width:\s*([^;]+)/);
2243
+ if (widthMatch?.[1]) attrs.width = widthMatch[1].trim().replace(/px$/, "");
2244
+ const heightMatch = styleAttr.match(/(?:^|;\s*)height:\s*([^;]+)/);
2245
+ if (heightMatch?.[1]) attrs.height = heightMatch[1].trim().replace(/px$/, "");
2246
+ if (Object.keys(attrs).length > 0) {
2247
+ return { value: src, attributes: attrs };
2248
+ }
2249
+ return { value: src };
2250
+ },
2251
+ toMarkdown(value) {
2252
+ const src = typeof value === "string" ? value : "";
2253
+ return `![Widget](${src})`;
2254
+ }
2255
+ };
2117
2256
 
2118
2257
  // src/schema/formats/embed/divider.ts
2119
2258
  var dividerFormat = {
@@ -2358,6 +2497,7 @@ var videoFormat = {
2358
2497
  return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
2359
2498
  },
2360
2499
  match(element) {
2500
+ if (element.getAttribute("data-code-widget") !== null) return null;
2361
2501
  const tagName = element.tagName.toLowerCase();
2362
2502
  if (tagName !== "video" && tagName !== "iframe") return null;
2363
2503
  const src = element.getAttribute("src");
@@ -2410,6 +2550,7 @@ var defaultBlockFormats = [
2410
2550
  var defaultEmbedFormats = [
2411
2551
  imageFormat,
2412
2552
  videoFormat,
2553
+ codeWidgetFormat,
2413
2554
  formulaFormat,
2414
2555
  dividerFormat,
2415
2556
  softBreakFormat,
@@ -3752,6 +3893,10 @@ function htmlToDelta(html, options = {}) {
3752
3893
  const heightMatch = style.match(/(?:^|;\s*)height:\s*([^;]+)/);
3753
3894
  if (heightMatch?.[1]) attrs.height = heightMatch[1].trim().replace(/px$/, "");
3754
3895
  const embedAttrs = Object.keys(attrs).length > 0 ? attrs : void 0;
3896
+ if (element.getAttribute("data-code-widget") !== null) {
3897
+ context.pushEmbed({ codeWidget: src }, embedAttrs);
3898
+ return;
3899
+ }
3755
3900
  context.pushEmbed({ video: fromVideoEmbedUrl(src) }, embedAttrs);
3756
3901
  }
3757
3902
  function processFootnotesSection(section) {
@@ -4609,6 +4754,28 @@ function renderEmbed2(embed, attributes, customRenderers, useLatexDelimiters = f
4609
4754
  }
4610
4755
  return `![Video](${src})`;
4611
4756
  }
4757
+ if (embedType === "codeWidget") {
4758
+ const src = typeof embedValue === "string" ? embedValue : "";
4759
+ const hasFloat = attributes?.float != null && typeof attributes.float === "string" && attributes.float !== "none";
4760
+ const hasWidth = attributes?.width != null;
4761
+ const hasHeight = attributes?.height != null;
4762
+ if (hasFloat || hasWidth || hasHeight) {
4763
+ const floatAttr = hasFloat ? ` data-float="${escapeHtml(String(attributes.float))}"` : "";
4764
+ const styles = [];
4765
+ if (hasWidth) {
4766
+ const w = typeof attributes.width === "string" || typeof attributes.width === "number" ? String(attributes.width) : "";
4767
+ if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
4768
+ }
4769
+ if (hasHeight) {
4770
+ const h = typeof attributes.height === "string" || typeof attributes.height === "number" ? String(attributes.height) : "";
4771
+ if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
4772
+ }
4773
+ const styleAttr = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
4774
+ const embedSrc = toCodeWidgetEmbedUrl(src);
4775
+ return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${floatAttr}${styleAttr}></iframe>`;
4776
+ }
4777
+ return `![Widget](${src})`;
4778
+ }
4612
4779
  if (embedType === "formula") {
4613
4780
  const latex = typeof embedValue === "string" ? embedValue : "";
4614
4781
  return useLatexDelimiters ? `\\(${latex}\\)` : `$${latex}$`;
@@ -5075,6 +5242,10 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
5075
5242
  context.pushEmbed({ video: url });
5076
5243
  return;
5077
5244
  }
5245
+ if (alt.toLowerCase() === "widget") {
5246
+ context.pushEmbed({ codeWidget: url });
5247
+ return;
5248
+ }
5078
5249
  const attrs = {};
5079
5250
  if (node.alt) attrs.alt = node.alt;
5080
5251
  if (url.toLowerCase().endsWith(".drawio")) {
@@ -5442,6 +5613,7 @@ function extractTableRegion(ops, hintOpIdx) {
5442
5613
  cloneDelta,
5443
5614
  codeBlockFormat,
5444
5615
  codeFormat,
5616
+ codeWidgetFormat,
5445
5617
  colorFormat,
5446
5618
  columnsBlockHandler,
5447
5619
  createDefaultBlockHandlers,
@@ -5505,6 +5677,7 @@ function extractTableRegion(ops, hintOpIdx) {
5505
5677
  tableColFormat,
5506
5678
  tableHeaderFormat,
5507
5679
  tableRowFormat,
5680
+ toCodeWidgetEmbedUrl,
5508
5681
  toHexColor,
5509
5682
  underlineFormat,
5510
5683
  unescapeHtml,