@scrider/formatter 1.8.7 → 1.10.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
@@ -2,6 +2,50 @@ import { AttributeMap, Op, Delta, InsertOp } from '@scrider/delta';
2
2
  export * from '@scrider/delta';
3
3
  export { InsertOp as ContentOp } from '@scrider/delta';
4
4
 
5
+ /**
6
+ * Document-level metadata (Scrider format extension).
7
+ *
8
+ * Concrete schema for the opaque `scrider-metadata` sibling field defined in
9
+ * `@scrider/delta` (`ScriderDocument`). It carries document-wide defaults ONCE
10
+ * (line spacing, paragraph spacing, indent, heading policy, fonts) instead of
11
+ * duplicating them as block attributes on every `\n`.
12
+ *
13
+ * Layering:
14
+ * - `@scrider/delta` treats the value as opaque `Record<string, unknown>` — it
15
+ * never participates in `length()`, selection indices, OT, or partial copy/paste.
16
+ * - `@scrider/formatter` is the CONTRACT OWNER of this concrete shape.
17
+ * - `@scrider/editor-core` / `@scrider/editor-react` consume it (state, CSS vars,
18
+ * export projection).
19
+ *
20
+ * All fields are optional and additive: extending the interface never changes the
21
+ * op-stream and is backward compatible. Presentation-relevant fields are projected
22
+ * to HTML via {@link documentMetadataToPresentation}; heading/font policy is applied
23
+ * by upstream layers (CSS vars, bake) and is intentionally NOT part of the inline
24
+ * export projection.
25
+ */
26
+ interface ScriderDocumentMetadata {
27
+ /** Line spacing multiplier, e.g. `1.5`. */
28
+ lineSpacing?: number;
29
+ /** Space before plain paragraphs in em, e.g. `0.5`. */
30
+ paragraphSpacingBeforeEm?: number;
31
+ /** Space after plain paragraphs in em, e.g. `0.5`. */
32
+ paragraphSpacingAfterEm?: number;
33
+ /** First-line indent in cm on `<p>` (lists use {@link listBlockIndentCm}). */
34
+ textIndentCm?: number;
35
+ /** Extra left indent in cm on top-level `<ul>`/`<ol>` (shifts marker + text). */
36
+ listBlockIndentCm?: number;
37
+ /** Document heading horizontal alignment policy. */
38
+ headingAlign?: 'left' | 'center' | 'right';
39
+ /** Document heading bold policy. */
40
+ headingBold?: boolean;
41
+ /** Named heading size-grid preset id (schema defined upstream). */
42
+ headingSizeGridPreset?: string;
43
+ /** Default document font family (bare family name, e.g. `Georgia`). */
44
+ defaultFont?: string;
45
+ /** Default document font size as a CSS length, e.g. `12pt`. */
46
+ defaultFontSize?: string;
47
+ }
48
+
5
49
  /**
6
50
  * DOM Adapter Interface
7
51
  *
@@ -192,6 +236,21 @@ interface Format<T = unknown> {
192
236
  * Scope of the format
193
237
  */
194
238
  readonly scope: FormatScope;
239
+ /**
240
+ * Block-level marker for `embed` formats.
241
+ *
242
+ * A block-level embed (e.g. `divider` → `<hr>`) stands on its own line: in
243
+ * Delta it is the `{embed}` op followed by its own paragraph-terminating
244
+ * `\n`, and in HTML it is NOT wrapped in a `<p>`. Inline embeds (image,
245
+ * formula, softBreak…) live inside a paragraph and rely on the surrounding
246
+ * block's `\n`, so they leave this unset.
247
+ *
248
+ * `htmlToDelta` uses this to emit the block `\n` after matching a block-level
249
+ * embed via the registry (otherwise the following content glues onto the
250
+ * embed's line — see the `<hr>` round-trip regression). Ignored for
251
+ * non-embed scopes.
252
+ */
253
+ readonly blockLevel?: boolean;
195
254
  /**
196
255
  * Normalize value to canonical form
197
256
  *
@@ -1368,8 +1427,12 @@ declare const SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
1368
1427
  declare const SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
1369
1428
  /** Space before plain paragraph (`margin-top`) on `\n` (Settings Apply). */
