@stll/folio-core 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +3 -9
- package/dist/ai-edits/apply.js +94 -14
- package/dist/ai-edits/blockRange.d.ts +33 -0
- package/dist/ai-edits/blockRange.js +58 -0
- package/dist/ai-edits/headless.d.ts +35 -5
- package/dist/ai-edits/headless.js +92 -22
- package/dist/ai-edits/index.d.ts +5 -3
- package/dist/ai-edits/index.js +3 -2
- package/dist/ai-edits/snapshot.d.ts +14 -2
- package/dist/ai-edits/snapshot.js +12 -1
- package/dist/ai-edits/types.d.ts +42 -3
- package/dist/compat/eigenpal.d.ts +3 -2
- package/dist/compat/eigenpal.js +6 -5
- package/dist/controller/layoutPipeline.d.ts +1 -0
- package/dist/controller/layoutPipeline.js +35 -115
- package/dist/controller/layoutScheduler.d.ts +2 -1
- package/dist/controller/layoutScheduler.js +6 -0
- package/dist/document-operations.d.ts +122 -0
- package/dist/document-operations.js +575 -0
- package/dist/docx/blockContentParser.js +17 -7
- package/dist/docx/encryption/agileDecryption.d.ts +6 -0
- package/dist/docx/encryption/agileDecryption.js +209 -0
- package/dist/docx/encryption/compoundFile.d.ts +20 -0
- package/dist/docx/encryption/compoundFile.js +251 -0
- package/dist/docx/encryption/containerFormat.d.ts +15 -0
- package/dist/docx/encryption/containerFormat.js +40 -0
- package/dist/docx/encryption/cryptoBytes.d.ts +11 -0
- package/dist/docx/encryption/cryptoBytes.js +55 -0
- package/dist/docx/encryption/encryptionInfo.d.ts +32 -0
- package/dist/docx/encryption/encryptionInfo.js +128 -0
- package/dist/docx/encryption/errors.d.ts +18 -0
- package/dist/docx/encryption/errors.js +13 -0
- package/dist/docx/encryption/index.d.ts +4 -0
- package/dist/docx/encryption/index.js +4 -0
- package/dist/docx/encryption/openEncryptedDocx.d.ts +28 -0
- package/dist/docx/encryption/openEncryptedDocx.js +65 -0
- package/dist/docx/footnoteParser.d.ts +1 -17
- package/dist/docx/footnoteParser.js +1 -27
- package/dist/docx/groupDrawingParser.d.ts +8 -0
- package/dist/docx/groupDrawingParser.js +131 -0
- package/dist/docx/index.d.ts +3 -1
- package/dist/docx/index.js +3 -1
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +8 -2
- package/dist/docx/paragraphParser.js +9 -3
- package/dist/docx/parser.d.ts +4 -5
- package/dist/docx/parser.js +8 -2
- package/dist/docx/runParser.js +8 -1
- package/dist/docx/selectiveXmlPatch.js +4 -10
- package/dist/docx/serializer/numberingSerializer.js +3 -1
- package/dist/docx/settingsParser.d.ts +6 -3
- package/dist/docx/settingsParser.js +2 -0
- package/dist/docx/unzip.d.ts +7 -5
- package/dist/docx/unzip.js +8 -2
- package/dist/docx/xmlParser.js +11 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -5
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +37 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +138 -14
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +6 -8
- package/dist/layout-bridge/convert/headerFooterLayout.js +21 -13
- package/dist/layout-bridge/convert/toFlowBlocks.js +58 -19
- package/dist/layout-engine/index.d.ts +6 -5
- package/dist/layout-engine/index.js +116 -27
- package/dist/layout-engine/keep-together.d.ts +13 -8
- package/dist/layout-engine/keep-together.js +40 -25
- package/dist/layout-engine/layoutInstrumentation.d.ts +11 -1
- package/dist/layout-engine/layoutInstrumentation.js +10 -1
- package/dist/layout-engine/measure/measureBlocks.js +43 -19
- package/dist/layout-engine/measure/measureParagraph.js +91 -14
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +9 -3
- package/dist/layout-engine/textBoxGroup.d.ts +7 -0
- package/dist/layout-engine/textBoxGroup.js +8 -0
- package/dist/layout-engine/types.d.ts +33 -9
- package/dist/layout-engine/types.js +20 -2
- package/dist/layout-painter/renderPage.d.ts +15 -1
- package/dist/layout-painter/renderPage.js +29 -12
- package/dist/layout-painter/renderParagraph.js +44 -9
- package/dist/layout-painter/renderTable.js +27 -7
- package/dist/managers/DocumentLoaderManager.d.ts +3 -1
- package/dist/managers/DocumentLoaderManager.js +13 -5
- package/dist/managers/TableSelectionManager.js +1 -1
- package/dist/model.js +1 -1
- package/dist/paged-layout/pageText.d.ts +14 -0
- package/dist/paged-layout/pageText.js +21 -0
- package/dist/prosemirror/attrs/index.js +3 -0
- package/dist/prosemirror/commands/comments.js +75 -5
- package/dist/prosemirror/commands/hyperlink.js +4 -2
- package/dist/prosemirror/commands/image.js +2 -1
- package/dist/prosemirror/commands/index.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +68 -0
- package/dist/prosemirror/commands/propertyChangeScope.js +242 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +21 -5
- package/dist/prosemirror/conversion/toProseDoc.js +43 -15
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +12 -11
- package/dist/prosemirror/findReplaceSelection.js +2 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/insertOperations.js +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +14 -7
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.js +4 -2
- package/dist/redline.d.ts +20 -0
- package/dist/redline.js +127 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +6 -2
- package/dist/symbols.d.ts +35 -0
- package/dist/symbols.js +512 -0
- package/dist/utils/clipboard.js +3 -3
- package/dist/utils/fontLoader.js +3 -0
- package/dist/utils/fontResolver.js +65 -1
- package/dist/utils/zoom.d.ts +21 -0
- package/dist/utils/zoom.js +21 -0
- package/dist/version-comparison.d.ts +92 -0
- package/dist/version-comparison.js +436 -0
- package/package.json +7 -3
- package/dist/paged-layout/headerFooterMargins.d.ts +0 -73
- package/dist/paged-layout/headerFooterMargins.js +0 -120
package/dist/docx/runParser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cloneWithXmlnsDeclarations, elementToXml, findChild, findChildren, getAttribute, getChildElements, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
2
2
|
import { EmphasisMarkSchema, FontThemeSchema, HighlightColorSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
3
3
|
import { parseImage } from "./imageParser.js";
|
|
4
|
+
import { parseGroupDrawing } from "./groupDrawingParser.js";
|
|
4
5
|
import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
|
|
5
6
|
import { parseVmlImageContent } from "./vmlImageParser.js";
|
|
6
7
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
@@ -442,6 +443,12 @@ function parseInstrText(element) {
|
|
|
442
443
|
* `shapeParser.parseShapeFromDrawing` into a `ShapeContent`.
|
|
443
444
|
*/
|
|
444
445
|
function parseDrawingContent(element, rels, media) {
|
|
446
|
+
const groupImage = parseGroupDrawing(element);
|
|
447
|
+
if (groupImage) return {
|
|
448
|
+
type: "drawing",
|
|
449
|
+
image: groupImage,
|
|
450
|
+
rawXml: elementToXml(element)
|
|
451
|
+
};
|
|
445
452
|
if (shouldPreserveRawShapeDrawing(element)) return {
|
|
446
453
|
type: "drawing",
|
|
447
454
|
image: {
|
|
@@ -550,7 +557,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
550
557
|
if (innerName === "drawing") {
|
|
551
558
|
const innerDrawing = parseDrawingContent(innerChild, rels, media);
|
|
552
559
|
if (innerDrawing) {
|
|
553
|
-
if (innerDrawing.type === "drawing" && !innerDrawing.image.src) innerDrawing.rawXml = elementToXml(child);
|
|
560
|
+
if (innerDrawing.type === "drawing" && (innerDrawing.rawXml !== void 0 || !innerDrawing.image.src)) innerDrawing.rawXml = elementToXml(child);
|
|
554
561
|
contents.push(innerDrawing);
|
|
555
562
|
}
|
|
556
563
|
} else if (innerName === "pict") {
|
|
@@ -50,11 +50,8 @@ function findParagraphOffsets(xml, paraId) {
|
|
|
50
50
|
start,
|
|
51
51
|
end: tagEnd + 1
|
|
52
52
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
depth++;
|
|
56
|
-
pos = tagEnd + 1;
|
|
57
|
-
}
|
|
53
|
+
} else depth++;
|
|
54
|
+
pos = tagEnd + 1;
|
|
58
55
|
} else pos = tagStart + 1;
|
|
59
56
|
} else if (xml.startsWith("</w:p>", tagStart)) {
|
|
60
57
|
depth--;
|
|
@@ -226,11 +223,8 @@ function scanElementRange(xml, start, openLiteral, closeTag) {
|
|
|
226
223
|
start,
|
|
227
224
|
end: tagEnd + 1
|
|
228
225
|
};
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
depth++;
|
|
232
|
-
pos = tagEnd + 1;
|
|
233
|
-
}
|
|
226
|
+
} else depth++;
|
|
227
|
+
pos = tagEnd + 1;
|
|
234
228
|
} else if (xml.startsWith(closeTag, tagStart)) {
|
|
235
229
|
depth--;
|
|
236
230
|
if (depth === 0) return {
|
|
@@ -87,7 +87,9 @@ function serializeNum(instance) {
|
|
|
87
87
|
* change detection and per-definition splicing, never to overwrite the part.
|
|
88
88
|
*/
|
|
89
89
|
function serializeNumberingXml(numbering) {
|
|
90
|
-
|
|
90
|
+
const abstractNums = numbering.abstractNums.map(serializeAbstractNum).join("");
|
|
91
|
+
const nums = numbering.nums.map(serializeNum).join("");
|
|
92
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:numbering xmlns:w="${W_NS}">${abstractNums}${nums}</w:numbering>`;
|
|
91
93
|
}
|
|
92
94
|
//#endregion
|
|
93
95
|
export { serializeNumberingXml };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
|
|
3
3
|
//#region src/docx/settingsParser.d.ts
|
|
4
|
+
type FolioDocumentSettings = document_d_exports.DocumentSettings & {
|
|
5
|
+
/** Swap left/right section margins on even physical pages. */mirrorMargins?: boolean;
|
|
6
|
+
};
|
|
4
7
|
/** OOXML default per §17.6.13 when `w:defaultTabStop` is absent. */
|
|
5
8
|
declare const DEFAULT_TAB_STOP_TWIPS = 720;
|
|
6
|
-
declare function parseSettings(xml: string | null):
|
|
7
|
-
|
|
8
|
-
export { DEFAULT_TAB_STOP_TWIPS,
|
|
9
|
+
declare function parseSettings(xml: string | null): FolioDocumentSettings;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { DEFAULT_TAB_STOP_TWIPS, FolioDocumentSettings, parseSettings };
|
|
@@ -13,6 +13,8 @@ function parseSettings(xml) {
|
|
|
13
13
|
const settings = { defaultTabStop: parseDefaultTabStop(root) };
|
|
14
14
|
const evenAndOddHeaders = root ? findChild(root, "w", "evenAndOddHeaders") : null;
|
|
15
15
|
if (evenAndOddHeaders && parseBooleanElement(evenAndOddHeaders)) settings.evenAndOddHeaders = true;
|
|
16
|
+
const mirrorMargins = root ? findChild(root, "w", "mirrorMargins") : null;
|
|
17
|
+
if (mirrorMargins && parseBooleanElement(mirrorMargins)) settings.mirrorMargins = true;
|
|
16
18
|
const themeFontLangEl = root ? findChild(root, "w", "themeFontLang") : null;
|
|
17
19
|
const eastAsiaLang = themeFontLangEl ? getAttribute(themeFontLangEl, "w", "eastAsia") || void 0 : void 0;
|
|
18
20
|
const bidiLang = themeFontLangEl ? getAttribute(themeFontLangEl, "w", "bidi") || void 0 : void 0;
|
package/dist/docx/unzip.d.ts
CHANGED
|
@@ -13,8 +13,9 @@ type DocxUnzipLimits = {
|
|
|
13
13
|
maxTotalUncompressedBytes: number;
|
|
14
14
|
allowedMediaMimeTypes: ReadonlySet<string>;
|
|
15
15
|
};
|
|
16
|
-
type
|
|
17
|
-
allowedMediaMimeTypes?: Iterable<string>;
|
|
16
|
+
type DocxUnzipOptions = Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
|
|
17
|
+
allowedMediaMimeTypes?: Iterable<string>; /** Password for Agile-encrypted .docx files (Office 2010+). */
|
|
18
|
+
password?: string | undefined;
|
|
18
19
|
};
|
|
19
20
|
/**
|
|
20
21
|
* Raw extracted content from a DOCX file
|
|
@@ -45,7 +46,8 @@ type RawDocxContent = {
|
|
|
45
46
|
allXml: Map<string, string>;
|
|
46
47
|
originalZip: JSZip;
|
|
47
48
|
originalBuffer: ArrayBuffer;
|
|
48
|
-
warnings: string[];
|
|
49
|
+
warnings: string[]; /** True when the input was a password-protected CFB container. */
|
|
50
|
+
wasEncrypted: boolean;
|
|
49
51
|
};
|
|
50
52
|
/**
|
|
51
53
|
* Extract all content from a DOCX file
|
|
@@ -53,7 +55,7 @@ type RawDocxContent = {
|
|
|
53
55
|
* @param buffer - DOCX file as ArrayBuffer
|
|
54
56
|
* @returns Promise resolving to extracted content
|
|
55
57
|
*/
|
|
56
|
-
declare function unzipDocx(buffer: ArrayBuffer, options?:
|
|
58
|
+
declare function unzipDocx(buffer: ArrayBuffer, options?: DocxUnzipOptions): Promise<RawDocxContent>;
|
|
57
59
|
declare function isPreservableDocxEntry(path: string): boolean;
|
|
58
60
|
/**
|
|
59
61
|
* Get a list of all files in the DOCX
|
|
@@ -115,4 +117,4 @@ declare function getContentSummary(content: RawDocxContent): {
|
|
|
115
117
|
totalFiles: number;
|
|
116
118
|
};
|
|
117
119
|
//#endregion
|
|
118
|
-
export { DocxSecurityError, DocxUnzipLimits, RawDocxContent, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, isPreservableDocxEntry, mediaToDataUrl, unzipDocx };
|
|
120
|
+
export { DocxSecurityError, DocxUnzipLimits, DocxUnzipOptions, RawDocxContent, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, isPreservableDocxEntry, mediaToDataUrl, unzipDocx };
|
package/dist/docx/unzip.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
|
|
2
|
+
import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
|
|
1
3
|
import JSZip from "jszip";
|
|
2
4
|
//#region src/docx/unzip.ts
|
|
3
5
|
/**
|
|
@@ -71,7 +73,10 @@ const ZIP_CENTRAL_DIRECTORY_FILE_HEADER_SIZE = 46;
|
|
|
71
73
|
async function unzipDocx(buffer, options = {}) {
|
|
72
74
|
const limits = createUnzipLimits(options);
|
|
73
75
|
if (buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
|
|
74
|
-
const
|
|
76
|
+
const zipBuffer = await openDocxBuffer(buffer, { password: options.password });
|
|
77
|
+
if (zipBuffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
|
|
78
|
+
const wasEncrypted = detectDocxContainerType(buffer) === DOCX_CONTAINER_TYPES.CFB;
|
|
79
|
+
const loaded = await loadDocxZip(zipBuffer, limits.maxFiles);
|
|
75
80
|
if (loaded.buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
|
|
76
81
|
const { zip } = loaded;
|
|
77
82
|
const entries = Object.entries(zip.files).filter(([, file]) => !file.dir);
|
|
@@ -102,7 +107,8 @@ async function unzipDocx(buffer, options = {}) {
|
|
|
102
107
|
allXml: /* @__PURE__ */ new Map(),
|
|
103
108
|
originalZip: zip,
|
|
104
109
|
originalBuffer: loaded.buffer,
|
|
105
|
-
warnings: []
|
|
110
|
+
warnings: [],
|
|
111
|
+
wasEncrypted
|
|
106
112
|
};
|
|
107
113
|
let totalUncompressedBytes = 0;
|
|
108
114
|
for (const [path, file] of entries) {
|
package/dist/docx/xmlParser.js
CHANGED
|
@@ -31,6 +31,7 @@ const fxpParserOptions = {
|
|
|
31
31
|
parseAttributeValue: false,
|
|
32
32
|
ignoreDeclaration: true,
|
|
33
33
|
ignorePiTags: true,
|
|
34
|
+
jPath: false,
|
|
34
35
|
processEntities: true,
|
|
35
36
|
htmlEntities: true
|
|
36
37
|
};
|
|
@@ -65,15 +66,19 @@ function fxpNodeToElement(node) {
|
|
|
65
66
|
text: node[TEXT_KEY]
|
|
66
67
|
};
|
|
67
68
|
const attrs = node[ATTR_KEY];
|
|
68
|
-
for (const key
|
|
69
|
+
for (const key in node) {
|
|
69
70
|
if (key === ATTR_KEY) continue;
|
|
70
71
|
const children = node[key];
|
|
71
72
|
const element = {
|
|
72
73
|
type: "element",
|
|
73
74
|
name: key
|
|
74
75
|
};
|
|
75
|
-
if (attrs
|
|
76
|
-
if (children.length > 0)
|
|
76
|
+
if (attrs) element.attributes = attrs;
|
|
77
|
+
if (children.length > 0) {
|
|
78
|
+
const elements = [];
|
|
79
|
+
for (const child of children) elements.push(fxpNodeToElement(child));
|
|
80
|
+
element.elements = elements;
|
|
81
|
+
}
|
|
77
82
|
return element;
|
|
78
83
|
}
|
|
79
84
|
return { type: "element" };
|
|
@@ -84,7 +89,9 @@ function fxpNodeToElement(node) {
|
|
|
84
89
|
* `{ elements: [...] }`.
|
|
85
90
|
*/
|
|
86
91
|
function fxpToRootElement(nodes) {
|
|
87
|
-
|
|
92
|
+
const elements = [];
|
|
93
|
+
for (const node of nodes) elements.push(fxpNodeToElement(node));
|
|
94
|
+
return { elements };
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
90
97
|
* Convert an XmlElement back into the fast-xml-parser preserveOrder format
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "./ai-edits/types.js";
|
|
1
|
+
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "./ai-edits/types.js";
|
|
2
2
|
import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
|
|
3
3
|
import { document_d_exports } from "./types/document.js";
|
|
4
|
+
import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationType, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
|
|
4
5
|
import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
|
|
5
6
|
import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
6
7
|
import { WordDiffSegment, diffWordSegments } from "./ai-edits/word-diff.js";
|
|
@@ -26,4 +27,4 @@ import { toMarkdown, toMarkdownResult } from "./markdown/index.js";
|
|
|
26
27
|
import { EmbeddedFont, EmbeddedFontParts, extractEmbeddedFonts, getEmbeddedFontFaces } from "./fonts/embeddedFonts.js";
|
|
27
28
|
import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "./utils/fontResolver.js";
|
|
28
29
|
type Document = document_d_exports.Document;
|
|
29
|
-
export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocxCompatibility, type EmbeddedFont, type EmbeddedFontParts, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type ImageMeta, type ImageRef, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
|
|
30
|
+
export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocxCompatibility, type EmbeddedFont, type EmbeddedFontParts, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type ImageMeta, type ImageRef, InvalidFolioDocumentOperationBatchError, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
2
|
+
import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
|
|
3
|
+
import { diffWordSegments } from "./ai-edits/word-diff.js";
|
|
4
|
+
import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
|
|
5
|
+
import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
|
|
1
6
|
import { createEmptyDocument } from "./utils/createDocument.js";
|
|
2
7
|
import { createDocx } from "./docx/rezip.js";
|
|
3
|
-
import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
4
8
|
import { DEFAULT_AI_SUGGESTION_PRESETS } from "./ai-suggestions/types.js";
|
|
5
9
|
import { buildPositionalText } from "./ai-suggestions/text-positions.js";
|
|
6
10
|
import { isSuggestionStale, resolveSuggestionAnchor } from "./ai-suggestions/conflict.js";
|
|
7
11
|
import { applySuggestions } from "./ai-suggestions/apply.js";
|
|
8
|
-
import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
|
|
9
|
-
import { diffWordSegments } from "./ai-edits/word-diff.js";
|
|
10
|
-
import { applyFolioAIEditOperations } from "./ai-edits/apply.js";
|
|
11
12
|
import { setAISuggestionsMeta, setFocusedSuggestionMeta } from "./prosemirror/plugins/aiSuggestionDecorations.js";
|
|
12
13
|
import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "./utils/fontResolver.js";
|
|
13
14
|
import { scrollFolioPositionIntoView } from "./paged-layout/scrollToPmPosition.js";
|
|
@@ -21,4 +22,4 @@ import { DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acc
|
|
|
21
22
|
import { fromMarkdown } from "./markdown/fromMarkdown.js";
|
|
22
23
|
import { toMarkdown, toMarkdownResult } from "./markdown/index.js";
|
|
23
24
|
import { extractEmbeddedFonts, getEmbeddedFontFaces } from "./fonts/embeddedFonts.js";
|
|
24
|
-
export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applySuggestions, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isSequentialFolioBlockId, isSuggestionStale, normalizeFolioAIBlockText, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
|
|
25
|
+
export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, deriveBlockId, diffWordSegments, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
|
|
@@ -23,6 +23,42 @@ declare function collectFootnoteRefs(blocks: FlowBlock[]): {
|
|
|
23
23
|
footnoteId: number;
|
|
24
24
|
pmPos: number;
|
|
25
25
|
}[];
|
|
26
|
+
/**
|
|
27
|
+
* Scan FlowBlocks for runs with endnoteRefId set, in document order.
|
|
28
|
+
* Same recursive walk as `collectFootnoteRefs`.
|
|
29
|
+
*/
|
|
30
|
+
declare function collectEndnoteRefs(blocks: FlowBlock[]): {
|
|
31
|
+
endnoteId: number;
|
|
32
|
+
pmPos: number;
|
|
33
|
+
}[];
|
|
34
|
+
type NumberableNote = {
|
|
35
|
+
id: number;
|
|
36
|
+
noteType?: document_d_exports.Footnote["noteType"];
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Assign sequential display numbers (1, 2, 3, …) to notes in order of first
|
|
40
|
+
* reference. Word numbers footnotes/endnotes by reference order, not by their
|
|
41
|
+
* `w:id` values, which may be non-contiguous or out of document order. Only
|
|
42
|
+
* `normal` notes are numbered; separator/continuation notes and refs to
|
|
43
|
+
* missing notes consume no number.
|
|
44
|
+
*/
|
|
45
|
+
declare function computeNoteDisplayNumbers(notes: readonly NumberableNote[], refNoteIds: readonly number[]): Map<number, number>;
|
|
46
|
+
type NoteDisplayNumberMaps = {
|
|
47
|
+
/** footnote `w:id` → sequential display number */footnoteNumbers?: ReadonlyMap<number, number>; /** endnote `w:id` → sequential display number */
|
|
48
|
+
endnoteNumbers?: ReadonlyMap<number, number>;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Rewrite body reference-marker run text from the raw `w:id` (which the PM
|
|
52
|
+
* doc stores as the marker text; the save path serializes from the mark
|
|
53
|
+
* attrs, never from this text) to the sequential display number, so the
|
|
54
|
+
* inline marker matches the number rendered in the note area. Must run
|
|
55
|
+
* before measurement so marker widths match the painted digits. Untouched
|
|
56
|
+
* blocks/runs are returned by reference so the painter's fingerprinting and
|
|
57
|
+
* the incremental measure path see them unchanged. Runs keep their PM range,
|
|
58
|
+
* so click-to-position still resolves into the marker (same contract as the
|
|
59
|
+
* template preview substitution).
|
|
60
|
+
*/
|
|
61
|
+
declare function remapNoteMarkerText(blocks: FlowBlock[], maps: NoteDisplayNumberMaps): FlowBlock[];
|
|
26
62
|
/**
|
|
27
63
|
* After layout, determine which footnotes appear on which pages.
|
|
28
64
|
* Checks each page's fragments to see if any footnoteRef PM positions fall within.
|
|
@@ -54,4 +90,4 @@ declare function calculateFootnoteReservedHeights(pageFootnoteMap: Map<number, n
|
|
|
54
90
|
height: number;
|
|
55
91
|
}>): Map<number, number>;
|
|
56
92
|
//#endregion
|
|
57
|
-
export { ConvertFootnoteOptions, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, MeasureBlocksFn, applyFootnotePresentation, buildFootnoteContentMap, calculateFootnoteReservedHeights, collectFootnoteRefs, convertFootnoteToContent, mapFootnotesToPages };
|
|
93
|
+
export { ConvertFootnoteOptions, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, MeasureBlocksFn, NoteDisplayNumberMaps, applyFootnotePresentation, buildFootnoteContentMap, calculateFootnoteReservedHeights, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, convertFootnoteToContent, mapFootnotesToPages, remapNoteMarkerText };
|
|
@@ -16,20 +16,149 @@ const FOOTNOTE_FONT_SIZE = 8;
|
|
|
16
16
|
* area.
|
|
17
17
|
*/
|
|
18
18
|
function collectFootnoteRefs(blocks) {
|
|
19
|
+
return collectNoteRefs(blocks, "footnoteRefId").map(({ noteId, pmPos }) => ({
|
|
20
|
+
footnoteId: noteId,
|
|
21
|
+
pmPos
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Scan FlowBlocks for runs with endnoteRefId set, in document order.
|
|
26
|
+
* Same recursive walk as `collectFootnoteRefs`.
|
|
27
|
+
*/
|
|
28
|
+
function collectEndnoteRefs(blocks) {
|
|
29
|
+
return collectNoteRefs(blocks, "endnoteRefId").map(({ noteId, pmPos }) => ({
|
|
30
|
+
endnoteId: noteId,
|
|
31
|
+
pmPos
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
function collectNoteRefs(blocks, idKey) {
|
|
19
35
|
const refs = [];
|
|
20
36
|
const walk = (containerBlocks) => {
|
|
21
|
-
for (const block of containerBlocks) if (block.kind === "paragraph") {
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
for (const block of containerBlocks) if (block.kind === "paragraph") for (const run of block.runs) {
|
|
38
|
+
if (run.kind !== "text") continue;
|
|
39
|
+
const noteId = run[idKey];
|
|
40
|
+
if (noteId !== void 0) refs.push({
|
|
41
|
+
noteId,
|
|
24
42
|
pmPos: run.pmStart ?? 0
|
|
25
43
|
});
|
|
26
|
-
}
|
|
44
|
+
}
|
|
45
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) walk(cell.blocks);
|
|
27
46
|
else if (block.kind === "textBox") walk(block.content);
|
|
28
47
|
};
|
|
29
48
|
walk(blocks);
|
|
30
49
|
return refs;
|
|
31
50
|
}
|
|
32
51
|
/**
|
|
52
|
+
* Assign sequential display numbers (1, 2, 3, …) to notes in order of first
|
|
53
|
+
* reference. Word numbers footnotes/endnotes by reference order, not by their
|
|
54
|
+
* `w:id` values, which may be non-contiguous or out of document order. Only
|
|
55
|
+
* `normal` notes are numbered; separator/continuation notes and refs to
|
|
56
|
+
* missing notes consume no number.
|
|
57
|
+
*/
|
|
58
|
+
function computeNoteDisplayNumbers(notes, refNoteIds) {
|
|
59
|
+
const numberable = /* @__PURE__ */ new Set();
|
|
60
|
+
for (const note of notes) if (note.noteType === "normal") numberable.add(note.id);
|
|
61
|
+
const displayNumbers = /* @__PURE__ */ new Map();
|
|
62
|
+
let nextNumber = 1;
|
|
63
|
+
for (const noteId of refNoteIds) {
|
|
64
|
+
if (displayNumbers.has(noteId) || !numberable.has(noteId)) continue;
|
|
65
|
+
displayNumbers.set(noteId, nextNumber);
|
|
66
|
+
nextNumber++;
|
|
67
|
+
}
|
|
68
|
+
return displayNumbers;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Rewrite body reference-marker run text from the raw `w:id` (which the PM
|
|
72
|
+
* doc stores as the marker text; the save path serializes from the mark
|
|
73
|
+
* attrs, never from this text) to the sequential display number, so the
|
|
74
|
+
* inline marker matches the number rendered in the note area. Must run
|
|
75
|
+
* before measurement so marker widths match the painted digits. Untouched
|
|
76
|
+
* blocks/runs are returned by reference so the painter's fingerprinting and
|
|
77
|
+
* the incremental measure path see them unchanged. Runs keep their PM range,
|
|
78
|
+
* so click-to-position still resolves into the marker (same contract as the
|
|
79
|
+
* template preview substitution).
|
|
80
|
+
*/
|
|
81
|
+
function remapNoteMarkerText(blocks, maps) {
|
|
82
|
+
if ((maps.footnoteNumbers?.size ?? 0) === 0 && (maps.endnoteNumbers?.size ?? 0) === 0) return blocks;
|
|
83
|
+
let changed = false;
|
|
84
|
+
const next = blocks.map((block) => {
|
|
85
|
+
const remapped = remapNoteMarkerBlock(block, maps);
|
|
86
|
+
changed ||= remapped !== block;
|
|
87
|
+
return remapped;
|
|
88
|
+
});
|
|
89
|
+
return changed ? next : blocks;
|
|
90
|
+
}
|
|
91
|
+
function remapNoteMarkerBlock(block, maps) {
|
|
92
|
+
if (block.kind === "paragraph") return remapNoteMarkerParagraph(block, maps);
|
|
93
|
+
if (block.kind === "table") {
|
|
94
|
+
let tableChanged = false;
|
|
95
|
+
const rows = block.rows.map((row) => {
|
|
96
|
+
let rowChanged = false;
|
|
97
|
+
const cells = row.cells.map((cell) => {
|
|
98
|
+
let cellChanged = false;
|
|
99
|
+
const cellBlocks = cell.blocks.map((cellBlock) => {
|
|
100
|
+
const remapped = remapNoteMarkerBlock(cellBlock, maps);
|
|
101
|
+
cellChanged ||= remapped !== cellBlock;
|
|
102
|
+
return remapped;
|
|
103
|
+
});
|
|
104
|
+
return cellChanged ? {
|
|
105
|
+
...cell,
|
|
106
|
+
blocks: cellBlocks
|
|
107
|
+
} : cell;
|
|
108
|
+
});
|
|
109
|
+
rowChanged = cells.some((cell, index) => cell !== row.cells[index]);
|
|
110
|
+
tableChanged ||= rowChanged;
|
|
111
|
+
return rowChanged ? {
|
|
112
|
+
...row,
|
|
113
|
+
cells
|
|
114
|
+
} : row;
|
|
115
|
+
});
|
|
116
|
+
return tableChanged ? {
|
|
117
|
+
...block,
|
|
118
|
+
rows
|
|
119
|
+
} : block;
|
|
120
|
+
}
|
|
121
|
+
if (block.kind === "textBox") {
|
|
122
|
+
let boxChanged = false;
|
|
123
|
+
const content = block.content.map((paragraph) => {
|
|
124
|
+
const remapped = remapNoteMarkerParagraph(paragraph, maps);
|
|
125
|
+
boxChanged ||= remapped !== paragraph;
|
|
126
|
+
return remapped;
|
|
127
|
+
});
|
|
128
|
+
return boxChanged ? {
|
|
129
|
+
...block,
|
|
130
|
+
content
|
|
131
|
+
} : block;
|
|
132
|
+
}
|
|
133
|
+
return block;
|
|
134
|
+
}
|
|
135
|
+
function remapNoteMarkerParagraph(block, maps) {
|
|
136
|
+
let changed = false;
|
|
137
|
+
const runs = block.runs.map((run) => {
|
|
138
|
+
const remapped = remapNoteMarkerRun(run, maps);
|
|
139
|
+
changed ||= remapped !== run;
|
|
140
|
+
return remapped;
|
|
141
|
+
});
|
|
142
|
+
return changed ? {
|
|
143
|
+
...block,
|
|
144
|
+
runs
|
|
145
|
+
} : block;
|
|
146
|
+
}
|
|
147
|
+
function remapNoteMarkerRun(run, maps) {
|
|
148
|
+
if (run.kind !== "text") return run;
|
|
149
|
+
const displayNumber = getRunDisplayNumber(run, maps);
|
|
150
|
+
if (displayNumber === void 0) return run;
|
|
151
|
+
const text = String(displayNumber);
|
|
152
|
+
return run.text === text ? run : {
|
|
153
|
+
...run,
|
|
154
|
+
text
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function getRunDisplayNumber(run, maps) {
|
|
158
|
+
if (run.footnoteRefId !== void 0) return maps.footnoteNumbers?.get(run.footnoteRefId);
|
|
159
|
+
if (run.endnoteRefId !== void 0) return maps.endnoteNumbers?.get(run.endnoteRefId);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
33
162
|
* After layout, determine which footnotes appear on which pages.
|
|
34
163
|
* Checks each page's fragments to see if any footnoteRef PM positions fall within.
|
|
35
164
|
*
|
|
@@ -279,16 +408,11 @@ function buildFootnoteContentMap(footnotes, footnoteRefs, contentWidth, options
|
|
|
279
408
|
const contentMap = /* @__PURE__ */ new Map();
|
|
280
409
|
const footnoteById = /* @__PURE__ */ new Map();
|
|
281
410
|
for (const fn of footnotes) if (fn.noteType === "normal") footnoteById.set(fn.id, fn);
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
if (seen.has(ref.footnoteId)) continue;
|
|
286
|
-
seen.add(ref.footnoteId);
|
|
287
|
-
const footnote = footnoteById.get(ref.footnoteId);
|
|
411
|
+
const displayNumbers = computeNoteDisplayNumbers(footnotes, footnoteRefs.map((ref) => ref.footnoteId));
|
|
412
|
+
for (const [footnoteId, displayNumber] of displayNumbers) {
|
|
413
|
+
const footnote = footnoteById.get(footnoteId);
|
|
288
414
|
if (!footnote) continue;
|
|
289
|
-
|
|
290
|
-
contentMap.set(ref.footnoteId, content);
|
|
291
|
-
displayNumber++;
|
|
415
|
+
contentMap.set(footnoteId, convertFootnoteToContent(footnote, displayNumber, contentWidth, options));
|
|
292
416
|
}
|
|
293
417
|
return contentMap;
|
|
294
418
|
}
|
|
@@ -313,4 +437,4 @@ function calculateFootnoteReservedHeights(pageFootnoteMap, footnoteContentMap) {
|
|
|
313
437
|
return reserved;
|
|
314
438
|
}
|
|
315
439
|
//#endregion
|
|
316
|
-
export { FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, applyFootnotePresentation, buildFootnoteContentMap, calculateFootnoteReservedHeights, collectFootnoteRefs, convertFootnoteToContent, mapFootnotesToPages };
|
|
440
|
+
export { FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, applyFootnotePresentation, buildFootnoteContentMap, calculateFootnoteReservedHeights, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, convertFootnoteToContent, mapFootnotesToPages, remapNoteMarkerText };
|
|
@@ -12,7 +12,7 @@ type HeaderFooterMetrics = {
|
|
|
12
12
|
};
|
|
13
13
|
margins: PageMargins;
|
|
14
14
|
};
|
|
15
|
-
declare function normalizeHeaderFooterMeasureBlocks(blocks: FlowBlock[]): FlowBlock[];
|
|
15
|
+
declare function normalizeHeaderFooterMeasureBlocks(blocks: FlowBlock[], section?: HeaderFooterMetrics["section"]): FlowBlock[];
|
|
16
16
|
declare function resolveHeaderFooterPositionedVisualTop(position: ImageRunPosition | undefined, elementHeight: number, sourceY: number, flowHeight: number, metrics: HeaderFooterMetrics): number;
|
|
17
17
|
declare function resolveHeaderFooterVisualTop(run: ImageRun, paragraphY: number, flowHeight: number, metrics: HeaderFooterMetrics): number;
|
|
18
18
|
declare function calculateHeaderFooterVisualBounds(blocks: FlowBlock[], measures: Measure[], flowHeight: number, metrics: HeaderFooterMetrics): {
|
|
@@ -20,13 +20,11 @@ declare function calculateHeaderFooterVisualBounds(blocks: FlowBlock[], measures
|
|
|
20
20
|
visualBottom: number;
|
|
21
21
|
};
|
|
22
22
|
/**
|
|
23
|
-
* Compute
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* page-hash invalidation signal), but the margin extender needs the flow-only
|
|
29
|
-
* extent.
|
|
23
|
+
* Compute flow-only header/footer bounds, excluding anchored/floating objects
|
|
24
|
+
* such as full-page letterheads and watermarks. Word positions those objects on
|
|
25
|
+
* the page independently from header/footer flow. Keeping them in
|
|
26
|
+
* `visualBottom` is still correct for rendering and page-hash invalidation;
|
|
27
|
+
* these bounds separately describe the in-flow portion for API consumers.
|
|
30
28
|
*/
|
|
31
29
|
declare function calculateHeaderFooterMarginPushBounds(blocks: FlowBlock[], measures: Measure[], flowHeight: number, metrics: HeaderFooterMetrics): {
|
|
32
30
|
top: number;
|
|
@@ -16,13 +16,13 @@ function hasAuthoredVisualContent(block) {
|
|
|
16
16
|
if (attrs.spacingExplicit?.before || attrs.spacingExplicit?.after) return true;
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
|
-
function normalizeHeaderFooterMeasureBlocks(blocks) {
|
|
20
|
-
return normalizeFlowBlockArray(blocks);
|
|
19
|
+
function normalizeHeaderFooterMeasureBlocks(blocks, section = "header") {
|
|
20
|
+
return normalizeFlowBlockArray(blocks, { suppressTrailingEmptyAfterTable: section === "header" });
|
|
21
21
|
}
|
|
22
|
-
function normalizeFlowBlockArray(blocks) {
|
|
22
|
+
function normalizeFlowBlockArray(blocks, normalization) {
|
|
23
23
|
const trailingEmptyAfterTable = /* @__PURE__ */ new Set();
|
|
24
24
|
const lastIndex = blocks.length - 1;
|
|
25
|
-
if (lastIndex > 0) {
|
|
25
|
+
if (normalization.suppressTrailingEmptyAfterTable && lastIndex > 0) {
|
|
26
26
|
const cur = blocks[lastIndex];
|
|
27
27
|
if (blocks[lastIndex - 1]?.kind === "table" && cur?.kind === "paragraph" && cur.runs.length === 0 && !hasAuthoredVisualContent(cur)) trailingEmptyAfterTable.add(lastIndex);
|
|
28
28
|
}
|
|
@@ -70,12 +70,21 @@ function normalizeFlowBlockArray(blocks) {
|
|
|
70
70
|
};
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
function isCanonicalTrailingEmptyParagraphAfterTable(blocks, index) {
|
|
74
|
+
const cur = blocks[index];
|
|
75
|
+
const prev = blocks[index - 1];
|
|
76
|
+
return index === blocks.length - 1 && prev?.kind === "table" && cur?.kind === "paragraph" && isVisuallyEmptyParagraph(cur) && !hasAuthoredVisualContent(cur);
|
|
77
|
+
}
|
|
78
|
+
function isVisuallyEmptyParagraph(block) {
|
|
79
|
+
if (block.kind !== "paragraph") return false;
|
|
80
|
+
return block.runs.every((run) => run.kind === "text" && run.text.trim().length === 0);
|
|
81
|
+
}
|
|
73
82
|
function normalizeTableBlock(block) {
|
|
74
83
|
const blockState = { changed: false };
|
|
75
84
|
const rows = block.rows.map((row) => {
|
|
76
85
|
const rowState = { changed: false };
|
|
77
86
|
const cells = row.cells.map((cell) => {
|
|
78
|
-
const normalizedBlocks = normalizeFlowBlockArray(cell.blocks);
|
|
87
|
+
const normalizedBlocks = normalizeFlowBlockArray(cell.blocks, { suppressTrailingEmptyAfterTable: true });
|
|
79
88
|
if (!normalizedBlocks.some((normalizedBlock, idx) => normalizedBlock !== cell.blocks[idx])) return cell;
|
|
80
89
|
rowState.changed = true;
|
|
81
90
|
return {
|
|
@@ -216,13 +225,11 @@ function calculateHeaderFooterVisualBounds(blocks, measures, flowHeight, metrics
|
|
|
216
225
|
};
|
|
217
226
|
}
|
|
218
227
|
/**
|
|
219
|
-
* Compute
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* page-hash invalidation signal), but the margin extender needs the flow-only
|
|
225
|
-
* extent.
|
|
228
|
+
* Compute flow-only header/footer bounds, excluding anchored/floating objects
|
|
229
|
+
* such as full-page letterheads and watermarks. Word positions those objects on
|
|
230
|
+
* the page independently from header/footer flow. Keeping them in
|
|
231
|
+
* `visualBottom` is still correct for rendering and page-hash invalidation;
|
|
232
|
+
* these bounds separately describe the in-flow portion for API consumers.
|
|
226
233
|
*/
|
|
227
234
|
function calculateHeaderFooterMarginPushBounds(blocks, measures, flowHeight, metrics) {
|
|
228
235
|
let top = 0;
|
|
@@ -232,6 +239,7 @@ function calculateHeaderFooterMarginPushBounds(blocks, measures, flowHeight, met
|
|
|
232
239
|
const block = blocks[i];
|
|
233
240
|
const measure = measures[i];
|
|
234
241
|
if (!block || !measure) continue;
|
|
242
|
+
if (isCanonicalTrailingEmptyParagraphAfterTable(blocks, i)) continue;
|
|
235
243
|
if (block.kind === "paragraph" && measure.kind === "paragraph") {
|
|
236
244
|
const paragraphBottomY = cursorY + measure.totalHeight;
|
|
237
245
|
top = Math.min(top, cursorY);
|
|
@@ -312,7 +320,7 @@ function convertHeaderFooterPmDocToContent(pmDoc, contentWidth, metrics, options
|
|
|
312
320
|
}
|
|
313
321
|
function finalizeHeaderFooterContent(blocks, contentWidth, metrics, options) {
|
|
314
322
|
if (blocks.length === 0) return;
|
|
315
|
-
const blocksForMeasure = normalizeHeaderFooterMeasureBlocks(blocks);
|
|
323
|
+
const blocksForMeasure = normalizeHeaderFooterMeasureBlocks(blocks, metrics.section);
|
|
316
324
|
const measures = options.measureBlocks(blocksForMeasure, contentWidth);
|
|
317
325
|
let flowHeight = 0;
|
|
318
326
|
for (let i = 0; i < measures.length; i++) {
|