@scrider/formatter 1.7.1 → 1.8.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
@@ -1026,7 +1026,7 @@ declare const blockFormat: Format<Record<string, unknown>>;
1026
1026
  * Delta: { insert: { codeWidget: "https://codesandbox.io/s/abc123" } }
1027
1027
  *
1028
1028
  * Value is a URL to an interactive code playground (StackBlitz, CodeSandbox,
1029
- * Replit, CodePen, JSFiddle). Rendered as an <iframe> carrying a
1029
+ * Replit, CodePen, JSFiddle, Trinket, OneCompiler). Rendered as an <iframe> carrying a
1030
1030
  * `data-code-widget` marker so it can be told apart from a plain video iframe
1031
1031
  * during HTML → Delta (see videoFormat.match guard).
1032
1032
  *
@@ -1656,6 +1656,8 @@ declare function unescapeHtml(text: string): string;
1656
1656
  * Replit | replit.com/@{u}/{repl} | …?embed=true
1657
1657
  * CodePen | codepen.io/{u}/pen/{id} | codepen.io/{u}/embed/{id}
1658
1658
  * JSFiddle | jsfiddle.net/{u}/{id}/ | jsfiddle.net/{u}/{id}/embedded/
1659
+ * Trinket | trinket.io/{lang}/{id} | trinket.io/embed/{lang}/{id}
1660
+ * OneCompiler | onecompiler.com/{lang}/{id} | onecompiler.com/embed/{lang}/{id}
1659
1661
  *
1660
1662
  * Unknown hosts are returned unchanged (the marker `data-code-widget` still
1661
1663
  * makes them render as an iframe; auto-detection of bare URLs lives in the
package/dist/index.d.ts CHANGED
@@ -1026,7 +1026,7 @@ declare const blockFormat: Format<Record<string, unknown>>;
1026
1026
  * Delta: { insert: { codeWidget: "https://codesandbox.io/s/abc123" } }
1027
1027
  *
1028
1028
  * Value is a URL to an interactive code playground (StackBlitz, CodeSandbox,
1029
- * Replit, CodePen, JSFiddle). Rendered as an <iframe> carrying a
1029
+ * Replit, CodePen, JSFiddle, Trinket, OneCompiler). Rendered as an <iframe> carrying a
1030
1030
  * `data-code-widget` marker so it can be told apart from a plain video iframe
1031
1031
  * during HTML → Delta (see videoFormat.match guard).
1032
1032
  *
@@ -1656,6 +1656,8 @@ declare function unescapeHtml(text: string): string;
1656
1656
  * Replit | replit.com/@{u}/{repl} | …?embed=true
1657
1657
  * CodePen | codepen.io/{u}/pen/{id} | codepen.io/{u}/embed/{id}
1658
1658
  * JSFiddle | jsfiddle.net/{u}/{id}/ | jsfiddle.net/{u}/{id}/embedded/
1659
+ * Trinket | trinket.io/{lang}/{id} | trinket.io/embed/{lang}/{id}
1660
+ * OneCompiler | onecompiler.com/{lang}/{id} | onecompiler.com/embed/{lang}/{id}
1659
1661
  *
