@scrider/formatter 1.1.0 → 1.2.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
@@ -1,5 +1,6 @@
1
- import { AttributeMap, Op, Delta } from '@scrider/delta';
1
+ import { AttributeMap, Op, Delta, InsertOp } from '@scrider/delta';
2
2
  export * from '@scrider/delta';
3
+ export { InsertOp as ContentOp } from '@scrider/delta';
3
4
 
4
5
  /**
5
6
  * DOM Adapter Interface
@@ -1556,6 +1557,18 @@ interface DeltaToMarkdownOptions {
1556
1557
  * `render()` is used as HTML fallback in Markdown.
1557
1558
  */
1558
1559
  registry?: Registry;
1560
+ /**
1561
+ * Strip trailing newlines from the final output.
1562
+ *
1563
+ * Useful when serialising a single block (e.g. one table for inline
1564
+ * editing) where the GFM padding (blank line after a table, trailing
1565
+ * paragraph newline, etc.) is not wanted. The internal structure of the
1566
+ * markdown is unaffected — only trailing `\n+` at the very end of the
1567
+ * returned string is removed.
1568
+ *
1569
+ * @default false
1570
+ */
1571
+ trimTrailingNewlines?: boolean;
1559
1572
  }
1560
1573
  /**
1561
1574
  * Convert Delta to Markdown
@@ -1661,9 +1674,39 @@ interface ParserContext {
1661
1674
  pushNewline(attrs?: AttributeMap): void;
1662
1675
  }
1663
1676
  /**
1664
- * Check if remark is available
1677
+ * Check if remark is available for synchronous use.
1678
+ *
1679
+ * Returns true if either:
1680
+ * - remark modules have been preloaded (via {@link preloadRemark} or a prior
1681
+ * `markdownToDelta` / `markdownToDeltaSync` call), OR
1682
+ * - CommonJS `require()` is available and can resolve `unified` and
1683
+ * `remark-parse` (Node.js without ESM-only mode).
1684
+ *
1685
+ * In browser ESM environments where `require` is undefined, this returns
1686
+ * `false` until {@link preloadRemark} has been awaited at least once.
1665
1687
  */
1666
1688
  declare function isRemarkAvailable(): boolean;
1689
+ /**
1690
+ * Preload remark modules (`unified`, `remark-parse`, `remark-gfm`, optionally
1691
+ * `remark-math`) asynchronously. After this resolves successfully, the
1692
+ * synchronous {@link markdownToDeltaSync} is usable in environments where
1693
+ * `require()` is not available (e.g. browser ESM).
1694
+ *
1695
+ * Safe to call multiple times: subsequent calls short-circuit if modules are
1696
+ * already loaded.
1697
+ *
1698
+ * @returns `true` if mandatory modules (`unified`, `remark-parse`,
1699
+ * `remark-gfm`) are now loaded; `false` if any required module is missing.
1700
+ * The function never throws — callers can branch on the boolean for
1701
+ * graceful degradation.
1702
+ *
1703
+ * @example
1704
+ * // On editor mount:
1705
+ * useEffect(() => {
1706
+ * preloadRemark();
1707
+ * }, []);
1708
+ */
1709
+ declare function preloadRemark(): Promise<boolean>;
1667
1710
  /**
1668
1711
  * Convert Markdown to Delta (async)
1669
1712
  */
