@scrider/formatter 1.2.0 → 1.3.1

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
@@ -1039,6 +1039,27 @@ declare const formulaFormat: Format<string>;
1039
1039
  */
1040
1040
  declare const imageFormat: Format<string>;
1041
1041
 
1042
+ /**
1043
+ * Soft Line Break embed format
1044
+ *
1045
+ * Represents a "Shift+Enter" style line break that does NOT split the
1046
+ * containing block (paragraph, list item, table cell, etc.). This is the
1047
+ * Delta-level analogue of HTML `<br>` used as an inline line break and
1048
+ * of the GFM "hard break" Markdown construct (two trailing spaces + `\n`).
1049
+ *
1050
+ * Delta: `{ insert: { softBreak: true } }`
1051
+ * HTML: `<br data-scrider-embed>` (with the explicit marker so that
1052
+ * round-trip parsing can distinguish a soft break from the
1053
+ * placeholder `<br>` that appears inside an empty paragraph)
1054
+ * Markdown: ` \n` (default GFM hard break) or inline `<br>` (configurable
1055
+ * via `softBreakStyle` option on `deltaToMarkdown`)
1056
+ *
1057
+ * Value is always `true` — the embed has no additional data.
1058
+ *
1059
+ * @see {@link https://github.github.com/gfm/#hard-line-breaks GFM hard line break}
1060
+ */
1061
+ declare const softBreakFormat: Format<boolean>;
1062
+
1042
1063
  /**
1043
1064
  * Video embed format
1044
1065
  *
@@ -1557,6 +1578,22 @@ interface DeltaToMarkdownOptions {
1557
1578
  * `render()` is used as HTML fallback in Markdown.
1558
1579
  */
1559
1580
  registry?: Registry;
1581
+ /**
1582
+ * Rendering style for `{ softBreak: true }` embeds (Phase 7 Part 0).
1583
+ *
1584
+ * - `'spaces'` (default): GFM-canonical hard break — two trailing spaces
1585
+ * followed by `\n` (`" \n"`). Round-trips losslessly through remark.
1586
+ * - `'html'`: inline `<br>` tag. Slightly more visible in source view
1587
+ * and immune to editor whitespace trimming. Recommended for the
1588
+ * LFM (LLM-Flavored Markdown) flavour exposed by the editor's
1589
+ * "source" toggle.
1590
+ *
1591
+ * Does not affect how soft breaks are rendered inside table cells —
1592
+ * those always use inline `<br>` because GFM tables forbid raw `\n`.
1593
+ *
1594
+ * @default 'spaces'
1595
+ */
1596
+ softBreakStyle?: 'spaces' | 'html';
1560
1597
  /**
1561
1598
  * Strip trailing newlines from the final output.
1562
1599
  *
@@ -1779,4 +1816,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1779
1816
  */
1780
1817
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1781
1818
 
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 };
1819
+ 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, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -1039,6 +1039,27 @@ declare const formulaFormat: Format<string>;
1039
1039
  */
1040
1040
  declare const imageFormat: Format<string>;
1041
1041
 
1042
+ /**
1043
+ * Soft Line Break embed format
1044
+ *
1045
+ * Represents a "Shift+Enter" style line break that does NOT split the
1046
+ * containing block (paragraph, list item, table cell, etc.). This is the
1047
+ * Delta-level analogue of HTML `<br>` used as an inline line break and
1048
+ * of the GFM "hard break" Markdown construct (two trailing spaces + `\n`).
1049
+ *
1050
+ * Delta: `{ insert: { softBreak: true } }`
1051
+ * HTML: `<br data-scrider-embed>` (with the explicit marker so that
1052
+ * round-trip parsing can distinguish a soft break from the
1053
+ * placeholder `<br>` that appears inside an empty paragraph)
1054
+ * Markdown: ` \n` (default GFM hard break) or inline `<br>` (configurable
1055
+ * via `softBreakStyle` option on `deltaToMarkdown`)
1056
+ *
1057
+ * Value is always `true` — the embed has no additional data.
1058
+ *
1059
+ * @see {@link https://github.github.com/gfm/#hard-line-breaks GFM hard line break}
1060
+ */
1061
+ declare const softBreakFormat: Format<boolean>;
1062
+
1042
1063
  /**
1043
1064
  * Video embed format
1044
1065
  *
@@ -1557,6 +1578,22 @@ interface DeltaToMarkdownOptions {
1557
1578
  * `render()` is used as HTML fallback in Markdown.
1558
1579
  */
1559
1580
  registry?: Registry;
