superdoc 2.11.0 → 2.11.1-next.2
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/collaboration-upgrade-engine.cjs +1 -1
- package/dist/collaboration-upgrade-engine.es.js +1 -1
- package/dist/layout-engine/contracts/src/index.d.ts +4 -0
- package/dist/layout-engine/layout-bridge/src/coupled-footnote-pagination.d.ts +59 -0
- package/dist/layout-engine/layout-bridge/src/coupled-footnote-profile.d.ts +37 -0
- package/dist/layout-engine/layout-bridge/src/footnote-content.d.ts +39 -0
- package/dist/layout-engine/layout-bridge/src/footnote-page-certificate.d.ts +38 -0
- package/dist/layout-engine/layout-bridge/src/footnote-page-planner.d.ts +49 -0
- package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +92 -0
- package/dist/layout-engine/layout-engine/src/execution.d.ts +1 -1
- package/dist/layout-engine/layout-engine/src/footnote-anchor-index.d.ts +3 -0
- package/dist/layout-engine/layout-engine/src/footnote-page-flow.d.ts +28 -0
- package/dist/layout-engine/layout-engine/src/index.d.ts +8 -0
- package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +4 -0
- package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +2 -0
- package/dist/layout-engine/layout-engine/src/paginator.d.ts +5 -0
- package/dist/superdoc.cjs +2 -2
- package/dist/superdoc.es.js +2 -2
- package/dist-cdn/superdoc.min.js +3 -3
- 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.11.
|
|
22
|
+
superdocVersion: "2.11.1-next.2",
|
|
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.11.
|
|
21
|
+
superdocVersion: "2.11.1-next.2",
|
|
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
|
|
@@ -2762,9 +2762,13 @@ export type HeaderFooterLayout = {
|
|
|
2762
2762
|
export type LayoutBlockResumeCheckpoint = {
|
|
2763
2763
|
blockId: BlockId;
|
|
2764
2764
|
pageIndex: number;
|
|
2765
|
+
/** Page before keep/fit decisions; editing may pull a deferred paragraph back here. */
|
|
2766
|
+
preflightPageIndex?: number;
|
|
2765
2767
|
prefixFragmentCount: number;
|
|
2766
2768
|
cursorY: number;
|
|
2767
2769
|
maxCursorY: number;
|
|
2770
|
+
/** Exact accepted body footprint for coupled paragraph/note pagination. */
|
|
2771
|
+
committedBodyBottom?: number;
|
|
2768
2772
|
columnIndex: number;
|
|
2769
2773
|
trailingSpacing: number;
|
|
2770
2774
|
lastParagraphStyleId?: string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Page } from '../../contracts/src/index.js';
|
|
2
|
+
import { FootnoteSlice } from './footnote-content.js';
|
|
3
|
+
import { FootnotePageLedgerDraft, FootnotePagePlanInput, PendingFootnote } from './footnote-page-planner.js';
|
|
4
|
+
export type CoupledFootnotePaginationOptions = Omit<FootnotePagePlanInput, 'pageIndex' | 'columnCount' | 'availableHeight' | 'idsByColumn' | 'pendingByColumn'> & {
|
|
5
|
+
pageSize: {
|
|
6
|
+
w: number;
|
|
7
|
+
h: number;
|
|
8
|
+
};
|
|
9
|
+
/** The bridge must validate exact boundary provenance before supplying a start, never a height-only seed. */
|
|
10
|
+
start?: {
|
|
11
|
+
pageIndex: number;
|
|
12
|
+
pendingByColumn: FootnotePagePlanInput['pendingByColumn'];
|
|
13
|
+
isPreviouslyAnchored(id: string): boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type CoupledFootnotePageCompletion = {
|
|
17
|
+
page: Page;
|
|
18
|
+
pageIndex: number;
|
|
19
|
+
bodyBottom: number;
|
|
20
|
+
physicalBottom: number;
|
|
21
|
+
anchors: readonly {
|
|
22
|
+
refId: string;
|
|
23
|
+
}[];
|
|
24
|
+
};
|
|
25
|
+
export type CoupledFootnotePageFlow = {
|
|
26
|
+
incomingDemand(pageIndex: number): {
|
|
27
|
+
height: number;
|
|
28
|
+
refs: number;
|
|
29
|
+
};
|
|
30
|
+
completePage(completion: CoupledFootnotePageCompletion): void;
|
|
31
|
+
hasPendingContinuation(): boolean;
|
|
32
|
+
};
|
|
33
|
+
export type CoupledFootnotePagination = {
|
|
34
|
+
flow: CoupledFootnotePageFlow;
|
|
35
|
+
plan: {
|
|
36
|
+
slicesByPage: Map<number, FootnoteSlice[]>;
|
|
37
|
+
reserves: number[];
|
|
38
|
+
hasContinuationByColumn: Map<string, boolean>;
|
|
39
|
+
separatorSpacingBefore: number;
|
|
40
|
+
ledgersByPage: Map<number, FootnotePageLedgerDraft>;
|
|
41
|
+
incomingByPage: Map<number, Map<number, PendingFootnote[]>>;
|
|
42
|
+
outgoingByPage: Map<number, Map<number, PendingFootnote[]>>;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export type CoupledFootnotePaginationFailure = 'unsupported-page' | 'page-sequence' | 'missing-anchor' | 'duplicate-claim' | 'ordered-minimum' | 'physical-overflow' | 'range-conservation' | 'reference-conservation' | 'no-progress';
|
|
46
|
+
export declare class CoupledFootnotePaginationError extends Error {
|
|
47
|
+
readonly code: CoupledFootnotePaginationFailure;
|
|
48
|
+
readonly pageIndex: number;
|
|
49
|
+
readonly footnoteId?: string | undefined;
|
|
50
|
+
readonly name = "CoupledFootnotePaginationError";
|
|
51
|
+
constructor(code: CoupledFootnotePaginationFailure, pageIndex: number, message: string, footnoteId?: string | undefined);
|
|
52
|
+
}
|
|
53
|
+
/** Call only at real document EOF; expected IDs exclude references anchored before this pass. */
|
|
54
|
+
export declare const validateCoupledFootnoteReferenceConservation: (plan: Pick<CoupledFootnotePagination["plan"], "ledgersByPage">, expectedAnchorIds: Iterable<string>) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Owns forward page admission from a fresh start or a bridge-validated boundary.
|
|
57
|
+
* Prepared inventories and prior anchor ownership remain lookup-only.
|
|
58
|
+
*/
|
|
59
|
+
export declare const createCoupledFootnotePagination: (options: CoupledFootnotePaginationOptions) => CoupledFootnotePagination;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FlowBlock, Measure } from '../../contracts/src/index.js';
|
|
2
|
+
import { LayoutOptions } from '../../layout-engine/src/index.js';
|
|
3
|
+
export type CoupledFootnoteReference = {
|
|
4
|
+
id: string;
|
|
5
|
+
pos: number;
|
|
6
|
+
blockId?: string;
|
|
7
|
+
runOrdinal?: number | null;
|
|
8
|
+
};
|
|
9
|
+
export type CoupledFootnoteProfileRejection = 'no-footnote-references' | 'partial-or-semantic-flow' | 'multiple-sections' | 'invalid-page-geometry' | 'unsupported-columns' | 'unsupported-vertical-alignment' | 'unsupported-section-boundary' | 'unsupported-body-block' | 'unsupported-paragraph-geometry' | 'body-page-token' | 'measure-mismatch' | 'duplicate-block-id' | 'duplicate-reference-id' | 'missing-native-owner' | 'unresolved-reference-line' | 'unordered-native-references' | 'retained-profile-unproved' | 'reference-topology-changed';
|
|
10
|
+
export type CoupledFootnoteParagraphUpdate = {
|
|
11
|
+
block: FlowBlock;
|
|
12
|
+
measure: Measure;
|
|
13
|
+
refs: readonly CoupledFootnoteReference[];
|
|
14
|
+
previousRefs: readonly CoupledFootnoteReference[];
|
|
15
|
+
/** Immediate CURRENT neighbors; null only at a true story boundary. */
|
|
16
|
+
predecessor: FlowBlock | null;
|
|
17
|
+
successor: FlowBlock | null;
|
|
18
|
+
};
|
|
19
|
+
export type CoupledFootnoteLocalProfileProof = {
|
|
20
|
+
baselineProfileExact: true;
|
|
21
|
+
referenceTopologyExact: true;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Revalidates only changed paragraphs beneath a bridge-owned retained global
|
|
25
|
+
* profile and exact current note topology. The bridge must prove those owners
|
|
26
|
+
* and supply all affected paragraphs, including referenced neighbors whose
|
|
27
|
+
* skip context changed. These flags do not mint a global/profile certificate.
|
|
28
|
+
*/
|
|
29
|
+
export declare const getCoupledFootnoteParagraphUpdatesUnsupportedReason: (updates: readonly CoupledFootnoteParagraphUpdate[], proof: CoupledFootnoteLocalProfileProof | null | undefined) => CoupledFootnoteProfileRejection | null;
|
|
30
|
+
export declare const supportsCoupledFootnoteParagraphUpdates: (updates: readonly CoupledFootnoteParagraphUpdate[], proof: CoupledFootnoteLocalProfileProof | null | undefined) => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Admission for fresh, single-section body/note flow. This does not certify
|
|
33
|
+
* note-content inventories or retained pages; their owners validate those.
|
|
34
|
+
* Rejection codes carry no document text and are suitable for diagnostics.
|
|
35
|
+
*/
|
|
36
|
+
export declare const getCoupledFootnotePaginationUnsupportedReason: (blocks: FlowBlock[], measures: readonly Measure[], options: LayoutOptions, refs: readonly CoupledFootnoteReference[]) => CoupledFootnoteProfileRejection | null;
|
|
37
|
+
export declare const supportsCoupledFootnotePagination: (blocks: FlowBlock[], measures: readonly Measure[], options: LayoutOptions, refs: readonly CoupledFootnoteReference[]) => boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FlowBlock, Measure, ParagraphBlock } from '../../contracts/src/index.js';
|
|
2
|
+
export type FootnoteRange = {
|
|
3
|
+
kind: 'paragraph';
|
|
4
|
+
blockId: string;
|
|
5
|
+
fromLine: number;
|
|
6
|
+
toLine: number;
|
|
7
|
+
totalLines: number;
|
|
8
|
+
height: number;
|
|
9
|
+
spacingAfter: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'list-item';
|
|
12
|
+
blockId: string;
|
|
13
|
+
itemId: string;
|
|
14
|
+
fromLine: number;
|
|
15
|
+
toLine: number;
|
|
16
|
+
totalLines: number;
|
|
17
|
+
height: number;
|
|
18
|
+
spacingAfter: number;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'table' | 'image' | 'drawing';
|
|
21
|
+
blockId: string;
|
|
22
|
+
height: number;
|
|
23
|
+
};
|
|
24
|
+
export type FootnoteSlice = {
|
|
25
|
+
id: string;
|
|
26
|
+
pageIndex: number;
|
|
27
|
+
columnIndex: number;
|
|
28
|
+
isContinuation: boolean;
|
|
29
|
+
ranges: FootnoteRange[];
|
|
30
|
+
totalHeight: number;
|
|
31
|
+
};
|
|
32
|
+
export declare const getParagraphSpacingAfter: (block: ParagraphBlock) => number;
|
|
33
|
+
export declare const resolveSeparatorSpacingBefore: (rangesByFootnoteId: ReadonlyMap<string, readonly FootnoteRange[]>, measuresById: ReadonlyMap<string, Measure>, explicitValue: number | undefined, fallbackValue: number) => number;
|
|
34
|
+
export declare const getRangeRenderHeight: (range: FootnoteRange) => number;
|
|
35
|
+
export declare const buildFootnoteRanges: (blocks: readonly FlowBlock[], measuresById: ReadonlyMap<string, Measure>) => FootnoteRange[];
|
|
36
|
+
export declare const fitFootnoteContent: (id: string, inputRanges: readonly FootnoteRange[], availableHeight: number, pageIndex: number, columnIndex: number, isContinuation: boolean, measuresById: ReadonlyMap<string, Measure>, forceFirstRange: boolean) => {
|
|
37
|
+
slice: FootnoteSlice;
|
|
38
|
+
remainingRanges: FootnoteRange[];
|
|
39
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Page } from '../../contracts/src/index.js';
|
|
2
|
+
import { CoupledFootnotePaginationOptions } from './coupled-footnote-pagination.js';
|
|
3
|
+
import { FootnoteRange } from './footnote-content.js';
|
|
4
|
+
export type FootnoteCertificatePreparedOptions = Readonly<Omit<CoupledFootnotePaginationOptions, 'start'>>;
|
|
5
|
+
type PendingEntry = Readonly<{
|
|
6
|
+
id: string;
|
|
7
|
+
ranges: readonly Readonly<FootnoteRange>[];
|
|
8
|
+
}>;
|
|
9
|
+
export type FootnotePendingQueue = ReadonlyMap<number, readonly PendingEntry[]>;
|
|
10
|
+
declare const ownerBrand: unique symbol;
|
|
11
|
+
declare const certificateBrand: unique symbol;
|
|
12
|
+
export type FootnoteCertificateOwner = Readonly<{
|
|
13
|
+
prepared: FootnoteCertificatePreparedOptions;
|
|
14
|
+
[ownerBrand]: true;
|
|
15
|
+
}>;
|
|
16
|
+
export type FootnotePageCertificateFacts = Readonly<{
|
|
17
|
+
/** Issuance/source index; transporting a certificate does not rebase it. */
|
|
18
|
+
pageIndex: number;
|
|
19
|
+
incomingByColumn: FootnotePendingQueue;
|
|
20
|
+
outgoingByColumn: FootnotePendingQueue;
|
|
21
|
+
}>;
|
|
22
|
+
export type FootnotePageCertificate = FootnotePageCertificateFacts & Readonly<{
|
|
23
|
+
owner: FootnoteCertificateOwner;
|
|
24
|
+
[certificateBrand]: true;
|
|
25
|
+
}>;
|
|
26
|
+
/** Prepared inputs must remain unchanged for the owner's lifetime; no inventory is copied or scanned here. */
|
|
27
|
+
export declare const createFootnoteCertificateOwner: (prepared: FootnoteCertificatePreparedOptions) => FootnoteCertificateOwner;
|
|
28
|
+
export declare const issueFootnotePageCertificate: (page: Page, owner: FootnoteCertificateOwner, facts: FootnotePageCertificateFacts) => FootnotePageCertificate;
|
|
29
|
+
/** Omitting expectedOwner discovers retained inputs only; tail admission must supply its validated owner. */
|
|
30
|
+
export declare const readFootnotePageCertificate: (page: Page | null | undefined, expectedOwner?: FootnoteCertificateOwner) => FootnotePageCertificate | null;
|
|
31
|
+
/**
|
|
32
|
+
* Restores note-continuation provenance lost by string-key page cloning.
|
|
33
|
+
* The caller must separately prove body/rekey equivalence; this does not certify body reuse.
|
|
34
|
+
*/
|
|
35
|
+
export declare const transferFootnotePageCertificate: (source: Page, target: Page, pageIndexDelta: number) => FootnotePageCertificate | null;
|
|
36
|
+
/** Queue order and every range field are part of the boundary; equal height is not equivalence. */
|
|
37
|
+
export declare const pendingFootnoteQueuesEqual: (left: FootnotePendingQueue, right: FootnotePendingQueue) => boolean;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FootnotePageLedger, Measure } from '../../contracts/src/index.js';
|
|
2
|
+
import { FootnoteRange, FootnoteSlice } from './footnote-content.js';
|
|
3
|
+
export type PendingFootnote = {
|
|
4
|
+
id: string;
|
|
5
|
+
ranges: FootnoteRange[];
|
|
6
|
+
};
|
|
7
|
+
type IncomingFootnote = {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly ranges: readonly FootnoteRange[];
|
|
10
|
+
};
|
|
11
|
+
export type FootnotePageLedgerDraft = Omit<FootnotePageLedger, 'pageIndex' | 'appliedBodyReservePx' | 'deadReservePx'>;
|
|
12
|
+
export type FootnotePagePlanInput = {
|
|
13
|
+
pageIndex: number;
|
|
14
|
+
columnCount: number;
|
|
15
|
+
/** Physical capacity for this page's note band, including separator, padding, and gaps. */
|
|
16
|
+
availableHeight: number;
|
|
17
|
+
idsByColumn: ReadonlyMap<number, readonly string[]>;
|
|
18
|
+
rangesByFootnoteId: ReadonlyMap<string, readonly FootnoteRange[]>;
|
|
19
|
+
measuresById: ReadonlyMap<string, Measure>;
|
|
20
|
+
fullHeightById: ReadonlyMap<string, number>;
|
|
21
|
+
firstLineHeightById: ReadonlyMap<string, number>;
|
|
22
|
+
pendingByColumn: ReadonlyMap<number, readonly IncomingFootnote[]>;
|
|
23
|
+
separatorSpacingBefore: number;
|
|
24
|
+
dividerHeight: number;
|
|
25
|
+
continuationDividerHeight: number;
|
|
26
|
+
topPadding: number;
|
|
27
|
+
gap: number;
|
|
28
|
+
};
|
|
29
|
+
export type FootnotePagePlan = {
|
|
30
|
+
slices: FootnoteSlice[];
|
|
31
|
+
pendingByColumn: Map<number, PendingFootnote[]>;
|
|
32
|
+
ledger: FootnotePageLedgerDraft;
|
|
33
|
+
/** Rounded reserve capped at the supplied capacity; not a proof that every slice fits. */
|
|
34
|
+
reserve: number;
|
|
35
|
+
/** Unrounded, uncapped height of the tallest placed column. */
|
|
36
|
+
actualReserve: number;
|
|
37
|
+
hasContinuationByColumn: Map<number, boolean>;
|
|
38
|
+
capped: boolean;
|
|
39
|
+
overflowHeightPx: number;
|
|
40
|
+
overflowHeightByColumn: Map<number, number>;
|
|
41
|
+
/** Outgoing ranges or physical overflow still require a controller decision. */
|
|
42
|
+
hasUnresolvedContent: boolean;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Places one page from exact incoming ranges. Global inventories are lookup-only;
|
|
46
|
+
* body placement and any future-page capacity decision belong to the caller.
|
|
47
|
+
*/
|
|
48
|
+
export declare const planFootnotePage: (input: FootnotePagePlanInput) => FootnotePagePlan;
|
|
49
|
+
export {};
|
|
@@ -4,6 +4,7 @@ import { LayoutOptions, HeaderFooterConstraints, LayoutExecutionCheckpoint } fro
|
|
|
4
4
|
import { computeDirtyRegions } from './diff.js';
|
|
5
5
|
import { MeasureCache } from './cache.js';
|
|
6
6
|
import { HeaderFooterBatch } from './layoutHeaderFooter.js';
|
|
7
|
+
import { FootnoteCertificateOwner, FootnoteCertificatePreparedOptions } from './footnote-page-certificate.js';
|
|
7
8
|
export type HeaderFooterMeasureFn = (block: FlowBlock, constraints: {
|
|
8
9
|
maxWidth: number;
|
|
9
10
|
maxHeight: number;
|
|
@@ -32,6 +33,10 @@ export type HeaderFooterLayoutResult = {
|
|
|
32
33
|
* hashes — see the SD-3418 post-mortem for why identity keys are forbidden).
|
|
33
34
|
*/
|
|
34
35
|
export type FootnoteReserveSeed = {
|
|
36
|
+
/** Identifies canonical forward output; not, by itself, a reuse certificate. */
|
|
37
|
+
paginationPolicy?: 'coupled-v1';
|
|
38
|
+
/** Opaque bridge-issued note/profile state; numeric reserve vectors never authorize page reuse. */
|
|
39
|
+
coupled?: CoupledFootnoteRetainedState;
|
|
35
40
|
reserves: number[];
|
|
36
41
|
/** Sparse page indexes that held a reserve, ledger, or injected note slice. */
|
|
37
42
|
notePageIndexes?: number[];
|
|
@@ -63,6 +68,23 @@ export type FootnoteReserveSeed = {
|
|
|
63
68
|
referencePageIndexes: number[];
|
|
64
69
|
};
|
|
65
70
|
};
|
|
71
|
+
type CoupledFootnoteRetainedState = {
|
|
72
|
+
readonly owner: FootnoteCertificateOwner;
|
|
73
|
+
/** Current note inventory; may extend the certificate owner's page input during exact Undo. */
|
|
74
|
+
readonly prepared: FootnoteCertificatePreparedOptions;
|
|
75
|
+
readonly headerFooterGeometryFingerprint: string;
|
|
76
|
+
readonly reserves: readonly number[];
|
|
77
|
+
readonly notePageIndexes: readonly number[];
|
|
78
|
+
readonly refs: readonly FootnoteReference[];
|
|
79
|
+
readonly referencePlaneIdentity: FootnotesLayoutInput['refs'];
|
|
80
|
+
readonly referenceTopologyRevision: string | undefined;
|
|
81
|
+
readonly refIndexesByBlock: ReadonlyMap<string, readonly number[]>;
|
|
82
|
+
readonly refIndexById: ReadonlyMap<string, number>;
|
|
83
|
+
readonly blocksById: Map<string, FlowBlock>;
|
|
84
|
+
readonly extraBlocks: FlowBlock[];
|
|
85
|
+
readonly extraMeasures: Measure[];
|
|
86
|
+
readonly extraIndexById: ReadonlyMap<string, number>;
|
|
87
|
+
};
|
|
66
88
|
export type IncrementalLayoutResult = {
|
|
67
89
|
layout: Layout;
|
|
68
90
|
/** Pass-owned block plane after derived layout annotations are attached. */
|
|
@@ -104,6 +126,8 @@ export type IncrementalLayoutResult = {
|
|
|
104
126
|
bridgeTiming: IncrementalLayoutBridgeTiming;
|
|
105
127
|
};
|
|
106
128
|
export type IncrementalLayoutBridgeTiming = {
|
|
129
|
+
/** Content-free reason a document used the conservative note fallback. */
|
|
130
|
+
footnoteCoupledRejection?: string;
|
|
107
131
|
totalMs: number;
|
|
108
132
|
inputPreparationMs: number;
|
|
109
133
|
measureTotalMs: number;
|
|
@@ -169,6 +193,9 @@ export type IncrementalLayoutBridgeTiming = {
|
|
|
169
193
|
footnoteWidowRelayouts: number;
|
|
170
194
|
footnoteRevertRelayouts: number;
|
|
171
195
|
footnoteOtherRelayouts: number;
|
|
196
|
+
footnoteCoupledRelayouts?: number;
|
|
197
|
+
footnoteCoupledPages?: number;
|
|
198
|
+
footnoteCoupledFallbacks?: number;
|
|
172
199
|
footnoteAssignmentReferencesRead: number;
|
|
173
200
|
footnoteAssignmentReferencesReused: number;
|
|
174
201
|
footnoteAssignmentPagesIndexed: number;
|
|
@@ -397,6 +424,11 @@ export type IncrementalLayoutReuseOptions = {
|
|
|
397
424
|
noteIds: readonly string[];
|
|
398
425
|
bodyReferenceBlockIds: readonly string[];
|
|
399
426
|
};
|
|
427
|
+
/** History-only inverse of a compiler-proved footnote removal. */
|
|
428
|
+
provedNoteReferenceRestoration?: {
|
|
429
|
+
sourceTxId: string;
|
|
430
|
+
restoredNoteIds: readonly string[];
|
|
431
|
+
};
|
|
400
432
|
/**
|
|
401
433
|
* Host-proved header/footer-only refresh. The bridge still compares the
|
|
402
434
|
* current pre-layout height fingerprint before retaining body pagination.
|
|
@@ -416,6 +448,45 @@ export declare const measureCache: MeasureCache<Measure>;
|
|
|
416
448
|
* state that can legally shift page geometry.
|
|
417
449
|
*/
|
|
418
450
|
export declare function clearIncrementalModuleState(): void;
|
|
451
|
+
type FootnoteReference = {
|
|
452
|
+
id: string;
|
|
453
|
+
/**
|
|
454
|
+
* Legacy v1 PM-position anchor. Resolved via fragment `pmStart` / `pmEnd`
|
|
455
|
+
* range matching. Required for v1 producers; v2 producers may set this
|
|
456
|
+
* to a synthetic value (e.g. block ordinal) and supply a `blockId`
|
|
457
|
+
* anchor for resolution.
|
|
458
|
+
*/
|
|
459
|
+
pos: number;
|
|
460
|
+
/**
|
|
461
|
+
* v2 source anchor identifying the rendered body
|
|
462
|
+
* reference marker. When set, `assignFootnotesToColumns` resolves
|
|
463
|
+
* the reference's page/column by matching against
|
|
464
|
+
* `layout.pages[].fragments[].blockId` instead of falling back to
|
|
465
|
+
* positional fragment lookup. Editor-neutral by design.
|
|
466
|
+
*/
|
|
467
|
+
blockId?: string;
|
|
468
|
+
/**
|
|
469
|
+
* Optional paragraph-run anchor used by v2 refs.
|
|
470
|
+
*
|
|
471
|
+
* A long paragraph can span multiple page fragments. When `blockId` alone
|
|
472
|
+
* is used, the bridge can only resolve the FIRST fragment carrying that
|
|
473
|
+
* block, which places later-line footnotes too early and cascades reserve
|
|
474
|
+
* drift across the document. When `runOrdinal` is present and a paragraph
|
|
475
|
+
* measure is available, the bridge resolves the fragment whose line range
|
|
476
|
+
* actually contains the referenced run.
|
|
477
|
+
*/
|
|
478
|
+
runOrdinal?: number | null;
|
|
479
|
+
};
|
|
480
|
+
type FootnotesLayoutInput = {
|
|
481
|
+
refs: FootnoteReference[];
|
|
482
|
+
blocksById: Map<string, FlowBlock[]>;
|
|
483
|
+
/** Host-issued identity for the body reference topology. */
|
|
484
|
+
referenceTopologyRevision?: string;
|
|
485
|
+
gap?: number;
|
|
486
|
+
topPadding?: number;
|
|
487
|
+
dividerHeight?: number;
|
|
488
|
+
separatorSpacingBefore?: number;
|
|
489
|
+
};
|
|
419
490
|
type FootnoteGrowConvergenceState = {
|
|
420
491
|
appliedReserves: readonly number[];
|
|
421
492
|
plannedReserves: readonly number[];
|
|
@@ -472,12 +543,33 @@ export declare function incrementalLayout(previousBlocks: FlowBlock[], _previous
|
|
|
472
543
|
blocks: FlowBlock[];
|
|
473
544
|
measures: Measure[];
|
|
474
545
|
};
|
|
546
|
+
/** Bridge-issued pre-removal owner selected by the exact history source tx. */
|
|
547
|
+
historyFootnoteRestoration?: {
|
|
548
|
+
sourceTxId: string;
|
|
549
|
+
restoredNoteIds: readonly string[];
|
|
550
|
+
sourceSeed: FootnoteReserveSeed;
|
|
551
|
+
};
|
|
475
552
|
/** Current immutable host owner and the prior bridge-issued geometry seed. */
|
|
476
553
|
headerFooterGeometry?: {
|
|
477
554
|
ownerFingerprint: string;
|
|
478
555
|
retainedSeed: HeaderFooterGeometrySeed | null;
|
|
479
556
|
};
|
|
480
557
|
}, layoutReuse?: IncrementalLayoutReuseOptions, measureReuseProof?: IncrementalMeasureReuseProof, execution?: IncrementalLayoutExecutionControl): Promise<IncrementalLayoutResult>;
|
|
558
|
+
declare function shouldAttemptPreparedCoupledConvergenceRetry(input: {
|
|
559
|
+
preparedCoupled: boolean;
|
|
560
|
+
provedPrefixToDocumentEnd: boolean;
|
|
561
|
+
sameInvocationReserveRelayout: boolean;
|
|
562
|
+
paginationPrefix: boolean;
|
|
563
|
+
boundedRenderDiagnosticRetry: boolean;
|
|
564
|
+
retryAttempted: boolean;
|
|
565
|
+
terminalAttempted: boolean;
|
|
566
|
+
reachesSourceTail: boolean;
|
|
567
|
+
currentPageHorizon: number;
|
|
568
|
+
sourceAffectedFrontierPageIndex: number;
|
|
569
|
+
checkpointPageIndex: number;
|
|
570
|
+
previousPageCount: number;
|
|
571
|
+
}): boolean;
|
|
572
|
+
export declare const __test_only_shouldAttemptPreparedCoupledConvergenceRetry: typeof shouldAttemptPreparedCoupledConvergenceRetry;
|
|
481
573
|
export declare function __test_only_splicedCheckpointStats(map: ReadonlyMap<string, LayoutBlockResumeCheckpoint> | undefined): {
|
|
482
574
|
pieceCount: number;
|
|
483
575
|
sourceCount: number;
|
|
@@ -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: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: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;
|
|
@@ -2,10 +2,13 @@ import { FlowBlock } from '../../contracts/src/index.js';
|
|
|
2
2
|
import { LayoutWorkCheckpoint } from './execution.js';
|
|
3
3
|
import { FootnoteAnchorRef } from './layout-paragraph.js';
|
|
4
4
|
export type FootnoteAnchorIndexInput = {
|
|
5
|
+
/** Use proved native paragraph/run ownership in the coupled page path. */
|
|
6
|
+
nativeRunOwnership?: boolean;
|
|
5
7
|
refs?: Array<{
|
|
6
8
|
id: string;
|
|
7
9
|
pos: number;
|
|
8
10
|
blockId?: string;
|
|
11
|
+
runOrdinal?: number | null;
|
|
9
12
|
}>;
|
|
10
13
|
bodyHeightById?: Map<string, number>;
|
|
11
14
|
firstLineHeightById?: Map<string, number>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Page } from '../../contracts/src/index.js';
|
|
2
|
+
import { PageState } from './paginator.js';
|
|
3
|
+
import { FootnoteAnchorRef } from './layout-paragraph.js';
|
|
4
|
+
/** Internal body/notes handshake. The bridge owns exact continuation ranges. */
|
|
5
|
+
export type FootnotePageFlow = {
|
|
6
|
+
/** False for a bounded block horizon: its EOF is not the document's EOF. */
|
|
7
|
+
completeDocument?: boolean;
|
|
8
|
+
incomingDemand(pageIndex: number): {
|
|
9
|
+
height: number;
|
|
10
|
+
refs: number;
|
|
11
|
+
};
|
|
12
|
+
completePage(input: {
|
|
13
|
+
page: Page;
|
|
14
|
+
pageIndex: number;
|
|
15
|
+
bodyBottom: number;
|
|
16
|
+
physicalBottom: number;
|
|
17
|
+
anchors: readonly FootnoteAnchorRef[];
|
|
18
|
+
}): void;
|
|
19
|
+
hasPendingContinuation(): boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Both kept groups and individual lines use this budget. A numeric reserve
|
|
23
|
+
* from an earlier pagination has no authority over this page's exact queue.
|
|
24
|
+
*/
|
|
25
|
+
export declare function coupledFootnoteBodyBottom(state: PageState, anchoredHeight: number, anchoredRefs: number, incoming: {
|
|
26
|
+
height: number;
|
|
27
|
+
refs: number;
|
|
28
|
+
}, overhead: (refs: number) => number, minimumBodyHeight?: number): number;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { ColumnLayout, FlowBlock, HeaderFooterLayout, Layout, Measure, Page, ParagraphBlock, ParagraphMeasure, SectionMetadata, FlowMode, DocumentBackground, ParagraphLineRegion } from '../../contracts/src/index.js';
|
|
2
|
+
import { FootnotePageFlow } from './footnote-page-flow.js';
|
|
2
3
|
import { PageState, ConstraintBoundary } from './paginator.js';
|
|
3
4
|
import { LayoutExecutionControl } from './execution.js';
|
|
5
|
+
export type { FootnotePageFlow } from './footnote-page-flow.js';
|
|
6
|
+
export { findLineIndexForRunOrdinal } from './layout-utils.js';
|
|
4
7
|
type PageSize = {
|
|
5
8
|
w: number;
|
|
6
9
|
h: number;
|
|
@@ -19,6 +22,8 @@ type Margins = {
|
|
|
19
22
|
*/
|
|
20
23
|
export declare const SEMANTIC_PAGE_HEIGHT_PX = 1000000;
|
|
21
24
|
export type LayoutOptions = {
|
|
25
|
+
/** @internal Exact forward body/note pagination; never supplied by document authors. */
|
|
26
|
+
footnotePageFlow?: FootnotePageFlow;
|
|
22
27
|
pageSize?: PageSize;
|
|
23
28
|
margins?: Margins;
|
|
24
29
|
documentBackground?: DocumentBackground;
|
|
@@ -53,6 +58,8 @@ export type LayoutOptions = {
|
|
|
53
58
|
refs?: Array<{
|
|
54
59
|
id: string;
|
|
55
60
|
pos: number;
|
|
61
|
+
blockId?: string;
|
|
62
|
+
runOrdinal?: number | null;
|
|
56
63
|
}>;
|
|
57
64
|
/**
|
|
58
65
|
* SD-3049: total measured body height per footnote id (sum of measured
|
|
@@ -204,6 +211,7 @@ export type LayoutOptions = {
|
|
|
204
211
|
prefixFragments: readonly Page['fragments'][number][];
|
|
205
212
|
cursorY: number;
|
|
206
213
|
maxCursorY: number;
|
|
214
|
+
committedBodyBottom?: number;
|
|
207
215
|
columnIndex: number;
|
|
208
216
|
trailingSpacing: number;
|
|
209
217
|
lastParagraphStyleId?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FloatingObjectManager } from './floating-objects.js';
|
|
2
2
|
import { PageState } from './paginator.js';
|
|
3
|
+
import { FootnotePageFlow } from './footnote-page-flow.js';
|
|
3
4
|
import { PageMargins, ParagraphBlock, ParagraphMeasure, ImageBlock, ImageMeasure, DrawingBlock, DrawingMeasure, ParagraphLineRegion } from '../../contracts/src/index.js';
|
|
4
5
|
import { AnchoredTable } from './anchors.js';
|
|
5
6
|
/**
|
|
@@ -9,11 +10,14 @@ import { AnchoredTable } from './anchors.js';
|
|
|
9
10
|
*/
|
|
10
11
|
export type FootnoteAnchorRef = {
|
|
11
12
|
pmPos: number;
|
|
13
|
+
/** Exact run in this paragraph; absent for legacy or containing-table anchors. */
|
|
14
|
+
runOrdinal?: number;
|
|
12
15
|
refId: string;
|
|
13
16
|
fullHeight: number;
|
|
14
17
|
firstLineHeight: number;
|
|
15
18
|
};
|
|
16
19
|
export type ParagraphLayoutContext = {
|
|
20
|
+
incomingFootnoteDemand?: FootnotePageFlow['incomingDemand'];
|
|
17
21
|
block: ParagraphBlock;
|
|
18
22
|
measure: ParagraphMeasure;
|
|
19
23
|
columnWidth: number;
|
|
@@ -66,6 +66,8 @@ export declare function sliceLines(lines: ParagraphMeasure['lines'], startIndex:
|
|
|
66
66
|
height: number;
|
|
67
67
|
};
|
|
68
68
|
export type { LinePmRange };
|
|
69
|
+
/** A marker belongs to the first measured line containing its run. */
|
|
70
|
+
export declare const findLineIndexForRunOrdinal: (lines: readonly Line[] | undefined, runOrdinal: number) => number | null;
|
|
69
71
|
export declare const computeFragmentPmRange: (block: ParagraphBlock, lines: ParagraphMeasure["lines"], fromLine: number, toLine: number) => LinePmRange;
|
|
70
72
|
export declare const computeLinePmRange: (block: ParagraphBlock, line: Line) => LinePmRange;
|
|
71
73
|
/**
|
|
@@ -20,6 +20,8 @@ export type PageState = {
|
|
|
20
20
|
* Used when starting a mid-page region so the new section begins below
|
|
21
21
|
* all column content, not just the current column's cursor. */
|
|
22
22
|
maxCursorY: number;
|
|
23
|
+
/** Committed paragraph footprint, excluding speculative before/after spacing. */
|
|
24
|
+
committedBodyBottom?: number;
|
|
23
25
|
/**
|
|
24
26
|
* SD-3049: Page-level footnote reserve already baked into `contentBottom`
|
|
25
27
|
* via `getActiveBottomMargin`. The block-aware break decision compares
|
|
@@ -78,6 +80,7 @@ export type PaginatorOptions = {
|
|
|
78
80
|
prefixFragments: readonly Page['fragments'][number][];
|
|
79
81
|
cursorY: number;
|
|
80
82
|
maxCursorY: number;
|
|
83
|
+
committedBodyBottom?: number;
|
|
81
84
|
columnIndex: number;
|
|
82
85
|
trailingSpacing: number;
|
|
83
86
|
lastParagraphStyleId?: string;
|
|
@@ -108,6 +111,8 @@ export type PaginatorOptions = {
|
|
|
108
111
|
* created. Defaults to 0 when not provided.
|
|
109
112
|
*/
|
|
110
113
|
getFootnoteReserveForPage?: (pageIndex: number) => number;
|
|
114
|
+
/** Coupled note-only continuation pages are real content before note injection. */
|
|
115
|
+
keepNoteOnlyPages?: boolean;
|
|
111
116
|
};
|
|
112
117
|
export declare class PaginationEarlyStop extends Error {
|
|
113
118
|
constructor();
|
package/dist/superdoc.cjs
CHANGED
|
@@ -316,7 +316,7 @@ var shuffleArray = (array) => {
|
|
|
316
316
|
var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
|
|
317
317
|
function getSuperdocVersion() {
|
|
318
318
|
try {
|
|
319
|
-
return "2.11.
|
|
319
|
+
return "2.11.1-next.2";
|
|
320
320
|
} catch {
|
|
321
321
|
return "unknown";
|
|
322
322
|
}
|
|
@@ -45188,7 +45188,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
|
|
|
45188
45188
|
this.config.colors = shuffleArray(this.config.colors);
|
|
45189
45189
|
this.userColorMap = /* @__PURE__ */ new Map();
|
|
45190
45190
|
this.colorIndex = 0;
|
|
45191
|
-
this.version = "2.11.
|
|
45191
|
+
this.version = "2.11.1-next.2";
|
|
45192
45192
|
this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
|
|
45193
45193
|
this.superdocId = config.superdocId || require_uuid.v4();
|
|
45194
45194
|
this.colors = this.config.colors ?? [];
|
package/dist/superdoc.es.js
CHANGED
|
@@ -315,7 +315,7 @@ var shuffleArray = (array) => {
|
|
|
315
315
|
var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
|
|
316
316
|
function getSuperdocVersion() {
|
|
317
317
|
try {
|
|
318
|
-
return "2.11.
|
|
318
|
+
return "2.11.1-next.2";
|
|
319
319
|
} catch {
|
|
320
320
|
return "unknown";
|
|
321
321
|
}
|
|
@@ -45119,7 +45119,7 @@ var SuperDoc = class extends import_eventemitter3.default {
|
|
|
45119
45119
|
this.config.colors = shuffleArray(this.config.colors);
|
|
45120
45120
|
this.userColorMap = /* @__PURE__ */ new Map();
|
|
45121
45121
|
this.colorIndex = 0;
|
|
45122
|
-
this.version = "2.11.
|
|
45122
|
+
this.version = "2.11.1-next.2";
|
|
45123
45123
|
this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
|
|
45124
45124
|
this.superdocId = config.superdocId || v4();
|
|
45125
45125
|
this.colors = this.config.colors ?? [];
|