1660
1662
  * Unknown hosts are returned unchanged (the marker `data-code-widget` still
1661
1663
  * makes them render as an iframe; auto-detection of bare URLs lives in the
package/dist/index.js CHANGED
@@ -333,6 +333,20 @@ function cloneDelta(delta) {
333
333
  return new Delta(deepClone(delta.ops));
334
334
  }
335
335
 
336
+ // src/conversion/markdown/table-header-markdown.ts
337
+ function isHeaderDashPlaceholder(text) {
338
+ const t = text.trim();
339
+ return t === "-" && t.length === 1;
340
+ }
341
+ function normalizeHeaderCellForParse(text, isHeaderRow) {
342
+ if (isHeaderRow && isHeaderDashPlaceholder(text)) return "";
343
+ return text;
344
+ }
345
+ function serializeHeaderCell(text) {
346
+ if (text.trim() === "") return "-";
347
+ return text;
348
+ }
349
+
336
350
  // src/conversion/adapters/types.ts
337
351
  var NODE_TYPE = {
338
352
  ELEMENT_NODE: 1,
@@ -454,19 +468,21 @@ function renderGfmTable(data, context) {
454
468
  if (headerRows === 0) {
455
469
  const emptyParts = [];
456
470
  for (let c = 0; c < cols; c++) {
457
- emptyParts.push(" ");
471
+ emptyParts.push("");
458
472
  }
459
473
  lines.push("| " + emptyParts.join(" | ") + " |");
460
474
  lines.push(renderGfmSeparator(cols, data.colAligns));
461
475
  }
462
476
  for (let r = 0; r < rows; r++) {
463
477
  const parts = [];
478
+ const isHeaderRow = headerRows > 0 && r < headerRows;
464
479
  for (let c = 0; c < cols; c++) {
465
480
  const cell = data.cells[`${r}:${c}`];
466
481
  let text = "";
467
482
  if (cell && context.renderDelta) {
468
483
  text = stripCellContent(context.renderDelta(cell.ops));
469
484
  }
485
+ if (isHeaderRow) text = serializeHeaderCell(text);
470
486
  parts.push(text.replace(/\|/g, "\\|"));
471
487
  }
472
488
  lines.push("| " + parts.join(" | ") + " |");
@@ -2069,6 +2085,14 @@ function toCodeWidgetEmbedUrl(url) {
2069
2085
  const trimmed = base.replace(/\/+$/, "");
2070
2086
  return `${trimmed}/embedded/${query}${hash}`;
2071
2087
  }
2088
+ if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
2089
+ if (/trinket\.io\/embed\//i.test(u)) return u;
2090
+ return u.replace(/trinket\.io\//i, "trinket.io/embed/");
2091
+ }
2092
+ if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
2093
+ if (/onecompiler\.com\/embed\//i.test(u)) return u;
2094
+ return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
2095
+ }
2072
2096
  return u;
2073
2097
  }
2074
2098
 
@@ -4424,7 +4448,15 @@ function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = fa
4424
4448
  if (headerRows.length > 0) {
4425
4449
  for (const [, row] of headerRows) {
4426
4450
  mdLines.push(
4427
- renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
4451
+ renderMdRow(
4452
+ row.cells,
4453
+ maxCol,
4454
+ embedRenderers,
4455
+ useLatexDelimiters,
4456
+ registry,
4457
+ softBreakStyle,
4458
+ true
4459
+ )
4428
4460
  );
4429
4461
  }
4430
4462
  mdLines.push(renderMdSeparator(maxCol, colAligns));
@@ -4434,22 +4466,38 @@ function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = fa
4434
4466
  emptyRow.set(col, { ops: [] });
4435
4467
  }
4436
4468
  mdLines.push(
4437
- renderMdRow(emptyRow, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
4469
+ renderMdRow(
4470
+ emptyRow,
4471
+ maxCol,
4472
+ embedRenderers,
4473
+ useLatexDelimiters,
4474
+ registry,
4475
+ softBreakStyle,
4476
+ false
4477
+ )
4438
4478
  );
4439
4479
  mdLines.push(renderMdSeparator(maxCol, colAligns));
4440
4480
  }
4441
4481
  for (const [, row] of bodyRows) {
4442
4482
  mdLines.push(
4443
- renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
4483
+ renderMdRow(
4484
+ row.cells,
4485
+ maxCol,
4486
+ embedRenderers,
4487
+ useLatexDelimiters,
4488
+ registry,
4489
+ softBreakStyle,
4490
+ false
4491
+ )
4444
4492
  );
4445
4493
  }
4446
4494
  return mdLines.join("\n");
4447
4495
  }
4448
- function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false, registry, softBreakStyle = "spaces") {
4496
+ function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false, registry, softBreakStyle = "spaces", isHeaderRow = false) {
4449
4497
  const parts = [];
4450
4498
  for (let col = 0; col <= maxCol; col++) {
4451
4499
  const cell = cells.get(col);
4452
- const content = cell ? renderLineContent2(
4500
+ let content = cell ? renderLineContent2(
4453
4501
  cell.ops,
4454
4502
  embedRenderers,
4455
4503
  false,
@@ -4461,6 +4509,7 @@ function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false,
4461
4509
  true
4462
4510
  // inTableCell — softBreak must use <br>, never " \n"
4463
4511
  ) : "";
4512
+ if (isHeaderRow) content = serializeHeaderCell(content);
4464
4513
  parts.push(content.replace(/\|/g, "\\|"));
4465
4514
  }
4466
4515
  return "| " + parts.join(" | ") + " |";
@@ -4725,7 +4774,138 @@ function renderBlockFormat(content, attributes, orderedIndex, _strict) {
4725
4774
  }
4726
4775
 
4727
4776
  // src/conversion/markdown/markdown-to-delta.ts
4728
- import { Delta as Delta10 } from "@scrider/delta";
4777
+ import { Delta as Delta10, isInsert as isInsert5 } from "@scrider/delta";
4778
+
4779
+ // src/conversion/markdown/table-header-normalize.ts
4780
+ import { isInsert as isInsert4 } from "@scrider/delta";
4781
+ function isTableCellTerminator(op) {
4782
+ return isInsert4(op) && typeof op.insert === "string" && op.insert === "\n" && op.attributes !== void 0 && typeof op.attributes["table-row"] === "number";
4783
+ }
4784
+ function normalizeSyntheticEmptyHeaderRow(ops) {
4785
+ const ends = [];
4786
+ let buf = "";
4787
+ const textOpIndicesByCell = [];
4788
+ let currentTextOps = [];
4789
+ for (let i = 0; i < ops.length; i++) {
4790
+ const op = ops[i];
4791
+ if (!isInsert4(op)) continue;
4792
+ if (typeof op.insert === "string" && op.insert !== "\n") {
4793
+ buf += op.insert;
4794
+ currentTextOps.push(i);
4795
+ continue;
4796
+ }
4797
+ if (isTableCellTerminator(op)) {
4798
+ const row = op.attributes["table-row"];
4799
+ const col = op.attributes["table-col"];
4800
+ ends.push({ row, col, newlineIdx: i, text: buf });
4801
+ textOpIndicesByCell.push([...currentTextOps]);
4802
+ buf = "";
4803
+ currentTextOps = [];
4804
+ }
4805
+ }
4806
+ const row0 = ends.filter((e) => e.row === 0);
4807
+ if (row0.length === 0) return [...ops];
4808
+ if (!row0.every((e) => e.text.trim() === "")) return [...ops];
4809
+ if (row0.some((e) => isHeaderDashPlaceholder(e.text))) return [...ops];
4810
+ const remove = /* @__PURE__ */ new Set();
4811
+ for (let j = 0; j < ends.length; j++) {
4812
+ const end = ends[j];
4813
+ if (end.row !== 0) continue;
4814
+ remove.add(end.newlineIdx);
4815
+ for (const ti of textOpIndicesByCell[j] ?? []) remove.add(ti);
4816
+ }
4817
+ const out = [];
4818
+ for (let i = 0; i < ops.length; i++) {
4819
+ if (remove.has(i)) continue;
4820
+ const op = ops[i];
4821
+ if (!isTableCellTerminator(op)) {
4822
+ out.push(op);
4823
+ continue;
4824
+ }
4825
+ const row = op.attributes["table-row"];
4826
+ if (row === 0) continue;
4827
+ const attrs = { ...op.attributes };
4828
+ delete attrs["table-header"];
4829
+ out.push({
4830
+ insert: "\n",
4831
+ attributes: { ...attrs, "table-row": row - 1 }
4832
+ });
4833
+ }
4834
+ return out;
4835
+ }
4836
+ function normalizeHeaderDashPlaceholders(ops) {
4837
+ const ends = [];
4838
+ let buf = "";
4839
+ let currentTextOps = [];
4840
+ for (let i = 0; i < ops.length; i++) {
4841
+ const op = ops[i];
4842
+ if (!isInsert4(op)) continue;
4843
+ if (typeof op.insert === "string" && op.insert !== "\n") {
4844
+ buf += op.insert;
4845
+ currentTextOps.push(i);
4846
+ continue;
4847
+ }
4848
+ if (isTableCellTerminator(op)) {
4849
+ const row = op.attributes["table-row"];
4850
+ const isHeader = row === 0 && op.attributes["table-header"] === true;
4851
+ ends.push({ isHeader, textOpIndices: [...currentTextOps], text: buf });
4852
+ buf = "";
4853
+ currentTextOps = [];
4854
+ }
4855
+ }
4856
+ const dashHeaderCells = ends.filter(
4857
+ (e) => e.isHeader && e.textOpIndices.length > 0 && isHeaderDashPlaceholder(e.text)
4858
+ );
4859
+ if (dashHeaderCells.length === 0) return [...ops];
4860
+ const replaceIndices = /* @__PURE__ */ new Set();
4861
+ for (const end of dashHeaderCells) {
4862
+ for (const ti of end.textOpIndices) replaceIndices.add(ti);
4863
+ }
4864
+ const out = [];
4865
+ for (let i = 0; i < ops.length; i++) {
4866
+ const op = ops[i];
4867
+ if (!replaceIndices.has(i)) {
4868
+ out.push(op);
4869
+ continue;
4870
+ }
4871
+ if (!isInsert4(op)) {
4872
+ out.push(op);
4873
+ continue;
4874
+ }
4875
+ if (typeof op.insert === "string" && op.insert !== "\n") {
4876
+ const normalized = normalizeHeaderCellForParse(op.insert, true);
4877
+ if (normalized.length === 0) continue;
4878
+ out.push(
4879
+ op.attributes ? { insert: normalized, attributes: op.attributes } : { insert: normalized }
4880
+ );
4881
+ } else {
4882
+ out.push(op);
4883
+ }
4884
+ }
4885
+ return out;
4886
+ }
4887
+ function normalizeImportedTableOps(ops) {
4888
+ if (ops.length === 0) return [...ops];
4889
+ const ends = [];
4890
+ let buf = "";
4891
+ for (const op of ops) {
4892
+ if (!isInsert4(op)) continue;
4893
+ if (typeof op.insert === "string" && op.insert !== "\n") {
4894
+ buf += op.insert;
4895
+ continue;
4896
+ }
4897
+ if (isTableCellTerminator(op)) {
4898
+ ends.push({ row: op.attributes["table-row"], text: buf });
4899
+ buf = "";
4900
+ }
4901
+ }
4902
+ const row0 = ends.filter((e) => e.row === 0);
4903
+ const headerless = row0.length > 0 && row0.every((e) => e.text.trim() === "") && !row0.some((e) => isHeaderDashPlaceholder(e.text));
4904
+ if (headerless) return normalizeSyntheticEmptyHeaderRow(ops);
4905
+ return normalizeHeaderDashPlaceholders(ops);
4906
+ }
4907
+
4908
+ // src/conversion/markdown/markdown-to-delta.ts
4729
4909
  var remarkParse = null;
4730
4910
  var remarkGfm = null;
4731
4911
  var remarkMath = null;
@@ -5214,6 +5394,7 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
5214
5394
  }
5215
5395
  function processTable(node) {
5216
5396
  if (!node.children) return;
5397
+ const tableStart = delta.ops.length;
5217
5398
  const aligns = node.align || [];
5218
5399
  for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
5219
5400
  const rowNode = node.children[rowIdx];
@@ -5242,6 +5423,15 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
5242
5423
  context.pushNewline(cellBlockAttrs);
5243
5424
  }
5244
5425
  }
5426
+ const tableOps = delta.ops.splice(tableStart);
5427
+ for (const op of normalizeImportedTableOps(tableOps)) {
5428
+ if (!isInsert5(op)) continue;
5429
+ if (op.attributes && Object.keys(op.attributes).length > 0) {
5430
+ delta.insert(op.insert, op.attributes);
5431
+ } else {
5432
+ delta.insert(op.insert);
5433
+ }
5434
+ }
5245
5435
  }
5246
5436
  function isBlockLevelHtml(html) {
5247
5437
  return /^\s*<(div|table|section|article|aside|nav|header|footer|figure|pre|hr|ol|ul|dl|details|iframe|video)\b/i.test(
@@ -5423,9 +5613,9 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
5423
5613
  }
5424
5614
 
5425
5615
  // src/conversion/markdown/table-region.ts
5426
- import { isInsert as isInsert4, isTextInsert as isTextInsert3 } from "@scrider/delta";
5616
+ import { isInsert as isInsert6, isTextInsert as isTextInsert3 } from "@scrider/delta";
5427
5617
  function isTableNewlineOp(op) {
5428
- if (!op || !isInsert4(op) || !isTextInsert3(op)) return false;
5618
+ if (!op || !isInsert6(op) || !isTextInsert3(op)) return false;
5429
5619
  if (!op.insert.includes("\n")) return false;
5430
5620
  return !!op.attributes && "table-row" in op.attributes;
5431
5621
  }
@@ -5434,7 +5624,7 @@ function extractTableRegion(ops, hintOpIdx) {
5434
5624
  let probeIdx = -1;
5435
5625
  for (let i = hintOpIdx; i < ops.length; i++) {
5436
5626
  const op = ops[i];
5437
- if (!op || !isInsert4(op)) continue;
5627
+ if (!op || !isInsert6(op)) continue;
5438
5628
  if (isTextInsert3(op) && op.insert.includes("\n")) {
5439
5629
  probeIdx = i;
5440
5630
  break;
@@ -5445,7 +5635,7 @@ function extractTableRegion(ops, hintOpIdx) {
5445
5635
  let endOpIdx = probeIdx;
5446
5636
  for (let i = probeIdx + 1; i < ops.length; i++) {
5447
5637
  const op = ops[i];
5448
- if (!op || !isInsert4(op)) break;
5638
+ if (!op || !isInsert6(op)) break;
5449
5639
  if (isTextInsert3(op) && op.insert.includes("\n")) {
5450
5640
  if (isTableNewlineOp(op)) {
5451
5641
  endOpIdx = i;
@@ -5457,7 +5647,7 @@ function extractTableRegion(ops, hintOpIdx) {
5457
5647
  let startOpIdx = 0;
5458
5648
  for (let i = probeIdx - 1; i >= 0; i--) {
5459
5649
  const op = ops[i];
5460
- if (!op || !isInsert4(op)) {
5650
+ if (!op || !isInsert6(op)) {
5461
5651
  startOpIdx = i + 1;
5462
5652
  break;
5463
5653
  }