superdoc 2.22.0-next.14 → 2.22.0-next.16

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.
Files changed (32) hide show
  1. package/dist/collaboration-upgrade-engine.cjs +1 -1
  2. package/dist/collaboration-upgrade-engine.es.js +1 -1
  3. package/dist/layout-engine/contracts/src/index.d.ts +15 -7
  4. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +30 -4
  5. package/dist/layout-engine/layout-bridge/src/index.d.ts +3 -3
  6. package/dist/layout-engine/layout-bridge/src/layoutHeaderFooter.d.ts +4 -0
  7. package/dist/layout-engine/layout-engine/src/anchors.d.ts +4 -4
  8. package/dist/layout-engine/layout-engine/src/column-balancing.d.ts +2 -2
  9. package/dist/layout-engine/layout-engine/src/execution.d.ts +1 -1
  10. package/dist/layout-engine/layout-engine/src/index.d.ts +28 -1
  11. package/dist/layout-engine/layout-engine/src/inline-drawing-alignment.d.ts +4 -1
  12. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +9 -1
  13. package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +5 -1
  14. package/dist/layout-engine/layout-engine/src/paragraph-continuation.d.ts +6 -0
  15. package/dist/layout-engine/layout-engine/src/section-props.d.ts +1 -1
  16. package/dist/layout-engine/layout-engine/src/source-preparation.d.ts +52 -0
  17. package/dist/layout-engine/painters/dom/src/page-content.d.ts +6 -0
  18. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphContent.d.ts +3 -1
  19. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphFragment.d.ts +3 -1
  20. package/dist/layout-engine/painters/dom/src/persistent-page-surface.d.ts +5 -4
  21. package/dist/layout-engine/painters/dom/src/pm-position-validation.d.ts +1 -0
  22. package/dist/layout-engine/painters/dom/src/render-work.d.ts +3 -0
  23. package/dist/layout-engine/painters/dom/src/renderer.d.ts +28 -5
  24. package/dist/layout-engine/painters/dom/src/runs/render-line.d.ts +3 -1
  25. package/dist/layout-engine/painters/dom/src/runs/tab-run.d.ts +1 -1
  26. package/dist/layout-engine/painters/dom/src/table/renderTableCell.d.ts +3 -1
  27. package/dist/layout-engine/painters/dom/src/table/renderTableFragment.d.ts +3 -1
  28. package/dist/layout-engine/painters/dom/src/table/renderTableRow.d.ts +3 -1
  29. package/dist/superdoc.cjs +2 -2
  30. package/dist/superdoc.es.js +2 -2
  31. package/dist-cdn/superdoc.min.js +3 -3
  32. package/package.json +2 -2