@@ -1673,4 +1716,67 @@ declare function markdownToDelta(markdown: string, options?: MarkdownToDeltaOpti
1673
1716
  */
1674
1717
  declare function markdownToDeltaSync(markdown: string, options?: MarkdownToDeltaOptions): Delta;
1675
1718
 
1676
- 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 FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, Registry, type SanitizeOptions, type TableBlockData, type TableColAlignType, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, escapeHtml, extractBoxOpAttributes, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTextNode, isValidColor, isValidHexColor, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1719
+ /**
1720
+ * Simple-table region detection in flat Delta.
1721
+ *
1722
+ * Helpers for callers (e.g. editors that need to find the boundaries of a
1723
+ * markdown-style table within a Delta op stream — for example to enter
1724
+ * "edit as markdown source" mode on double-click of a rendered table cell).
1725
+ *
1726
+ * A simple-table region is a contiguous run of ops that ends, for each cell,
1727
+ * with a `\n`-op carrying the `table-row` attribute (the standard format
1728
+ * produced by {@link markdownToDelta} for GFM tables and consumed by
1729
+ * {@link deltaToMarkdown}).
1730
+ */
1731
+
1732
+ /**
1733
+ * Detected boundaries of a simple-table region.
1734
+ */
1735
+ interface TableRegion {
1736
+ /** Inclusive start index in the original ops array. */
1737
+ startOpIdx: number;
1738
+ /**
1739
+ * Inclusive end index — always points at the last `\n`-op of the table
1740
+ * (the terminator of the last cell of the last row).
1741
+ */
1742
+ endOpIdx: number;
1743
+ /** Slice of the original ops array covering the region. */
1744
+ ops: InsertOp[];
1745
+ }
1746
+ /**
1747
+ * Predicate: this op is a `\n`-op that terminates a simple-table cell
1748
+ * (i.e. it carries a `table-row` attribute).
1749
+ */
1750
+ declare function isTableNewlineOp(op: Op | undefined): boolean;
1751
+ /**
1752
+ * Find the boundaries of the simple-table region containing the given hint
1753
+ * op index. The hint may be:
1754
+ * - an inline op inside a cell,
1755
+ * - the cell-terminating `\n`-op itself,
1756
+ * - any op between two table newlines.
1757
+ *
1758
+ * The function walks **forward** from the hint to find the nearest `\n`-op:
1759
+ * if it does not carry a `table-row` attribute, the hint is not inside a
1760
+ * table and `null` is returned. Otherwise the algorithm extends the region
1761
+ * forward through contiguous table newlines and backward to the op just
1762
+ * after the previous non-table `\n`-op (or the start of the array).
1763
+ *
1764
+ * @param ops - The full ops array (e.g. `delta.ops`).
1765
+ * @param hintOpIdx - Any op index known or guessed to be within a table.
1766
+ * @returns The detected region, or `null` if `hintOpIdx` is out of range or
1767
+ * not within any simple-table region.
1768
+ *
1769
+ * @example
1770
+ * // After hit-testing a `<td>` element to a Delta op index:
1771
+ * const region = extractTableRegion(state.delta.ops, hitOpIdx);
1772
+ * if (region) {
1773
+ * const md = deltaToMarkdown(new Delta(region.ops), {
1774
+ * trimTrailingNewlines: true,
1775
+ * });
1776
+ * // replace ops in [region.startOpIdx, region.endOpIdx] with a single
1777
+ * // { insert: md + '\n' } op to enter source-edit mode
1778
+ * }
1779
+ */
1780
+ declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1781
+
1782
+ 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 FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, Registry, type SanitizeOptions, type TableBlockData, type TableColAlignType, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, preloadRemark, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { AttributeMap, Op, Delta } from '@scrider/delta';
1
+ import { AttributeMap, Op, Delta, InsertOp } from '@scrider/delta';
2
2
  export * from '@scrider/delta';
3
+ export { InsertOp as ContentOp } from '@scrider/delta';
3
4
 
4
5
  /**
5
6
  * DOM Adapter Interface
@@ -1556,6 +1557,18 @@ interface DeltaToMarkdownOptions {
1556
1557
  * `render()` is used as HTML fallback in Markdown.
1557
1558
  */
1558
1559
  registry?: Registry;
1560
+ /**
1561
+ * Strip trailing newlines from the final output.
1562
+ *
1563
+ * Useful when serialising a single block (e.g. one table for inline
1564
+ * editing) where the GFM padding (blank line after a table, trailing
1565
+ * paragraph newline, etc.) is not wanted. The internal structure of the
1566
+ * markdown is unaffected — only trailing `\n+` at the very end of the
1567
+ * returned string is removed.
1568
+ *
1569
+ * @default false
1570
+ */
1571
+ trimTrailingNewlines?: boolean;
1559
1572
  }
1560
1573
  /**
1561
1574
  * Convert Delta to Markdown
@@ -1661,9 +1674,39 @@ interface ParserContext {
1661
1674
  pushNewline(attrs?: AttributeMap): void;
1662
1675
  }
1663
1676
  /**
1664
- * Check if remark is available
1677
+ * Check if remark is available for synchronous use.
1678
+ *
1679
+ * Returns true if either:
1680
+ * - remark modules have been preloaded (via {@link preloadRemark} or a prior
1681
+ * `markdownToDelta` / `markdownToDeltaSync` call), OR
1682
+ * - CommonJS `require()` is available and can resolve `unified` and
1683
+ * `remark-parse` (Node.js without ESM-only mode).
1684
+ *
1685
+ * In browser ESM environments where `require` is undefined, this returns
1686
+ * `false` until {@link preloadRemark} has been awaited at least once.
1665
1687
  */
