@scrider/formatter 1.0.1 → 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
@@ -67,6 +68,8 @@ interface DOMCSSStyleDeclaration {
67
68
  setProperty(property: string, value: string): void;
68
69
  color?: string;
69
70
  backgroundColor?: string;
71
+ fontFamily?: string;
72
+ fontSize?: string;
70
73
  fontWeight?: string;
71
74
  fontStyle?: string;
72
75
  textDecoration?: string;
@@ -788,6 +791,13 @@ declare const codeFormat: Format<boolean>;
788
791
  */
789
792
  declare const colorFormat: Format<string>;
790
793
 
794
+ /**
795
+ * Font family format
796
+ *
797
+ * Delta: { insert: "text", attributes: { font: "Times New Roman" } }
798
+ */
799
+ declare const fontFormat: Format<string>;
800
+
791
801
  /**
792
802
  * Italic format
793
803
  *
@@ -825,6 +835,15 @@ declare const linkFormat: Format<string>;
825
835
  */
826
836
  declare const markFormat: Format<boolean>;
827
837
 
838
+ /**
839
+ * Font size format
840
+ *
841
+ * Delta: { insert: "text", attributes: { size: "14pt" } }
842
+ *
843
+ * Value is a string with CSS unit (e.g. "14pt", "16px", "1.2em").
844
+ */
845
+ declare const sizeFormat: Format<string>;
846
+
828
847
  /**
829
848
  * Strikethrough format
830
849
  *
@@ -1538,6 +1557,18 @@ interface DeltaToMarkdownOptions {
1538
1557
  * `render()` is used as HTML fallback in Markdown.
1539
1558
  */
1540
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;
1541
1572
  }
1542
1573
  /**
1543
1574
  * Convert Delta to Markdown
@@ -1643,9 +1674,39 @@ interface ParserContext {
1643
1674
  pushNewline(attrs?: AttributeMap): void;
1644
1675
  }
1645
1676
  /**
1646
- * 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.
1647
1687
  */
1648
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>;
1649
1710
  /**
1650
1711
  * Convert Markdown to Delta (async)
1651
1712
  */