@@ -19,7 +19,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
19
19
  var PRIVATE_ENGINE_INFO = (0, _superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
20
20
  var ENGINE_INFO = Object.freeze({
21
21
  ...PRIVATE_ENGINE_INFO,
22
- superdocVersion: "2.22.0-next.14",
22
+ superdocVersion: "2.22.0-next.16",
23
23
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
24
24
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
25
25
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -18,7 +18,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
18
18
  var PRIVATE_ENGINE_INFO = getCollaborationUpgradeEngineInfo$1();
19
19
  var ENGINE_INFO = Object.freeze({
20
20
  ...PRIVATE_ENGINE_INFO,
21
- superdocVersion: "2.22.0-next.14",
21
+ superdocVersion: "2.22.0-next.16",
22
22
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
23
23
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
24
24
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -840,6 +840,8 @@ export declare const isExplicitNoneBorder: (border?: unknown) => boolean;
840
840
  */
841
841
  export declare const resolveBorderConflict: (first?: BorderSpec, second?: BorderSpec) => BorderSpec | undefined;
842
842
  export type TableCellAttrs = {
843
+ /** Resolved ordinary Latin text rotation from w:tcPr/w:textDirection. */
844
+ textDirection?: 'btLr' | 'tbRl';
843
845
  borders?: CellBorders;
844
846
  padding?: BoxSpacing;
845
847
  verticalAlign?: 'top' | 'middle' | 'center' | 'bottom';
@@ -891,6 +893,8 @@ export type TableCell = {
891
893
  attrs?: TableCellAttrs;
892
894
  sourceAnchor?: SourceAnchor;
893
895
  };
896
+ /** Read the resolved cell axis, with compatibility for older raw carriers. */
897
+ export declare function getTableCellTextDirection(attrs: TableCellAttrs | undefined): 'btLr' | 'tbRl' | undefined;
894
898
  export type TableRowProperties = {
895
899
  repeatHeader?: boolean;
896
900
  cantSplit?: boolean;
@@ -1671,13 +1675,9 @@ export declare function allExplicitColumnWidthsEqual(widths: readonly number[]):
1671
1675
  /**
1672
1676
  * Shared "genuinely unequal explicit column widths" predicate (SD-2324).
1673
1677
  *
1674
- * The layout engine skips end-of-section column balancing exactly when this
1675
- * is true: Word fills genuinely-unequal explicit columns column-by-column
1676
- * rather than rebalancing them. Host retained-layout dependency scanning
1677
- * (SD-3772) consumes the same predicate so checkpoint admission can never
1678
- * drift from the engine's balancing semantics. Explicit widths that are all
1679
- * EQUAL (`equalWidth="0"` with every `<w:col w:w>` equal) still balance like
1680
- * implicit equal columns and are NOT genuinely unequal.
1678
+ * Unequal geometry requires width-aware paragraph reflow during balancing;
1679
+ * equal explicit widths can use the same fixed-line balancing as implicit
1680
+ * equal columns. This predicate describes geometry, not balancing eligibility.
1681
1681
  */
1682
1682
  export declare function hasGenuinelyUnequalExplicitColumnWidths(columns: {
1683
1683
  equalWidth?: boolean | undefined;
@@ -2067,6 +2067,8 @@ export type ParagraphAttrs = {
2067
2067
  * See `@superdoc/contracts/direction-context` for axis semantics.
2068
2068
  */
2069
2069
  directionContext?: ParagraphDirectionContext;
2070
+ /** Authored paragraph writing-mode override; kept distinct from inherited cell rotation. */
2071
+ textDirection?: string;
2070
2072
  isTocEntry?: boolean;
2071
2073
  tocInstruction?: string;
2072
2074
  /** Stable id shared by every paragraph in the same TOC (docPartObj uniqueId or parent sdBlockId). */
@@ -2413,6 +2415,12 @@ export type TableCellMeasure = {
2413
2415
  paragraph?: ParagraphMeasure;
2414
2416
  width: number;
2415
2417
  height: number;
2418
+ /** Resolved whole-cell rotated flow; dimensions exclude physical padding/borders. */
2419
+ verticalText?: {
2420
+ direction: 'btLr' | 'tbRl';
2421
+ inlineSize: number;
2422
+ blockSize: number;
2423
+ };
2416
2424
  /** Starting grid column index (0-based) */
2417
2425
  gridColumnStart?: number;
2418
2426
  /** Number of grid columns this cell spans */
@@ -1,6 +1,6 @@
1
1
  import { NonFlowingPageRelativeAnchorDependencyProof, PageCheckpointDependencyClass, FlowBlock, Layout, LayoutBlockResumeCheckpoint, Measure, Page, HeaderFooterLayout, ColumnLayout } from '../../contracts/src/index.js';
2
2
  import { FontMeasureCapabilities, FontMeasureContext } from '../../../shared/font-system/src/index.js';
3
- import { LayoutOptions, HeaderFooterConstraints, LayoutExecutionCheckpoint } from '../../layout-engine/src/index.js';
3
+ import { LayoutContinuationResume, LayoutOptions, HeaderFooterConstraints, LayoutExecutionCheckpoint } from '../../layout-engine/src/index.js';
4
4
  import { computeDirtyRegions } from './diff.js';
5
5
  import { MeasureCache } from './cache.js';
6
6
  import { HeaderFooterBatch } from './layoutHeaderFooter.js';
@@ -127,8 +127,28 @@ export type IncrementalLayoutResult = {
127
127
  * and `pageTokenTotalMs` includes token remeasure/relayout details.
128
128
  */
129
129
  bridgeTiming: IncrementalLayoutBridgeTiming;
130
+ progressive?: {
131
+ status: 'pending';
132
+ completedPageRange: {
133
+ startPageIndex: number;
134
+ endPageIndexExclusive: number;
135
+ };
136
+ completedRanges: readonly {
137
+ startOrdinal: number;
138
+ endOrdinalExclusive: number;
139
+ }[];
140
+ resume: LayoutContinuationResume;
141
+ continuation: IncrementalLayoutContinuation;
142
+ pageCountFieldsExact: false;
143
+ };
144
+ progressiveFallbackReason?: string;
145
+ };
146
+ export type IncrementalLayoutContinuation = {
147
+ advance(execution?: IncrementalLayoutExecutionControl): Promise<IncrementalLayoutResult>;
148
+ dispose(): void;
130
149
  };
131
150
  export type IncrementalLayoutBridgeTiming = {
151
+ paginationPreparationRejection?: string;
132
152
  /** Content-free reason a document used the conservative note fallback. */
133
153
  footnoteCoupledRejection?: string;
134
154
  totalMs: number;
@@ -184,6 +204,7 @@ export type IncrementalLayoutBridgeTiming = {
184
204
  pagesPaginated: number | null;
185
205
  pagesSplicedByReuse: number;
186
206
  paginationPasses: number;
207
+ paginationPreparationReuses?: number;
187
208
  pageTokenRelayouts: number;
188
209
  headerFooterPreLayoutReuses: number;
189
210
  headerFooterPreLayoutBodyBlocksEnumerated: number;
@@ -351,8 +372,7 @@ export type IncrementalPaginationProof = IncrementalPaginationProofBase & ({
351
372
  predecessorBlockId: string | null;
352
373
  };
353
374
  /**
354
- * SD-3772 D1: true means the host proved (via the shared
355
- * `hasGenuinelyUnequalExplicitColumnWidths` predicate) that no
375
+ * SD-3772 D1: true means the host proved that no
356
376
  * potentially balanceable multi-column section exists anywhere in the
357
377
  * retained document. Balancing is a post-pagination finalizer that a
358
378
  * mid-section checkpoint cannot seed. False is admissible only
@@ -513,8 +533,14 @@ export interface IncrementalLayoutExecutionControl {
513
533
  yieldEveryBlocks?: number;
514
534
  /** Time-aware mounted probe. Null is the allocation-free under-budget path. */
515
535
  checkpointIfDue?: (checkpoint?: LayoutExecutionCheckpoint) => Promise<void> | null;
536
+ progressive?: {
537
+ firstBatchPageCount?: number;
538
+ restartBeforeBlockIndex?: number;
539
+ restartBeforePageIndex?: number;
540
+ };
516
541
  }
517
- export declare function incrementalLayout(previousBlocks: FlowBlock[], _previousLayout: Layout | null, nextBlocks: FlowBlock[], options: LayoutOptions, measureBlock: (block: FlowBlock, constraints: {
542
+ export declare function incrementalLayout(...args: Parameters<typeof incrementalLayoutComplete>): Promise<IncrementalLayoutResult>;
543
+ declare function incrementalLayoutComplete(previousBlocks: FlowBlock[], _previousLayout: Layout | null, nextBlocks: FlowBlock[], options: LayoutOptions, measureBlock: (block: FlowBlock, constraints: {
518
544
  maxWidth: number;
519
545
  maxHeight: number;
520
546
  }) => Promise<Measure>, headerFooter?: {
@@ -4,9 +4,9 @@ export type { HeaderFooterType } from '../../contracts/src/index.js';
4
4
  export { selectHeaderFooterVariantForPage, resolveEffectiveHeaderFooterRef, type HeaderFooterKind, type HeaderFooterVariant, type HeaderFooterSectionRefs, type HeaderFooterResolutionSection, type HeaderFooterVariantSelectionInput, type HeaderFooterEffectiveRefInput, type HeaderFooterEffectiveRefResult, } from '../../contracts/src/index.js';
5
5
  export { extractIdentifierFromConverter, getHeaderFooterType, defaultHeaderFooterIdentifier, resolveHeaderFooterForPage, buildMultiSectionIdentifier, defaultMultiSectionIdentifier, getHeaderFooterTypeForSection, getHeaderFooterIdForPage, resolveHeaderFooterForPageAndSection, } from './headerFooterUtils.js';
6
6
  export type { HeaderFooterIdentifier, MultiSectionHeaderFooterIdentifier, SectionHeaderFooterIds, } from './headerFooterUtils.js';
7
- export { layoutHeaderFooterWithCache, invalidateHeaderFooterMeasureCache, type HeaderFooterBatchResult, getBucketForPageNumber, getBucketRepresentative, } from './layoutHeaderFooter.js';
7
+ export { layoutHeaderFooterWithCache, createRetainedHeaderFooterPageReader, invalidateHeaderFooterMeasureCache, type HeaderFooterBatchResult, getBucketForPageNumber, getBucketRepresentative, } from './layoutHeaderFooter.js';
8
8
  export type { HeaderFooterBatch, DigitBucket } from './layoutHeaderFooter.js';
9
- export { resolveHeaderFooterTokens, PROVISIONAL_PAGE_COUNT_PLACEHOLDER, type ResolveHeaderFooterTokensOptions, } from './resolveHeaderFooterTokens.js';
9
+ export { resolveHeaderFooterTokens, cloneHeaderFooterBlocks, PROVISIONAL_PAGE_COUNT_PLACEHOLDER, type ResolveHeaderFooterTokensOptions, } from './resolveHeaderFooterTokens.js';
10
10
  export { buildPageStyleReferenceResolver, flowBlocksContainCrossReferenceMetadata, type PageStyleReferenceResolver, } from './resolveLiveCrossReferences.js';
11
11
  export { findWordBoundaries, findParagraphBoundaries } from './text-boundaries.js';
12
12
  export type { BoundaryRange } from './text-boundaries.js';
@@ -14,7 +14,7 @@ export { buildSectionAwareHeaderFooterLayoutKey, buildSectionContentWidth, build
14
14
  export type { HeaderFooterSectionKind, HeaderFooterRefs, SectionAwareHeaderFooterMeasurementGroup, } from './sectionAwareHeaderFooter.js';
15
15
  export { clearIncrementalModuleState, incrementalLayout, measureCache, normalizeMargin } from './incrementalLayout.js';
16
16
  export { hashMeasureContent } from './cache.js';
17
- export type { HeaderFooterLayoutResult, HeaderFooterGeometrySeed, IncrementalLayoutBridgeTiming, IncrementalLayoutResult, IncrementalLayoutExecutionControl, IncrementalMeasureReuseProof, IncrementalLayoutReuseOptions, IncrementalLayoutReuseSummary, IncrementalSectionPageNumberTransform, IncrementalLayoutTailAdoption, IncrementalLayoutTailDisposition, IncrementalPaginationProof, LayoutPositionTransform, FootnoteReserveSeed, } from './incrementalLayout.js';
17
+ export type { HeaderFooterLayoutResult, HeaderFooterGeometrySeed, IncrementalLayoutBridgeTiming, IncrementalLayoutResult, IncrementalLayoutExecutionControl, IncrementalLayoutContinuation, IncrementalMeasureReuseProof, IncrementalLayoutReuseOptions, IncrementalLayoutReuseSummary, IncrementalSectionPageNumberTransform, IncrementalLayoutTailAdoption, IncrementalLayoutTailDisposition, IncrementalPaginationProof, LayoutPositionTransform, FootnoteReserveSeed, } from './incrementalLayout.js';
18
18
  export { collectFootnoteLedgers, getPreferredReserveCandidates, getPreferredReserveTrialTargets, isMandatoryOnlyFootnotePage, scoreFootnoteWindow, summarizeFootnoteWindow, } from './footnote-scorer.js';
19
19
  export type { FootnotePreferredReserveCandidate, FootnoteWindowScoreInput, FootnoteWindowScoreReason, FootnoteWindowScoreResult, FootnoteWindowStats, } from './footnote-scorer.js';
20
20
  export { computeDisplayPageNumber } from '../../layout-engine/src/index.js';
@@ -85,6 +85,9 @@ export declare function getBucketForPageNumber(pageNumber: number): DigitBucket;
85
85
  * ```
86
86
  */
87
87
  export declare function getBucketRepresentative(bucket: DigitBucket): number;
88
+ type RetainedBucketContext = ReturnType<PageResolver>;
89
+ /** Reads only a measured bucket issued by this canonical layout operation. */
90
+ export declare function createRetainedHeaderFooterPageReader(layout: HeaderFooterLayout, blocks: FlowBlock[], constraints: HeaderFooterConstraints, fontSignature: string): ((context: RetainedBucketContext) => HeaderFooterLayout['pages'][number] | null) | null;
88
91
  export declare class HeaderFooterLayoutCache {
89
92
  private readonly cache;
90
93
  /** Drop every cached header/footer measure (equivalence-oracle cold start). */
@@ -128,3 +131,4 @@ export declare function invalidateHeaderFooterMeasureCache(blockIds: string[]):
128
131
  * @returns Batch result with layouts, blocks, and measures for each variant
129
132
  */
130
133
  export declare function layoutHeaderFooterWithCache(sections: HeaderFooterBatch, constraints: HeaderFooterConstraints, measureBlock: MeasureResolver, cache?: HeaderFooterLayoutCache, totalPages?: number, pageResolver?: PageResolver, kind?: 'header' | 'footer', fontSignature?: string, remeasureParagraph?: (block: ParagraphBlock, maxWidth: number, firstLineIndent?: number, lineRegions?: readonly (readonly ParagraphLineRegion[])[]) => ParagraphMeasure, options?: ResolveHeaderFooterTokensOptions, execution?: HeaderFooterLayoutExecution): Promise<HeaderFooterBatchResult>;
134
+ export {};
@@ -40,19 +40,19 @@ export type AnchoredTableCollection = {
40
40
  * @param measures - Corresponding measures for each block
41
41
  * @returns Array of anchored drawings that should be pre-registered
42
42
  */
43
- export declare function collectPreRegisteredAnchorsSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null): Generator<LayoutWorkCheckpoint, AnchoredDrawing[], void>;
43
+ export declare function collectPreRegisteredAnchorsSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null, candidateIndices?: readonly number[]): Generator<LayoutWorkCheckpoint, AnchoredDrawing[], void>;
44
44
  export declare function collectPreRegisteredAnchors(blocks: FlowBlock[], measures: Measure[]): AnchoredDrawing[];
45
45
  /** Collect page- or margin-relative floating tables before paragraph layout. */
46
- export declare function collectPreRegisteredTablesSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null): Generator<LayoutWorkCheckpoint, AnchoredTable[], void>;
46
+ export declare function collectPreRegisteredTablesSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null, candidateIndices?: readonly number[]): Generator<LayoutWorkCheckpoint, AnchoredTable[], void>;
47
47
  /**
48
48
  * Collect anchored drawings (images/drawings) mapped to their anchor paragraph index.
49
49
  * Map of paragraph block index -> anchored images/drawings associated with that paragraph.
50
50
  */
51
- export declare function collectAnchoredDrawingsSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null): Generator<LayoutWorkCheckpoint, AnchoredDrawingCollection, void>;
51
+ export declare function collectAnchoredDrawingsSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null, preparedParagraphIndexById?: Map<string, number>, candidateIndices?: readonly number[]): Generator<LayoutWorkCheckpoint, AnchoredDrawingCollection, void>;
52
52
  export declare function collectAnchoredDrawings(blocks: FlowBlock[], measures: Measure[]): AnchoredDrawingCollection;
53
53
  /**
54
54
  * Collect anchored/floating tables mapped to their anchor paragraph index.
55
55
  * Also returns anchored tables that have no paragraph to attach to.
56
56
  */
57
- export declare function collectAnchoredTablesSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null): Generator<LayoutWorkCheckpoint, AnchoredTableCollection, void>;
57
+ export declare function collectAnchoredTablesSteps(blocks: FlowBlock[], measures: Measure[], checkpointEveryBlocks?: number | null, preparedParagraphIndexById?: Map<string, number>, candidateIndices?: readonly number[]): Generator<LayoutWorkCheckpoint, AnchoredTableCollection, void>;
58
58
  export declare function collectAnchoredTables(blocks: FlowBlock[], measures: Measure[]): AnchoredTableCollection;
@@ -250,8 +250,8 @@ export interface BalanceSectionOnPageArgs {
250
250
  * Guards (skip balancing when):
251
251
  * - Section has <= 1 column (nothing to balance)
252
252
  * - Section contains an explicit column break (author intent wins)
253
- * - Section uses GENUINELY-unequal column widths (Word fills these column-by-column;
254
- * explicit widths that are all equal still balance — SD-2324)
253
+ * - Section uses genuinely unequal widths, which require width-aware reflow
254
+ * rather than this redistribution of fixed line measurements
255
255
  * - No fragments on this page belong to the section
256
256
  */
257
257
  export declare function balanceSectionOnPage(args: BalanceSectionOnPageArgs): {
@@ -1,4 +1,4 @@
1
- export type LayoutExecutionPhase = 'measure:block' | 'layout-document:prepare' | 'layout-document:preflight-section' | 'layout-document:preflight-footnote' | 'layout-document:preflight-anchor' | 'layout-document:preflight-keep-next' | 'layout-document:block' | 'layout-document:table-fragment' | 'layout-document:footnote-continuation' | 'layout-document:finalize-page' | 'layout-document:finalize-section' | 'page-token:prepare' | 'page-token:page' | 'numbering-context:chapter' | 'numbering-context:page' | 'footnote:phase' | 'header-footer:variant' | 'header-footer:block' | 'header-footer:page';
1
+ export type LayoutExecutionPhase = 'measure:block' | 'layout-document:prepare' | 'layout-document:preflight-section' | 'layout-document:preflight-footnote' | 'layout-document:preflight-anchor' | 'layout-document:preflight-keep-next' | 'layout-document:block' | 'layout-document:paragraph-fragment' | 'layout-document:table-fragment' | 'layout-document:footnote-continuation' | 'layout-document:finalize-page' | 'layout-document:finalize-section' | 'page-token:prepare' | 'page-token:page' | 'numbering-context:chapter' | 'numbering-context:page' | 'footnote:phase' | 'header-footer:variant' | 'header-footer:block' | 'header-footer:page';
2
2
  export type LayoutExecutionCheckpoint = {
3
3
  phase: LayoutExecutionPhase;
4
4
  index?: number;
@@ -3,7 +3,7 @@ import { FootnotePageFlow } from './footnote-page-flow.js';
3
3
  import { PageState, ConstraintBoundary } from './paginator.js';
4
4
  import { LayoutExecutionControl } from './execution.js';
5
5
  export type { FootnotePageFlow } from './footnote-page-flow.js';
6
- export { findLineIndexForRunOrdinal } from './layout-utils.js';
6
+ export { findLineIndexForRunOrdinal, findKeepNextChainStart, isOutOfFlowDrawing } from './layout-utils.js';
7
7
  type PageSize = {
8
8
  w: number;
9
9
  h: number;
@@ -275,6 +275,33 @@ export type HeaderFooterConstraints = {
275
275
  */
276
276
  export declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], options?: LayoutOptions): Layout;
277
277
  export declare function layoutDocumentCooperatively(blocks: FlowBlock[], measures: Measure[], options?: LayoutOptions, execution?: LayoutExecutionControl): Promise<Layout>;
278
+ export type LayoutContinuationResume = {
279
+ nextBlockIndex: number;
280
+ nextPageIndex: number;
281
+ pageIndex: number;
282
+ checkpointIdentity: object;
283
+ paragraphLineIndex?: number;
284
+ tableRowIndex?: number;
285
+ };
286
+ export type LayoutContinuationBatch = {
287
+ layout: Layout;
288
+ pageRange: {
289
+ startPageIndex: number;
290
+ endPageIndexExclusive: number;
291
+ };
292
+ status: 'pending' | 'complete';
293
+ resume: LayoutContinuationResume;
294
+ completeReason?: string;
295
+ };
296
+ export type LayoutDocumentContinuation = {
297
+ advance(input?: {
298
+ pageCount?: number;
299
+ execution?: LayoutExecutionControl;
300
+ }): Promise<LayoutContinuationBatch>;
301
+ dispose(): void;
302
+ };
303
+ /** AIDEV-NOTE: The handle owns one live paginator; advancing never replays a stopped prefix. */
304
+ export declare function createLayoutDocumentContinuation(blocks: FlowBlock[], measures: Measure[], options?: LayoutOptions, execution?: LayoutExecutionControl): LayoutDocumentContinuation;
278
305
  export type LayoutRangeBoundaryPolicy = 'conservative-context' | 'overscan' | 'degraded-on-incomplete-state';
279
306
  export type LayoutRangeExactness = 'exact' | 'pending-layout' | 'degraded-unsupported' | 'stale';
280
307
  export type LayoutRangeSourceRange = {
@@ -6,4 +6,7 @@ import { FlowBlock, Measure, Page } from '../../contracts/src/index.js';
6
6
  * is laid out. Reposition only its paint fragment once that carrier's measured
7
7
  * baseline is known; later flow remains untouched.
8
8
  */
9
- export declare function alignInlineZeroHeightDrawingFragments(pages: Page[], blocks: FlowBlock[], measures: Measure[]): void;
9
+ export declare function alignInlineZeroHeightDrawingFragments(pages: Page[], blocks: FlowBlock[], measures: Measure[], indices?: {
10
+ blockById: ReadonlyMap<string, FlowBlock>;
11
+ measureById: ReadonlyMap<string, Measure | undefined>;
12
+ }): void;
@@ -1,7 +1,7 @@
1
1
  import { FloatingObjectManager } from './floating-objects.js';
2
2
  import { PageState } from './paginator.js';
3
3
  import { FootnotePageFlow } from './footnote-page-flow.js';
4
- import { PageMargins, ParagraphBlock, ParagraphMeasure, ImageBlock, ImageMeasure, DrawingBlock, DrawingMeasure, ParagraphLineRegion, ColumnLayoutForAnchor } from '../../contracts/src/index.js';
4
+ import { PageMargins, ParagraphBlock, ParagraphMeasure, ImageBlock, ImageMeasure, DrawingBlock, DrawingMeasure, TableBlock, TableMeasure, ParagraphLineRegion, ColumnLayoutForAnchor } from '../../contracts/src/index.js';
5
5
  import { AnchoredTable } from './anchors.js';
6
6
  /**
7
7
  * SD-2656: ordered footnote anchor entry. The body slicer reads the candidate
@@ -21,6 +21,7 @@ export type ParagraphLayoutContext = {
21
21
  block: ParagraphBlock;
22
22
  measure: ParagraphMeasure;
23
23
  columnWidth: number;
24
+ columnWidthForState?: (state: PageState) => number;
24
25
  ensurePage: () => PageState;
25
26
  advanceColumn: (state: PageState) => PageState;
26
27
  columnX: (state: PageState, columnIndex?: number) => number;
@@ -32,6 +33,8 @@ export type ParagraphLayoutContext = {
32
33
  * When undefined, uses the value from block.attrs.spacing.after.
33
34
  */
34
35
  overrideSpacingAfter?: number;
36
+ /** A keep-next preflight moved this paragraph to a fresh page. */
37
+ suppressSpacingBefore?: boolean;
35
38
  /**
36
39
  * SD-3049 / SD-2656: footnote demand under the ordered-cluster rule.
37
40
  *
@@ -99,6 +102,7 @@ export type AnchoredDrawingEntry = {
99
102
  export type ParagraphAnchorsContext = {
100
103
  anchoredDrawings?: AnchoredDrawingEntry[];
101
104
  anchoredTables?: AnchoredTable[];
105
+ resolveCurrentTable?: (id: string, measure: TableMeasure) => TableBlock;
102
106
  columnWidth: number;
103
107
  pageWidth: number;
104
108
  pageMargins: PageMargins;
@@ -106,4 +110,8 @@ export type ParagraphAnchorsContext = {
106
110
  columns: ColumnLayoutForAnchor;
107
111
  placedAnchoredIds: Set<string>;
108
112
  };
113
+ export declare function layoutParagraphBlockSteps(ctx: ParagraphLayoutContext, anchors?: ParagraphAnchorsContext): Generator<{
114
+ index: number;
115
+ total: number;
116
+ }, void, void>;
109
117
  export declare function layoutParagraphBlock(ctx: ParagraphLayoutContext, anchors?: ParagraphAnchorsContext): void;
@@ -1,4 +1,4 @@
1
- import { Line, ParagraphBlock, ParagraphMeasure, LinePmRange } from '../../contracts/src/index.js';
1
+ import { FlowBlock, Line, ParagraphBlock, ParagraphMeasure, LinePmRange } from '../../contracts/src/index.js';
2
2
  /**
3
3
  * Tracks which paragraph spacing properties were explicitly set.
4
4
  *
@@ -104,3 +104,7 @@ export declare function computeParagraphLayoutStartY(input: {
104
104
  export declare const extractBlockPmRange: (block: {
105
105
  attrs?: Record<string, unknown>;
106
106
  } | null | undefined) => LinePmRange;
107
+ /** Positioned drawings are laid out by their owner, rather than consuming a flow slot. */
108
+ export declare function isOutOfFlowDrawing(block: FlowBlock): boolean;
109
+ /** Find the kept paragraph whose page decision also owns this flow block. */
110
+ export declare function findKeepNextChainStart(blocks: readonly FlowBlock[], index: number): number;
@@ -0,0 +1,6 @@
1
+ import { Line, ParagraphBlock } from '../../contracts/src/index.js';
2
+ /** Build a measurement-only suffix while keeping line coordinates in the original paragraph. */
3
+ export declare function paragraphContinuation(block: ParagraphBlock, first: Line): {
4
+ block: ParagraphBlock;
5
+ mapLine: (line: Line) => Line;
6
+ };
@@ -66,5 +66,5 @@ export type SectionPropsScanCheckpoint = {
66
66
  index: number;
67
67
  total: number;
68
68
  };
69
- export declare function computeNextSectionPropsAtBreakSteps(blocks: FlowBlock[], checkpointEveryBlocks?: number | null): Generator<SectionPropsScanCheckpoint, Map<number, SectionProps>, void>;
69
+ export declare function computeNextSectionPropsAtBreakSteps(blocks: FlowBlock[], checkpointEveryBlocks?: number | null, candidateIndices?: readonly number[]): Generator<SectionPropsScanCheckpoint, Map<number, SectionProps>, void>;
70
70
  export declare function computeNextSectionPropsAtBreak(blocks: FlowBlock[]): Map<number, SectionProps>;
@@ -0,0 +1,52 @@
1
+ import { FlowBlock, Measure } from '../../contracts/src/index.js';
2
+ export type CanonicalPaginationSourceSplice = {
3
+ readonly previous: readonly FlowBlock[];
4
+ readonly current: readonly FlowBlock[];
5
+ readonly authoredChangedBlockIds: readonly string[];
6
+ readonly replacements: readonly {
7
+ start: number;
8
+ deleteCount: number;
9
+ insertCount: number;
10
+ }[];
11
+ };
12
+ export type PaginationSourceScan = {
13
+ readonly blocks: FlowBlock[];
14
+ readonly measures: Measure[];
15
+ readonly indexById: ReadonlyMap<string, number>;
16
+ readonly candidateIds: readonly string[];
17
+ readonly dependencyIds?: readonly string[];
18
+ readonly offset: number;
19
+ readonly isCurrent?: () => boolean;
20
+ };
21
+ /**
22
+ * Trusted internal dependency injection for the canonical host source issuer.
23
+ * This constructor is intentionally absent from the engine's main exports;
24
+ * opaque tokens offered by generic bridge callers have no authority here.
25
+ */
26
+ export declare function createCanonicalPaginationSourceReader(read: (previous: readonly FlowBlock[], current: readonly FlowBlock[]) => CanonicalPaginationSourceSplice | null, owns: (source: readonly FlowBlock[]) => boolean): object;
27
+ export declare function isPaginationPreparationCandidate(block: FlowBlock): boolean;
28
+ /** Only the engine's completed input validation issues a scan plan. */
29
+ export declare function retainPaginationSourceScan(blocks: FlowBlock[], measures: Measure[], indexById: ReadonlyMap<string, number>, candidateIds: readonly string[]): void;
30
+ export declare function readPaginationSourceScan(blocks: FlowBlock[], measures: Measure[], requirePrepared?: boolean): PaginationSourceScan | null;
31
+ /**
32
+ * The canonical owner certifies the exact immutable source recipe. The bridge
33
+ * separately proves its current dirty measures and index rewrites before calling.
34
+ * Foreign/copy tokens and mismatched source or measure vectors lose the shortcut.
35
+ */
36
+ export declare function prepareCanonicalPaginationSource(input: {
37
+ reader: object;
38
+ previousBlocks: FlowBlock[];
39
+ blocks: FlowBlock[];
40
+ previousMeasures: Measure[];
41
+ measures: Measure[];
42
+ dirtyBlockIds: readonly string[];
43
+ currentIndexById: ReadonlyMap<string, number>;
44
+ previousToCurrentBlockId?: ReadonlyMap<string, string> | null;
45
+ onReject?: (reason: string) => void;
46
+ }): boolean;
47
+ /** Only an owned prepared scan may substitute metadata for a clean table preflight read. */
48
+ export declare function createPreparedPaginationMetadataView(blocks: FlowBlock[], measures: Measure[]): FlowBlock[];
49
+ export declare function createPreparedPaginationSuffix(blocks: FlowBlock[], measures: Measure[], start: number, end: number): {
50
+ blocks: FlowBlock[];
51
+ measures: Measure[];
52
+ } | null;
@@ -276,3 +276,9 @@ export type WindowPositionRemapPlan = {
276
276
  * sequences — no per-paint key strings or Maps on the steady-state path.
277
277
  */
278
278
  export declare function planWindowPositionRemap(state: PageDomState, resolvedPage: ResolvedPage): WindowPositionRemapPlan;
279
+ export declare function planDetachedPageFragments(ctx: PageContentContext, state: PageDomState | undefined, page: ResolvedPage, pageIndex: number): Generator<void, {
280
+ item: ResolvedPaintItem;
281
+ context: FragmentRenderContext;
282
+ sdtBoundary?: SdtBoundaryOptions;
283
+ betweenInfo?: BetweenBorderInfo;
284
+ }[], void>;
@@ -1,3 +1,4 @@
1
+ import { RenderWork } from '../render-work.js';
1
2
  import { DropCapDescriptor, Line, ParagraphBlock, ParagraphMeasure, ResolvedParagraphContent, Run, SdtMetadata, SourceAnchor } from '../../../../contracts/src/index.js';
2
3
  import { MinimalWordLayout } from '../../../../../../shared/common/list-marker-utils';
3
4
  import { ResolvePhysicalFamily } from '../../../../../shared/font-system/src/index.js';
@@ -20,7 +21,7 @@ export type ParagraphRenderLineInput = {
20
21
  indentOffsetOverride?: number;
21
22
  paragraphMarkLeftOffsetOverride?: number;
22
23
  };
23
- export type ParagraphRenderLine = (input: ParagraphRenderLineInput) => HTMLElement;
24
+ export type ParagraphRenderLine = (input: ParagraphRenderLineInput) => HTMLElement | RenderWork<HTMLElement>;
24
25
  export type ParagraphRenderDropCap = (descriptor: DropCapDescriptor, measure?: {
25
26
  width: number;
26
27
  height: number;
@@ -83,5 +84,6 @@ export type RenderParagraphContentResult = {
83
84
  totalHeight: number;
84
85
  renderedLines: RenderedParagraphLineInfo[];
85
86
  };
87
+ export declare function renderParagraphContentWork(params: RenderParagraphContentParams): RenderWork<RenderParagraphContentResult>;
86
88
  export declare const renderParagraphContent: (params: RenderParagraphContentParams) => RenderParagraphContentResult;
87
89
  export {};
@@ -1,3 +1,4 @@
1
+ import { RenderWork } from '../render-work.js';
1
2
  import { ParaFragment, ResolvedFragmentItem, SdtMetadata } from '../../../../contracts/src/index.js';
2
3
  import { ResolvePhysicalFamily } from '../../../../../shared/font-system/src/index.js';
3
4
  import { SdtBoundaryOptions } from '../sdt/container.js';
@@ -15,7 +16,7 @@ type RenderParagraphFragmentParams = {
15
16
  applyFragmentFrame: (el: HTMLElement, fragment: ParaFragment) => void;
16
17
  applySdtDataset: (el: HTMLElement | null, metadata?: SdtMetadata | null) => void;
17
18
  applyContainerSdtDataset: (el: HTMLElement | null, metadata?: SdtMetadata | null) => void;
18
- renderLine: (input: ParagraphRenderLineInput) => HTMLElement;
19
+ renderLine: (input: ParagraphRenderLineInput) => HTMLElement | RenderWork<HTMLElement>;
19
20
  captureLineSnapshot: (lineEl: HTMLElement, options?: {
20
21
  sourceAnchor?: ResolvedFragmentItem['sourceAnchor'];
21
22
  wrapperEl?: HTMLElement;
@@ -29,5 +30,6 @@ type RenderParagraphFragmentParams = {
29
30
  */
30
31
  resolvePhysical?: ResolvePhysicalFamily;
31
32
  };
33
+ export declare function renderParagraphFragmentWork(params: RenderParagraphFragmentParams): RenderWork<HTMLElement>;
32
34
  export declare const renderParagraphFragment: (params: RenderParagraphFragmentParams) => HTMLElement;
33
35
  export {};
@@ -75,8 +75,8 @@ export type DomPainterPersistentPageInput = {
75
75
  };
76
76
  /** Shell registry entry: the persistent page root and its geometry/style reuse key. */
77
77
  export type PersistentShellEntry = {
78
- element: HTMLElement;
79
- shellKey: string;
78
+ readonly element: HTMLElement;
79
+ readonly shellKey: string;
80
80
  };
81
81
  /** Bounded content state for one hydrated page. */
82
82
  export type PersistentContentEntry = {
@@ -94,9 +94,10 @@ export type PersistentPageSurfaceState = {
94
94
  mount: HTMLElement;
95
95
  /** The committed scaffold; reference identity gates the O(1) skip. */
96
96
  scaffold: DomPainterPersistentScaffold;
97
- shells: Map<number, PersistentShellEntry>;
97
+ shells: ReadonlyMap<number, PersistentShellEntry>;
98
98
  content: Map<number, PersistentContentEntry>;
99
99
  integrity: PersistentSurfaceIntegrity;
100
+ shellStyleSignature: string;
100
101
  };
101
102
  /**
102
103
  * The narrow renderer capabilities this module consumes. The deep
@@ -141,6 +142,6 @@ export declare function disposePersistentPageSurfaceState(state: PersistentPageS
141
142
  * shell registry by page index; the content window reconciles every call.
142
143
  */
143
144
  export declare function reconcilePersistentPageSurface(ctx: PersistentSurfaceRenderContext, previous: PersistentPageSurfaceState | null, input: DomPainterPersistentPageInput, mount: HTMLElement): PersistentPageSurfaceState;
144
- /** Deep-clone the retained planes for the painter's rollback snapshot. */
145
+ /** Snapshot mutable content metadata; scaffold reconciliation replaces the shell registry. */
145
146
  export declare function clonePersistentPageSurfaceState(state: PersistentPageSurfaceState | null, clonePageState: (pageState: PageDomState) => PageDomState): PersistentPageSurfaceState | null;
146
147
  export {};
@@ -135,6 +135,7 @@ export declare class PositionValidationCollector {
135
135
  private verboseLines;
136
136
  private suppressedConsole;
137
137
  constructor(options?: PositionValidationOptions);
138
+ clone(policy?: PositionValidationConsolePolicy): PositionValidationCollector;
138
139
  get isEnabled(): boolean;
139
140
  record(obs: RunPositionObservation): void;
140
141
  /** Verbose-mode: one content-free warning per NEW structural key, capped. */
@@ -0,0 +1,3 @@
1
+ export type RenderWork<T> = Generator<void, T, void>;
2
+ export declare function completeRenderWork<T>(work: RenderWork<T>): T;
3
+ export declare function resolveRenderWork<T>(value: T | RenderWork<T>): RenderWork<T>;
@@ -236,6 +236,7 @@ type PainterFragmentFailure = {
236
236
  readonly code: 'painter-fragment-unavailable';
237
237
  };
238
238
  type PersistentPagePainterTransaction = {
239
+ prepareCommit(): void;
239
240
  commit(): void;
240
241
  rollback(): void;
241
242
  readFragmentFailures(): readonly PainterFragmentFailure[];
@@ -254,6 +255,11 @@ export declare class DomPainter {
254
255
  private totalPages;
255
256
  private sectionPageCounts;
256
257
  private linkIdCounter;
258
+ private retainedPaintRevision;
259
+ private preparedDecorations;
260
+ private preparedFragments;
261
+ private detachedPreparationActive;
262
+ private detachedFragmentFailures;
257
263
  private shapeImageFillCounter;
258
264
  private wordArtPathCounter;
259
265
  private sdtLabelsRendered;
@@ -306,7 +312,7 @@ export declare class DomPainter {
306
312
  * live persistent surface and fresh-state oracle never mix counts.
307
313
  * Dark unless enabled via options; when dark, `record()` is a single branch.
308
314
  */
309
- private readonly positionValidation;
315
+ private positionValidation;
310
316
  private paintSnapshotBuilder;
311
317
  private lastPaintSnapshot;
312
318
  private onPaintSnapshotCallback;
@@ -316,7 +322,7 @@ export declare class DomPainter {
316
322
  * from the public DomPainterHandle contract.
317
323
  */
318
324
  private activePersistentPageTransaction;
319
- private persistentPageIndices;
325
+ private persistentPageCount;
320
326
  /** Resolved layout for the next-gen paint pipeline. */
321
327
  private resolvedLayout;
322
328
  private showFormattingMarks;
@@ -358,8 +364,7 @@ export declare class DomPainter {
358
364
  beginPersistentPageTransaction(): PersistentPagePainterTransaction;
359
365
  private capturePersistentPagePainterState;
360
366
  private restorePersistentPagePainterState;
361
- private createAllPageIndices;
362
- private setPersistentPageIndices;
367
+ private setPersistentPageCount;
363
368
  private emitPaintSnapshot;
364
369
  private beginPaintSnapshot;
365
370
  private finalizePaintSnapshotFromBuilder;
@@ -367,6 +372,19 @@ export declare class DomPainter {
367
372
  private collectPaintSnapshotFromDomRoot;
368
373
  /** Semantic continuous paint. Paginated flow has only `paintPersistentPages()`. */
369
374
  paint(input: DomPainterInput, mount: HTMLElement, mapping?: PositionMapping): void;
375
+ /** Prepare detached content before the host opens its atomic visible transaction. */
376
+ preparePersistentPages(input: DomPainterPersistentPageInput, options: {
377
+ mount: HTMLElement;
378
+ signal?: AbortSignal;
379
+ yieldToHost?: (checkpoint: {
380
+ phase: 'paint:content';
381
+ index: number;
382
+ total: number;
383
+ }) => Promise<void>;
384
+ }): Promise<{
385
+ input: DomPainterPersistentPageInput;
386
+ discard(): void;
387
+ }>;
370
388
  /**
371
389
  * The persistent paginated reconcile (default persistent page geometry
372
390
  * plan, Unit 1): one generation-scoped scaffold owns every page root for
@@ -414,6 +432,7 @@ export declare class DomPainter {
414
432
  */
415
433
  private resolveSeparatorColumnGeometry;
416
434
  private renderDecorationsForPage;
435
+ private renderDecorationsForPageWork;
417
436
  /**
418
437
  * Check if a fragment is vertically anchored to the page.
419
438
  * Used to determine special Y positioning for page-relative anchored content
@@ -434,7 +453,7 @@ export declare class DomPainter {
434
453
  */
435
454
  private getDecorationAnchorPageOriginY;
436
455
  private getFooterFragmentAnchorPageOriginY;
437
- private renderDecorationSection;
456
+ private renderDecorationSectionWork;
438
457
  private resetState;
439
458
  dispose(): void;
440
459
  private getSectionPageCount;
@@ -467,6 +486,7 @@ export declare class DomPainter {
467
486
  private applySemanticPageOverrides;
468
487
  private getEffectivePageStyles;
469
488
  private renderFragment;
489
+ private renderFragmentWork;
470
490
  /**
471
491
  * Renders a paragraph fragment with defensive error handling.
472
492
  * Falls back to error placeholder on rendering errors to prevent full paint failure.
@@ -477,6 +497,7 @@ export declare class DomPainter {
477
497
  * @returns HTMLElement containing the rendered fragment or error placeholder
478
498
  */
479
499
  private renderParagraphFragment;
500
+ private renderParagraphFragmentWork;
480
501
  private createErrorPlaceholder;
481
502
  private renderImageFragment;
482
503
  /**
@@ -606,7 +627,9 @@ export declare class DomPainter {
606
627
  /** Render drawing content nested in any canonical table, including a textbox-owned table. */
607
628
  private renderDrawingContentForTable;
608
629
  private renderTableFragment;
630
+ private renderTableFragmentWork;
609
631
  private renderLine;
632
+ private renderLineWork;
610
633
  private createRunRenderContext;
611
634
  private defaultFragmentRenderContext;
612
635
  /**
@@ -1,2 +1,4 @@
1
+ import { RenderWork } from '../render-work.js';
1
2
  import { RenderLineParams } from './types.js';
2
- export declare const renderLine: ({ block, line, context, availableWidthOverride, lineIndex, skipJustify, preExpandedRuns, resolvedListTextStartPx, indentOffsetOverride, paragraphMarkLeftOffsetOverride, runContext, }: RenderLineParams) => HTMLElement;
3
+ export declare function renderLineWork({ block, line, context, availableWidthOverride, lineIndex, skipJustify, preExpandedRuns, resolvedListTextStartPx, indentOffsetOverride, paragraphMarkLeftOffsetOverride, runContext, }: RenderLineParams): RenderWork<HTMLElement>;
4
+ export declare const renderLine: (params: RenderLineParams) => HTMLElement;