1370
1429
  declare const SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
1430
+ /** Per-paragraph first-line indent stored on `\n` (Word paste, Settings Apply). */
1431
+ declare const SCRIDER_TEXT_INDENT_KEY = "scrider-text-indent";
1371
1432
  /** Block tags that receive line spacing (not headings). */
1372
1433
  declare const LINE_HEIGHT_BLOCK_TAGS: Set<string>;
1434
+ /** Block tags that receive first-line indent (plain `<p>` only). */
1435
+ declare const TEXT_INDENT_BLOCK_TAGS: Set<string>;
1373
1436
  /** Block tags that receive paragraph spacing after (plain `<p>` only). */
1374
1437
  declare const PARAGRAPH_SPACING_BLOCK_TAGS: Set<string>;
1375
1438
  /**
@@ -1381,6 +1444,14 @@ declare function parseScriderLineHeightMultiplier(value: string): number | undef
1381
1444
  * Line-height for a block tag: `scrider-line-height` on the line → documentPresentation → none.
1382
1445
  */
1383
1446
  declare function blockLineHeightStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1447
+ /**
1448
+ * First-line indent for a plain `<p>`: `scrider-text-indent` on the line (baked,
1449
+ * e.g. Word paste) → documentPresentation `textIndentCm` → none.
1450
+ *
1451
+ * The block attr wins over the document default (mirrors line-height / margin
1452
+ * resolution). The raw block value is a CSS length string (e.g. `1.25cm`).
1453
+ */
1454
+ declare function blockTextIndentStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1384
1455
  /**
1385
1456
  * Parse paragraph margin in em from Delta / inline CSS (`0.5em`, `0.5`).
1386
1457
  */
@@ -1431,10 +1502,32 @@ interface ResolvedDocumentPresentation {
1431
1502
  listBlockIndentCm: number | undefined;
1432
1503
  }
1433
1504
  declare function resolveDocumentPresentation(presentation?: DocumentPresentation): ResolvedDocumentPresentation | undefined;
1434
- /** Document-level styles only (line-height merged via {@link blockLineHeightStyleParts}). */
1505
+ /**
1506
+ * Project {@link ScriderDocumentMetadata} onto an HTML {@link DocumentPresentation}
1507
+ * (export / clipboard inline CSS).
1508
+ *
1509
+ * Only presentation-relevant fields are mapped (line/paragraph spacing, indent).
1510
+ * Heading policy (align/bold/size grid) and fonts are applied by upstream layers
1511
+ * (CSS vars in the editor, bake into block/inline attrs) and are deliberately not
1512
+ * part of the inline projection. Returns `undefined` when nothing maps, so callers
1513
+ * can fall back cleanly.
1514
+ */
1515
+ declare function documentMetadataToPresentation(metadata: ScriderDocumentMetadata | undefined): DocumentPresentation | undefined;
1516
+ /**
1517
+ * Document-level first-line indent only (no per-block attr).
1518
+ *
1519
+ * @deprecated Superseded by {@link blockTextIndentStyleParts}, which resolves the
1520
+ * per-block `scrider-text-indent` attr with the document default as fallback and
1521
+ * is what {@link blockPresentationStyleParts} now uses. Kept exported for
1522
+ * backward compatibility; output is unchanged for document-only callers.
1523
+ */
1435
1524
  declare function documentPresentationStyleParts(tag: string, resolved: ResolvedDocumentPresentation | undefined): string[];
1436
1525
  /**
1437
1526
  * All block presentation styles for deltaToHtml: Delta attrs, then document defaults.
1527
+ *
1528
+ * First-line indent now resolves per-block: baked `scrider-text-indent` wins over
1529
+ * the document `textIndentCm` default (previously the per-block attr was dropped
1530
+ * on export). Output for document-only paragraphs is unchanged.
1438
1531
  */
1439
1532
  declare function blockPresentationStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1440
1533
 
