@scrider/formatter 1.4.3 → 1.5.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
@@ -1296,6 +1296,8 @@ declare function cloneDelta(delta: Delta): Delta;
1296
1296
  declare const SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
1297
1297
  /** Space after plain paragraph (`margin-bottom`) on `\n` (Settings Apply). */
1298
1298
  declare const SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
1299
+ /** Space before plain paragraph (`margin-top`) on `\n` (Settings Apply). */
1300
+ declare const SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
1299
1301
  /** Block tags that receive line spacing (not headings). */
1300
1302
  declare const LINE_HEIGHT_BLOCK_TAGS: Set<string>;
1301
1303
  /** Block tags that receive paragraph spacing after (plain `<p>` only). */
@@ -1310,19 +1312,33 @@ declare function parseScriderLineHeightMultiplier(value: string): number | undef
1310
1312
  */
1311
1313
  declare function blockLineHeightStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1312
1314
  /**
1313
- * Parse `margin-bottom` in em from Delta / inline CSS (`0.5em`, `0.5`).
1315
+ * Parse paragraph margin in em from Delta / inline CSS (`0.5em`, `0.5`).
1314
1316
  */
1315
- declare function parseScriderMarginAfterEm(value: string): number | undefined;
1317
+ declare function parseScriderMarginEm(value: string): number | undefined;
1318
+ /** Same parser as {@link parseScriderMarginEm} for margin-after values. */
1319
+ declare const parseScriderMarginAfterEm: typeof parseScriderMarginEm;
1320
+ /** Same parser as {@link parseScriderMarginEm} for margin-before values. */
1321
+ declare const parseScriderMarginBeforeEm: typeof parseScriderMarginEm;
1322
+ /**
1323
+ * Paragraph spacing before/after on plain `<p>`: block attrs → documentPresentation → none.
1324
+ * When only after is set, adds `margin-top:0` for Word paste compatibility.
1325
+ */
1326
+ declare function blockParagraphMarginStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1316
1327
  /**
1317
1328
  * Paragraph spacing after: `scrider-margin-after` on the line → documentPresentation → none.
1318
1329
  */
1319
1330
  declare function blockMarginAfterStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1331
+ /**
1332
+ * Paragraph spacing before: `scrider-margin-before` on the line → documentPresentation → none.
1333
+ */
1334
+ declare function blockMarginBeforeStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1320
1335
 
1321
1336
  /**
1322
1337
  * Document-level HTML presentation for deltaToHtml (clipboard, export).
1323
1338
  * Mirrors editor Settings when block attrs are absent.
1324
1339
  * Per-block line spacing: {@link blockLineHeightStyleParts} (`scrider-line-height` on `\n`).
1325
- * Per-block paragraph spacing: {@link blockMarginAfterStyleParts} (`scrider-margin-after` on `\n`).
1340
+ * Per-block paragraph spacing: {@link blockParagraphMarginStyleParts}
1341
+ * (`scrider-margin-before` / `scrider-margin-after` on `\n`).
1326
1342
  */
1327
1343
 