1666
1688
  declare function isRemarkAvailable(): boolean;
1689
+ /**
1690
+ * Preload remark modules (`unified`, `remark-parse`, `remark-gfm`, optionally
1691
+ * `remark-math`) asynchronously. After this resolves successfully, the
1692
+ * synchronous {@link markdownToDeltaSync} is usable in environments where
1693
+ * `require()` is not available (e.g. browser ESM).
1694
+ *
1695
+ * Safe to call multiple times: subsequent calls short-circuit if modules are
1696
+ * already loaded.
1697
+ *
1698
+ * @returns `true` if mandatory modules (`unified`, `remark-parse`,
1699
+ * `remark-gfm`) are now loaded; `false` if any required module is missing.
1700
+ * The function never throws — callers can branch on the boolean for
1701
+ * graceful degradation.
1702
+ *
1703
+ * @example
1704
+ * // On editor mount:
1705
+ * useEffect(() => {
1706
+ * preloadRemark();
1707
+ * }, []);
1708
+ */
1709
+ declare function preloadRemark(): Promise<boolean>;
1667
1710
  /**
1668
1711
  * Convert Markdown to Delta (async)
1669
1712
  */
@@ -1673,4 +1716,67 @@ declare function markdownToDelta(markdown: string, options?: MarkdownToDeltaOpti
1673
1716
  */
1674
1717
  declare function markdownToDeltaSync(markdown: string, options?: MarkdownToDeltaOptions): Delta;
1675
1718
 
1676
- 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 FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, Registry, type SanitizeOptions, type TableBlockData, type TableColAlignType, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, escapeHtml, extractBoxOpAttributes, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTextNode, isValidColor, isValidHexColor, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1719
+ /**
1720
+ * Simple-table region detection in flat Delta.
1721
+ *
1722
+ * Helpers for callers (e.g. editors that need to find the boundaries of a
1723
+ * markdown-style table within a Delta op stream — for example to enter
1724
+ * "edit as markdown source" mode on double-click of a rendered table cell).
1725
+ *
1726
+ * A simple-table region is a contiguous run of ops that ends, for each cell,
1727
+ * with a `\n`-op carrying the `table-row` attribute (the standard format
1728
+ * produced by {@link markdownToDelta} for GFM tables and consumed by
1729
+ * {@link deltaToMarkdown}).
1730
+ */
1731
+
1732
+ /**
1733
+ * Detected boundaries of a simple-table region.
1734
+ */
1735
+ interface TableRegion {
1736
+ /** Inclusive start index in the original ops array. */
1737
+ startOpIdx: number;
1738
+ /**
1739
+ * Inclusive end index — always points at the last `\n`-op of the table
1740
+ * (the terminator of the last cell of the last row).
1741
+ */
1742
+ endOpIdx: number;
1743
+ /** Slice of the original ops array covering the region. */
1744
+ ops: InsertOp[];
1745
+ }
1746
+ /**
1747
+ * Predicate: this op is a `\n`-op that terminates a simple-table cell
1748
+ * (i.e. it carries a `table-row` attribute).
1749
+ */
1750
+ declare function isTableNewlineOp(op: Op | undefined): boolean;
1751
+ /**
1752
+ * Find the boundaries of the simple-table region containing the given hint
1753
+ * op index. The hint may be:
1754
+ * - an inline op inside a cell,
1755
+ * - the cell-terminating `\n`-op itself,
1756
+ * - any op between two table newlines.
1757
+ *
1758
+ * The function walks **forward** from the hint to find the nearest `\n`-op:
1759
+ * if it does not carry a `table-row` attribute, the hint is not inside a
1760
+ * table and `null` is returned. Otherwise the algorithm extends the region
1761
+ * forward through contiguous table newlines and backward to the op just
1762
+ * after the previous non-table `\n`-op (or the start of the array).
1763
+ *
1764
+ * @param ops - The full ops array (e.g. `delta.ops`).
1765
+ * @param hintOpIdx - Any op index known or guessed to be within a table.
1766
+ * @returns The detected region, or `null` if `hintOpIdx` is out of range or
1767
+ * not within any simple-table region.
1768
+ *
1769
+ * @example
1770
+ * // After hit-testing a `<td>` element to a Delta op index:
1771
+ * const region = extractTableRegion(state.delta.ops, hitOpIdx);
1772
+ * if (region) {
1773
+ * const md = deltaToMarkdown(new Delta(region.ops), {
1774
+ * trimTrailingNewlines: true,
1775
+ * });
1776
+ * // replace ops in [region.startOpIdx, region.endOpIdx] with a single
1777
+ * // { insert: md + '\n' } op to enter source-edit mode
1778
+ * }
1779
+ */
1780
+ declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1781
+
1782
+ 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 FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, Registry, type SanitizeOptions, type TableBlockData, type TableColAlignType, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, preloadRemark, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -3654,7 +3654,8 @@ function deltaToMarkdown(delta, options = {}) {
3654
3654
  embedRenderers = {},
3655
3655
  blockHandlers,
3656
3656
  prettyHtml = false,
3657
- registry
3657
+ registry,
3658
+ trimTrailingNewlines = false
3658
3659
  } = options;
3659
3660
  const useLatexDelimiters = mathSyntax === "latex";
3660
3661
  const lines = splitIntoLines2(delta.ops);
@@ -3752,7 +3753,8 @@ ${code}
3752
3753
  lastIndent = indent;
3753
3754
  lastWasBlockquote = isBlockquote;
3754
3755
  }
