import { FootnotePageLedger, Layout } from '../../contracts/src/index.js'; export type FootnoteWindowScoreReason = 'globally-safe' | 'cluster-spill' | 'new-mandatory-only' | 'candidate-not-improved' | 'page-count-grew' | 'dead-reserve-bloat'; export type FootnotePreferredReserveCandidate = { pageIndex: number; anchorIds: string[]; mandatoryReservePx: number; preferredReservePx: number; reserveDeltaPx: number; actualBandHeightPx: number; lastAnchorRenderedLines: number; }; export type FootnoteWindowStats = { totalPages: number; mandatoryOnlyCount: number; deadReserveSum: number; clusterSplitCount: number; candidateRenderedLines?: number; /** * Analyzer-only metric. Runtime layout does not know the Word baseline, but * the scorer can carry this when a caller has external alignment data. */ driftEvents?: number; }; export type FootnoteWindowScoreInput = { beforeLayout: Layout; afterLayout: Layout; candidatePageIndex: number; candidateAnchorId?: string; beforeLedger: FootnotePageLedger[]; afterLedger: FootnotePageLedger[]; windowAhead?: number; preferredDeltaThresholdPx?: number; mandatoryOnlyTolerancePx?: number; deadReserveBloatThresholdPx?: number; wholeDocumentDeadReserveBloatThresholdPx?: number; }; export type FootnoteWindowScoreResult = { accept: boolean; reason: FootnoteWindowScoreReason; before: FootnoteWindowStats; after: FootnoteWindowStats; }; export declare const isMandatoryOnlyFootnotePage: (ledger: FootnotePageLedger, preferredDeltaThresholdPx?: number, mandatoryOnlyTolerancePx?: number) => boolean; /** * SD-2656 (post-Vivienne-feedback): a page whose LAST anchor partially rendered * but spilled to a later page. The user-visible bug is a footnote split across * pages even when the preferred reserve would fit the whole anchor on the * anchor page (Word does keep it together). * * The "mandatory-only" predicate catches first-line-only splits; this predicate * catches partial splits (lastAnchorRenderedLines > 1 but the rest still spilled). * Both feed into the same scorer trial. The scorer's accept criteria * (no new cluster spills, no new mandatory-only pages, bounded dead-reserve * growth, candidate rendered lines improved) still gates whether the bump * actually lands. */ export declare const isSplitLastAnchorFootnotePage: (ledger: FootnotePageLedger, preferredDeltaThresholdPx?: number) => boolean; export declare const getPreferredReserveCandidates: (ledgers: FootnotePageLedger[], preferredDeltaThresholdPx?: number, mandatoryOnlyTolerancePx?: number) => FootnotePreferredReserveCandidate[]; export declare const getPreferredReserveTrialTargets: (candidate: FootnotePreferredReserveCandidate, currentReservePx: number, preferredDeltaThresholdPx?: number, maxTargets?: number) => number[]; export declare const collectFootnoteLedgers: (layout: Layout) => FootnotePageLedger[]; export declare const summarizeFootnoteWindow: (layout: Layout, ledgers: FootnotePageLedger[], candidatePageIndex: number, windowAhead?: number, preferredDeltaThresholdPx?: number, mandatoryOnlyTolerancePx?: number, candidateAnchorId?: string) => FootnoteWindowStats; export declare const scoreFootnoteWindow: (input: FootnoteWindowScoreInput) => FootnoteWindowScoreResult; //# sourceMappingURL=footnote-scorer.d.ts.map