@stll/folio-core 0.37.4 → 0.38.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.js +275 -88
- package/dist/ai-edits/clean-text.js +12 -1
- package/dist/ai-edits/headless.d.ts +110 -0
- package/dist/ai-edits/headless.js +569 -40
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/snapshot.d.ts +24 -1
- package/dist/ai-edits/snapshot.js +206 -9
- package/dist/ai-edits/table-geometry.d.ts +7 -7
- package/dist/ai-edits/table-geometry.js +43 -27
- package/dist/ai-edits/table-template.d.ts +2 -1
- package/dist/ai-edits/table-template.js +9 -3
- package/dist/ai-edits/types.d.ts +38 -16
- package/dist/compare/__fixtures__/body-sequence.d.ts +4 -0
- package/dist/compare/__fixtures__/body-sequence.js +5 -3
- package/dist/compare/compare.d.ts +26 -2
- package/dist/compare/compare.js +444 -16
- package/dist/compare/content-alignment.js +216 -137
- package/dist/compare/content-types.d.ts +36 -2
- package/dist/compare/content.d.ts +13 -10
- package/dist/compare/content.js +69 -23
- package/dist/compare/formatting.d.ts +4 -22
- package/dist/compare/formatting.js +8 -112
- package/dist/compare/inline-atom-resources.d.ts +6 -0
- package/dist/compare/inline-atom-resources.js +26 -0
- package/dist/compare/inline-atoms.d.ts +35 -0
- package/dist/compare/inline-atoms.js +383 -0
- package/dist/compare/inline-provenance.d.ts +36 -0
- package/dist/compare/inline-provenance.js +334 -0
- package/dist/compare/plan.d.ts +4 -1
- package/dist/compare/plan.js +32 -4
- package/dist/compare/scenario.d.ts +4 -6
- package/dist/compare/section-boundary-properties.d.ts +63 -0
- package/dist/compare/section-boundary-properties.js +235 -0
- package/dist/compare/style-resources.d.ts +30 -0
- package/dist/compare/style-resources.js +404 -0
- package/dist/compare/types.d.ts +50 -11
- package/dist/compare/types.js +3 -1
- package/dist/compare/verification.d.ts +3 -5
- package/dist/compare/verification.js +8 -39
- package/dist/compat/eigenpal.d.ts +4 -4
- package/dist/compat/eigenpal.js +3 -3
- package/dist/document-operations.d.ts +5 -3
- package/dist/document-operations.js +112 -5
- package/dist/docx/blockContentParser.js +14 -2
- package/dist/docx/drawingIdNormalization.js +43 -2
- package/dist/docx/drawingRelationships.d.ts +10 -0
- package/dist/docx/drawingRelationships.js +28 -0
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/hyperlinkParser.js +3 -5
- package/dist/docx/imageRawXml.d.ts +8 -0
- package/dist/docx/imageRawXml.js +13 -0
- package/dist/docx/newImage.d.ts +2 -2
- package/dist/docx/newImage.js +3 -3
- package/dist/docx/numberingParser.d.ts +1 -13
- package/dist/docx/numberingParser.js +31 -37
- package/dist/docx/paragraphParser.js +12 -9
- package/dist/docx/paragraphPropertySource.d.ts +8 -5
- package/dist/docx/paragraphPropertySource.js +16 -4
- package/dist/docx/removeHeaderFooterParts.d.ts +14 -0
- package/dist/docx/removeHeaderFooterParts.js +87 -0
- package/dist/docx/rezip.d.ts +3 -1
- package/dist/docx/rezip.js +143 -41
- package/dist/docx/runParser.js +16 -5
- package/dist/docx/sectionParser.js +3 -0
- package/dist/docx/sectionReferenceHistory.d.ts +17 -0
- package/dist/docx/sectionReferenceHistory.js +59 -0
- package/dist/docx/selectiveSave.js +11 -31
- package/dist/docx/selectiveXmlPatch.d.ts +8 -1
- package/dist/docx/selectiveXmlPatch.js +62 -2
- package/dist/docx/serializer/borderSerializer.d.ts +4 -1
- package/dist/docx/serializer/borderSerializer.js +20 -18
- package/dist/docx/serializer/commentSerializer.js +1 -1
- package/dist/docx/serializer/numberingSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +13 -177
- package/dist/docx/serializer/partNamespaces.d.ts +1 -1
- package/dist/docx/serializer/partNamespaces.js +4 -0
- package/dist/docx/serializer/runSerializer.d.ts +1 -5
- package/dist/docx/serializer/runSerializer.js +4 -130
- package/dist/docx/serializer/sectionPropertiesSerializer.js +5 -2
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/textFormattingSerializer.d.ts +17 -0
- package/dist/docx/serializer/textFormattingSerializer.js +144 -0
- package/dist/docx/tableParser.js +21 -11
- package/dist/docx/xmlParser.d.ts +10 -1
- package/dist/docx/xmlParser.js +23 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/internal/compare/inline-presentation.d.ts +20 -0
- package/dist/internal/compare/inline-presentation.js +222 -0
- package/dist/internal/paragraphFormattingSerialization.d.ts +25 -0
- package/dist/internal/paragraphFormattingSerialization.js +164 -0
- package/dist/internal/sectionReferenceResolution.d.ts +21 -0
- package/dist/internal/sectionReferenceResolution.js +72 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
- package/dist/markdown/renderRuns.js +1 -0
- package/dist/markdown/renderTable.js +1 -0
- package/dist/prosemirror/attrs/index.js +20 -1
- package/dist/prosemirror/commands/comments.d.ts +11 -1
- package/dist/prosemirror/commands/comments.js +68 -6
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +9 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -10
- package/dist/prosemirror/conversion/fromProseDoc.js +22 -22
- package/dist/prosemirror/conversion/toProseDoc.js +16 -57
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +3 -0
- package/dist/prosemirror/imageCommit.js +7 -9
- package/dist/prosemirror/paragraphIndentation.d.ts +21 -0
- package/dist/prosemirror/paragraphIndentation.js +72 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +3 -1
- package/dist/prosemirror/plugins/documentNumbering.js +7 -1
- package/dist/prosemirror/plugins/documentStyles.d.ts +3 -1
- package/dist/prosemirror/plugins/documentStyles.js +7 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +3 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +15 -2
- package/dist/prosemirror/runFormattingInlineCarriers.js +33 -3
- package/dist/prosemirror/runStyleFormatting.d.ts +15 -2
- package/dist/prosemirror/runStyleFormatting.js +76 -2
- package/dist/prosemirror/schema/marks.d.ts +3 -1
- package/dist/prosemirror/schema/marks.js +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +3 -0
- package/dist/prosemirror/tableCellRevisionVisibility.d.ts +6 -0
- package/dist/prosemirror/tableCellRevisionVisibility.js +14 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/redline.js +15 -2
- package/dist/server.d.ts +3 -2
- package/dist/server.js +2 -2
- package/dist/version-comparison.d.ts +2 -0
- package/dist/version-comparison.js +30 -29
- package/package.json +2 -2
|
@@ -1,29 +1,44 @@
|
|
|
1
|
+
import { matchInlineAtoms } from "../compare/inline-atoms.js";
|
|
2
|
+
import { matchInlineProvenance } from "../compare/inline-provenance.js";
|
|
3
|
+
import { stageSectionBoundaryProperties } from "../compare/section-boundary-properties.js";
|
|
4
|
+
import { importReferencedStyleDefinitions } from "../compare/style-resources.js";
|
|
1
5
|
import { applyFolioDocumentOperations } from "../document-operations.js";
|
|
6
|
+
import { rebindDrawingImageRelationship } from "../docx/drawingRelationships.js";
|
|
2
7
|
import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
3
8
|
import { getHeaderFooterText } from "../docx/headerFooterParser.js";
|
|
9
|
+
import { getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
4
10
|
import { recreateProseNodeWithParagraphPropertySource, transferProseParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
5
11
|
import { parseDocx } from "../docx/parser.js";
|
|
6
12
|
import { createReply } from "../docx/replyToComment.js";
|
|
7
13
|
import { repackDocx } from "../docx/rezip.js";
|
|
14
|
+
import { sectionReferenceHistory } from "../docx/sectionReferenceHistory.js";
|
|
8
15
|
import { attemptSelectiveSave } from "../docx/selectiveSave.js";
|
|
9
16
|
import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisionResolutionGuard.js";
|
|
10
17
|
import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
|
|
18
|
+
import { captureSectionReferenceInventory, resolvedSectionReferenceLosses, withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
|
|
19
|
+
import { expectCharacterStyleMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
11
20
|
import { acceptAIEditRevision, rejectAIEditRevision, resolveAllChangesInHeadlessState } from "../prosemirror/commands/comments.js";
|
|
21
|
+
import { sectionRejectProperties } from "../prosemirror/commands/propertyChangeScope.js";
|
|
12
22
|
import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
|
|
13
23
|
import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
14
24
|
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
15
25
|
import { getChangedParagraphIds, getTrackedSectionEndpointRemoval, hasStructuralChanges, hasUntrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
26
|
+
import "../prosemirror/listMarker.js";
|
|
27
|
+
import { createDocumentNumberingPlugin, withDocumentNumbering } from "../prosemirror/plugins/documentNumbering.js";
|
|
28
|
+
import { createDocumentStylesPlugin, getDocumentStyleResolver, withDocumentStyles } from "../prosemirror/plugins/documentStyles.js";
|
|
18
29
|
import { schema, singletonManager } from "../prosemirror/schema/index.js";
|
|
30
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
19
31
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
20
32
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
21
33
|
import { getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
|
|
22
|
-
import { createFolioAIEditSnapshotWithStyleResolver, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
|
|
34
|
+
import { createFolioAIEditSnapshotWithStyleResolver, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
|
|
23
35
|
import { matchTableGeometry } from "./table-geometry.js";
|
|
36
|
+
import { tableTemplateCanCrossPackageLosslessly } from "./table-template.js";
|
|
24
37
|
import { TaggedError, panic } from "better-result";
|
|
25
38
|
import { Fragment } from "prosemirror-model";
|
|
26
39
|
import { EditorState } from "prosemirror-state";
|
|
40
|
+
import { REVIEW_CARRIERS } from "@stll/docx-core/model";
|
|
41
|
+
import JSZip from "jszip";
|
|
27
42
|
//#region src/ai-edits/headless.ts
|
|
28
43
|
/**
|
|
29
44
|
* Headless `.docx` review path: buffer -> apply AI edits -> buffer, with
|
|
@@ -160,6 +175,47 @@ const resolvedStoryBlockProjection = (block) => {
|
|
|
160
175
|
delete persisted.idStability;
|
|
161
176
|
return persisted;
|
|
162
177
|
};
|
|
178
|
+
const numberingLevelsOf = (numbering) => {
|
|
179
|
+
if (!numbering) return [];
|
|
180
|
+
const levelsByAbstractId = new Map(numbering.abstractNums.map((abstractNum) => [abstractNum.abstractNumId, abstractNum.levels]));
|
|
181
|
+
const levels = [];
|
|
182
|
+
for (const instance of numbering.nums) {
|
|
183
|
+
const overrides = new Map((instance.levelOverrides ?? []).map((override) => [override.ilvl, override]));
|
|
184
|
+
for (const level of levelsByAbstractId.get(instance.abstractNumId) ?? []) {
|
|
185
|
+
const override = overrides.get(level.ilvl);
|
|
186
|
+
const resolved = override?.lvl ?? level;
|
|
187
|
+
levels.push({
|
|
188
|
+
numId: instance.numId,
|
|
189
|
+
level: resolved.ilvl,
|
|
190
|
+
format: resolved.numFmt,
|
|
191
|
+
levelText: resolved.lvlText,
|
|
192
|
+
...(override?.startOverride ?? resolved.start) !== void 0 ? { start: override?.startOverride ?? resolved.start } : {}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return levels;
|
|
197
|
+
};
|
|
198
|
+
const referencedNumberingLevelsByNumId = ({ references }) => {
|
|
199
|
+
const levelsByNumId = /* @__PURE__ */ new Map();
|
|
200
|
+
for (const { numId, level } of references) {
|
|
201
|
+
const levels = levelsByNumId.get(numId) ?? /* @__PURE__ */ new Set();
|
|
202
|
+
levelsByNumId.set(numId, levels);
|
|
203
|
+
levels.add(level);
|
|
204
|
+
for (let ancestor = 0; ancestor <= Math.min(level, 8); ancestor += 1) levels.add(ancestor);
|
|
205
|
+
}
|
|
206
|
+
return new Map([...levelsByNumId].map(([numId, levels]) => [numId, [...levels].toSorted((left, right) => left - right)]));
|
|
207
|
+
};
|
|
208
|
+
const sameReferencedNumberingLevels = ({ current, target, numId, levelsByNumId }) => {
|
|
209
|
+
if (!current) return false;
|
|
210
|
+
const currentNumbering = getCachedNumberingMap(current);
|
|
211
|
+
const targetNumbering = getCachedNumberingMap(target);
|
|
212
|
+
for (const level of levelsByNumId.get(numId) ?? []) {
|
|
213
|
+
const currentLevel = currentNumbering.getLevel(numId, level);
|
|
214
|
+
const targetLevel = targetNumbering.getLevel(numId, level);
|
|
215
|
+
if (!currentLevel || !targetLevel || canonicalJson(currentLevel) !== canonicalJson(targetLevel)) return false;
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
};
|
|
163
219
|
const comparisonAccessByReviewer = /* @__PURE__ */ new WeakMap();
|
|
164
220
|
const MAIN_STORY = Object.freeze({ type: "main" });
|
|
165
221
|
const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
|
|
@@ -242,9 +298,16 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
242
298
|
/** Default author for tracked changes and comments. */
|
|
243
299
|
author;
|
|
244
300
|
baseDocument;
|
|
301
|
+
finalSectionPropertiesOverride;
|
|
245
302
|
originalBuffer;
|
|
246
303
|
state;
|
|
247
304
|
secondaryStoryStates = /* @__PURE__ */ new Map();
|
|
305
|
+
removedHeaderFooterStories = /* @__PURE__ */ new Map();
|
|
306
|
+
sectionReferenceRemovals = [];
|
|
307
|
+
importedStyles;
|
|
308
|
+
importedMedia = /* @__PURE__ */ new Map();
|
|
309
|
+
importedHeaders = /* @__PURE__ */ new Map();
|
|
310
|
+
importedFooters = /* @__PURE__ */ new Map();
|
|
248
311
|
resolvedStoryExpectations = /* @__PURE__ */ new Map();
|
|
249
312
|
createdComments = [];
|
|
250
313
|
usedCommentIds;
|
|
@@ -265,10 +328,231 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
265
328
|
this.author = args.author;
|
|
266
329
|
this.usedCommentIds = new Set((args.baseDocument.package.document.comments ?? []).map(({ id }) => id));
|
|
267
330
|
comparisonAccessByReviewer.set(this, Object.freeze({
|
|
331
|
+
stageTerminalTableReviewCarrier: (target) => this.stageTerminalTableReviewCarrier(target),
|
|
332
|
+
stageTargetStyles: (source, snapshots, importedHeaderFooterSnapshots) => this.stageTargetStyles(source, snapshots, importedHeaderFooterSnapshots),
|
|
333
|
+
createComparisonHeaderFooter: (source, story) => this.createComparisonHeaderFooter(source, story),
|
|
334
|
+
finalSectionProperties: () => this.currentFinalSectionProperties(),
|
|
335
|
+
stageFinalSectionProperties: ({ target, previous, revision }) => this.stageFinalSectionProperties({
|
|
336
|
+
target,
|
|
337
|
+
previous,
|
|
338
|
+
revision
|
|
339
|
+
}),
|
|
340
|
+
stageMappedSectionBoundaries: (options) => this.stageMappedSectionBoundaries(options),
|
|
341
|
+
matchInlineAtoms: ({ story, ...options }) => {
|
|
342
|
+
const state = this.getEditableStoryState(story);
|
|
343
|
+
if (!state) return panic("A compared story lost its editable state", { story });
|
|
344
|
+
const result = matchInlineAtoms({
|
|
345
|
+
...options,
|
|
346
|
+
state,
|
|
347
|
+
author: this.author
|
|
348
|
+
});
|
|
349
|
+
switch (result.status) {
|
|
350
|
+
case "matched":
|
|
351
|
+
if (result.transaction.docChanged) this.setEditableStoryState(story, state.apply(result.transaction));
|
|
352
|
+
return {
|
|
353
|
+
status: "matched",
|
|
354
|
+
nextRevisionId: result.nextRevisionId,
|
|
355
|
+
changedTargetBlockIds: result.changedTargetBlockIds,
|
|
356
|
+
rangeCount: result.rangeCount,
|
|
357
|
+
documentChanged: result.transaction.docChanged
|
|
358
|
+
};
|
|
359
|
+
case "unalignable":
|
|
360
|
+
case "budget-exceeded": return result;
|
|
361
|
+
default: return panic("Unhandled inline atom result", { result });
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
matchInlineProvenance: ({ story, ...options }) => {
|
|
365
|
+
const state = this.getEditableStoryState(story);
|
|
366
|
+
if (!state) return panic("A compared story lost its editable state", { story });
|
|
367
|
+
const result = matchInlineProvenance({
|
|
368
|
+
...options,
|
|
369
|
+
state,
|
|
370
|
+
author: this.author
|
|
371
|
+
});
|
|
372
|
+
switch (result.status) {
|
|
373
|
+
case "matched":
|
|
374
|
+
if (result.transaction.docChanged) this.setEditableStoryState(story, state.apply(result.transaction));
|
|
375
|
+
return {
|
|
376
|
+
status: "matched",
|
|
377
|
+
nextRevisionId: result.nextRevisionId,
|
|
378
|
+
changedTargetBlockIds: result.changedTargetBlockIds,
|
|
379
|
+
rangeCount: result.rangeCount,
|
|
380
|
+
documentChanged: result.transaction.docChanged
|
|
381
|
+
};
|
|
382
|
+
case "unalignable":
|
|
383
|
+
case "budget-exceeded": return result;
|
|
384
|
+
default: return panic("Unhandled inline provenance result", { result });
|
|
385
|
+
}
|
|
386
|
+
},
|
|
268
387
|
projectStories: (mode) => this.projectComparisonStoriesInternal(mode),
|
|
269
|
-
snapshotReviewedStory: (options) => this.snapshotReviewedStoryInternal(options)
|
|
388
|
+
snapshotReviewedStory: (options) => this.snapshotReviewedStoryInternal(options),
|
|
389
|
+
numberingDefinitions: () => this.baseDocument.package.numbering,
|
|
390
|
+
planTargetNumberingReferences: (target, references) => this.planTargetNumberingReferences(target, references),
|
|
391
|
+
stageTargetNumbering: (target, references, remappedNumIds) => this.stageTargetNumbering(target, references, remappedNumIds)
|
|
270
392
|
}));
|
|
271
393
|
}
|
|
394
|
+
stageTargetStyles(source, snapshots, importedHeaderFooterSnapshots) {
|
|
395
|
+
const destination = this.baseDocument.package;
|
|
396
|
+
const sourcePackage = source.baseDocument.package;
|
|
397
|
+
const destinationStyles = this.importedStyles ?? destination.styles;
|
|
398
|
+
const existing = new Set(destinationStyles?.styles.map(({ styleId }) => styleId));
|
|
399
|
+
const sourceStyleIds = new Set(sourcePackage.styles?.styles.map(({ styleId }) => styleId));
|
|
400
|
+
const collect = (document) => {
|
|
401
|
+
const references = /* @__PURE__ */ new Set();
|
|
402
|
+
document.descendants((node) => {
|
|
403
|
+
const styleId = node.attrs["styleId"];
|
|
404
|
+
if (typeof styleId === "string" && styleId.length > 0) references.add(styleId);
|
|
405
|
+
for (const mark of node.marks) if (mark.type.name === "characterStyle") references.add(expectCharacterStyleMarkAttrs(mark).styleId);
|
|
406
|
+
});
|
|
407
|
+
return references;
|
|
408
|
+
};
|
|
409
|
+
const hasUnstyledParagraph = (document) => {
|
|
410
|
+
let found = false;
|
|
411
|
+
document.descendants((node) => {
|
|
412
|
+
if (node.isTextblock && typeof node.attrs["styleId"] !== "string") {
|
|
413
|
+
found = true;
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
417
|
+
});
|
|
418
|
+
return found;
|
|
419
|
+
};
|
|
420
|
+
const resourceSnapshots = canonicalJson(sourcePackage.styles?.docDefaults) === canonicalJson(destinationStyles?.docDefaults) && canonicalJson(sourcePackage.theme) === canonicalJson(destination.theme) ? snapshots : importedHeaderFooterSnapshots;
|
|
421
|
+
const referencedStyleIds = /* @__PURE__ */ new Set();
|
|
422
|
+
let materializeDefaultParagraphStyle = false;
|
|
423
|
+
for (const snapshot of resourceSnapshots) {
|
|
424
|
+
if (hasUnstyledParagraph(sourceDocumentOf(snapshot))) materializeDefaultParagraphStyle = true;
|
|
425
|
+
for (const styleId of collect(sourceDocumentOf(snapshot))) if (sourceStyleIds.has(styleId) && !existing.has(styleId)) referencedStyleIds.add(styleId);
|
|
426
|
+
}
|
|
427
|
+
const reservedStyleIds = /* @__PURE__ */ new Set();
|
|
428
|
+
if (referencedStyleIds.size > 0) for (const handle of this.listStoryHandlesInternal()) {
|
|
429
|
+
const state = this.getEditableStoryState(handle);
|
|
430
|
+
if (!state) continue;
|
|
431
|
+
for (const styleId of collect(state.doc)) if (referencedStyleIds.has(styleId)) reservedStyleIds.add(styleId);
|
|
432
|
+
}
|
|
433
|
+
const result = importReferencedStyleDefinitions({
|
|
434
|
+
sourceStyles: sourcePackage.styles,
|
|
435
|
+
destinationStyles,
|
|
436
|
+
sourceTheme: sourcePackage.theme,
|
|
437
|
+
destinationTheme: destination.theme,
|
|
438
|
+
referencedStyleIds: [...referencedStyleIds],
|
|
439
|
+
reservedStyleIds: [...reservedStyleIds],
|
|
440
|
+
materializeDefaultParagraphStyle
|
|
441
|
+
});
|
|
442
|
+
if (result.status === "imported") {
|
|
443
|
+
this.importedStyles = result.styles;
|
|
444
|
+
this.state = withDocumentStyles(this.state, result.styles);
|
|
445
|
+
for (const entry of this.secondaryStoryStates.values()) {
|
|
446
|
+
const wasUntouched = entry.state === entry.initialState;
|
|
447
|
+
const refreshed = withDocumentStyles(entry.state, result.styles);
|
|
448
|
+
entry.state = refreshed;
|
|
449
|
+
if (wasUntouched) entry.initialState = refreshed;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* A raw final table has no paragraph mark after it for Word to merge into
|
|
456
|
+
* when the target ends in a paragraph. Folio-exact comparison adds this
|
|
457
|
+
* untracked receiver before planning; its private marker makes resolving in
|
|
458
|
+
* Folio restore the raw source view.
|
|
459
|
+
*/
|
|
460
|
+
stageTerminalTableReviewCarrier(target) {
|
|
461
|
+
if (this.state.doc.lastChild?.type.name !== "table" || target.lastChild?.type.name !== "paragraph") return false;
|
|
462
|
+
const paragraphType = this.state.schema.nodes["paragraph"];
|
|
463
|
+
if (!paragraphType) return panic("The schema has no paragraph node for a terminal-table review carrier");
|
|
464
|
+
const carrier = paragraphType.create({ reviewCarrier: REVIEW_CARRIERS.TERMINAL_TABLE });
|
|
465
|
+
this.state = this.state.apply(this.state.tr.insert(this.state.doc.content.size, carrier));
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
stageTargetNumbering(target, references, remappedNumIds) {
|
|
469
|
+
if (references.length === 0) return "unchanged";
|
|
470
|
+
if (!target) return "conflict";
|
|
471
|
+
const current = this.baseDocument.package.numbering ?? {
|
|
472
|
+
abstractNums: [],
|
|
473
|
+
nums: []
|
|
474
|
+
};
|
|
475
|
+
const nums = new Map(current.nums.map((entry) => [entry.numId, entry]));
|
|
476
|
+
const abstracts = new Map(current.abstractNums.map((entry) => [entry.abstractNumId, entry]));
|
|
477
|
+
const targetNums = new Map(target.nums.map((entry) => [entry.numId, entry]));
|
|
478
|
+
const targetAbstracts = new Map(target.abstractNums.map((entry) => [entry.abstractNumId, entry]));
|
|
479
|
+
const levelsByNumId = referencedNumberingLevelsByNumId({ references });
|
|
480
|
+
const importedAbstractIds = /* @__PURE__ */ new Map();
|
|
481
|
+
let nextAbstractId = 0;
|
|
482
|
+
for (const id of abstracts.keys()) nextAbstractId = Math.max(nextAbstractId, id + 1);
|
|
483
|
+
for (const numId of levelsByNumId.keys()) {
|
|
484
|
+
const targetNum = targetNums.get(numId);
|
|
485
|
+
if (!targetNum) return "conflict";
|
|
486
|
+
const targetAbstract = targetAbstracts.get(targetNum.abstractNumId);
|
|
487
|
+
if (!targetAbstract) return "conflict";
|
|
488
|
+
const stagedNumId = remappedNumIds.get(numId) ?? numId;
|
|
489
|
+
if (nums.get(stagedNumId)) {
|
|
490
|
+
if (stagedNumId === numId && sameReferencedNumberingLevels({
|
|
491
|
+
current,
|
|
492
|
+
target,
|
|
493
|
+
numId,
|
|
494
|
+
levelsByNumId
|
|
495
|
+
})) continue;
|
|
496
|
+
return "conflict";
|
|
497
|
+
}
|
|
498
|
+
let abstractNumId = importedAbstractIds.get(targetAbstract.abstractNumId);
|
|
499
|
+
if (abstractNumId === void 0) {
|
|
500
|
+
abstractNumId = targetAbstract.abstractNumId;
|
|
501
|
+
const collision = abstracts.get(abstractNumId);
|
|
502
|
+
if (collision && canonicalJson(collision) !== canonicalJson(targetAbstract)) abstractNumId = nextAbstractId++;
|
|
503
|
+
nextAbstractId = Math.max(nextAbstractId, abstractNumId + 1);
|
|
504
|
+
importedAbstractIds.set(targetAbstract.abstractNumId, abstractNumId);
|
|
505
|
+
abstracts.set(abstractNumId, {
|
|
506
|
+
...targetAbstract,
|
|
507
|
+
abstractNumId
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
nums.set(stagedNumId, {
|
|
511
|
+
...targetNum,
|
|
512
|
+
numId: stagedNumId,
|
|
513
|
+
abstractNumId
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
if (nums.size === current.nums.length) return "unchanged";
|
|
517
|
+
const numbering = {
|
|
518
|
+
...current,
|
|
519
|
+
abstractNums: [...abstracts.values()],
|
|
520
|
+
nums: [...nums.values()]
|
|
521
|
+
};
|
|
522
|
+
this.baseDocument.package.numbering = numbering;
|
|
523
|
+
this.state = withDocumentNumbering(this.state, numbering);
|
|
524
|
+
for (const entry of this.secondaryStoryStates.values()) entry.state = withDocumentNumbering(entry.state, numbering);
|
|
525
|
+
return "staged";
|
|
526
|
+
}
|
|
527
|
+
planTargetNumberingReferences(target, references) {
|
|
528
|
+
if (references.length === 0) return /* @__PURE__ */ new Map();
|
|
529
|
+
if (!target) return null;
|
|
530
|
+
const current = this.baseDocument.package.numbering ?? {
|
|
531
|
+
abstractNums: [],
|
|
532
|
+
nums: []
|
|
533
|
+
};
|
|
534
|
+
const nums = new Map(current.nums.map((entry) => [entry.numId, entry]));
|
|
535
|
+
const targetNums = new Map(target.nums.map((entry) => [entry.numId, entry]));
|
|
536
|
+
const targetAbstracts = new Map(target.abstractNums.map((entry) => [entry.abstractNumId, entry]));
|
|
537
|
+
const levelsByNumId = referencedNumberingLevelsByNumId({ references });
|
|
538
|
+
let nextNumId = 0;
|
|
539
|
+
for (const id of nums.keys()) nextNumId = Math.max(nextNumId, id + 1);
|
|
540
|
+
for (const id of targetNums.keys()) nextNumId = Math.max(nextNumId, id + 1);
|
|
541
|
+
const remapped = /* @__PURE__ */ new Map();
|
|
542
|
+
for (const numId of levelsByNumId.keys()) {
|
|
543
|
+
const targetNum = targetNums.get(numId);
|
|
544
|
+
const targetAbstract = targetNum ? targetAbstracts.get(targetNum.abstractNumId) : void 0;
|
|
545
|
+
if (!targetNum || !targetAbstract) return null;
|
|
546
|
+
if (!nums.get(numId)) continue;
|
|
547
|
+
if (!sameReferencedNumberingLevels({
|
|
548
|
+
current,
|
|
549
|
+
target,
|
|
550
|
+
numId,
|
|
551
|
+
levelsByNumId
|
|
552
|
+
})) remapped.set(numId, nextNumId++);
|
|
553
|
+
}
|
|
554
|
+
return remapped;
|
|
555
|
+
}
|
|
272
556
|
/** Parse a `.docx` buffer into a reviewer. */
|
|
273
557
|
static async fromBuffer(buffer, options = {}) {
|
|
274
558
|
const baseDocument = await parseDocx(buffer, {
|
|
@@ -308,25 +592,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
308
592
|
* packages can be compared entry by entry.
|
|
309
593
|
*/
|
|
310
594
|
readNumberingDefinitions() {
|
|
311
|
-
|
|
312
|
-
if (!numbering) return [];
|
|
313
|
-
const levelsByAbstractId = new Map(numbering.abstractNums.map((abstractNum) => [abstractNum.abstractNumId, abstractNum.levels]));
|
|
314
|
-
const levels = [];
|
|
315
|
-
for (const instance of numbering.nums) {
|
|
316
|
-
const overrides = new Map((instance.levelOverrides ?? []).map((override) => [override.ilvl, override]));
|
|
317
|
-
for (const level of levelsByAbstractId.get(instance.abstractNumId) ?? []) {
|
|
318
|
-
const override = overrides.get(level.ilvl);
|
|
319
|
-
const resolved = override?.lvl ?? level;
|
|
320
|
-
levels.push({
|
|
321
|
-
numId: instance.numId,
|
|
322
|
-
level: resolved.ilvl,
|
|
323
|
-
format: resolved.numFmt,
|
|
324
|
-
levelText: resolved.lvlText,
|
|
325
|
-
...(override?.startOverride ?? resolved.start) !== void 0 ? { start: override?.startOverride ?? resolved.start } : {}
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
return levels.toSorted((left, right) => left.numId - right.numId || left.level - right.level);
|
|
595
|
+
return numberingLevelsOf(this.baseDocument.package.numbering).toSorted((left, right) => left.numId - right.numId || left.level - right.level);
|
|
330
596
|
}
|
|
331
597
|
/** Return parsed package metadata without exposing the mutable document model. */
|
|
332
598
|
getDocumentProperties() {
|
|
@@ -357,12 +623,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
357
623
|
const handles = this.listStoryHandlesInternal();
|
|
358
624
|
let highestId = 0;
|
|
359
625
|
let present = false;
|
|
360
|
-
if (mode === "with-revision-census")
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
626
|
+
if (mode === "with-revision-census") {
|
|
627
|
+
for (const change of this.currentFinalSectionProperties()?.propertyChanges ?? []) {
|
|
628
|
+
highestId = Math.max(highestId, change.info.id);
|
|
629
|
+
present = true;
|
|
630
|
+
}
|
|
631
|
+
for (const handle of handles) {
|
|
632
|
+
const state = this.getEditableStoryState(handle);
|
|
633
|
+
if (!state) continue;
|
|
634
|
+
const stats = getTrackedChangeStatsFromDoc(state.doc);
|
|
635
|
+
highestId = Math.max(highestId, stats.highestId);
|
|
636
|
+
present ||= stats.present;
|
|
637
|
+
}
|
|
366
638
|
}
|
|
367
639
|
const stories = [];
|
|
368
640
|
for (const handle of handles) stories.push({
|
|
@@ -661,10 +933,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
661
933
|
type: "header",
|
|
662
934
|
relationshipId
|
|
663
935
|
});
|
|
936
|
+
for (const relationshipId of this.importedHeaders.keys()) handles.push({
|
|
937
|
+
type: "header",
|
|
938
|
+
relationshipId
|
|
939
|
+
});
|
|
664
940
|
for (const relationshipId of pkg.footers?.keys() ?? []) handles.push({
|
|
665
941
|
type: "footer",
|
|
666
942
|
relationshipId
|
|
667
943
|
});
|
|
944
|
+
for (const relationshipId of this.importedFooters.keys()) handles.push({
|
|
945
|
+
type: "footer",
|
|
946
|
+
relationshipId
|
|
947
|
+
});
|
|
668
948
|
for (const footnote of pkg.footnotes ?? []) if (!isSeparatorFootnote(footnote)) handles.push({
|
|
669
949
|
type: "footnote",
|
|
670
950
|
noteId: footnote.id
|
|
@@ -673,7 +953,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
673
953
|
type: "endnote",
|
|
674
954
|
noteId: endnote.id
|
|
675
955
|
});
|
|
676
|
-
return handles;
|
|
956
|
+
return handles.filter((handle) => handle.type !== "header" && handle.type !== "footer" || !this.removedHeaderFooterStories.has(headerFooterStoryKey(handle)));
|
|
677
957
|
}
|
|
678
958
|
/** Discover every readable document story through a typed, serializable handle. */
|
|
679
959
|
listStories() {
|
|
@@ -696,7 +976,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
696
976
|
* entry where the document model identifies them as one authored change.
|
|
697
977
|
*/
|
|
698
978
|
getChanges(filter) {
|
|
699
|
-
const changes = getTrackedChangesFromDoc(this.state.doc)
|
|
979
|
+
const changes = [...getTrackedChangesFromDoc(this.state.doc), ...(this.currentFinalSectionProperties()?.propertyChanges ?? []).map(({ info }) => ({
|
|
980
|
+
id: info.id,
|
|
981
|
+
type: "sectionPropertiesChanged",
|
|
982
|
+
author: info.author,
|
|
983
|
+
date: info.date ?? null,
|
|
984
|
+
text: "",
|
|
985
|
+
blockId: null
|
|
986
|
+
}))];
|
|
700
987
|
if (!filter) return changes;
|
|
701
988
|
return changes.filter((change) => (filter.author === void 0 || change.author === filter.author) && (filter.type === void 0 || change.type === filter.type));
|
|
702
989
|
}
|
|
@@ -786,6 +1073,86 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
786
1073
|
this.resolvedOverrides.set(target.id, resolved);
|
|
787
1074
|
return true;
|
|
788
1075
|
}
|
|
1076
|
+
currentFinalSectionProperties() {
|
|
1077
|
+
return this.finalSectionPropertiesOverride ?? this.baseDocument.package.document.finalSectionProperties;
|
|
1078
|
+
}
|
|
1079
|
+
stageFinalSectionProperties({ target, previous, revision }) {
|
|
1080
|
+
if (canonicalJson(target) === canonicalJson(previous)) return false;
|
|
1081
|
+
const previousReferences = sectionReferenceHistory({
|
|
1082
|
+
previous,
|
|
1083
|
+
target
|
|
1084
|
+
});
|
|
1085
|
+
this.finalSectionPropertiesOverride = {
|
|
1086
|
+
...target,
|
|
1087
|
+
propertyChanges: [{
|
|
1088
|
+
type: "sectionPropertyChange",
|
|
1089
|
+
info: {
|
|
1090
|
+
id: revision.idSeed,
|
|
1091
|
+
author: this.author,
|
|
1092
|
+
date: revision.date
|
|
1093
|
+
},
|
|
1094
|
+
previousProperties: previous,
|
|
1095
|
+
...previousReferences !== void 0 && { previousReferences },
|
|
1096
|
+
currentProperties: target
|
|
1097
|
+
}]
|
|
1098
|
+
};
|
|
1099
|
+
return true;
|
|
1100
|
+
}
|
|
1101
|
+
stageMappedSectionBoundaries({ target, originalRevisionIdSeed, maxRanges, revision, mapTargetProperties }) {
|
|
1102
|
+
const result = stageSectionBoundaryProperties({
|
|
1103
|
+
state: this.state,
|
|
1104
|
+
target,
|
|
1105
|
+
originalRevisionIdSeed,
|
|
1106
|
+
revisionStamp: revision,
|
|
1107
|
+
author: this.author,
|
|
1108
|
+
maxRanges,
|
|
1109
|
+
mapTargetProperties
|
|
1110
|
+
});
|
|
1111
|
+
if (result.status !== "matched") return result;
|
|
1112
|
+
const documentChanged = result.transaction.docChanged;
|
|
1113
|
+
if (documentChanged) this.state = this.state.apply(result.transaction);
|
|
1114
|
+
return {
|
|
1115
|
+
status: "matched",
|
|
1116
|
+
rangeCount: result.rangeCount,
|
|
1117
|
+
nextRevisionId: result.nextRevisionId,
|
|
1118
|
+
documentChanged
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
resolveFinalSectionProperties(mode, id) {
|
|
1122
|
+
const current = this.currentFinalSectionProperties();
|
|
1123
|
+
const changes = current?.propertyChanges;
|
|
1124
|
+
const targetIndex = changes?.findIndex(({ info }) => info.id === id);
|
|
1125
|
+
if (!current || !changes || changes.length === 0 || targetIndex === void 0 || targetIndex < 0) return 0;
|
|
1126
|
+
const { propertyChanges: _propertyChanges, ...liveProperties } = current;
|
|
1127
|
+
const previousStates = changes.map((change) => sectionRejectProperties({
|
|
1128
|
+
live: current,
|
|
1129
|
+
previousProperties: change.previousProperties,
|
|
1130
|
+
previousReferences: change.previousReferences
|
|
1131
|
+
}));
|
|
1132
|
+
let resolvedProperties = previousStates[0] ?? liveProperties;
|
|
1133
|
+
const remaining = [];
|
|
1134
|
+
for (const [index, change] of changes.entries()) {
|
|
1135
|
+
const nextProperties = previousStates[index + 1] ?? liveProperties;
|
|
1136
|
+
if (index === targetIndex) {
|
|
1137
|
+
if (mode === "accept") resolvedProperties = nextProperties;
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
remaining.push({
|
|
1141
|
+
...change,
|
|
1142
|
+
previousProperties: resolvedProperties,
|
|
1143
|
+
...change.previousReferences !== void 0 && { previousReferences: {
|
|
1144
|
+
...resolvedProperties.headerReferences !== void 0 && { headerReferences: resolvedProperties.headerReferences },
|
|
1145
|
+
...resolvedProperties.footerReferences !== void 0 && { footerReferences: resolvedProperties.footerReferences }
|
|
1146
|
+
} }
|
|
1147
|
+
});
|
|
1148
|
+
resolvedProperties = nextProperties;
|
|
1149
|
+
}
|
|
1150
|
+
this.finalSectionPropertiesOverride = {
|
|
1151
|
+
...resolvedProperties,
|
|
1152
|
+
...remaining.length > 0 && { propertyChanges: remaining }
|
|
1153
|
+
};
|
|
1154
|
+
return 1;
|
|
1155
|
+
}
|
|
789
1156
|
/**
|
|
790
1157
|
* Accept an existing tracked change, keeping its text and dropping the
|
|
791
1158
|
* redline. Pass a {@link FolioReviewChange} from {@link getChanges} or its
|
|
@@ -794,14 +1161,26 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
794
1161
|
* revision is no longer present (already resolved, or never existed).
|
|
795
1162
|
*/
|
|
796
1163
|
acceptChange(target) {
|
|
797
|
-
return this.
|
|
1164
|
+
return this.resolveWithSectionReferenceHistory(() => this.acceptChangeInternal(target));
|
|
1165
|
+
}
|
|
1166
|
+
acceptChangeInternal(target) {
|
|
1167
|
+
const id = revisionIdOf(target);
|
|
1168
|
+
const bodyChanged = this.runCommand(acceptAIEditRevision(id));
|
|
1169
|
+
const sectionChanged = this.resolveFinalSectionProperties("accept", id) > 0;
|
|
1170
|
+
return bodyChanged || sectionChanged;
|
|
798
1171
|
}
|
|
799
1172
|
/**
|
|
800
1173
|
* Reject an existing tracked change: an insertion's text is removed, a
|
|
801
1174
|
* deletion's text is restored. See {@link acceptChange} for targeting.
|
|
802
1175
|
*/
|
|
803
1176
|
rejectChange(target) {
|
|
804
|
-
return this.
|
|
1177
|
+
return this.resolveWithSectionReferenceHistory(() => this.rejectChangeInternal(target));
|
|
1178
|
+
}
|
|
1179
|
+
rejectChangeInternal(target) {
|
|
1180
|
+
const id = revisionIdOf(target);
|
|
1181
|
+
const bodyChanged = this.runCommand(rejectAIEditRevision(id));
|
|
1182
|
+
const sectionChanged = this.resolveFinalSectionProperties("reject", id) > 0;
|
|
1183
|
+
return bodyChanged || sectionChanged;
|
|
805
1184
|
}
|
|
806
1185
|
/**
|
|
807
1186
|
* Accept every tracked change in the package. Returns the number of changes
|
|
@@ -812,11 +1191,45 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
812
1191
|
* document still carries a redline nobody can see from the body.
|
|
813
1192
|
*/
|
|
814
1193
|
acceptAll() {
|
|
815
|
-
return this.resolveEveryStory("accept");
|
|
1194
|
+
return this.resolveWithSectionReferenceHistory(() => this.resolveEveryStory("accept") + this.resolveEveryFinalSectionChange("accept"));
|
|
816
1195
|
}
|
|
817
1196
|
/** Reject every tracked change in the package. See {@link acceptAll}. */
|
|
818
1197
|
rejectAll() {
|
|
819
|
-
return this.resolveEveryStory("reject");
|
|
1198
|
+
return this.resolveWithSectionReferenceHistory(() => this.resolveEveryStory("reject") + this.resolveEveryFinalSectionChange("reject"));
|
|
1199
|
+
}
|
|
1200
|
+
resolveWithSectionReferenceHistory(resolve) {
|
|
1201
|
+
const before = captureSectionReferenceInventory(this.state.doc, this.currentFinalSectionProperties());
|
|
1202
|
+
const result = resolve();
|
|
1203
|
+
const removedEndpointReferences = getTrackedSectionEndpointRemoval(this.state)?.removedReferences ?? [];
|
|
1204
|
+
if (before.revisionRelationships.size > 0 || removedEndpointReferences.length > 0) {
|
|
1205
|
+
const after = captureSectionReferenceInventory(this.state.doc, this.currentFinalSectionProperties());
|
|
1206
|
+
const removed = resolvedSectionReferenceLosses({
|
|
1207
|
+
before,
|
|
1208
|
+
after
|
|
1209
|
+
});
|
|
1210
|
+
this.sectionReferenceRemovals.push(...removed);
|
|
1211
|
+
for (const reference of [...removed, ...removedEndpointReferences]) {
|
|
1212
|
+
if (after.references.some(({ part, relationshipId }) => part === reference.part && relationshipId === reference.relationshipId)) continue;
|
|
1213
|
+
const handle = {
|
|
1214
|
+
type: reference.part,
|
|
1215
|
+
relationshipId: reference.relationshipId
|
|
1216
|
+
};
|
|
1217
|
+
this.removedHeaderFooterStories.set(headerFooterStoryKey(handle), handle);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return result;
|
|
1221
|
+
}
|
|
1222
|
+
resolveEveryFinalSectionChange(mode) {
|
|
1223
|
+
const current = this.currentFinalSectionProperties();
|
|
1224
|
+
const firstChange = current?.propertyChanges?.at(0);
|
|
1225
|
+
if (!current || !firstChange) return 0;
|
|
1226
|
+
const { propertyChanges, ...liveProperties } = current;
|
|
1227
|
+
this.finalSectionPropertiesOverride = mode === "accept" ? liveProperties : sectionRejectProperties({
|
|
1228
|
+
live: current,
|
|
1229
|
+
previousProperties: firstChange.previousProperties,
|
|
1230
|
+
previousReferences: firstChange.previousReferences
|
|
1231
|
+
});
|
|
1232
|
+
return propertyChanges?.length ?? 0;
|
|
820
1233
|
}
|
|
821
1234
|
resolveEveryStory(mode) {
|
|
822
1235
|
let count = 0;
|
|
@@ -841,14 +1254,39 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
841
1254
|
});
|
|
842
1255
|
return {
|
|
843
1256
|
mainState: this.state,
|
|
1257
|
+
finalSectionPropertiesOverride: this.finalSectionPropertiesOverride,
|
|
844
1258
|
secondaryStoryStates,
|
|
1259
|
+
sectionReferenceRemovals: [...this.sectionReferenceRemovals],
|
|
1260
|
+
removedHeaderFooterStories: [...this.removedHeaderFooterStories.values()],
|
|
1261
|
+
importedStyles: this.importedStyles,
|
|
1262
|
+
importedMedia: new Map(this.importedMedia),
|
|
1263
|
+
importedHeaders: new Map(this.importedHeaders),
|
|
1264
|
+
importedFooters: new Map(this.importedFooters),
|
|
845
1265
|
createdComments: [...this.createdComments],
|
|
846
1266
|
resolvedOverrides: new Map(this.resolvedOverrides),
|
|
847
1267
|
resolvedStoryExpectations: [...this.resolvedStoryExpectations.values()]
|
|
848
1268
|
};
|
|
849
1269
|
}
|
|
850
1270
|
documentFromStateSnapshot(snapshot) {
|
|
851
|
-
const document = updateDocumentContent(this.baseDocument
|
|
1271
|
+
const document = updateDocumentContent(snapshot.importedStyles === void 0 ? this.baseDocument : {
|
|
1272
|
+
...this.baseDocument,
|
|
1273
|
+
package: {
|
|
1274
|
+
...this.baseDocument.package,
|
|
1275
|
+
styles: snapshot.importedStyles
|
|
1276
|
+
}
|
|
1277
|
+
}, snapshot.mainState.doc);
|
|
1278
|
+
if (snapshot.finalSectionPropertiesOverride !== void 0) document.package.document.finalSectionProperties = snapshot.finalSectionPropertiesOverride;
|
|
1279
|
+
if (snapshot.importedStyles !== void 0) document.package.styles = snapshot.importedStyles;
|
|
1280
|
+
if (snapshot.importedMedia.size > 0) document.package.media = new Map([...document.package.media ?? [], ...snapshot.importedMedia]);
|
|
1281
|
+
if (snapshot.importedHeaders.size > 0) document.package.headers = new Map([...document.package.headers ?? [], ...snapshot.importedHeaders]);
|
|
1282
|
+
if (snapshot.importedFooters.size > 0) document.package.footers = new Map([...document.package.footers ?? [], ...snapshot.importedFooters]);
|
|
1283
|
+
for (const handle of snapshot.removedHeaderFooterStories) if (handle.type === "header") {
|
|
1284
|
+
document.package.headers = new Map(document.package.headers);
|
|
1285
|
+
document.package.headers.delete(handle.relationshipId);
|
|
1286
|
+
} else {
|
|
1287
|
+
document.package.footers = new Map(document.package.footers);
|
|
1288
|
+
document.package.footers.delete(handle.relationshipId);
|
|
1289
|
+
}
|
|
852
1290
|
this.mergeEditedSecondaryStories(document, snapshot.secondaryStoryStates);
|
|
853
1291
|
if (snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides([...document.package.document.comments ?? [], ...snapshot.createdComments], snapshot.resolvedOverrides);
|
|
854
1292
|
return document;
|
|
@@ -856,7 +1294,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
856
1294
|
captureSaveSnapshot() {
|
|
857
1295
|
const snapshot = this.captureReviewerState();
|
|
858
1296
|
const changedParaIds = new Set(getChangedParagraphIds(snapshot.mainState));
|
|
859
|
-
let structuralChange = hasStructuralChanges(snapshot.mainState);
|
|
1297
|
+
let structuralChange = hasStructuralChanges(snapshot.mainState) || snapshot.finalSectionPropertiesOverride !== void 0 || snapshot.importedStyles !== void 0 || snapshot.importedHeaders.size > 0 || snapshot.importedFooters.size > 0;
|
|
860
1298
|
let untrackedChanges = hasUntrackedChanges(snapshot.mainState);
|
|
861
1299
|
const changedNoteParaIds = /* @__PURE__ */ new Set();
|
|
862
1300
|
for (const entry of snapshot.secondaryStoryStates) {
|
|
@@ -875,6 +1313,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
875
1313
|
changedParaIds
|
|
876
1314
|
},
|
|
877
1315
|
changedNoteParaIds,
|
|
1316
|
+
sectionReferenceRemovals: snapshot.sectionReferenceRemovals,
|
|
878
1317
|
sectionEndpointRemoval: getTrackedSectionEndpointRemoval(snapshot.mainState),
|
|
879
1318
|
resolvedStoryExpectations: snapshot.resolvedStoryExpectations
|
|
880
1319
|
};
|
|
@@ -896,11 +1335,17 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
896
1335
|
...save.document,
|
|
897
1336
|
originalBuffer: this.originalBuffer
|
|
898
1337
|
};
|
|
1338
|
+
const repack = () => repackDocx(repackDocument, { changedNoteParaIds: save.changedNoteParaIds });
|
|
1339
|
+
const repackReferences = () => save.sectionReferenceRemovals.length > 0 ? withSectionReferenceResolution({
|
|
1340
|
+
document: repackDocument,
|
|
1341
|
+
removedReferences: save.sectionReferenceRemovals,
|
|
1342
|
+
repack
|
|
1343
|
+
}) : repack();
|
|
899
1344
|
const buffer = save.sectionEndpointRemoval ? await withTrackedSectionEndpointRemoval({
|
|
900
1345
|
document: repackDocument,
|
|
901
1346
|
resolution: save.sectionEndpointRemoval,
|
|
902
|
-
repack:
|
|
903
|
-
}) : await
|
|
1347
|
+
repack: repackReferences
|
|
1348
|
+
}) : await repackReferences();
|
|
904
1349
|
await this.assertResolvedStoriesSerialized(buffer, save.resolvedStoryExpectations);
|
|
905
1350
|
return buffer;
|
|
906
1351
|
}
|
|
@@ -995,8 +1440,92 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
995
1440
|
entry.state = state;
|
|
996
1441
|
}
|
|
997
1442
|
getHeaderFooterStory(story) {
|
|
1443
|
+
if (this.removedHeaderFooterStories.has(headerFooterStoryKey(story))) return void 0;
|
|
1444
|
+
const importedStory = (story.type === "header" ? this.importedHeaders : this.importedFooters).get(story.relationshipId);
|
|
1445
|
+
if (importedStory) return importedStory;
|
|
998
1446
|
return (story.type === "header" ? this.baseDocument.package.headers : this.baseDocument.package.footers)?.get(story.relationshipId);
|
|
999
1447
|
}
|
|
1448
|
+
async createComparisonHeaderFooter(source, story) {
|
|
1449
|
+
const target = source.getHeaderFooterStory(story);
|
|
1450
|
+
if (!target || !this.canImportHeaderFooterContent(source, story)) return null;
|
|
1451
|
+
let importedWatermark = {};
|
|
1452
|
+
let importedMedia;
|
|
1453
|
+
if (target.watermark !== void 0 || target.rawWatermarkXml !== void 0) {
|
|
1454
|
+
const watermark = target.watermark;
|
|
1455
|
+
if (story.type !== "header" || watermark?.kind !== "picture" || watermark.imageTargetExternal || !watermark.imageTarget || !target.rawWatermarkXml) return null;
|
|
1456
|
+
const media = source.baseDocument.package.media?.get(watermark.imageTarget);
|
|
1457
|
+
if (!media || !/^image\/(?:png|jpeg|gif|tiff|bmp)$/u.test(media.mimeType)) return null;
|
|
1458
|
+
const xml = rebindDrawingImageRelationship({
|
|
1459
|
+
xml: target.rawWatermarkXml,
|
|
1460
|
+
previousId: watermark.imageRId,
|
|
1461
|
+
nextId: watermark.imageRId
|
|
1462
|
+
});
|
|
1463
|
+
if (xml === null) return null;
|
|
1464
|
+
const extension = media.path.split(".").at(-1)?.toLowerCase();
|
|
1465
|
+
if (!extension || !/^[a-z0-9]+$/u.test(extension)) return null;
|
|
1466
|
+
const originalZip = await JSZip.loadAsync(this.originalBuffer);
|
|
1467
|
+
const existingPaths = new Set(Object.keys(originalZip.files).map((path) => path.toLowerCase()));
|
|
1468
|
+
let suffix = 1;
|
|
1469
|
+
let path = `word/media/folio-import-${suffix}.${extension}`;
|
|
1470
|
+
while (existingPaths.has(path.toLowerCase()) || this.baseDocument.package.media?.has(path) || this.importedMedia.has(path)) path = `word/media/folio-import-${++suffix}.${extension}`;
|
|
1471
|
+
importedMedia = {
|
|
1472
|
+
...media,
|
|
1473
|
+
path,
|
|
1474
|
+
data: media.data.slice(0)
|
|
1475
|
+
};
|
|
1476
|
+
importedWatermark = {
|
|
1477
|
+
watermark: {
|
|
1478
|
+
...watermark,
|
|
1479
|
+
imageTarget: path
|
|
1480
|
+
},
|
|
1481
|
+
rawWatermarkXml: xml,
|
|
1482
|
+
...target.watermarkBlockIndex !== void 0 && { watermarkBlockIndex: target.watermarkBlockIndex }
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
const taken = /* @__PURE__ */ new Set([
|
|
1486
|
+
...this.baseDocument.package.relationships?.keys() ?? [],
|
|
1487
|
+
...this.baseDocument.package.headers?.keys() ?? [],
|
|
1488
|
+
...this.baseDocument.package.footers?.keys() ?? [],
|
|
1489
|
+
...this.importedHeaders.keys(),
|
|
1490
|
+
...this.importedFooters.keys()
|
|
1491
|
+
]);
|
|
1492
|
+
let suffix = 1;
|
|
1493
|
+
let relationshipId = `rId${suffix}`;
|
|
1494
|
+
while (taken.has(relationshipId)) relationshipId = `rId${++suffix}`;
|
|
1495
|
+
const handle = {
|
|
1496
|
+
type: story.type,
|
|
1497
|
+
relationshipId
|
|
1498
|
+
};
|
|
1499
|
+
const empty = {
|
|
1500
|
+
type: story.type,
|
|
1501
|
+
hdrFtrType: target.hdrFtrType,
|
|
1502
|
+
...importedWatermark,
|
|
1503
|
+
content: [{
|
|
1504
|
+
type: "paragraph",
|
|
1505
|
+
content: []
|
|
1506
|
+
}]
|
|
1507
|
+
};
|
|
1508
|
+
if (importedMedia) this.importedMedia.set(importedMedia.path, importedMedia);
|
|
1509
|
+
(story.type === "header" ? this.importedHeaders : this.importedFooters).set(relationshipId, empty);
|
|
1510
|
+
const state = ensureBaseDirectionInState(EditorState.create({
|
|
1511
|
+
schema,
|
|
1512
|
+
doc: ensureDeterministicParaIdsInDoc(headerFooterToProseDoc(empty.content)),
|
|
1513
|
+
plugins: createHeadlessPlugins(this.baseDocument.package.styles, this.baseDocument.package.numbering)
|
|
1514
|
+
}));
|
|
1515
|
+
this.secondaryStoryStates.set(headerFooterStoryKey(handle), {
|
|
1516
|
+
handle,
|
|
1517
|
+
initialState: state,
|
|
1518
|
+
state
|
|
1519
|
+
});
|
|
1520
|
+
return handle;
|
|
1521
|
+
}
|
|
1522
|
+
canImportHeaderFooterContent(source, story) {
|
|
1523
|
+
const state = source.getEditableStoryState(story);
|
|
1524
|
+
if (!state) return false;
|
|
1525
|
+
if (!tableTemplateCanCrossPackageLosslessly(state.doc, "rebind-external")) return false;
|
|
1526
|
+
const snapshot = source.snapshotStory(story);
|
|
1527
|
+
return snapshot !== null && detachFolioAIEditSnapshotExternalHyperlinks(snapshot) !== null;
|
|
1528
|
+
}
|
|
1000
1529
|
getNoteStory(story) {
|
|
1001
1530
|
if (story.type === "footnote") return this.baseDocument.package.footnotes?.find((note) => note.id === story.noteId && !isSeparatorFootnote(note));
|
|
1002
1531
|
return this.baseDocument.package.endnotes?.find((note) => note.id === story.noteId && !isSeparatorEndnote(note));
|
|
@@ -1029,7 +1558,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1029
1558
|
for (const entry of secondaryStoryStates) {
|
|
1030
1559
|
if (entry.state === entry.initialState) continue;
|
|
1031
1560
|
if (entry.handle.type === "header" || entry.handle.type === "footer") {
|
|
1032
|
-
const source =
|
|
1561
|
+
const source = entry.handle.type === "header" ? document.package.headers?.get(entry.handle.relationshipId) : document.package.footers?.get(entry.handle.relationshipId);
|
|
1033
1562
|
if (!source) continue;
|
|
1034
1563
|
const edited = {
|
|
1035
1564
|
...source,
|