3755
- return result.join("\n");
3756
+ const md = result.join("\n");
3757
+ return trimTrailingNewlines ? md.replace(/\n+$/, "") : md;
3756
3758
  }
3757
3759
  function hasBlockFormat(attrs) {
3758
3760
  return !!(attrs.header || attrs.list || attrs.blockquote || attrs["code-block"] || attrs.align || attrs.indent);
@@ -4141,6 +4143,8 @@ var remarkGfm = null;
4141
4143
  var remarkMath = null;
4142
4144
  var unified = null;
4143
4145
  function isRemarkAvailable() {
4146
+ if (unified && remarkParse) return true;
4147
+ if (typeof __require === "undefined") return false;
4144
4148
  try {
4145
4149
  __require.resolve("unified");
4146
4150
  __require.resolve("remark-parse");
@@ -4149,8 +4153,8 @@ function isRemarkAvailable() {
4149
4153
  return false;
4150
4154
  }
4151
4155
  }
4152
- async function loadRemark() {
4153
- if (unified) return;
4156
+ async function preloadRemark() {
4157
+ if (unified && remarkParse && remarkGfm) return true;
4154
4158
  try {
4155
4159
  const [unifiedMod, remarkParseMod, remarkGfmMod] = await Promise.all([
4156
4160
  import("unified"),
@@ -4161,15 +4165,16 @@ async function loadRemark() {
4161
4165
  remarkParse = remarkParseMod.default;
4162
4166
  remarkGfm = remarkGfmMod.default;
4163
4167
  } catch {
4164
- throw new Error(
4165
- "remark is not installed. Install with: pnpm add unified remark-parse remark-gfm"
4166
- );
4168
+ return false;
4167
4169
  }
4168
- try {
4169
- const remarkMathMod = await import("remark-math");
4170
- remarkMath = remarkMathMod.default;
4171
- } catch {
4170
+ if (!remarkMath) {
4171
+ try {
4172
+ const remarkMathMod = await import("remark-math");
4173
+ remarkMath = remarkMathMod.default;
4174
+ } catch {
4175
+ }
4172
4176
  }
4177
+ return true;
4173
4178
  }
4174
4179
  function preprocessMarkdown(markdown, mathBlock) {
4175
4180
  markdown = markdown.replace(/\\\((.+?)\\\)/g, (_match, content) => `$${content}$`);
@@ -4193,9 +4198,11 @@ async function markdownToDelta(markdown, options = {}) {
4193
4198
  nodeHandlers = {}
4194
4199
  } = options;
4195
4200
  markdown = preprocessMarkdown(markdown, mathBlock);
4196
- await loadRemark();
4197
- if (!unified || !remarkParse) {
4198
- throw new Error("Failed to load remark");
4201
+ const loaded = await preloadRemark();
4202
+ if (!loaded || !unified || !remarkParse) {
4203
+ throw new Error(
4204
+ "remark is not installed. Install with: pnpm add unified remark-parse remark-gfm"
4205
+ );
4199
4206
  }
4200
4207
  let processor = unified().use(remarkParse);
4201
4208
  if (gfm && remarkGfm) {
@@ -4224,6 +4231,11 @@ function markdownToDeltaSync(markdown, options = {}) {
4224
4231
  } = options;
4225
4232
  markdown = preprocessMarkdown(markdown, mathBlock);
4226
4233
  if (!unified || !remarkParse) {
4234
+ if (typeof __require === "undefined") {
4235
+ throw new Error(
4236
+ "markdownToDeltaSync requires remark to be preloaded in this environment. `require()` is not available (likely browser ESM). Call `await preloadRemark()` once on application startup before using the sync API, or use the async `markdownToDelta()` instead."
4237
+ );
4238
+ }
4227
4239
  try {
4228
4240
  const unifiedMod = __require("unified");
4229
4241
  const remarkParseMod = __require("remark-parse");
@@ -4812,6 +4824,54 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4812
4824
  }
4813
4825
  return delta;
4814
4826
  }
4827
+
4828
+ // src/conversion/markdown/table-region.ts
4829
+ import { isInsert as isInsert4, isTextInsert as isTextInsert3 } from "@scrider/delta";
4830
+ function isTableNewlineOp(op) {
4831
+ if (!op || !isInsert4(op) || !isTextInsert3(op)) return false;
4832
+ if (!op.insert.includes("\n")) return false;
4833
+ return !!op.attributes && "table-row" in op.attributes;
4834
+ }
4835
+ function extractTableRegion(ops, hintOpIdx) {
4836
+ if (hintOpIdx < 0 || hintOpIdx >= ops.length) return null;
4837
+ let probeIdx = -1;
4838
+ for (let i = hintOpIdx; i < ops.length; i++) {
4839
+ const op = ops[i];
4840
+ if (!op || !isInsert4(op)) continue;
4841
+ if (isTextInsert3(op) && op.insert.includes("\n")) {
4842
+ probeIdx = i;
4843
+ break;
4844
+ }
4845
+ }
4846
+ if (probeIdx < 0) return null;
4847
+ if (!isTableNewlineOp(ops[probeIdx])) return null;
4848
+ let endOpIdx = probeIdx;
4849
+ for (let i = probeIdx + 1; i < ops.length; i++) {
4850
+ const op = ops[i];
4851
+ if (!op || !isInsert4(op)) break;
4852
+ if (isTextInsert3(op) && op.insert.includes("\n")) {
4853
+ if (isTableNewlineOp(op)) {
4854
+ endOpIdx = i;
4855
+ } else {
4856
+ break;
4857
+ }
4858
+ }
4859
+ }
4860
+ let startOpIdx = 0;
4861
+ for (let i = probeIdx - 1; i >= 0; i--) {
4862
+ const op = ops[i];
4863
+ if (!op || !isInsert4(op)) {
4864
+ startOpIdx = i + 1;
4865
+ break;
4866
+ }
4867
+ if (isTextInsert3(op) && op.insert.includes("\n") && !isTableNewlineOp(op)) {
4868
+ startOpIdx = i + 1;
4869
+ break;
4870
+ }
4871
+ }
4872
+ const regionOps = ops.slice(startOpIdx, endOpIdx + 1);
4873
+ return { startOpIdx, endOpIdx, ops: regionOps };
4874
+ }
4815
4875
  export {
4816
4876
  ALERT_TYPES,
4817
4877
  BOX_FLOAT_VALUES,
@@ -4845,6 +4905,7 @@ export {
4845
4905
  dividerFormat,
4846
4906
  escapeHtml,
4847
4907
  extractBoxOpAttributes,
4908
+ extractTableRegion,
4848
4909
  fontFormat,
4849
4910
  footnoteRefFormat,
4850
4911
  footnotesBlockHandler,
@@ -4859,6 +4920,7 @@ export {
4859
4920
  isAdapterAvailable,
4860
4921
  isElement,
4861
4922
  isRemarkAvailable,
4923
+ isTableNewlineOp,
4862
4924
  isTextNode,
4863
4925
  isValidColor,
4864
4926
  isValidHexColor,
@@ -4871,6 +4933,7 @@ export {
4871
4933
  markdownToDeltaSync,
4872
4934
  nodeAdapter,
4873
4935
  normalizeDelta,
4936
+ preloadRemark,
4874
4937
  sanitizeDelta,
4875
4938
  sizeFormat,
4876
4939
  slugify,