@@ -1549,6 +1642,15 @@ interface DeltaToHtmlOptions {
1549
1642
  * Office/HTML export and clipboard. Does not change Delta.
1550
1643
  */
1551
1644
  documentPresentation?: DocumentPresentation;
1645
+ /**
1646
+ * Document-level metadata (Scrider format extension, `scrider-metadata`).
1647
+ *
1648
+ * When {@link documentPresentation} is not provided, the presentation-relevant
1649
+ * fields of this metadata are projected to inline CSS via
1650
+ * `documentMetadataToPresentation` (export/clipboard). An explicit
1651
+ * `documentPresentation` always takes precedence. Does not change Delta.
1652
+ */
1653
+ documentMetadata?: ScriderDocumentMetadata;
1552
1654
  /**
1553
1655
  * Cross-origin iframe isolation for embed formats (codeWidget, video iframe).
1554
1656
  * Default: both off — standard third-party iframes load with browser cookies.
@@ -1575,6 +1677,36 @@ interface DeltaToHtmlOptions {
1575
1677
  * ```
1576
1678
  */
1577
1679
  declare function deltaToHtml(delta: Delta, options?: DeltaToHtmlOptions): string;
1680
+ /**
1681
+ * Rendering profile: how a Delta is turned into markup.
1682
+ *
1683
+ * - `export`: full fidelity for clipboard / Office / HTML export. Document-level
1684
+ * presentation (line/paragraph spacing, first-line/list indent) and table
1685
+ * presentation are projected to inline CSS so the markup is self-contained.
1686
+ * - `editor`: markup for the editor's contenteditable surface. Document-level
1687
+ * presentation and table presentation are intentionally dropped — the editor
1688
+ * applies them live via CSS custom properties on the container, so inline
1689
+ * copies would double-apply and fight the Settings panel. Per-block baked
1690
+ * attributes (`scrider-line-height`, `scrider-margin-*`, `scrider-text-indent`)
1691
+ * still render, since they are part of the document content.
1692
+ */
1693
+ type RenderProfile = 'export' | 'editor';
1694
+ /**
1695
+ * Render a Delta with an explicit {@link RenderProfile}.
1696
+ *
1697
+ * Thin dispatcher over {@link deltaToHtml} (the export core). See
1698
+ * {@link RenderProfile} for how the profiles differ.
1699
+ */
1700
+ declare function renderDelta(delta: Delta, profile: RenderProfile, options?: DeltaToHtmlOptions): string;
1701
+ /**
1702
+ * Render a Delta for the editor's contenteditable surface (`editor` profile).
1703
+ *
1704
+ * Equivalent to `renderDelta(delta, 'editor', options)`. Document-level and table
1705
+ * presentation are dropped (applied live via CSS vars); the built-in `softBreak`
1706
+ * renderer already emits `<br data-scrider-embed>`, so the editor needs no
1707
+ * embed-renderer override.
1708
+ */
1709
+ declare function deltaToDom(delta: Delta, options?: DeltaToHtmlOptions): string;
1578
1710
 
1579
1711
  /**
1580
1712
  * HTML → Delta Conversion
@@ -2072,4 +2204,4 @@ declare function collectAdjacentTableLines<T extends {
2072
2204
  */
2073
2205
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
2074
2206
 
2075
- 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 CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, 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 TableBlockFloat, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, 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, isAdjacentSimpleTableGridBoundary, 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, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
2207
+ 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 CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type RenderProfile, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, SCRIDER_TEXT_INDENT_KEY, type SanitizeOptions, type ScriderDocumentMetadata, TEXT_INDENT_BLOCK_TAGS, type TableBlockData, type TableBlockFloat, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockTextIndentStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToDom, deltaToHtml, deltaToMarkdown, dividerFormat, documentMetadataToPresentation, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, renderDelta, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,50 @@ import { AttributeMap, Op, Delta, InsertOp } from '@scrider/delta';
2
2
  export * from '@scrider/delta';
3
3
  export { InsertOp as ContentOp } from '@scrider/delta';
4
4
 
5
+ /**
6
+ * Document-level metadata (Scrider format extension).
7
+ *
8
+ * Concrete schema for the opaque `scrider-metadata` sibling field defined in
9
+ * `@scrider/delta` (`ScriderDocument`). It carries document-wide defaults ONCE
10
+ * (line spacing, paragraph spacing, indent, heading policy, fonts) instead of
11
+ * duplicating them as block attributes on every `\n`.
12
+ *
13
+ * Layering:
14
+ * - `@scrider/delta` treats the value as opaque `Record<string, unknown>` — it
15
+ * never participates in `length()`, selection indices, OT, or partial copy/paste.
16
+ * - `@scrider/formatter` is the CONTRACT OWNER of this concrete shape.
17
+ * - `@scrider/editor-core` / `@scrider/editor-react` consume it (state, CSS vars,
18
+ * export projection).
19
+ *
20
+ * All fields are optional and additive: extending the interface never changes the
21
+ * op-stream and is backward compatible. Presentation-relevant fields are projected
22
+ * to HTML via {@link documentMetadataToPresentation}; heading/font policy is applied
23
+ * by upstream layers (CSS vars, bake) and is intentionally NOT part of the inline
24
+ * export projection.
25
+ */
26
+ interface ScriderDocumentMetadata {
27
+ /** Line spacing multiplier, e.g. `1.5`. */
28
+ lineSpacing?: number;
29
+ /** Space before plain paragraphs in em, e.g. `0.5`. */
30
+ paragraphSpacingBeforeEm?: number;
31
+ /** Space after plain paragraphs in em, e.g. `0.5`. */
32
+ paragraphSpacingAfterEm?: number;
33
+ /** First-line indent in cm on `<p>` (lists use {@link listBlockIndentCm}). */
34
+ textIndentCm?: number;
35
+ /** Extra left indent in cm on top-level `<ul>`/`<ol>` (shifts marker + text). */
36
+ listBlockIndentCm?: number;
37
+ /** Document heading horizontal alignment policy. */
38
+ headingAlign?: 'left' | 'center' | 'right';
39
+ /** Document heading bold policy. */
40
+ headingBold?: boolean;
41
+ /** Named heading size-grid preset id (schema defined upstream). */
42
+ headingSizeGridPreset?: string;
43
+ /** Default document font family (bare family name, e.g. `Georgia`). */
44
+ defaultFont?: string;
45
+ /** Default document font size as a CSS length, e.g. `12pt`. */
46
+ defaultFontSize?: string;
47
+ }
48
+
5
49
  /**
6
50
  * DOM Adapter Interface
7
51
  *
@@ -192,6 +236,21 @@ interface Format<T = unknown> {
192
236
  * Scope of the format
193
237
  */
194
238
  readonly scope: FormatScope;
239
+ /**
240
+ * Block-level marker for `embed` formats.
241
+ *
242
+ * A block-level embed (e.g. `divider` → `<hr>`) stands on its own line: in
243
+ * Delta it is the `{embed}` op followed by its own paragraph-terminating
244
+ * `\n`, and in HTML it is NOT wrapped in a `<p>`. Inline embeds (image,
245
+ * formula, softBreak…) live inside a paragraph and rely on the surrounding
246
+ * block's `\n`, so they leave this unset.
247
+ *
248
+ * `htmlToDelta` uses this to emit the block `\n` after matching a block-level
249
+ * embed via the registry (otherwise the following content glues onto the
250
+ * embed's line — see the `<hr>` round-trip regression). Ignored for
251
+ * non-embed scopes.
252
+ */
253
+ readonly blockLevel?: boolean;
195
254
  /**
196
255
  * Normalize value to canonical form
197
256
  *
@@ -1368,8 +1427,12 @@ declare const SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
1368
1427
  declare const SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
1369
1428
  /** Space before plain paragraph (`margin-top`) on `\n` (Settings Apply). */
1370
1429
  declare const SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
1430
+ /** Per-paragraph first-line indent stored on `\n` (Word paste, Settings Apply). */
1431
+ declare const SCRIDER_TEXT_INDENT_KEY = "scrider-text-indent";
1371
1432
  /** Block tags that receive line spacing (not headings). */
1372
1433
  declare const LINE_HEIGHT_BLOCK_TAGS: Set<string>;
1434
+ /** Block tags that receive first-line indent (plain `<p>` only). */
1435
+ declare const TEXT_INDENT_BLOCK_TAGS: Set<string>;
1373
1436
  /** Block tags that receive paragraph spacing after (plain `<p>` only). */
1374
1437
  declare const PARAGRAPH_SPACING_BLOCK_TAGS: Set<string>;
1375
1438
  /**
@@ -1381,6 +1444,14 @@ declare function parseScriderLineHeightMultiplier(value: string): number | undef
1381
1444
  * Line-height for a block tag: `scrider-line-height` on the line → documentPresentation → none.
1382
1445
  */
1383
1446
  declare function blockLineHeightStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1447
+ /**
1448
+ * First-line indent for a plain `<p>`: `scrider-text-indent` on the line (baked,
1449
+ * e.g. Word paste) → documentPresentation `textIndentCm` → none.
1450
+ *
1451
+ * The block attr wins over the document default (mirrors line-height / margin
1452
+ * resolution). The raw block value is a CSS length string (e.g. `1.25cm`).
1453
+ */
1454
+ declare function blockTextIndentStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1384
1455
  /**
1385
1456
  * Parse paragraph margin in em from Delta / inline CSS (`0.5em`, `0.5`).
1386
1457
  */
@@ -1431,10 +1502,32 @@ interface ResolvedDocumentPresentation {
1431
1502
  listBlockIndentCm: number | undefined;
1432
1503
  }
1433
1504
  declare function resolveDocumentPresentation(presentation?: DocumentPresentation): ResolvedDocumentPresentation | undefined;
1434
- /** Document-level styles only (line-height merged via {@link blockLineHeightStyleParts}). */
1505
+ /**
1506
+ * Project {@link ScriderDocumentMetadata} onto an HTML {@link DocumentPresentation}
1507
+ * (export / clipboard inline CSS).
1508
+ *
1509
+ * Only presentation-relevant fields are mapped (line/paragraph spacing, indent).
1510
+ * Heading policy (align/bold/size grid) and fonts are applied by upstream layers
1511
+ * (CSS vars in the editor, bake into block/inline attrs) and are deliberately not
1512
+ * part of the inline projection. Returns `undefined` when nothing maps, so callers
1513
+ * can fall back cleanly.
1514
+ */
1515
+ declare function documentMetadataToPresentation(metadata: ScriderDocumentMetadata | undefined): DocumentPresentation | undefined;
1516
+ /**
1517
+ * Document-level first-line indent only (no per-block attr).
1518
+ *
1519
+ * @deprecated Superseded by {@link blockTextIndentStyleParts}, which resolves the
1520
+ * per-block `scrider-text-indent` attr with the document default as fallback and
1521
+ * is what {@link blockPresentationStyleParts} now uses. Kept exported for
1522
+ * backward compatibility; output is unchanged for document-only callers.
1523
+ */
1435
1524
  declare function documentPresentationStyleParts(tag: string, resolved: ResolvedDocumentPresentation | undefined): string[];
1436
1525
  /**
1437
1526
  * All block presentation styles for deltaToHtml: Delta attrs, then document defaults.
1527
+ *
1528
+ * First-line indent now resolves per-block: baked `scrider-text-indent` wins over
1529
+ * the document `textIndentCm` default (previously the per-block attr was dropped
1530
+ * on export). Output for document-only paragraphs is unchanged.
1438
1531
  */
1439
1532
  declare function blockPresentationStyleParts(tag: string, blockAttributes: AttributeMap | undefined, resolved: ResolvedDocumentPresentation | undefined): string[];
1440
1533
 
@@ -1549,6 +1642,15 @@ interface DeltaToHtmlOptions {
1549
1642
  * Office/HTML export and clipboard. Does not change Delta.
1550
1643
  */
1551
1644
  documentPresentation?: DocumentPresentation;
1645
+ /**
1646
+ * Document-level metadata (Scrider format extension, `scrider-metadata`).
1647
+ *
1648
+ * When {@link documentPresentation} is not provided, the presentation-relevant
1649
+ * fields of this metadata are projected to inline CSS via
1650
+ * `documentMetadataToPresentation` (export/clipboard). An explicit
1651
+ * `documentPresentation` always takes precedence. Does not change Delta.
1652
+ */
1653
+ documentMetadata?: ScriderDocumentMetadata;
1552
1654
  /**
1553
1655
  * Cross-origin iframe isolation for embed formats (codeWidget, video iframe).
1554
1656
  * Default: both off — standard third-party iframes load with browser cookies.
@@ -1575,6 +1677,36 @@ interface DeltaToHtmlOptions {
1575
1677
  * ```
1576
1678
  */
1577
1679
  declare function deltaToHtml(delta: Delta, options?: DeltaToHtmlOptions): string;
1680
+ /**
1681
+ * Rendering profile: how a Delta is turned into markup.
1682
+ *
1683
+ * - `export`: full fidelity for clipboard / Office / HTML export. Document-level
1684
+ * presentation (line/paragraph spacing, first-line/list indent) and table
1685
+ * presentation are projected to inline CSS so the markup is self-contained.
1686
+ * - `editor`: markup for the editor's contenteditable surface. Document-level
1687
+ * presentation and table presentation are intentionally dropped — the editor
1688
+ * applies them live via CSS custom properties on the container, so inline
1689
+ * copies would double-apply and fight the Settings panel. Per-block baked
1690
+ * attributes (`scrider-line-height`, `scrider-margin-*`, `scrider-text-indent`)
1691
+ * still render, since they are part of the document content.
1692
+ */
1693
+ type RenderProfile = 'export' | 'editor';
1694
+ /**
1695
+ * Render a Delta with an explicit {@link RenderProfile}.
1696
+ *
1697
+ * Thin dispatcher over {@link deltaToHtml} (the export core). See
1698
+ * {@link RenderProfile} for how the profiles differ.
1699
+ */
1700
+ declare function renderDelta(delta: Delta, profile: RenderProfile, options?: DeltaToHtmlOptions): string;
1701
+ /**
1702
+ * Render a Delta for the editor's contenteditable surface (`editor` profile).
1703
+ *
1704
+ * Equivalent to `renderDelta(delta, 'editor', options)`. Document-level and table
1705
+ * presentation are dropped (applied live via CSS vars); the built-in `softBreak`
1706
+ * renderer already emits `<br data-scrider-embed>`, so the editor needs no
1707
+ * embed-renderer override.
1708
+ */
1709
+ declare function deltaToDom(delta: Delta, options?: DeltaToHtmlOptions): string;
1578
1710
 
1579
1711
  /**
1580
1712
  * HTML → Delta Conversion
@@ -2072,4 +2204,4 @@ declare function collectAdjacentTableLines<T extends {
2072
2204
  */
2073
2205
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
2074
2206
 
2075
- 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 CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, 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 TableBlockFloat, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, 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, isAdjacentSimpleTableGridBoundary, 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, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
2207
+ 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 CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type RenderProfile, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, SCRIDER_TEXT_INDENT_KEY, type SanitizeOptions, type ScriderDocumentMetadata, TEXT_INDENT_BLOCK_TAGS, type TableBlockData, type TableBlockFloat, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockTextIndentStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToDom, deltaToHtml, deltaToMarkdown, dividerFormat, documentMetadataToPresentation, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, renderDelta, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -868,10 +868,12 @@ function renderTableHostWrapperOpen(data, pretty) {
868
868
  attrs.push(`data-block-align="${escapeHtml(data.blockAlign)}"`);
869
869
  }
870
870
  const styleParts = [];
871
- if (data.width != null && data.width > 0) {
871
+ if (data.blockAlign === "justify" && !data.float) {
872
+ styleParts.push("width: 100%");
873
+ } else if (data.width != null && data.width > 0) {
872
874
  styleParts.push(`width: ${Math.round(data.width)}px`);
873
875
  styleParts.push("max-width: 100%");
874
- } else if (data.blockAlign === "justify" && !data.float) {
876
+ } else if (data.blockAlign && !data.float) {
875
877
  styleParts.push("width: 100%");
876
878
  }
877
879
  if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
@@ -2492,6 +2494,7 @@ var codeWidgetFormat = {
2492
2494
  var dividerFormat = {
2493
2495
  name: "divider",
2494
2496
  scope: "embed",
2497
+ blockLevel: true,
2495
2498
  normalize(value) {
2496
2499
  return !!value;
2497
2500
  },
@@ -2996,7 +2999,9 @@ function slugifyWithDedup(text, usedSlugs) {
2996
2999
  var SCRIDER_LINE_HEIGHT_KEY = "scrider-line-height";
2997
3000
  var SCRIDER_MARGIN_AFTER_KEY = "scrider-margin-after";
2998
3001
  var SCRIDER_MARGIN_BEFORE_KEY = "scrider-margin-before";
3002
+ var SCRIDER_TEXT_INDENT_KEY = "scrider-text-indent";
2999
3003
  var LINE_HEIGHT_BLOCK_TAGS = /* @__PURE__ */ new Set(["p", "li", "blockquote"]);
3004
+ var TEXT_INDENT_BLOCK_TAGS = /* @__PURE__ */ new Set(["p"]);
3000
3005
  var PARAGRAPH_SPACING_BLOCK_TAGS = /* @__PURE__ */ new Set(["p"]);
3001
3006
  function parseScriderLineHeightMultiplier(value) {
3002
3007
  const trimmed = value.trim();
@@ -3026,6 +3031,18 @@ function blockLineHeightStyleParts(tag, blockAttributes, resolved) {
3026
3031
  }
3027
3032
  return [];
3028
3033
  }
3034
+ function blockTextIndentStyleParts(tag, blockAttributes, resolved) {
3035
+ if (!TEXT_INDENT_BLOCK_TAGS.has(tag)) return [];
3036
+ const raw = blockAttributes?.[SCRIDER_TEXT_INDENT_KEY];
3037
+ if (typeof raw === "string") {
3038
+ const trimmed = raw.trim();
3039
+ if (trimmed) return [`text-indent:${trimmed}`];
3040
+ }
3041
+ if (resolved?.textIndentCm !== void 0) {
3042
+ return [`text-indent:${resolved.textIndentCm}cm`];
3043
+ }
3044
+ return [];
3045
+ }
3029
3046
  function parseScriderMarginEm(value) {
3030
3047
  const trimmed = value.trim();
3031
3048
  if (!trimmed) return void 0;
@@ -3113,6 +3130,26 @@ function resolveDocumentPresentation(presentation) {
3113
3130
  listBlockIndentCm
3114
3131
  };
3115
3132
  }
3133
+ function documentMetadataToPresentation(metadata) {
3134
+ if (!metadata) return void 0;
3135
+ const presentation = {};
3136
+ if (typeof metadata.lineSpacing === "number") {
3137
+ presentation.lineSpacing = metadata.lineSpacing;
3138
+ }
3139
+ if (typeof metadata.paragraphSpacingBeforeEm === "number") {
3140
+ presentation.paragraphSpacingBeforeEm = metadata.paragraphSpacingBeforeEm;
3141
+ }
3142
+ if (typeof metadata.paragraphSpacingAfterEm === "number") {
3143
+ presentation.paragraphSpacingAfterEm = metadata.paragraphSpacingAfterEm;
3144
+ }
3145
+ if (typeof metadata.textIndentCm === "number") {
3146
+ presentation.textIndentCm = metadata.textIndentCm;
3147
+ }
3148
+ if (typeof metadata.listBlockIndentCm === "number") {
3149
+ presentation.listBlockIndentCm = metadata.listBlockIndentCm;
3150
+ }
3151
+ return Object.keys(presentation).length > 0 ? presentation : void 0;
3152
+ }
3116
3153
  var TEXT_INDENT_TAGS = /* @__PURE__ */ new Set(["p"]);
3117
3154
  function documentPresentationListWrapperStyleParts(resolved) {
3118
3155
  if (!resolved?.listBlockIndentCm) return [];
@@ -3130,7 +3167,7 @@ function blockPresentationStyleParts(tag, blockAttributes, resolved) {
3130
3167
  return [
3131
3168
  ...blockLineHeightStyleParts(tag, blockAttributes, resolved),
3132
3169
  ...blockParagraphMarginStyleParts(tag, blockAttributes, resolved),
3133
- ...documentPresentationStyleParts(tag, resolved)
3170
+ ...blockTextIndentStyleParts(tag, blockAttributes, resolved)
3134
3171
  ];
3135
3172
  }
3136
3173
  function joinStyleParts(parts) {
@@ -3298,7 +3335,9 @@ function deltaToHtml(delta, options = {}) {
3298
3335
  const hierarchicalNumbers = options.hierarchicalNumbers ?? false;
3299
3336
  const blockHandlers = options.blockHandlers;
3300
3337
  const anchorLinks = options.anchorLinks ?? false;
3301
- const resolvedDocumentPresentation = resolveDocumentPresentation(options.documentPresentation);
3338
+ const resolvedDocumentPresentation = resolveDocumentPresentation(
3339
+ options.documentPresentation ?? documentMetadataToPresentation(options.documentMetadata)
3340
+ );
3302
3341
  let html = "";
3303
3342
  let listStack = [];
3304
3343
  let counters = [];
@@ -3412,6 +3451,19 @@ function deltaToHtml(delta, options = {}) {
3412
3451
  }
3413
3452
  return html;
3414
3453
  }
3454
+ function stripPresentationForEditor(options) {
3455
+ const rest = { ...options };
3456
+ delete rest.documentPresentation;
3457
+ delete rest.documentMetadata;
3458
+ delete rest.tablePresentation;
3459
+ return rest;
3460
+ }
3461
+ function renderDelta(delta, profile, options = {}) {
3462
+ return profile === "editor" ? deltaToHtml(delta, stripPresentationForEditor(options)) : deltaToHtml(delta, options);
3463
+ }
3464
+ function deltaToDom(delta, options = {}) {
3465
+ return renderDelta(delta, "editor", options);
3466
+ }
3415
3467
  function getIndent(level) {
3416
3468
  return " ".repeat(level);
3417
3469
  }
@@ -3936,7 +3988,13 @@ function htmlToDelta(html, options = {}) {
3936
3988
  if (format.match) {
3937
3989
  const result = format.match(node);
3938
3990
  if (result != null) {
3991
+ if (format.blockLevel && !atLineStart) {
3992
+ context.pushNewline();
3993
+ }
3939
3994
  context.pushEmbed({ [format.name]: result.value }, result.attributes);
3995
+ if (format.blockLevel) {
3996
+ context.pushNewline();
3997
+ }
3940
3998
  return;
3941
3999
  }
3942
4000
  }
@@ -3951,6 +4009,9 @@ function htmlToDelta(html, options = {}) {
3951
4009
  return;
3952
4010
  }
3953
4011
  if (tagName === "hr") {
4012
+ if (!atLineStart) {
4013
+ context.pushNewline();
4014
+ }
3954
4015
  context.pushEmbed({ divider: true });
3955
4016
  context.pushNewline();
3956
4017
  return;
@@ -6040,6 +6101,8 @@ export {
6040
6101
  SCRIDER_LINE_HEIGHT_KEY,
6041
6102
  SCRIDER_MARGIN_AFTER_KEY,
6042
6103
  SCRIDER_MARGIN_BEFORE_KEY,
6104
+ SCRIDER_TEXT_INDENT_KEY,
6105
+ TEXT_INDENT_BLOCK_TAGS,
6043
6106
  alertBlockHandler,
6044
6107
  alignFormat,
6045
6108
  backgroundFormat,
@@ -6049,6 +6112,7 @@ export {
6049
6112
  blockMarginBeforeStyleParts,
6050
6113
  blockParagraphMarginStyleParts,
6051
6114
  blockPresentationStyleParts,
6115
+ blockTextIndentStyleParts,
6052
6116
  blockquoteFormat,
6053
6117
  boldFormat,
6054
6118
  boxBlockHandler,
@@ -6066,9 +6130,11 @@ export {
6066
6130
  defaultEmbedFormats,
6067
6131
  defaultFormats,
6068
6132
  defaultInlineFormats,
6133
+ deltaToDom,
6069
6134
  deltaToHtml,
6070
6135
  deltaToMarkdown,
6071
6136
  dividerFormat,
6137
+ documentMetadataToPresentation,
6072
6138
  documentPresentationStyleParts,
6073
6139
  escapeHtml,
6074
6140
  extractBoxOpAttributes,
@@ -6107,6 +6173,7 @@ export {
6107
6173
  parseScriderMarginBeforeEm,
6108
6174
  parseScriderMarginEm,
6109
6175
  preloadRemark,
6176
+ renderDelta,
6110
6177
  resolveDocumentPresentation,
6111
6178
  resolveTablePresentation,
6112
6179
  sanitizeDelta,