@@ -1655,4 +1716,67 @@ declare function markdownToDelta(markdown: string, options?: MarkdownToDeltaOpti
1655
1716
  */
1656
1717
  declare function markdownToDeltaSync(markdown: string, options?: MarkdownToDeltaOptions): Delta;
1657
1718
 
1658
- 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, 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, 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
@@ -67,6 +68,8 @@ interface DOMCSSStyleDeclaration {
67
68
  setProperty(property: string, value: string): void;
68
69
  color?: string;
69
70
  backgroundColor?: string;
71
+ fontFamily?: string;
72
+ fontSize?: string;
70
73
  fontWeight?: string;
71
74
  fontStyle?: string;
72
75
  textDecoration?: string;
@@ -788,6 +791,13 @@ declare const codeFormat: Format<boolean>;
788
791
  */
789
792
  declare const colorFormat: Format<string>;
790
793
 
794
+ /**
795
+ * Font family format
796
+ *
797
+ * Delta: { insert: "text", attributes: { font: "Times New Roman" } }
798
+ */
799
+ declare const fontFormat: Format<string>;
800
+
791
801
  /**
792
802
  * Italic format
793
803
  *
@@ -825,6 +835,15 @@ declare const linkFormat: Format<string>;
825
835
  */
826
836
  declare const markFormat: Format<boolean>;
827
837
 
838
+ /**
839
+ * Font size format
840
+ *
841
+ * Delta: { insert: "text", attributes: { size: "14pt" } }
842
+ *
843
+ * Value is a string with CSS unit (e.g. "14pt", "16px", "1.2em").
844
+ */
845
+ declare const sizeFormat: Format<string>;
846
+
828
847
  /**
829
848
  * Strikethrough format
830
849
  *
@@ -1538,6 +1557,18 @@ interface DeltaToMarkdownOptions {
1538
1557
  * `render()` is used as HTML fallback in Markdown.
1539
1558
  */
1540
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;
1541
1572
  }
1542
1573
  /**
1543
1574
  * Convert Delta to Markdown
@@ -1643,9 +1674,39 @@ interface ParserContext {
1643
1674
  pushNewline(attrs?: AttributeMap): void;
1644
1675
  }
1645
1676
  /**
1646
- * 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.
1647
1687
  */
1648
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>;
1649
1710
  /**
1650
1711
  * Convert Markdown to Delta (async)
1651
1712
  */
@@ -1655,4 +1716,67 @@ declare function markdownToDelta(markdown: string, options?: MarkdownToDeltaOpti
1655
1716
  */
1656
1717
  declare function markdownToDeltaSync(markdown: string, options?: MarkdownToDeltaOptions): Delta;
1657
1718
 
1658
- 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, 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, 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
@@ -1563,6 +1563,15 @@ var colorFormat = {
1563
1563
  }
1564
1564
  };
1565
1565
 
1566
+ // src/schema/formats/inline/font.ts
1567
+ var fontFormat = {
1568
+ name: "font",
1569
+ scope: "inline",
1570
+ validate(value) {
1571
+ return typeof value === "string" && value.length > 0;
1572
+ }
1573
+ };
1574
+
1566
1575
  // src/schema/formats/inline/italic.ts
1567
1576
  var italicFormat = {
1568
1577
  name: "italic",
@@ -1620,6 +1629,15 @@ var markFormat = {
1620
1629
  }
1621
1630
  };
1622
1631
 
1632
+ // src/schema/formats/inline/size.ts
1633
+ var sizeFormat = {
1634
+ name: "size",
1635
+ scope: "inline",
1636
+ validate(value) {
1637
+ return typeof value === "string" && value.length > 0;
1638
+ }
1639
+ };
1640
+
1623
1641
  // src/schema/formats/inline/strike.ts
1624
1642
  var strikeFormat = {
1625
1643
  name: "strike",
@@ -1827,7 +1845,9 @@ var INLINE_FORMAT_ORDER = [
1827
1845
  ];
1828
1846
  var INLINE_STYLE_FORMATS = {
1829
1847
  color: "color",
1830
- background: "background-color"
1848
+ background: "background-color",
1849
+ font: "font-family",
1850
+ size: "font-size"
1831
1851
  };
1832
1852
  var BLOCK_FORMAT_TAGS = {
1833
1853
  header: (value) => `h${String(value)}`,
@@ -2212,6 +2232,8 @@ var defaultInlineFormats = [
2212
2232
  linkFormat,
2213
2233
  colorFormat,
2214
2234
  backgroundFormat,
2235
+ fontFormat,
2236
+ sizeFormat,
2215
2237
  markFormat,
2216
2238
  kbdFormat
2217
2239
  ];
@@ -3242,6 +3264,14 @@ function htmlToDelta(html, options = {}) {
3242
3264
  if (bg) {
3243
3265
  currentAttributes.background = bg;
3244
3266
  }
3267
+ const fontFamily = element.style?.fontFamily || element.style?.getPropertyValue?.("font-family");
3268
+ if (fontFamily) {
3269
+ currentAttributes.font = fontFamily.replace(/^["']|["']$/g, "");
3270
+ }
3271
+ const fontSize = element.style?.fontSize || element.style?.getPropertyValue?.("font-size");
3272
+ if (fontSize) {
3273
+ currentAttributes.size = fontSize;
3274
+ }
3245
3275
  processChildren(element);
3246
3276
  flushText();
3247
3277
  currentAttributes = prevAttrs;
@@ -3624,7 +3654,8 @@ function deltaToMarkdown(delta, options = {}) {
3624
3654
  embedRenderers = {},
3625
3655
  blockHandlers,
3626
3656
  prettyHtml = false,
3627
- registry
3657
+ registry,
3658
+ trimTrailingNewlines = false
3628
3659
  } = options;
3629
3660
  const useLatexDelimiters = mathSyntax === "latex";
3630
3661
  const lines = splitIntoLines2(delta.ops);
@@ -3722,7 +3753,8 @@ ${code}
3722
3753
  lastIndent = indent;
3723
3754
  lastWasBlockquote = isBlockquote;
3724
3755
  }
3725
- return result.join("\n");
3756
+ const md = result.join("\n");
3757
+ return trimTrailingNewlines ? md.replace(/\n+$/, "") : md;
3726
3758
  }
3727
3759
  function hasBlockFormat(attrs) {
3728
3760
  return !!(attrs.header || attrs.list || attrs.blockquote || attrs["code-block"] || attrs.align || attrs.indent);
@@ -3943,11 +3975,14 @@ function renderInlineText2(text, attributes) {
3943
3975
  if (link) {
3944
3976
  result = renderLink(result, link);
3945
3977
  }
3946
- if (typeof attributes.color === "string") {
3947
- result = `<span style="color: ${attributes.color}">${result}</span>`;
3948
- }
3949
- if (typeof attributes.background === "string") {
3950
- result = `<span style="background-color: ${attributes.background}">${result}</span>`;
3978
+ const styleProps = [];
3979
+ if (typeof attributes.color === "string") styleProps.push(`color: ${attributes.color}`);
3980
+ if (typeof attributes.background === "string")
3981
+ styleProps.push(`background-color: ${attributes.background}`);
3982
+ if (typeof attributes.font === "string") styleProps.push(`font-family: ${attributes.font}`);
3983
+ if (typeof attributes.size === "string") styleProps.push(`font-size: ${attributes.size}`);
3984
+ if (styleProps.length > 0) {
3985
+ result = `<span style="${styleProps.join("; ")}">${result}</span>`;
3951
3986
  }
3952
3987
  return result;
3953
3988
  }
@@ -4108,6 +4143,8 @@ var remarkGfm = null;
4108
4143
  var remarkMath = null;
4109
4144
  var unified = null;
4110
4145
  function isRemarkAvailable() {
4146
+ if (unified && remarkParse) return true;
4147
+ if (typeof __require === "undefined") return false;
4111
4148
  try {
4112
4149
  __require.resolve("unified");
4113
4150
  __require.resolve("remark-parse");
@@ -4116,8 +4153,8 @@ function isRemarkAvailable() {
4116
4153
  return false;
4117
4154
  }
4118
4155
  }
4119
- async function loadRemark() {
4120
- if (unified) return;
4156
+ async function preloadRemark() {
4157
+ if (unified && remarkParse && remarkGfm) return true;
4121
4158
  try {
4122
4159
  const [unifiedMod, remarkParseMod, remarkGfmMod] = await Promise.all([
4123
4160
  import("unified"),
@@ -4128,15 +4165,16 @@ async function loadRemark() {
4128
4165
  remarkParse = remarkParseMod.default;
4129
4166
  remarkGfm = remarkGfmMod.default;
4130
4167
  } catch {
4131
- throw new Error(
4132
- "remark is not installed. Install with: pnpm add unified remark-parse remark-gfm"
4133
- );
4168
+ return false;
4134
4169
  }
4135
- try {
4136
- const remarkMathMod = await import("remark-math");
4137
- remarkMath = remarkMathMod.default;
4138
- } catch {
4170
+ if (!remarkMath) {
4171
+ try {
4172
+ const remarkMathMod = await import("remark-math");
4173
+ remarkMath = remarkMathMod.default;
4174
+ } catch {
4175
+ }
4139
4176
  }
4177
+ return true;
4140
4178
  }
4141
4179
  function preprocessMarkdown(markdown, mathBlock) {
4142
4180
  markdown = markdown.replace(/\\\((.+?)\\\)/g, (_match, content) => `$${content}$`);
@@ -4160,9 +4198,11 @@ async function markdownToDelta(markdown, options = {}) {
4160
4198
  nodeHandlers = {}
4161
4199
  } = options;
4162
4200
  markdown = preprocessMarkdown(markdown, mathBlock);
4163
- await loadRemark();
4164
- if (!unified || !remarkParse) {
4165
- 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
+ );
4166
4206
  }
4167
4207
  let processor = unified().use(remarkParse);
4168
4208
  if (gfm && remarkGfm) {
@@ -4191,6 +4231,11 @@ function markdownToDeltaSync(markdown, options = {}) {
4191
4231
  } = options;
4192
4232
  markdown = preprocessMarkdown(markdown, mathBlock);
4193
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
+ }
4194
4239
  try {
4195
4240
  const unifiedMod = __require("unified");
4196
4241
  const remarkParseMod = __require("remark-parse");
@@ -4689,6 +4734,19 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4689
4734
  currentInlineAttrs = { ...currentInlineAttrs, background: bgMatch[1].trim() };
4690
4735
  addedKeys.push("background");
4691
4736
  }
4737
+ const fontMatch = style.match(/(?:^|;)\s*font-family\s*:\s*([^;]+)/i);
4738
+ if (fontMatch) {
4739
+ currentInlineAttrs = {
4740
+ ...currentInlineAttrs,
4741
+ font: fontMatch[1].trim().replace(/^["']|["']$/g, "")
4742
+ };
4743
+ addedKeys.push("font");
4744
+ }
4745
+ const sizeMatch = style.match(/(?:^|;)\s*font-size\s*:\s*([^;]+)/i);
4746
+ if (sizeMatch) {
4747
+ currentInlineAttrs = { ...currentInlineAttrs, size: sizeMatch[1].trim() };
4748
+ addedKeys.push("size");
4749
+ }
4692
4750
  spanAttrStack.push(addedKeys);
4693
4751
  return;
4694
4752
  }
@@ -4766,6 +4824,54 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4766
4824
  }
4767
4825
  return delta;
4768
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
+ }
4769
4875
  export {
4770
4876
  ALERT_TYPES,
4771
4877
  BOX_FLOAT_VALUES,
@@ -4799,6 +4905,8 @@ export {
4799
4905
  dividerFormat,
4800
4906
  escapeHtml,
4801
4907
  extractBoxOpAttributes,
4908
+ extractTableRegion,
4909
+ fontFormat,
4802
4910
  footnoteRefFormat,
4803
4911
  footnotesBlockHandler,
4804
4912
  formulaFormat,
@@ -4812,6 +4920,7 @@ export {
4812
4920
  isAdapterAvailable,
4813
4921
  isElement,
4814
4922
  isRemarkAvailable,
4923
+ isTableNewlineOp,
4815
4924
  isTextNode,
4816
4925
  isValidColor,
4817
4926
  isValidHexColor,
@@ -4824,7 +4933,9 @@ export {
4824
4933
  markdownToDeltaSync,
4825
4934
  nodeAdapter,
4826
4935
  normalizeDelta,
4936
+ preloadRemark,
4827
4937
  sanitizeDelta,
4938
+ sizeFormat,
4828
4939
  slugify,
4829
4940
  slugifyWithDedup,
4830
4941
  strikeFormat,