1328
1344
  interface DocumentPresentation {
@@ -1330,6 +1346,8 @@ interface DocumentPresentation {
1330
1346
  lineSpacing?: number;
1331
1347
  /** Space after plain paragraphs in em, e.g. 0.5 */
1332
1348
  paragraphSpacingAfterEm?: number;
1349
+ /** Space before plain paragraphs in em, e.g. 0.5 */
1350
+ paragraphSpacingBeforeEm?: number;
1333
1351
  /** First-line indent in cm on `<p>` only (lists: use listBlockIndentCm). */
1334
1352
  textIndentCm?: number;
1335
1353
  /** Extra left padding on top-level `<ul>`/`<ol>` — shifts marker + text as a block. */
@@ -1338,6 +1356,7 @@ interface DocumentPresentation {
1338
1356
  interface ResolvedDocumentPresentation {
1339
1357
  lineSpacing: number | undefined;
1340
1358
  paragraphSpacingAfterEm: number | undefined;
1359
+ paragraphSpacingBeforeEm: number | undefined;
1341
1360
  textIndentCm: number | undefined;
1342
1361
  listBlockIndentCm: number | undefined;
1343
1362
  }
@@ -1925,4 +1944,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1925
1944
  */
1926
1945
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1927
1946
 
1928
- 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 DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1947
+ 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 DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -1296,6 +1296,8 @@ declare function cloneDelta(delta: Delta): Delta;
1296
1296
  declare const SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
1297
1297
  /** Space after plain paragraph (`margin-bottom`) on `\n` (Settings Apply). */
1298
1298
  declare const SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
1299
+ /** Space before plain paragraph (`margin-top`) on `\n` (Settings Apply). */
1300
+ declare const SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
1299
1301
  /** Block tags that receive line spacing (not headings). */
1300
1302
  declare const LINE_HEIGHT_BLOCK_TAGS: Set<string>;
1301
1303
  /** Block tags that receive paragraph spacing after (plain `<p>` only). */
@@ -1310,19 +1312,33 @@ declare function parseScriderLineHeightMultiplier(value: string): number | undef
1310
1312
  */
1311
1313
  declare function blockLineHeightStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1312
1314
  /**
1313
- * Parse `margin-bottom` in em from Delta / inline CSS (`0.5em`, `0.5`).
1315
+ * Parse paragraph margin in em from Delta / inline CSS (`0.5em`, `0.5`).
1314
1316
  */
1315
- declare function parseScriderMarginAfterEm(value: string): number | undefined;
1317
+ declare function parseScriderMarginEm(value: string): number | undefined;
1318
+ /** Same parser as {@link parseScriderMarginEm} for margin-after values. */
1319
+ declare const parseScriderMarginAfterEm: typeof parseScriderMarginEm;
1320
+ /** Same parser as {@link parseScriderMarginEm} for margin-before values. */
1321
+ declare const parseScriderMarginBeforeEm: typeof parseScriderMarginEm;
1322
+ /**
1323
+ * Paragraph spacing before/after on plain `<p>`: block attrs → documentPresentation → none.
1324
+ * When only after is set, adds `margin-top:0` for Word paste compatibility.
1325
+ */
1326
+ declare function blockParagraphMarginStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1316
1327
  /**
1317
1328
  * Paragraph spacing after: `scrider-margin-after` on the line → documentPresentation → none.
1318
1329
  */
1319
1330
  declare function blockMarginAfterStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1331
+ /**
1332
+ * Paragraph spacing before: `scrider-margin-before` on the line → documentPresentation → none.
1333
+ */
1334
+ declare function blockMarginBeforeStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1320
1335
 
1321
1336
  /**
1322
1337
  * Document-level HTML presentation for deltaToHtml (clipboard, export).
1323
1338
  * Mirrors editor Settings when block attrs are absent.
1324
1339
  * Per-block line spacing: {@link blockLineHeightStyleParts} (`scrider-line-height` on `\n`).
1325
- * Per-block paragraph spacing: {@link blockMarginAfterStyleParts} (`scrider-margin-after` on `\n`).
1340
+ * Per-block paragraph spacing: {@link blockParagraphMarginStyleParts}
1341
+ * (`scrider-margin-before` / `scrider-margin-after` on `\n`).
1326
1342
  */
1327
1343
 
1328
1344
  interface DocumentPresentation {
@@ -1330,6 +1346,8 @@ interface DocumentPresentation {
1330
1346
  lineSpacing?: number;
1331
1347
  /** Space after plain paragraphs in em, e.g. 0.5 */
1332
1348
  paragraphSpacingAfterEm?: number;
1349
+ /** Space before plain paragraphs in em, e.g. 0.5 */
1350
+ paragraphSpacingBeforeEm?: number;
1333
1351
  /** First-line indent in cm on `<p>` only (lists: use listBlockIndentCm). */
1334
1352
  textIndentCm?: number;
1335
1353
  /** Extra left padding on top-level `<ul>`/`<ol>` — shifts marker + text as a block. */
@@ -1338,6 +1356,7 @@ interface DocumentPresentation {
1338
1356
  interface ResolvedDocumentPresentation {
1339
1357
  lineSpacing: number | undefined;
1340
1358
  paragraphSpacingAfterEm: number | undefined;
1359
+ paragraphSpacingBeforeEm: number | undefined;
1341
1360
  textIndentCm: number | undefined;
1342
1361
  listBlockIndentCm: number | undefined;
1343
1362
  }
@@ -1925,4 +1944,4 @@ declare function isTableNewlineOp(op: Op | undefined): boolean;
1925
1944
  */
1926
1945
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
1927
1946
 
1928
- 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 DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
1947
+ 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 DocumentPresentation, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -2496,6 +2496,7 @@ function slugifyWithDedup(text, usedSlugs) {
2496
2496
  // src/conversion/html/block-presentation.ts
2497
2497
  var SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
2498
2498
  var SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
2499
+ var SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
2499
2500
  var LINE_HEIGHT_BLOCK_TAGS = /* @__PURE__ */ new Set(["p", "li", "blockquote"]);
2500
2501
  var PARAGRAPH_SPACING_BLOCK_TAGS = /* @__PURE__ */ new Set(["p"]);
2501
2502
  function parseScriderLineHeightMultiplier(value) {
@@ -2526,7 +2527,7 @@ function blockLineHeightStyleParts(tag, blockAttributes, resolved) {
2526
2527
  }
2527
2528
  return [];
2528
2529
  }
2529
- function parseScriderMarginAfterEm(value) {
2530
+ function parseScriderMarginEm(value) {
2530
2531
  const trimmed = value.trim();
2531
2532
  if (!trimmed) return void 0;
2532
2533
  const emMatch = trimmed.match(/^(-?\d+(?:\.\d+)?)\s*em$/i);
@@ -2539,20 +2540,59 @@ function parseScriderMarginAfterEm(value) {
2539
2540
  if (Number.isFinite(n) && n >= 0) return n;
2540
2541
  return void 0;
2541
2542
  }
2542
- function marginAfterStyleParts(em) {
2543
- return [`margin-top:0`, `margin-bottom:${em}em`];
2543
+ var parseScriderMarginAfterEm = parseScriderMarginEm;
2544
+ var parseScriderMarginBeforeEm = parseScriderMarginEm;
2545
+ function resolveParagraphMarginEm(blockAttributes, blockKey, documentEm) {
2546
+ const raw = blockAttributes?.[blockKey];
2547
+ if (typeof raw === "string") {
2548
+ const fromBlock = parseScriderMarginEm(raw);
2549
+ if (fromBlock !== void 0) return fromBlock;
2550
+ }
2551
+ return documentEm;
2544
2552
  }
2545
- function blockMarginAfterStyleParts(tag, blockAttributes, resolved) {
2553
+ function blockParagraphMarginStyleParts(tag, blockAttributes, resolved) {
2546
2554
  if (!PARAGRAPH_SPACING_BLOCK_TAGS.has(tag)) return [];
2547
- const raw = blockAttributes?.[SCRIDER_MARGIN_AFTER_KEY];
2548
- if (typeof raw === "string") {
2549
- const fromBlock = parseScriderMarginAfterEm(raw);
2550
- if (fromBlock !== void 0) return marginAfterStyleParts(fromBlock);
2555
+ const marginTop = resolveParagraphMarginEm(
2556
+ blockAttributes,
2557
+ SCRIDER_MARGIN_BEFORE_KEY,
2558
+ resolved?.paragraphSpacingBeforeEm
2559
+ );
2560
+ const marginBottom = resolveParagraphMarginEm(
2561
+ blockAttributes,
2562
+ SCRIDER_MARGIN_AFTER_KEY,
2563
+ resolved?.paragraphSpacingAfterEm
2564
+ );
2565
+ if (marginTop === void 0 && marginBottom === void 0) return [];
2566
+ const parts = [];
2567
+ if (marginTop !== void 0) {
2568
+ parts.push(`margin-top:${marginTop}em`);
2569
+ } else if (marginBottom !== void 0) {
2570
+ parts.push("margin-top:0");
2551
2571
  }
2552
- if (resolved?.paragraphSpacingAfterEm !== void 0) {
2553
- return marginAfterStyleParts(resolved.paragraphSpacingAfterEm);
2572
+ if (marginBottom !== void 0) {
2573
+ parts.push(`margin-bottom:${marginBottom}em`);
2554
2574
  }
2555
- return [];
2575
+ return parts;
2576
+ }
2577
+ function blockMarginAfterStyleParts(tag, blockAttributes, resolved) {
2578
+ if (!PARAGRAPH_SPACING_BLOCK_TAGS.has(tag)) return [];
2579
+ const marginBottom = resolveParagraphMarginEm(
2580
+ blockAttributes,
2581
+ SCRIDER_MARGIN_AFTER_KEY,
2582
+ resolved?.paragraphSpacingAfterEm
2583
+ );
2584
+ if (marginBottom === void 0) return [];
2585
+ return ["margin-top:0", `margin-bottom:${marginBottom}em`];
2586
+ }
2587
+ function blockMarginBeforeStyleParts(tag, blockAttributes, resolved) {
2588
+ if (!PARAGRAPH_SPACING_BLOCK_TAGS.has(tag)) return [];
2589
+ const marginTop = resolveParagraphMarginEm(
2590
+ blockAttributes,
2591
+ SCRIDER_MARGIN_BEFORE_KEY,
2592
+ resolved?.paragraphSpacingBeforeEm
2593
+ );
2594
+ if (marginTop === void 0) return [];
2595
+ return [`margin-top:${marginTop}em`];
2556
2596
  }
2557
2597
 
2558
2598
  // src/conversion/html/document-presentation.ts
@@ -2562,10 +2602,17 @@ function resolveDocumentPresentation(presentation) {
2562
2602
  const textIndentCm = typeof presentation.textIndentCm === "number" && presentation.textIndentCm > 0 ? presentation.textIndentCm : void 0;
2563
2603
  const listBlockIndentCm = typeof presentation.listBlockIndentCm === "number" && presentation.listBlockIndentCm > 0 ? presentation.listBlockIndentCm : void 0;
2564
2604
  const paragraphSpacingAfterEm = typeof presentation.paragraphSpacingAfterEm === "number" && Number.isFinite(presentation.paragraphSpacingAfterEm) && presentation.paragraphSpacingAfterEm >= 0 ? presentation.paragraphSpacingAfterEm : void 0;
2565
- if (lineSpacing === void 0 && paragraphSpacingAfterEm === void 0 && textIndentCm === void 0 && listBlockIndentCm === void 0) {
2605
+ const paragraphSpacingBeforeEm = typeof presentation.paragraphSpacingBeforeEm === "number" && Number.isFinite(presentation.paragraphSpacingBeforeEm) && presentation.paragraphSpacingBeforeEm >= 0 ? presentation.paragraphSpacingBeforeEm : void 0;
2606
+ if (lineSpacing === void 0 && paragraphSpacingAfterEm === void 0 && paragraphSpacingBeforeEm === void 0 && textIndentCm === void 0 && listBlockIndentCm === void 0) {
2566
2607
  return void 0;
2567
2608
  }
2568
- return { lineSpacing, paragraphSpacingAfterEm, textIndentCm, listBlockIndentCm };
2609
+ return {
2610
+ lineSpacing,
2611
+ paragraphSpacingAfterEm,
2612
+ paragraphSpacingBeforeEm,
2613
+ textIndentCm,
2614
+ listBlockIndentCm
2615
+ };
2569
2616
  }
2570
2617
  var TEXT_INDENT_TAGS = /* @__PURE__ */ new Set(["p"]);
2571
2618
  function documentPresentationListWrapperStyleParts(resolved) {
@@ -2583,7 +2630,7 @@ function documentPresentationStyleParts(tag, resolved) {
2583
2630
  function blockPresentationStyleParts(tag, blockAttributes, resolved) {
2584
2631
  return [
2585
2632
  ...blockLineHeightStyleParts(tag, blockAttributes, resolved),
2586
- ...blockMarginAfterStyleParts(tag, blockAttributes, resolved),
2633
+ ...blockParagraphMarginStyleParts(tag, blockAttributes, resolved),
2587
2634
  ...documentPresentationStyleParts(tag, resolved)
2588
2635
  ];
2589
2636
  }
@@ -2685,7 +2732,16 @@ function deltaToHtml(delta, options = {}) {
2685
2732
  counters = [];
2686
2733
  const codeLines = collectCodeBlockLines(lines, i);
2687
2734
  const language = getCodeBlockLanguage(line.attributes);
2688
- html += renderCodeBlock(codeLines, language, embedRenderers, pretty, blockHandlers, options);
2735
+ const codeBlockId = getCodeBlockId(line.attributes);
2736
+ html += renderCodeBlock(
2737
+ codeLines,
2738
+ language,
2739
+ embedRenderers,
2740
+ pretty,
2741
+ blockHandlers,
2742
+ options,
2743
+ codeBlockId
2744
+ );
2689
2745
  i += codeLines.length - 1;
2690
2746
  continue;
2691
2747
  }
@@ -2956,11 +3012,13 @@ function collectCodeBlockLines(lines, startIndex) {
2956
3012
  const startLine = lines[startIndex];
2957
3013
  if (!startLine) return codeLines;
2958
3014
  const startLang = getCodeBlockLanguage(startLine.attributes);
3015
+ const startId = getCodeBlockId(startLine.attributes);
2959
3016
  for (let i = startIndex; i < lines.length; i++) {
2960
3017
  const line = lines[i];
2961
3018
  if (!line || !line.attributes?.["code-block"]) break;
2962
3019
  const lang = getCodeBlockLanguage(line.attributes);
2963
- if (i > startIndex && lang !== startLang) break;
3020
+ const id = getCodeBlockId(line.attributes);
3021
+ if (i > startIndex && (lang !== startLang || id !== startId)) break;
2964
3022
  codeLines.push(line);
2965
3023
  }
2966
3024
  return codeLines;
@@ -2973,14 +3031,20 @@ function getCodeBlockLanguage(attributes) {
2973
3031
  }
2974
3032
  return void 0;
2975
3033
  }
2976
- function renderCodeBlock(codeLines, language, embedRenderers, pretty, blockHandlers, options) {
3034
+ function getCodeBlockId(attributes) {
3035
+ if (!attributes) return void 0;
3036
+ const id = attributes["code-block-id"];
3037
+ return typeof id === "string" ? id : void 0;
3038
+ }
3039
+ function renderCodeBlock(codeLines, language, embedRenderers, pretty, blockHandlers, options, codeBlockId) {
2977
3040
  const lineContents = codeLines.map(
2978
3041
  (line) => renderLineContent(line.ops, embedRenderers, blockHandlers, options)
2979
3042
  );
2980
3043
  const code = lineContents.join("\n");
2981
3044
  const langClass = language ? ` class="language-${escapeHtml(language)}"` : "";
2982
3045
  const langAttr = language ? ` data-language="${escapeHtml(language)}"` : "";
2983
- const html = `<pre${langAttr}><code${langClass}>${code}
3046
+ const idAttr = codeBlockId ? ` data-code-block-id="${escapeHtml(codeBlockId)}"` : "";
3047
+ const html = `<pre${langAttr}${idAttr}><code${langClass}>${code}
2984
3048
  </code></pre>`;
2985
3049
  return pretty ? html + "\n" : html;
2986
3050
  }
@@ -3194,6 +3258,7 @@ function htmlToDelta(html, options = {}) {
3194
3258
  let currentBlockAttributes = {};
3195
3259
  let pendingText = "";
3196
3260
  let atLineStart = true;
3261
+ let codeBlockIdSeq = 0;
3197
3262
  const context = {
3198
3263
  delta,
3199
3264
  get attributes() {
@@ -3406,6 +3471,8 @@ function htmlToDelta(html, options = {}) {
3406
3471
  }
3407
3472
  const codeBlockValue = language || true;
3408
3473
  currentBlockAttributes["code-block"] = codeBlockValue;
3474
+ const existingId = element.getAttribute("data-code-block-id");
3475
+ currentBlockAttributes["code-block-id"] = existingId || `cb-${++codeBlockIdSeq}`;
3409
3476
  const sourceElement = codeElement || element;
3410
3477
  const rawText = sourceElement.textContent ?? "";
3411
3478
  const text = rawText.endsWith("\n") ? rawText.slice(0, -1) : rawText;
@@ -4141,11 +4208,13 @@ function collectCodeBlock(lines, startIndex) {
4141
4208
  const startLine = lines[startIndex];
4142
4209
  if (!startLine) return codeLines;
4143
4210
  const startLang = getCodeBlockLanguage2(startLine.attributes);
4211
+ const startId = getCodeBlockId2(startLine.attributes);
4144
4212
  for (let i = startIndex; i < lines.length; i++) {
4145
4213
  const line = lines[i];
4146
4214
  if (!line || !line.attributes["code-block"]) break;
4147
4215
  const lang = getCodeBlockLanguage2(line.attributes);
4148
- if (i > startIndex && lang !== startLang) break;
4216
+ const id = getCodeBlockId2(line.attributes);
4217
+ if (i > startIndex && (lang !== startLang || id !== startId)) break;
4149
4218
  codeLines.push(line);
4150
4219
  }
4151
4220
  return codeLines;
@@ -4263,6 +4332,10 @@ function getCodeBlockLanguage2(attributes) {
4263
4332
  }
4264
4333
  return void 0;
4265
4334
  }
4335
+ function getCodeBlockId2(attributes) {
4336
+ const id = attributes["code-block-id"];
4337
+ return typeof id === "string" ? id : void 0;
4338
+ }
4266
4339
  function renderLineContent2(ops, embedRenderers, inCodeBlock, useLatexDelimiters = false, blockHandlers, prettyHtml = false, registry, softBreakStyle = "spaces", inTableCell = false) {
4267
4340
  let result = "";
4268
4341
  for (const op of ops) {
@@ -4616,6 +4689,7 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4616
4689
  let pendingText = "";
4617
4690
  const spanAttrStack = [];
4618
4691
  const footnoteDefinitions = /* @__PURE__ */ new Map();
4692
+ let codeBlockIdSeq = 0;
4619
4693
  const context = {
4620
4694
  delta,
4621
4695
  pushText(text, attrs) {
@@ -4931,7 +5005,8 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4931
5005
  }
4932
5006
  const lines = code.split("\n");
4933
5007
  const codeBlockAttr = {
4934
- "code-block": lang ?? true
5008
+ "code-block": lang ?? true,
5009
+ "code-block-id": `cb-${++codeBlockIdSeq}`
4935
5010
  };
4936
5011
  for (const line of lines) {
4937
5012
  context.pushText(line);
@@ -4945,7 +5020,10 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
4945
5020
  context.pushNewline();
4946
5021
  } else {
4947
5022
  const lines = value.split("\n");
4948
- const mathBlockAttr = { "code-block": "math" };
5023
+ const mathBlockAttr = {
5024
+ "code-block": "math",
5025
+ "code-block-id": `cb-${++codeBlockIdSeq}`
5026
+ };
4949
5027
  for (const line of lines) {
4950
5028
  context.pushText(line);
4951
5029
  context.pushNewline(mathBlockAttr);
@@ -5222,12 +5300,15 @@ export {
5222
5300
  Registry,
5223
5301
  SCRIDER_LINE_HEIGHT_KEY,
5224
5302
  SCRIDER_MARGIN_AFTER_KEY,
5303
+ SCRIDER_MARGIN_BEFORE_KEY,
5225
5304
  alertBlockHandler,
5226
5305
  alignFormat,
5227
5306
  backgroundFormat,
5228
5307
  blockFormat,
5229
5308
  blockLineHeightStyleParts,
5230
5309
  blockMarginAfterStyleParts,
5310
+ blockMarginBeforeStyleParts,
5311
+ blockParagraphMarginStyleParts,
5231
5312
  blockPresentationStyleParts,
5232
5313
  blockquoteFormat,
5233
5314
  boldFormat,
@@ -5281,6 +5362,8 @@ export {
5281
5362
  normalizeDelta,
5282
5363
  parseScriderLineHeightMultiplier,
5283
5364
  parseScriderMarginAfterEm,
5365
+ parseScriderMarginBeforeEm,
5366
+ parseScriderMarginEm,
5284
5367
  preloadRemark,
5285
5368
  resolveDocumentPresentation,
5286
5369
  resolveTablePresentation,