1581
+ /**
1582
+ * Rendering style for `{ softBreak: true }` embeds (Phase 7 Part 0).
1583
+ *
1584
+ * - `'spaces'` (default): GFM-canonical hard break — two trailing spaces
1585
+ * followed by `\n` (`" \n"`). Round-trips losslessly through remark.
1586
+ * - `'html'`: inline `<br>` tag. Slightly more visible in source view
1587
+ * and immune to editor whitespace trimming. Recommended for the
1588
+ * LFM (LLM-Flavored Markdown) flavour exposed by the editor's
1589
+ * "source" toggle.
1590
+ *
1591
+ * Does not affect how soft breaks are rendered inside table cells —
1592
+ * those always use inline `<br>` because GFM tables forbid raw `\n`.
1593
+ *
1594
+ * @default 'spaces'
1595
+ */
1596
+ softBreakStyle?: 'spaces' | 'html';
1560
1597
  /**
1561
1598
  * Strip trailing newlines from the final output.
1562
1599
  *
@@ -1779,4 +1816,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1779
1816
  */
1780
1817
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1781
1818
 
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 };
1819
+ 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, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -1915,7 +1915,13 @@ var EMBED_RENDERERS = {
1915
1915
  const id = typeof value === "string" ? value : String(value);
1916
1916
  return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
1917
1917
  },
1918
- divider: () => "<hr>"
1918
+ divider: () => "<hr>",
1919
+ // Soft line break (Shift+Enter equivalent). Emitted with an explicit
1920
+ // `data-scrider-embed` marker so that html-to-delta can distinguish this
1921
+ // embed from the placeholder `<br>` that appears inside an empty
1922
+ // paragraph (`<p><br></p>`) without relying solely on positional
1923
+ // heuristics. See `soft-break.ts` for the format definition.
1924
+ softBreak: () => "<br data-scrider-embed>"
1919
1925
  };
1920
1926
  var TAG_TO_INLINE_FORMAT = {
1921
1927
  strong: { format: "bold", value: true },
@@ -2153,6 +2159,33 @@ var imageFormat = {
2153
2159
  }
2154
2160
  };
2155
2161
 
2162
+ // src/schema/formats/embed/soft-break.ts
2163
+ var softBreakFormat = {
2164
+ name: "softBreak",
2165
+ scope: "embed",
2166
+ normalize(value) {
2167
+ return !!value;
2168
+ },
2169
+ validate(value) {
2170
+ return value === true;
2171
+ },
2172
+ render() {
2173
+ return "<br data-scrider-embed>";
2174
+ },
2175
+ match(element) {
2176
+ if (element.tagName.toLowerCase() !== "br") return null;
2177
+ if (!element.hasAttribute("data-scrider-embed")) return null;
2178
+ return { value: true };
2179
+ }
2180
+ // NB: Markdown rendering is intentionally NOT implemented on the format
2181
+ // itself. The choice between `" \n"` (GFM spaces) and inline `<br>`
2182
+ // depends on the caller-provided `softBreakStyle` option on
2183
+ // `deltaToMarkdown`, so the converter handles it as a built-in special
2184
+ // case instead of going through `Format.toMarkdown`. The Markdown side
2185
+ // of the round-trip is symmetric: `markdownToDelta` recognises both
2186
+ // `break` AST nodes and inline `<br>` HTML and emits this embed.
2187
+ };
2188
+
2156
2189
  // src/schema/formats/embed/video.ts
2157
2190
  var videoFormat = {
2158
2191
  name: "video",
@@ -2255,6 +2288,7 @@ var defaultEmbedFormats = [
2255
2288
  videoFormat,
2256
2289
  formulaFormat,
2257
2290
  dividerFormat,
2291
+ softBreakFormat,
2258
2292
  blockFormat,
2259
2293
  footnoteRefFormat
2260
2294
  ];
@@ -3085,7 +3119,12 @@ function htmlToDelta(html, options = {}) {
3085
3119
  return;
3086
3120
  }
3087
3121
  if (tagName === "br") {
3088
- context.pushNewline();
3122
+ const hasMarker = node.hasAttribute("data-scrider-embed");
3123
+ if (hasMarker || hasMeaningfulPrevSibling(node)) {
3124
+ context.pushEmbed({ softBreak: true });
3125
+ } else {
3126
+ context.pushNewline();
3127
+ }
3089
3128
  return;
3090
3129
  }
3091
3130
  processChildren(node);
@@ -3480,7 +3519,7 @@ function htmlToDelta(html, options = {}) {
3480
3519
  }
3481
3520
  const children2 = element.childNodes;
3482
3521
  const firstChild = children2[0];
3483
- const isBrOnlyParagraph = children2.length === 1 && firstChild !== void 0 && isElement(firstChild) && firstChild.tagName.toLowerCase() === "br";
3522
+ const isBrOnlyParagraph = children2.length === 1 && firstChild !== void 0 && isElement(firstChild) && firstChild.tagName.toLowerCase() === "br" && !firstChild.hasAttribute("data-scrider-embed");
3484
3523
  if (!isBrOnlyParagraph) {
3485
3524
  processChildren(element);
3486
3525
  }
@@ -3519,6 +3558,23 @@ function normalizeText(text, pendingText, atLineStart) {
3519
3558
  }
3520
3559
  return text;
3521
3560
  }
