@stll/folio-core 0.54.0 → 0.54.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +1 -1
- package/dist/ai-edits/apply.js +43 -31
- package/dist/ai-edits/headless.d.ts +1 -2
- package/dist/ai-edits/headless.js +12 -27
- package/dist/ai-edits/read.d.ts +1 -0
- package/dist/ai-edits/read.js +7 -2
- package/dist/ai-edits/table-mutation-plan.d.ts +2 -1
- package/dist/ai-edits/table-mutation-plan.js +39 -1
- package/dist/ai-suggestions/apply.d.ts +1 -1
- package/dist/ai-suggestions/apply.js +3 -2
- package/dist/compare/compare.js +2 -1
- package/dist/docx/bookmarkIds.d.ts +11 -0
- package/dist/docx/bookmarkIds.js +33 -0
- package/dist/docx/documentParser.d.ts +14 -1
- package/dist/docx/documentParser.js +17 -4
- package/dist/docx/drawingIdNormalization.js +23 -8
- package/dist/docx/ensureParaIds.d.ts +5 -0
- package/dist/docx/ensureParaIds.js +10 -2
- package/dist/docx/listNumberingInstances.js +11 -9
- package/dist/docx/noteIds.d.ts +4 -0
- package/dist/docx/noteIds.js +13 -0
- package/dist/docx/numberingIds.d.ts +14 -0
- package/dist/docx/numberingIds.js +16 -0
- package/dist/docx/numericIdAllocator.d.ts +12 -0
- package/dist/docx/numericIdAllocator.js +33 -0
- package/dist/docx/numericIdNormalization.d.ts +16 -0
- package/dist/docx/numericIdNormalization.js +174 -0
- package/dist/docx/paragraphPropertySource.js +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +2 -1
- package/dist/docx/parser.js +17 -5
- package/dist/docx/rasterMime.d.ts +64 -0
- package/dist/docx/rasterMime.js +116 -0
- package/dist/docx/replyToComment.js +13 -6
- package/dist/docx/rezip.js +14 -7
- package/dist/docx/selectiveSave.js +10 -0
- package/dist/docx/serializer/partNamespaces.js +4 -2
- package/dist/docx/serializer/runSerializer.js +5 -4
- package/dist/docx/serializer/trackedChangeAttributes.js +7 -1
- package/dist/docx/server/build.js +14 -3
- package/dist/docx/server/createBilingualDocument.js +10 -9
- package/dist/docx/streamingXmlParser.d.ts +10 -1
- package/dist/docx/streamingXmlParser.js +88 -19
- package/dist/docx/unzip.d.ts +3 -1
- package/dist/docx/unzip.js +36 -16
- package/dist/docx/verticalMergeProjection.d.ts +17 -0
- package/dist/docx/verticalMergeProjection.js +120 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.d.ts +6 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.js +23 -0
- package/dist/layout-bridge/convert/tableConversion.js +4 -1
- package/dist/layout-painter/index.js +2 -5
- package/dist/paged-layout/editorScrollRoot.d.ts +15 -0
- package/dist/paged-layout/editorScrollRoot.js +72 -0
- package/dist/paged-layout/scrollToPmPosition.js +8 -22
- package/dist/prosemirror/commands/clearParagraphIndent.d.ts +6 -0
- package/dist/prosemirror/commands/clearParagraphIndent.js +25 -0
- package/dist/prosemirror/commands/comments.js +6 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.js +18 -24
- package/dist/prosemirror/commands/resolveParagraphProperties.js +13 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -9
- package/dist/prosemirror/commands/tableCellMergeResolution.js +28 -25
- package/dist/prosemirror/commands/tableMergeFoldDecisions.d.ts +61 -0
- package/dist/prosemirror/commands/tableMergeFoldDecisions.js +79 -0
- package/dist/prosemirror/commentIdAllocator.d.ts +15 -22
- package/dist/prosemirror/commentIdAllocator.js +50 -25
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -6
- package/dist/prosemirror/conversion/toProseDoc.js +2 -117
- package/dist/prosemirror/extensions/core/HistoryExtension.js +97 -5
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +3 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +22 -28
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -6
- package/dist/prosemirror/listNumbering.js +14 -14
- package/dist/prosemirror/listRendering.d.ts +4 -2
- package/dist/prosemirror/listRendering.js +3 -4
- package/dist/prosemirror/paragraphPropertyCarry.js +2 -5
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -19
- package/dist/prosemirror/plugins/revisionIds.js +110 -29
- package/dist/prosemirror/plugins/suggestionMode.d.ts +2 -2
- package/dist/prosemirror/plugins/suggestionMode.js +197 -45
- package/dist/prosemirror/plugins/templateDirectives.d.ts +6 -0
- package/dist/prosemirror/plugins/templateDirectives.js +21 -7
- package/dist/prosemirror/plugins/templatePreviewValues.js +14 -21
- package/dist/prosemirror/storyListNumbering.js +11 -9
- package/dist/prosemirror/textInput.d.ts +6 -1
- package/dist/prosemirror/textInput.js +9 -2
- package/dist/prosemirror/utils/visualLineNavigation.js +6 -8
- package/dist/utils/mergeDocumentContent.d.ts +2 -2
- package/dist/utils/mergeDocumentContent.js +11 -8
- package/package.json +3 -3
package/dist/ai-edits/apply.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ type FolioAIEditView = {
|
|
|
20
20
|
/**
|
|
21
21
|
* Fixed revision provenance, for callers whose output must be reproducible.
|
|
22
22
|
* Both halves of the stamp travel together because either one left to the
|
|
23
|
-
* ambient clock (`new Date()` for the date,
|
|
23
|
+
* ambient clock (`new Date()` for the date, the shared bounded allocator for
|
|
24
24
|
* the ids) makes the produced package differ between two runs over the same
|
|
25
25
|
* inputs. `idSeed` starts a contiguous range the batch allocates from, so it
|
|
26
26
|
* must sit above every revision id the target document already carries.
|
package/dist/ai-edits/apply.js
CHANGED
|
@@ -21,6 +21,7 @@ import { carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, rec
|
|
|
21
21
|
import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
|
|
22
22
|
import { getDocumentNumbering, getDocumentNumberingInstanceIds } from "../prosemirror/plugins/documentNumbering.js";
|
|
23
23
|
import { getDocumentStyleDefinitions, getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
|
|
24
|
+
import { claimRevisionIds, nextRevisionId, nextRevisionIdRange, reserveRevisionIds, seedRevisionIdsFromDoc } from "../prosemirror/plugins/revisionIds.js";
|
|
24
25
|
import { rebaseParagraphRuns } from "../prosemirror/rebaseParagraphRuns.js";
|
|
25
26
|
import { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, placeTrackedInsertion, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
|
|
26
27
|
import { applyMarksToRunFormattingRepresentation, runFormattingInlineControlNodeName, selectRunFormattingCarrierRepresentations } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
@@ -825,24 +826,6 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
|
|
|
825
826
|
};
|
|
826
827
|
};
|
|
827
828
|
/**
|
|
828
|
-
* Module-scoped monotonic counter for tracked-change revision ids.
|
|
829
|
-
* Seeded once from `Date.now()` so ids are roughly time-ordered for
|
|
830
|
-
* humans reading raw DOCX, then incremented per allocation. A bare
|
|
831
|
-
* `Date.now()` seed per applyAIEditOperations call would collide
|
|
832
|
-
* across batches that fire within the same millisecond (the panel's
|
|
833
|
-
* Accept-all loop does exactly that — multiple calls in tight
|
|
834
|
-
* succession).
|
|
835
|
-
*
|
|
836
|
-
* A batch without a revision stamp allocates from it as it writes and claims
|
|
837
|
-
* each operation's ids once they are written, rather than reserving a guess
|
|
838
|
-
* up front: how many revisions an operation records is known only once it has
|
|
839
|
-
* recorded them (clearing a background records one run-property change per
|
|
840
|
-
* carrier). The claim also comes before anything that may start another
|
|
841
|
-
* batch, a comment-id callback or the dispatch, so batches in one realm never
|
|
842
|
-
* share an id and follow one another without a gap.
|
|
843
|
-
*/
|
|
844
|
-
let revisionIdCursor = Date.now() * 1e3;
|
|
845
|
-
/**
|
|
846
829
|
* Walk the live doc once and bucket every textblock by its
|
|
847
830
|
* normalised text hash. Resolution then maps each snapshot anchor
|
|
848
831
|
* to the live block at the same ordinal among same-hash siblings —
|
|
@@ -1486,6 +1469,13 @@ const finalParagraphPredecessor = (doc, at) => {
|
|
|
1486
1469
|
for (let index = $emptied.index() - 1; index >= 0; index--) {
|
|
1487
1470
|
const node = $emptied.parent.child(index);
|
|
1488
1471
|
position -= node.nodeSize;
|
|
1472
|
+
if (node.type.spec["tableRole"] === "table" && node.childCount > 0) {
|
|
1473
|
+
let allRowsDeleted = true;
|
|
1474
|
+
node.forEach((row) => {
|
|
1475
|
+
allRowsDeleted &&= expectTableRowAttrs(row).trDel != null;
|
|
1476
|
+
});
|
|
1477
|
+
if (allRowsDeleted) continue;
|
|
1478
|
+
}
|
|
1489
1479
|
if (node.type !== emptied.type) return null;
|
|
1490
1480
|
if (isPlainDeletedPPrMark(node.attrs["pPrMark"]) && holdsOnlyDeletedContent(node)) continue;
|
|
1491
1481
|
return {
|
|
@@ -1701,6 +1691,24 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1701
1691
|
};
|
|
1702
1692
|
collectSupersededDeletedBreaks(tr.doc, 0);
|
|
1703
1693
|
for (const { preceding, inserted, deletedMark, precedingMark } of supersededDeletedBreaks) {
|
|
1694
|
+
const insertionRevisionId = addedBreakRevisionId(precedingMark);
|
|
1695
|
+
if (insertionRevisionId !== null) {
|
|
1696
|
+
const previous = tr.doc.nodeAt(preceding);
|
|
1697
|
+
const current = tr.doc.nodeAt(inserted);
|
|
1698
|
+
if (!previous || !current) panic("A shifted paragraph break lost its carrier");
|
|
1699
|
+
const previousFormatting = paragraphPropertiesOnReject(previous);
|
|
1700
|
+
const existing = expectParagraphAttrs(current)._propertyChanges;
|
|
1701
|
+
if (!hasSerializableParagraphPropertyChange(existing) && JSON.stringify(previousFormatting) !== JSON.stringify(paragraphPropertiesSnapshot(current))) tr.setNodeAttribute(inserted, "_propertyChanges", [...Array.isArray(existing) ? existing : [], {
|
|
1702
|
+
type: "paragraphPropertyChange",
|
|
1703
|
+
info: {
|
|
1704
|
+
id: insertionRevisionId,
|
|
1705
|
+
author,
|
|
1706
|
+
date,
|
|
1707
|
+
...initials ? { initials } : {}
|
|
1708
|
+
},
|
|
1709
|
+
previousFormatting
|
|
1710
|
+
}]);
|
|
1711
|
+
}
|
|
1704
1712
|
tr.setNodeAttribute(preceding, "pPrMark", precedingMark);
|
|
1705
1713
|
tr.setNodeAttribute(inserted, "pPrMark", deletedMark);
|
|
1706
1714
|
}
|
|
@@ -2035,6 +2043,7 @@ const buildInsertedParagraphs = ({ item, schema, formattingFromStyle, mode, auth
|
|
|
2035
2043
|
};
|
|
2036
2044
|
};
|
|
2037
2045
|
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: requestedOperations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates, replacementBackground = "clear", undefinedStyles = "refuse" }) => {
|
|
2046
|
+
seedRevisionIdsFromDoc(view.state.doc);
|
|
2038
2047
|
const applied = [];
|
|
2039
2048
|
const skipped = [];
|
|
2040
2049
|
const normalizations = [];
|
|
@@ -2091,7 +2100,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2091
2100
|
id: operation.id,
|
|
2092
2101
|
reason: "unsupportedBlock"
|
|
2093
2102
|
})),
|
|
2094
|
-
nextRevisionId: revisionIdSeed ?? revisionStamp?.idSeed ??
|
|
2103
|
+
nextRevisionId: revisionIdSeed ?? revisionStamp?.idSeed ?? nextRevisionId()
|
|
2095
2104
|
};
|
|
2096
2105
|
const liveBlocks = collectLiveBlocksByHash(view.state.doc);
|
|
2097
2106
|
const liveBlocksByParaId = collectLiveBlocksByParaId(view.state.doc);
|
|
@@ -2201,14 +2210,14 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2201
2210
|
item,
|
|
2202
2211
|
operationId: item.operation.id,
|
|
2203
2212
|
target: getTableMutationPlanTarget(item)
|
|
2204
|
-
})));
|
|
2213
|
+
})), view.state.doc);
|
|
2205
2214
|
skipped.push(...tablePlan.skipped);
|
|
2206
2215
|
const executableResolved = tablePlan.executable;
|
|
2207
2216
|
if (executableResolved.length === 0) return {
|
|
2208
2217
|
applied,
|
|
2209
2218
|
skipped,
|
|
2210
2219
|
...normalizations.length > 0 && { normalizations },
|
|
2211
|
-
nextRevisionId: revisionIdSeed ?? revisionStamp?.idSeed ??
|
|
2220
|
+
nextRevisionId: revisionIdSeed ?? revisionStamp?.idSeed ?? nextRevisionId()
|
|
2212
2221
|
};
|
|
2213
2222
|
const moveSideCounts = /* @__PURE__ */ new Map();
|
|
2214
2223
|
for (const { operation } of executableResolved) {
|
|
@@ -2243,17 +2252,20 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2243
2252
|
const mergedPropertyTargets = [];
|
|
2244
2253
|
const deferredNoopFinalDeletions = /* @__PURE__ */ new Set();
|
|
2245
2254
|
const ownsSharedRevisionIdCursor = revisionIdSeed === void 0 && revisionStamp === void 0;
|
|
2246
|
-
let revisionSeed = revisionIdSeed ?? revisionStamp?.idSeed ??
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2255
|
+
let revisionSeed = revisionIdSeed ?? revisionStamp?.idSeed ?? nextRevisionIdRange();
|
|
2256
|
+
let claimedThrough = revisionSeed;
|
|
2257
|
+
const claimSharedRevisionIds = (next) => {
|
|
2258
|
+
if (revisionIdSeed !== void 0) return;
|
|
2259
|
+
if (ownsSharedRevisionIdCursor) claimRevisionIds(claimedThrough, next);
|
|
2260
|
+
else reserveRevisionIds(claimedThrough, next);
|
|
2261
|
+
claimedThrough = next;
|
|
2253
2262
|
};
|
|
2254
|
-
/** Continue past
|
|
2263
|
+
/** Continue past ids a reentrant callback allocated in this realm. */
|
|
2255
2264
|
const continueSharedRevisionIds = () => {
|
|
2256
|
-
if (ownsSharedRevisionIdCursor
|
|
2265
|
+
if (ownsSharedRevisionIdCursor && nextRevisionId() !== claimedThrough) {
|
|
2266
|
+
revisionSeed = nextRevisionIdRange();
|
|
2267
|
+
claimedThrough = revisionSeed;
|
|
2268
|
+
}
|
|
2257
2269
|
};
|
|
2258
2270
|
const date = revisionStamp?.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2259
2271
|
const insertedColumnCounts = /* @__PURE__ */ new Map();
|
|
@@ -3713,7 +3725,7 @@ const previewFolioAIEditOperationsWithResult = (options) => {
|
|
|
3713
3725
|
applied: result.applied.map(({ id }) => ({ id })),
|
|
3714
3726
|
skipped: result.skipped,
|
|
3715
3727
|
...result.normalizations !== void 0 && { normalizations: result.normalizations },
|
|
3716
|
-
nextRevisionId: options.revisionStamp?.idSeed ??
|
|
3728
|
+
nextRevisionId: options.revisionStamp?.idSeed ?? nextRevisionId()
|
|
3717
3729
|
},
|
|
3718
3730
|
doc: previewView.state.doc,
|
|
3719
3731
|
commentIds
|
|
@@ -179,7 +179,7 @@ type FolioDocxComparisonStoryProjection = {
|
|
|
179
179
|
type FolioDocxComparisonProjection = {
|
|
180
180
|
stories: readonly FolioDocxComparisonStoryProjection[];
|
|
181
181
|
revisions: {
|
|
182
|
-
|
|
182
|
+
ids: readonly number[];
|
|
183
183
|
present: boolean;
|
|
184
184
|
};
|
|
185
185
|
};
|
|
@@ -350,7 +350,6 @@ declare class FolioDocxReviewer {
|
|
|
350
350
|
private readonly importedFooters;
|
|
351
351
|
private readonly resolvedStoryExpectations;
|
|
352
352
|
private readonly createdComments;
|
|
353
|
-
private readonly usedCommentIds;
|
|
354
353
|
/** Comments the body anchored when it was loaded, read as the editor projects it. */
|
|
355
354
|
private readonly loadedMainCommentIds;
|
|
356
355
|
private readonly documentOperationUndoEntries;
|
|
@@ -7,6 +7,7 @@ import { applyReplyThreadMarkers } from "../docx/commentReplyMarkers.js";
|
|
|
7
7
|
import { rebindDrawingImageRelationship } from "../docx/drawingRelationships.js";
|
|
8
8
|
import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
9
9
|
import { getHeaderFooterText } from "../docx/headerFooterParser.js";
|
|
10
|
+
import { createNumberingIdAllocator } from "../docx/numberingIds.js";
|
|
10
11
|
import { getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
11
12
|
import { recreateProseNodeWithParagraphPropertySource, transferProseParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
12
13
|
import { parseDocx } from "../docx/parser.js";
|
|
@@ -20,6 +21,7 @@ import { captureSectionReferenceInventory, resolvedSectionReferenceLosses, withS
|
|
|
20
21
|
import { expectCharacterStyleMarkAttrs, expectFootnoteRefMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
21
22
|
import { acceptAIEditRevision, acceptSuggestion, getSuggestions, rejectAIEditRevision, rejectAllSuggestions, rejectSuggestion, resolveAllChangesInHeadlessState, suggestionIdOfRevision } from "../prosemirror/commands/comments.js";
|
|
22
23
|
import { sectionRejectProperties } from "../prosemirror/commands/propertyChangeScope.js";
|
|
24
|
+
import { allocateCommentId, createCommentIdAllocator, seedCommentAllocator } from "../prosemirror/commentIdAllocator.js";
|
|
23
25
|
import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
|
|
24
26
|
import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
25
27
|
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
@@ -68,14 +70,6 @@ import JSZip from "jszip";
|
|
|
68
70
|
*
|
|
69
71
|
* Scope: main, header, footer, footnote, and endnote blocks.
|
|
70
72
|
*/
|
|
71
|
-
/**
|
|
72
|
-
* Standalone comment-id allocator for the reviewer. The React editor uses
|
|
73
|
-
* `Date.now()`-seeded ids from `commentsHelpers`; that module is DOM-bound
|
|
74
|
-
* (`EditorView`, `findBodyPmAnchors`) and unimportable here, so the reviewer
|
|
75
|
-
* mints its own. Seeded once per realm and incremented so ids stay unique
|
|
76
|
-
* across reviewers created within the same millisecond.
|
|
77
|
-
*/
|
|
78
|
-
let commentIdCursor = Date.now();
|
|
79
73
|
let undoHandleCursor = Date.now();
|
|
80
74
|
const createReviewerComment = ({ id, text, author, date }) => ({
|
|
81
75
|
id,
|
|
@@ -399,7 +393,6 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
399
393
|
importedFooters = /* @__PURE__ */ new Map();
|
|
400
394
|
resolvedStoryExpectations = /* @__PURE__ */ new Map();
|
|
401
395
|
createdComments = [];
|
|
402
|
-
usedCommentIds;
|
|
403
396
|
/** Comments the body anchored when it was loaded, read as the editor projects it. */
|
|
404
397
|
loadedMainCommentIds;
|
|
405
398
|
documentOperationUndoEntries = [];
|
|
@@ -418,7 +411,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
418
411
|
this.originalBuffer = args.originalBuffer;
|
|
419
412
|
this.state = args.state;
|
|
420
413
|
this.author = args.author;
|
|
421
|
-
|
|
414
|
+
seedCommentAllocator(createCommentIdAllocator(), args.baseDocument.package.document.comments, { state: args.state });
|
|
422
415
|
this.loadedMainCommentIds = anchoredCommentIdsInProseDoc(args.state.doc);
|
|
423
416
|
comparisonAccessByReviewer.set(this, Object.freeze({
|
|
424
417
|
stageTerminalTableReviewCarrier: (target) => this.stageTerminalTableReviewCarrier(target),
|
|
@@ -571,8 +564,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
571
564
|
const targetAbstracts = new Map(target.abstractNums.map((entry) => [entry.abstractNumId, entry]));
|
|
572
565
|
const levelsByNumId = referencedNumberingLevelsByNumId({ references });
|
|
573
566
|
const importedAbstractIds = /* @__PURE__ */ new Map();
|
|
574
|
-
|
|
575
|
-
for (const id of abstracts.keys()) nextAbstractId = Math.max(nextAbstractId, id + 1);
|
|
567
|
+
const abstractIds = createNumberingIdAllocator("abstract", [...abstracts.keys(), ...targetAbstracts.keys()]);
|
|
576
568
|
for (const numId of levelsByNumId.keys()) {
|
|
577
569
|
const targetNum = targetNums.get(numId);
|
|
578
570
|
if (!targetNum) return "conflict";
|
|
@@ -592,8 +584,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
592
584
|
if (abstractNumId === void 0) {
|
|
593
585
|
abstractNumId = targetAbstract.abstractNumId;
|
|
594
586
|
const collision = abstracts.get(abstractNumId);
|
|
595
|
-
if (collision && canonicalJson(collision) !== canonicalJson(targetAbstract)) abstractNumId =
|
|
596
|
-
nextAbstractId = Math.max(nextAbstractId, abstractNumId + 1);
|
|
587
|
+
if (collision && canonicalJson(collision) !== canonicalJson(targetAbstract)) abstractNumId = abstractIds.next();
|
|
597
588
|
importedAbstractIds.set(targetAbstract.abstractNumId, abstractNumId);
|
|
598
589
|
abstracts.set(abstractNumId, {
|
|
599
590
|
...targetAbstract,
|
|
@@ -628,9 +619,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
628
619
|
const targetNums = new Map(target.nums.map((entry) => [entry.numId, entry]));
|
|
629
620
|
const targetAbstracts = new Map(target.abstractNums.map((entry) => [entry.abstractNumId, entry]));
|
|
630
621
|
const levelsByNumId = referencedNumberingLevelsByNumId({ references });
|
|
631
|
-
|
|
632
|
-
for (const id of nums.keys()) nextNumId = Math.max(nextNumId, id + 1);
|
|
633
|
-
for (const id of targetNums.keys()) nextNumId = Math.max(nextNumId, id + 1);
|
|
622
|
+
const numberingIds = createNumberingIdAllocator("num", [...nums.keys(), ...targetNums.keys()]);
|
|
634
623
|
const remapped = /* @__PURE__ */ new Map();
|
|
635
624
|
for (const numId of levelsByNumId.keys()) {
|
|
636
625
|
const targetNum = targetNums.get(numId);
|
|
@@ -642,7 +631,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
642
631
|
target,
|
|
643
632
|
numId,
|
|
644
633
|
levelsByNumId
|
|
645
|
-
})) remapped.set(numId,
|
|
634
|
+
})) remapped.set(numId, numberingIds.next());
|
|
646
635
|
}
|
|
647
636
|
return remapped;
|
|
648
637
|
}
|
|
@@ -759,18 +748,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
759
748
|
}
|
|
760
749
|
projectComparisonStoriesInternal(mode) {
|
|
761
750
|
const handles = this.listStoryHandlesInternal();
|
|
762
|
-
|
|
751
|
+
const ids = /* @__PURE__ */ new Set();
|
|
763
752
|
let present = false;
|
|
764
753
|
if (mode === "with-revision-census") {
|
|
765
754
|
for (const change of this.currentFinalSectionProperties()?.propertyChanges ?? []) {
|
|
766
|
-
|
|
755
|
+
ids.add(change.info.id);
|
|
767
756
|
present = true;
|
|
768
757
|
}
|
|
769
758
|
for (const handle of handles) {
|
|
770
759
|
const state = this.getEditableStoryState(handle);
|
|
771
760
|
if (!state) continue;
|
|
772
761
|
const stats = getTrackedChangeStatsFromDoc(state.doc);
|
|
773
|
-
|
|
762
|
+
for (const id of stats.ids) ids.add(id);
|
|
774
763
|
present ||= stats.present;
|
|
775
764
|
}
|
|
776
765
|
}
|
|
@@ -785,7 +774,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
785
774
|
return {
|
|
786
775
|
stories,
|
|
787
776
|
revisions: {
|
|
788
|
-
|
|
777
|
+
ids: [...ids],
|
|
789
778
|
present
|
|
790
779
|
}
|
|
791
780
|
};
|
|
@@ -1231,7 +1220,6 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1231
1220
|
});
|
|
1232
1221
|
if (!reply) return null;
|
|
1233
1222
|
this.createdComments.push(reply);
|
|
1234
|
-
this.usedCommentIds.add(reply.id);
|
|
1235
1223
|
this.anchorReplyInEditor({
|
|
1236
1224
|
parentId: reply.parentId ?? parentId,
|
|
1237
1225
|
replyId: reply.id
|
|
@@ -2186,10 +2174,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
2186
2174
|
}
|
|
2187
2175
|
/** Allocate outside every parsed or newly-created comment and reply id. */
|
|
2188
2176
|
nextCommentId() {
|
|
2189
|
-
|
|
2190
|
-
const id = commentIdCursor++;
|
|
2191
|
-
this.usedCommentIds.add(id);
|
|
2192
|
-
return id;
|
|
2177
|
+
return allocateCommentId();
|
|
2193
2178
|
}
|
|
2194
2179
|
/**
|
|
2195
2180
|
* Drive a ProseMirror command against the reviewer's headless state via the
|
package/dist/ai-edits/read.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ declare const getTrackedChangeGroupIdsFromDoc: (doc: Node, id: number) => number
|
|
|
76
76
|
declare const getTrackedChangeStatsFromDoc: (doc: Node) => {
|
|
77
77
|
highestId: number;
|
|
78
78
|
present: boolean;
|
|
79
|
+
ids: readonly number[];
|
|
79
80
|
};
|
|
80
81
|
/**
|
|
81
82
|
* The comment anchors present in the body, read from the `comment` mark the
|
package/dist/ai-edits/read.js
CHANGED
|
@@ -313,11 +313,16 @@ const getTrackedChangeGroupIdsFromDoc = (doc, id) => getTrackedChangeGroupsFromP
|
|
|
313
313
|
/** @internal Read revision statistics without paying for an unrelated block projection. */
|
|
314
314
|
const getTrackedChangeStatsFromDoc = (doc) => {
|
|
315
315
|
let highestId = 0;
|
|
316
|
+
const revisionIds = /* @__PURE__ */ new Set();
|
|
316
317
|
const groups = getTrackedChangeGroupsFromProjectedDoc(doc, null);
|
|
317
|
-
for (const { ids } of groups) for (const id of ids)
|
|
318
|
+
for (const { ids } of groups) for (const id of ids) {
|
|
319
|
+
highestId = Math.max(highestId, id);
|
|
320
|
+
revisionIds.add(id);
|
|
321
|
+
}
|
|
318
322
|
return {
|
|
319
323
|
highestId,
|
|
320
|
-
present: groups.length > 0
|
|
324
|
+
present: groups.length > 0,
|
|
325
|
+
ids: [...revisionIds]
|
|
321
326
|
};
|
|
322
327
|
};
|
|
323
328
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
1
2
|
//#region src/ai-edits/table-mutation-plan.d.ts
|
|
2
3
|
type TableRectangle = {
|
|
3
4
|
left: number;
|
|
@@ -32,7 +33,7 @@ type TableMutationPlan<T> = {
|
|
|
32
33
|
executable: T[];
|
|
33
34
|
skipped: TableMutationPlanSkip[];
|
|
34
35
|
};
|
|
35
|
-
declare const planTableMutations: <T>(candidates: readonly TableMutationPlanCandidate<T>[]) => TableMutationPlan<T>;
|
|
36
|
+
declare const planTableMutations: <T>(candidates: readonly TableMutationPlanCandidate<T>[], doc: Node) => TableMutationPlan<T>;
|
|
36
37
|
declare const tableRectanglesEqual: (left: TableRectangle, right: TableRectangle) => boolean;
|
|
37
38
|
//#endregion
|
|
38
39
|
export { TableMutationPlanTarget, TableRectangle, planTableMutations, tableRectanglesEqual };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { panic } from "better-result";
|
|
2
|
+
import { TableMap } from "prosemirror-tables";
|
|
1
3
|
//#region src/ai-edits/table-mutation-plan.ts
|
|
2
|
-
const planTableMutations = (candidates) => {
|
|
4
|
+
const planTableMutations = (candidates, doc) => {
|
|
3
5
|
const tableStructureMutations = /* @__PURE__ */ new Set();
|
|
4
6
|
const mergeTables = /* @__PURE__ */ new Set();
|
|
5
7
|
const splitTables = /* @__PURE__ */ new Set();
|
|
@@ -44,6 +46,17 @@ const planTableMutations = (candidates) => {
|
|
|
44
46
|
});
|
|
45
47
|
continue;
|
|
46
48
|
}
|
|
49
|
+
if (claimedRectangles.length > 0 && mergesRemoveRow({
|
|
50
|
+
doc,
|
|
51
|
+
tablePosition: target.tablePosition,
|
|
52
|
+
rectangles: [...claimedRectangles, target.rectangle]
|
|
53
|
+
})) {
|
|
54
|
+
skipped.push({
|
|
55
|
+
id: candidate.operationId,
|
|
56
|
+
reason: "unsupportedBlock"
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
47
60
|
claimedRectangles.push(target.rectangle);
|
|
48
61
|
mergeRectanglesByTable.set(target.tablePosition, claimedRectangles);
|
|
49
62
|
executable.push(candidate.item);
|
|
@@ -84,6 +97,31 @@ const planTableMutations = (candidates) => {
|
|
|
84
97
|
skipped
|
|
85
98
|
};
|
|
86
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* Disjoint merges can still share a row's survival. Direct merging closes a
|
|
102
|
+
* row left without cells, invalidating other rectangles' original row indices;
|
|
103
|
+
* tracked merging cannot record that removal. Keep a row-removing merge alone
|
|
104
|
+
* and refuse the later merge when their combined folded cells empty a row.
|
|
105
|
+
*/
|
|
106
|
+
const mergesRemoveRow = ({ doc, tablePosition, rectangles }) => {
|
|
107
|
+
const table = doc.nodeAt(tablePosition);
|
|
108
|
+
if (table?.type.spec["tableRole"] !== "table") return panic("A planned cell merge lost its table", { position: tablePosition });
|
|
109
|
+
const map = TableMap.get(table);
|
|
110
|
+
const folded = /* @__PURE__ */ new Set();
|
|
111
|
+
for (const rectangle of rectangles) {
|
|
112
|
+
const kept = map.map[rectangle.top * map.width + rectangle.left];
|
|
113
|
+
for (const position of map.cellsInRect(rectangle)) if (position !== kept) folded.add(position);
|
|
114
|
+
}
|
|
115
|
+
let removesRow = false;
|
|
116
|
+
table.forEach((row, rowOffset) => {
|
|
117
|
+
let remaining = row.childCount;
|
|
118
|
+
row.forEach((_cell, cellOffset) => {
|
|
119
|
+
if (folded.has(rowOffset + 1 + cellOffset)) remaining--;
|
|
120
|
+
});
|
|
121
|
+
if (row.childCount > 0 && remaining === 0) removesRow = true;
|
|
122
|
+
});
|
|
123
|
+
return removesRow;
|
|
124
|
+
};
|
|
87
125
|
const tableRectanglesOverlap = (left, right) => left.left < right.right && right.left < left.right && left.top < right.bottom && right.top < left.bottom;
|
|
88
126
|
const tableRectanglesEqual = (left, right) => left.left === right.left && left.top === right.top && left.right === right.right && left.bottom === right.bottom;
|
|
89
127
|
//#endregion
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mintRevisionId, seedRevisionIdsFromDoc } from "../prosemirror/plugins/revisionIds.js";
|
|
1
2
|
import { resolveSuggestionAnchor } from "./conflict.js";
|
|
2
3
|
//#region src/ai-suggestions/apply.ts
|
|
3
4
|
function applySuggestions(options) {
|
|
@@ -39,10 +40,10 @@ function applySuggestions(options) {
|
|
|
39
40
|
stale: [...stale, ...resolved.map((r) => r.suggestion.id)]
|
|
40
41
|
};
|
|
41
42
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
-
|
|
43
|
+
seedRevisionIdsFromDoc(view.state.doc);
|
|
43
44
|
for (const { suggestion, from, to } of resolved) {
|
|
44
45
|
const attrs = {
|
|
45
|
-
revisionId:
|
|
46
|
+
revisionId: mintRevisionId(),
|
|
46
47
|
author,
|
|
47
48
|
date
|
|
48
49
|
};
|
package/dist/compare/compare.js
CHANGED
|
@@ -10,6 +10,7 @@ import { isAltChunkMarkup } from "../docx/altChunk.js";
|
|
|
10
10
|
import { isOpaqueNestedRowMarkup, opaqueRevisionCarrierName } from "../docx/opaqueCarrier.js";
|
|
11
11
|
import { sectionReferenceHistory } from "../docx/sectionReferenceHistory.js";
|
|
12
12
|
import { sameCanonicalInlinePresentation } from "../internal/compare/inline-presentation.js";
|
|
13
|
+
import { revisionIdSeedAbove } from "../prosemirror/plugins/revisionIds.js";
|
|
13
14
|
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
14
15
|
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
15
16
|
import { createContentComparisonWorkSession } from "./content.js";
|
|
@@ -386,7 +387,7 @@ const parseComparison = async (base, target, options) => {
|
|
|
386
387
|
reviewer,
|
|
387
388
|
revisionStamp: {
|
|
388
389
|
date: options.timestamp,
|
|
389
|
-
idSeed: baseProjection.revisions.
|
|
390
|
+
idSeed: revisionIdSeedAbove(baseProjection.revisions.ids)
|
|
390
391
|
},
|
|
391
392
|
packageDate,
|
|
392
393
|
pairs: comparisonPairs,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/docx/bookmarkIds.d.ts
|
|
3
|
+
declare const reserveBookmarkIds: (ids: Iterable<number>) => void;
|
|
4
|
+
declare const mintBookmarkId: () => number;
|
|
5
|
+
declare const createBookmarkIdAllocator: (existingIds: Iterable<number>) => {
|
|
6
|
+
reserve: (ids: Iterable<number>) => void;
|
|
7
|
+
next: () => number;
|
|
8
|
+
};
|
|
9
|
+
declare const reserveProseBookmarkIds: (doc: Node) => void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { createBookmarkIdAllocator, mintBookmarkId, reserveBookmarkIds, reserveProseBookmarkIds };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createNumericIdAllocator } from "./numericIdAllocator.js";
|
|
2
|
+
//#region src/docx/bookmarkIds.ts
|
|
3
|
+
let allocator;
|
|
4
|
+
const getAllocator = () => allocator ??= createNumericIdAllocator({
|
|
5
|
+
space: "bookmark",
|
|
6
|
+
firstId: 0
|
|
7
|
+
});
|
|
8
|
+
const reserveBookmarkIds = (ids) => getAllocator().reserve(ids);
|
|
9
|
+
const mintBookmarkId = () => getAllocator().next();
|
|
10
|
+
const createBookmarkIdAllocator = (existingIds) => {
|
|
11
|
+
const scopedAllocator = createNumericIdAllocator({
|
|
12
|
+
space: "bookmark",
|
|
13
|
+
firstId: 0
|
|
14
|
+
});
|
|
15
|
+
scopedAllocator.reserve(existingIds);
|
|
16
|
+
return scopedAllocator;
|
|
17
|
+
};
|
|
18
|
+
const reserveProseBookmarkIds = (doc) => {
|
|
19
|
+
const ids = [];
|
|
20
|
+
doc.descendants((node) => {
|
|
21
|
+
const bookmarks = node.attrs["bookmarks"];
|
|
22
|
+
if (Array.isArray(bookmarks)) {
|
|
23
|
+
for (const bookmark of bookmarks) if (typeof bookmark === "object" && bookmark !== null && "id" in bookmark && typeof bookmark.id === "number") ids.push(bookmark.id);
|
|
24
|
+
}
|
|
25
|
+
if (node.type.name === "bookmarkBoundary" || node.type.name === "blockBookmarkBoundary") {
|
|
26
|
+
const id = node.attrs["id"];
|
|
27
|
+
if (typeof id === "number") ids.push(id);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
reserveBookmarkIds(ids);
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { createBookmarkIdAllocator, mintBookmarkId, reserveBookmarkIds, reserveProseBookmarkIds };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { NumberingMap } from "./numberingParser.js";
|
|
3
3
|
import { ParseContext } from "./parseContext.js";
|
|
4
|
+
import { XmlElement } from "./xmlParser.js";
|
|
4
5
|
import { PreviewLedger } from "./previewBudget.js";
|
|
5
6
|
import { StyleMap } from "./styleParser.js";
|
|
6
7
|
//#region src/docx/documentParser.d.ts
|
|
@@ -33,6 +34,18 @@ declare function extractAllTemplateVariables(content: document_d_exports.BlockCo
|
|
|
33
34
|
* @returns DocumentBody with content, sections, and template variables
|
|
34
35
|
*/
|
|
35
36
|
declare function parseDocumentBody(xml: string, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, context?: ParseContext, previews?: PreviewLedger): document_d_exports.DocumentBody;
|
|
37
|
+
type ParsedDocumentBodyOptions = {
|
|
38
|
+
doc: XmlElement;
|
|
39
|
+
styles: StyleMap | null;
|
|
40
|
+
theme: document_d_exports.Theme | null;
|
|
41
|
+
numbering: NumberingMap | null;
|
|
42
|
+
rels: document_d_exports.RelationshipMap | null;
|
|
43
|
+
media: Map<string, document_d_exports.MediaFile> | null;
|
|
44
|
+
context?: ParseContext | undefined;
|
|
45
|
+
previews: PreviewLedger;
|
|
46
|
+
};
|
|
47
|
+
/** Consume the same tree the import identity pass repaired. */
|
|
48
|
+
declare const parseDocumentBodyTree: ({ doc, styles, theme, numbering, rels, media, context, previews }: ParsedDocumentBodyOptions) => document_d_exports.DocumentBody;
|
|
36
49
|
/**
|
|
37
50
|
* Get all paragraphs from document body (flattened)
|
|
38
51
|
*/
|
|
@@ -66,4 +79,4 @@ declare function getSectionCount(body: document_d_exports.DocumentBody): number;
|
|
|
66
79
|
*/
|
|
67
80
|
declare function hasTemplateVariables(body: document_d_exports.DocumentBody): boolean;
|
|
68
81
|
//#endregion
|
|
69
|
-
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getCharacterCount, getDocumentText, getParagraphCount, getSectionCount, getWordCount, hasTemplateVariables, parseDocumentBody };
|
|
82
|
+
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getCharacterCount, getDocumentText, getParagraphCount, getSectionCount, getWordCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|
|
@@ -151,9 +151,22 @@ function parseDocumentBackground(documentEl, context) {
|
|
|
151
151
|
* @returns DocumentBody with content, sections, and template variables
|
|
152
152
|
*/
|
|
153
153
|
function parseDocumentBody(xml, styles = null, theme = null, numbering = null, rels = null, media = null, context, previews = standalonePreviewLedger()) {
|
|
154
|
+
if (!xml) return { content: [] };
|
|
155
|
+
return parseDocumentBodyTree({
|
|
156
|
+
doc: parseXml(xml),
|
|
157
|
+
styles,
|
|
158
|
+
theme,
|
|
159
|
+
numbering,
|
|
160
|
+
rels,
|
|
161
|
+
media,
|
|
162
|
+
context,
|
|
163
|
+
previews
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** Consume the same tree the import identity pass repaired. */
|
|
167
|
+
const parseDocumentBodyTree = ({ doc, styles, theme, numbering, rels, media, context, previews }) => {
|
|
154
168
|
const result = { content: [] };
|
|
155
|
-
|
|
156
|
-
const documentEl = (parseXml(xml).elements ?? []).find((el) => el.type === "element" && getLocalName(el.name ?? "") === "document");
|
|
169
|
+
const documentEl = (doc.elements ?? []).find((el) => el.type === "element" && getLocalName(el.name ?? "") === "document");
|
|
157
170
|
if (!documentEl) return result;
|
|
158
171
|
const background = parseDocumentBackground(documentEl, context);
|
|
159
172
|
if (background !== void 0) result.background = background;
|
|
@@ -169,7 +182,7 @@ function parseDocumentBody(xml, styles = null, theme = null, numbering = null, r
|
|
|
169
182
|
canonicalizeLeadingBodySectionProperties(bodyEl, result);
|
|
170
183
|
result.sections = buildSections(result.content, result.finalSectionProperties);
|
|
171
184
|
return result;
|
|
172
|
-
}
|
|
185
|
+
};
|
|
173
186
|
/**
|
|
174
187
|
* Get all paragraphs from document body (flattened)
|
|
175
188
|
*/
|
|
@@ -290,4 +303,4 @@ function hasTemplateVariables(body) {
|
|
|
290
303
|
return extractAllTemplateVariables(body.content).length > 0;
|
|
291
304
|
}
|
|
292
305
|
//#endregion
|
|
293
|
-
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getCharacterCount, getDocumentText, getParagraphCount, getSectionCount, getWordCount, hasTemplateVariables, parseDocumentBody };
|
|
306
|
+
export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getCharacterCount, getDocumentText, getParagraphCount, getSectionCount, getWordCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
|
|
@@ -4,6 +4,7 @@ import { toTransitionalNamespaceUri } from "./transitionalSpelling.js";
|
|
|
4
4
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
5
5
|
import { NAMESPACES, OOXML_NAMESPACE_SCOPE, getLocalName, getNamespaceUri, parseXml } from "./xmlParser.js";
|
|
6
6
|
import { panic } from "better-result";
|
|
7
|
+
import { assertValidOoxmlNumericId, isValidOoxmlNumericId } from "@stll/docx-core";
|
|
7
8
|
//#region src/docx/drawingIdNormalization.ts
|
|
8
9
|
const GENERATED_DRAWING_ID_START = 1e5;
|
|
9
10
|
const isDetachedRawDrawing = (drawing) => drawing?.rawXml !== void 0 && isNewDataUrlDrawing(drawing);
|
|
@@ -27,7 +28,7 @@ const drawingXmlWithoutIdentity = (xml) => reassignRawDrawingId({
|
|
|
27
28
|
xml,
|
|
28
29
|
id: "0"
|
|
29
30
|
}) ?? xml;
|
|
30
|
-
const needsGeneratedId = ({ id }) => id === void 0 || id
|
|
31
|
+
const needsGeneratedId = ({ id }) => id === void 0 || !isValidOoxmlNumericId(id, "unsigned32");
|
|
31
32
|
const normalizeDrawingIds = (surfaces) => {
|
|
32
33
|
const entries = [];
|
|
33
34
|
visitDocxParagraphs(surfaces, (paragraph) => {
|
|
@@ -48,22 +49,36 @@ const normalizeDrawingIds = (surfaces) => {
|
|
|
48
49
|
}
|
|
49
50
|
});
|
|
50
51
|
});
|
|
51
|
-
const usedIds = new Set(entries.flatMap(({ drawing, rawDrawing }) => needsGeneratedId(drawing) || isDetachedRawDrawing(rawDrawing) ? [] : [drawing.id]));
|
|
52
|
+
const usedIds = new Set(entries.flatMap(({ drawing, rawDrawing }) => needsGeneratedId(drawing) || isDetachedRawDrawing(rawDrawing) ? [] : [Number(drawing.id)]));
|
|
52
53
|
let nextId = GENERATED_DRAWING_ID_START;
|
|
53
54
|
for (const { drawing, generateWhenMissing } of entries) {
|
|
54
55
|
if (!generateWhenMissing || !needsGeneratedId(drawing)) continue;
|
|
55
|
-
while (usedIds.has(
|
|
56
|
+
while (usedIds.has(nextId)) nextId += 1;
|
|
57
|
+
assertValidOoxmlNumericId({
|
|
58
|
+
value: nextId,
|
|
59
|
+
domain: "unsigned32",
|
|
60
|
+
partPath: "drawing normalization",
|
|
61
|
+
elementName: "docPr",
|
|
62
|
+
attributeName: "id"
|
|
63
|
+
});
|
|
56
64
|
drawing.id = String(nextId);
|
|
57
|
-
usedIds.add(
|
|
65
|
+
usedIds.add(nextId);
|
|
58
66
|
nextId += 1;
|
|
59
67
|
}
|
|
60
68
|
for (const { drawing, rawDrawing } of entries) {
|
|
61
69
|
if (!isDetachedRawDrawing(rawDrawing)) continue;
|
|
62
|
-
if (!needsGeneratedId(drawing) && !usedIds.has(drawing.id)) {
|
|
63
|
-
usedIds.add(drawing.id);
|
|
70
|
+
if (!needsGeneratedId(drawing) && !usedIds.has(Number(drawing.id))) {
|
|
71
|
+
usedIds.add(Number(drawing.id));
|
|
64
72
|
continue;
|
|
65
73
|
}
|
|
66
|
-
while (usedIds.has(
|
|
74
|
+
while (usedIds.has(nextId)) nextId += 1;
|
|
75
|
+
assertValidOoxmlNumericId({
|
|
76
|
+
value: nextId,
|
|
77
|
+
domain: "unsigned32",
|
|
78
|
+
partPath: "drawing normalization",
|
|
79
|
+
elementName: "docPr",
|
|
80
|
+
attributeName: "id"
|
|
81
|
+
});
|
|
67
82
|
const sourceXml = rawDrawing.rawXml;
|
|
68
83
|
if (sourceXml === void 0) panic("Detached raw drawing must retain its XML.");
|
|
69
84
|
const rawXml = reassignRawDrawingId({
|
|
@@ -73,7 +88,7 @@ const normalizeDrawingIds = (surfaces) => {
|
|
|
73
88
|
if (rawXml === null) panic("Detached raw drawing must contain wp:docPr.");
|
|
74
89
|
drawing.id = String(nextId);
|
|
75
90
|
rawDrawing.rawXml = rawXml;
|
|
76
|
-
usedIds.add(
|
|
91
|
+
usedIds.add(nextId);
|
|
77
92
|
nextId += 1;
|
|
78
93
|
}
|
|
79
94
|
};
|