3561
+ function hasMeaningfulPrevSibling(brNode) {
3562
+ const parent = brNode.parentNode;
3563
+ if (!parent) return false;
3564
+ const children = parent.childNodes;
3565
+ for (let i = 0; i < children.length; i++) {
3566
+ const child = children[i];
3567
+ if (!child) continue;
3568
+ if (child === brNode) return false;
3569
+ if (child.nodeType === NODE_TYPE.TEXT_NODE) {
3570
+ const text = child.textContent ?? "";
3571
+ if (text.trim().length > 0) return true;
3572
+ } else if (isElement(child)) {
3573
+ return true;
3574
+ }
3575
+ }
3576
+ return false;
3577
+ }
3522
3578
  function findTagHandler(handlers, element, tagName) {
3523
3579
  const className = element.getAttribute("class");
3524
3580
  if (className) {
@@ -3655,6 +3711,7 @@ function deltaToMarkdown(delta, options = {}) {
3655
3711
  blockHandlers,
3656
3712
  prettyHtml = false,
3657
3713
  registry,
3714
+ softBreakStyle = "spaces",
3658
3715
  trimTrailingNewlines = false
3659
3716
  } = options;
3660
3717
  const useLatexDelimiters = mathSyntax === "latex";
@@ -3671,7 +3728,9 @@ function deltaToMarkdown(delta, options = {}) {
3671
3728
  const isBlockquote = !!attrs.blockquote;
3672
3729
  if (typeof attrs["table-row"] === "number" && typeof attrs["table-col"] === "number") {
3673
3730
  const tableLines = collectTableLines2(lines, i);
3674
- result.push(renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters, registry));
3731
+ result.push(
3732
+ renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
3733
+ );
3675
3734
  result.push("");
3676
3735
  i += tableLines.length - 1;
3677
3736
  lastListType = null;
@@ -3687,7 +3746,16 @@ function deltaToMarkdown(delta, options = {}) {
3687
3746
  const codeLines = collectCodeBlock(lines, i);
3688
3747
  const language = getCodeBlockLanguage2(attrs);
3689
3748
  const code = codeLines.map(
3690
- (l) => renderLineContent2(l.ops, embedRenderers, true, false, blockHandlers, false, registry)
3749
+ (l) => renderLineContent2(
3750
+ l.ops,
3751
+ embedRenderers,
3752
+ true,
3753
+ false,
3754
+ blockHandlers,
3755
+ false,
3756
+ registry,
3757
+ softBreakStyle
3758
+ )
3691
3759
  ).join("\n");
3692
3760
  if (language === "math") {
3693
3761
  if (mathBlock === false) {
@@ -3737,7 +3805,8 @@ ${code}
3737
3805
  useLatexDelimiters,
3738
3806
  blockHandlers,
3739
3807
  prettyHtml,
3740
- registry
3808
+ registry,
3809
+ softBreakStyle
3741
3810
  );
3742
3811
  if (!content && !hasBlockFormat(attrs)) {
3743
3812
  result.push(preserveEmptyLines ? "<br>" : "");
@@ -3839,7 +3908,7 @@ function collectTableLines2(lines, startIndex) {
3839
3908
  }
3840
3909
  return result;
3841
3910
  }
3842
- function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = false, registry) {
3911
+ function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = false, registry, softBreakStyle = "spaces") {
3843
3912
  const rows = /* @__PURE__ */ new Map();
3844
3913
  for (const line of tableLines) {
3845
3914
  const attrs = line.attributes;
@@ -3876,7 +3945,9 @@ function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = fa
3876
3945
  const mdLines = [];
3877
3946
  if (headerRows.length > 0) {
3878
3947
  for (const [, row] of headerRows) {
3879
- mdLines.push(renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry));
3948
+ mdLines.push(
3949
+ renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
3950
+ );
3880
3951
  }
3881
3952
  mdLines.push(renderMdSeparator(maxCol, colAligns));
3882
3953
  } else {
@@ -3884,15 +3955,19 @@ function renderMarkdownTable(tableLines, embedRenderers, useLatexDelimiters = fa
3884
3955
  for (let col = 0; col <= maxCol; col++) {
3885
3956
  emptyRow.set(col, { ops: [] });
3886
3957
  }
3887
- mdLines.push(renderMdRow(emptyRow, maxCol, embedRenderers, useLatexDelimiters, registry));
3958
+ mdLines.push(
3959
+ renderMdRow(emptyRow, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
3960
+ );
3888
3961
  mdLines.push(renderMdSeparator(maxCol, colAligns));
3889
3962
  }
3890
3963
  for (const [, row] of bodyRows) {
3891
- mdLines.push(renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry));
3964
+ mdLines.push(
3965
+ renderMdRow(row.cells, maxCol, embedRenderers, useLatexDelimiters, registry, softBreakStyle)
3966
+ );
3892
3967
  }
3893
3968
  return mdLines.join("\n");
3894
3969
  }
3895
- function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false, registry) {
3970
+ function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false, registry, softBreakStyle = "spaces") {
3896
3971
  const parts = [];
3897
3972
  for (let col = 0; col <= maxCol; col++) {
3898
3973
  const cell = cells.get(col);
@@ -3903,7 +3978,10 @@ function renderMdRow(cells, maxCol, embedRenderers, useLatexDelimiters = false,
3903
3978
  useLatexDelimiters,
3904
3979
  void 0,
3905
3980
  false,
3906
- registry
3981
+ registry,
3982
+ softBreakStyle,
3983
+ true
3984
+ // inTableCell — softBreak must use <br>, never " \n"
3907
3985
  ) : "";
3908
3986
  parts.push(content.replace(/\|/g, "\\|"));
3909
3987
  }
@@ -3932,7 +4010,7 @@ function getCodeBlockLanguage2(attributes) {
3932
4010
  }
3933
4011
  return void 0;
3934
4012
  }
3935
- function renderLineContent2(ops, embedRenderers, inCodeBlock, useLatexDelimiters = false, blockHandlers, prettyHtml = false, registry) {
4013
+ function renderLineContent2(ops, embedRenderers, inCodeBlock, useLatexDelimiters = false, blockHandlers, prettyHtml = false, registry, softBreakStyle = "spaces", inTableCell = false) {
3936
4014
  let result = "";
3937
4015
  for (const op of ops) {
3938
4016
  const attrs = op.attributes;
@@ -3952,7 +4030,9 @@ function renderLineContent2(ops, embedRenderers, inCodeBlock, useLatexDelimiters
3952
4030
  useLatexDelimiters,
3953
4031
  blockHandlers,
3954
4032
  prettyHtml,
3955
- registry
4033
+ registry,
4034
+ softBreakStyle,
4035
+ inTableCell
3956
4036
  );
3957
4037
  }
3958
4038
  }
@@ -3986,13 +4066,17 @@ function renderInlineText2(text, attributes) {
3986
4066
  }
3987
4067
  return result;
3988
4068
  }
3989
- function renderEmbed2(embed, attributes, customRenderers, useLatexDelimiters = false, blockHandlers, prettyHtml = false, registry) {
4069
+ function renderEmbed2(embed, attributes, customRenderers, useLatexDelimiters = false, blockHandlers, prettyHtml = false, registry, softBreakStyle = "spaces", inTableCell = false) {
3990
4070
  const entries = Object.entries(embed);
3991
4071
  if (entries.length === 0) return "";
3992
4072
  const firstEntry = entries[0];
3993
4073
  if (!firstEntry) return "";
3994
4074
  const embedType = firstEntry[0];
3995
4075
  const embedValue = firstEntry[1];
4076
+ if (embedType === "softBreak") {
4077
+ if (inTableCell) return "<br>";
4078
+ return softBreakStyle === "html" ? "<br>" : " \n";
4079
+ }
3996
4080
  if (embedType === "block" && blockHandlers) {
3997
4081
  const blockData = embedValue;
3998
4082
  if (blockData && typeof blockData.type === "string") {
@@ -4481,7 +4565,7 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4481
4565
  footnoteDefinitions.set(node.identifier ?? "", node);
4482
4566
  break;
4483
4567
  case "break":
4484
- context.pushNewline();
4568
+ context.pushEmbed({ softBreak: true });
4485
4569
  break;
4486
4570
  case "html": {
4487
4571
  const htmlContent = node.value ?? "";
@@ -4787,8 +4871,8 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4787
4871
  context.pushNewline();
4788
4872
  return;
4789
4873
  }
4790
- if (/^<br\s*\/?>$/i.test(html)) {
4791
- context.pushNewline();
4874
+ if (/^<br\b[^>]*\/?>$/i.test(html)) {
4875
+ context.pushEmbed({ softBreak: true });
4792
4876
  return;
4793
4877
  }
4794
4878
  }
@@ -4938,6 +5022,7 @@ export {
4938
5022
  sizeFormat,
4939
5023
  slugify,
4940
5024
  slugifyWithDedup,
5025
+ softBreakFormat,
4941
5026
  strikeFormat,
4942
5027
  subscriptFormat,
4943
5028
  superscriptFormat,