@stll/folio-core 0.44.0 → 0.46.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/__fixtures__/paragraphs.js +2 -2
- package/dist/ai-edits/apply.js +59 -19
- package/dist/ai-edits/headless.js +2 -1
- package/dist/compare/compare.js +2 -5
- package/dist/compare/content-alignment.js +78 -53
- package/dist/compare/inline-atoms.js +34 -20
- package/dist/compare/scenario.js +2 -5
- package/dist/compare/types.d.ts +9 -1
- package/dist/compare/types.js +12 -1
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/content-controls/checkboxDisplay.d.ts +11 -0
- package/dist/content-controls/checkboxDisplay.js +40 -0
- package/dist/content-controls/findContentControls.js +3 -1
- package/dist/content-controls/mutateContentControls.js +21 -4
- package/dist/controller/contentControlWidgetController.d.ts +1 -0
- package/dist/controller/contentControlWidgetController.js +3 -0
- package/dist/controller/hiddenEditorApi.js +3 -1
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +6 -3
- package/dist/display-list/build/buildDisplayList.js +20 -1
- package/dist/display-list/build/imagePrimitives.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +3 -1
- package/dist/display-list/build/paragraphPrimitives.js +21 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +18 -10
- package/dist/display-list/types.d.ts +17 -0
- package/dist/document-operations.js +14 -3
- package/dist/docx/attributeRemainder.d.ts +43 -0
- package/dist/docx/attributeRemainder.js +65 -0
- package/dist/docx/blockContentParser.d.ts +1 -1
- package/dist/docx/blockContentParser.js +87 -63
- package/dist/docx/blockPlainText.d.ts +3 -4
- package/dist/docx/blockPlainText.js +1 -0
- package/dist/docx/bookmarkPlacement.js +2 -0
- package/dist/docx/borderParser.js +5 -5
- package/dist/docx/commentAnchorIndex.d.ts +30 -0
- package/dist/docx/commentAnchorIndex.js +50 -0
- package/dist/docx/commentParser.d.ts +1 -1
- package/dist/docx/commentParser.js +78 -50
- package/dist/docx/commentRangeIntegrity.d.ts +16 -1
- package/dist/docx/commentRangeIntegrity.js +42 -1
- package/dist/docx/commentRangeJoin.d.ts +9 -0
- package/dist/docx/commentRangeJoin.js +33 -0
- package/dist/docx/commentReferenceCompletion.d.ts +9 -0
- package/dist/docx/commentReferenceCompletion.js +47 -0
- package/dist/docx/commentReferenceNormalization.js +1 -0
- package/dist/docx/commentReplyMarkers.js +11 -19
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/compatibility.js +1 -0
- package/dist/docx/containerChildren.d.ts +111 -0
- package/dist/docx/containerChildren.gen.d.ts +28 -0
- package/dist/docx/containerChildren.gen.js +246 -0
- package/dist/docx/containerChildren.js +103 -0
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/documentParser.d.ts +1 -1
- package/dist/docx/documentParser.js +3 -3
- package/dist/docx/ensureParaIds.js +17 -7
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +4 -4
- package/dist/docx/fieldState.d.ts +26 -0
- package/dist/docx/fieldState.js +53 -0
- package/dist/docx/footnoteParser.d.ts +1 -1
- package/dist/docx/graphicFrameLocks.d.ts +16 -4
- package/dist/docx/graphicFrameLocks.js +19 -6
- package/dist/docx/groupDrawingParser.js +3 -3
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/headerFooterReferenceNormalization.js +1 -0
- package/dist/docx/hyperlinkParser.d.ts +27 -8
- package/dist/docx/hyperlinkParser.js +78 -20
- package/dist/docx/imageParser.d.ts +9 -1
- package/dist/docx/imageParser.js +91 -20
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +30 -6
- package/dist/docx/inlineWrapperContent.d.ts +85 -0
- package/dist/docx/inlineWrapperContent.js +84 -0
- package/dist/docx/mathToMathml.js +12 -14
- package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
- package/dist/docx/nonVisualDrawingProps.js +46 -0
- package/dist/docx/normalizeBaseDirection.js +10 -1
- package/dist/docx/paraIdAttribute.d.ts +21 -0
- package/dist/docx/paraIdAttribute.js +64 -0
- package/dist/docx/paragraphParser.d.ts +1 -1
- package/dist/docx/paragraphParser.js +277 -168
- package/dist/docx/paragraphPropertySource.js +5 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +9 -52
- package/dist/docx/paragraphTraversal.js +7 -4
- package/dist/docx/parseWarningMessage.js +2 -1
- package/dist/docx/parser.js +2 -2
- package/dist/docx/preservedRunContent.d.ts +32 -0
- package/dist/docx/preservedRunContent.js +86 -0
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/renderedPageBreakNormalization.js +2 -4
- package/dist/docx/revisionIdNormalization.js +17 -5
- package/dist/docx/rezip.js +91 -36
- package/dist/docx/runParser.d.ts +1 -1
- package/dist/docx/runParser.js +16 -12
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- package/dist/docx/sectionParser.js +8 -1
- package/dist/docx/sectionReferenceHistory.js +2 -2
- package/dist/docx/selectiveSave.js +6 -6
- package/dist/docx/selectiveXmlPatch.d.ts +46 -2
- package/dist/docx/selectiveXmlPatch.js +86 -39
- package/dist/docx/serializer/blockSdtSerializer.js +38 -26
- package/dist/docx/serializer/borderSerializer.d.ts +1 -1
- package/dist/docx/serializer/borderSerializer.js +15 -14
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +87 -92
- package/dist/docx/serializer/documentSerializer.js +8 -11
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +12 -14
- package/dist/docx/serializer/markupRangeAttributes.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +8 -8
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.js +93 -72
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +73 -44
- package/dist/docx/serializer/sectionPropertiesSerializer.js +19 -14
- package/dist/docx/serializer/settingsSerializer.js +4 -3
- package/dist/docx/serializer/stylesSerializer.js +6 -6
- package/dist/docx/serializer/tableSerializer.js +28 -17
- package/dist/docx/serializer/textFormattingSerializer.js +29 -28
- package/dist/docx/serializer/themeSerializer.js +6 -6
- package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
- package/dist/docx/serializer/xmlUtils.d.ts +1 -2
- package/dist/docx/serializer/xmlUtils.js +1 -13
- package/dist/docx/server/boundedArchive.d.ts +12 -0
- package/dist/docx/server/boundedArchive.js +20 -1
- package/dist/docx/server/createBilingualDocument.js +3 -1
- package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
- package/dist/docx/server/materializeYjsDocx.js +9 -1
- package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
- package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
- package/dist/docx/server/validateDocxConformance.js +22 -1
- package/dist/docx/shapeParser.js +12 -10
- package/dist/docx/tableParser.d.ts +1 -1
- package/dist/docx/tableParser.js +206 -85
- package/dist/docx/textBoxParser.d.ts +25 -2
- package/dist/docx/textBoxParser.js +59 -4
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +37 -26
- package/dist/docx/verbatimCapture.js +1 -1
- package/dist/docx/vmlImageParser.d.ts +17 -1
- package/dist/docx/vmlImageParser.js +20 -3
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/xmlEncoding.d.ts +5 -0
- package/dist/docx/xmlEncoding.js +12 -0
- package/dist/docx/xmlParser.d.ts +26 -2
- package/dist/docx/xmlParser.js +70 -27
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/headless-layout.js +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
- package/dist/internal/paragraphFormattingSerialization.js +3 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +2 -0
- package/dist/layout-engine/types.d.ts +23 -1
- package/dist/layout-painter/renderImage.d.ts +5 -1
- package/dist/layout-painter/renderImage.js +14 -6
- package/dist/layout-painter/renderPage.d.ts +2 -0
- package/dist/layout-painter/renderPage.js +4 -0
- package/dist/layout-painter/renderParagraph.d.ts +13 -1
- package/dist/layout-painter/renderParagraph.js +26 -6
- package/dist/managers/autoSaveCodec.d.ts +9 -1
- package/dist/managers/autoSaveCodec.js +14 -12
- package/dist/markdown/images.js +1 -4
- package/dist/markdown/renderBlock.js +1 -0
- package/dist/markdown/renderRuns.d.ts +1 -1
- package/dist/markdown/renderRuns.js +28 -5
- package/dist/markdown/renderTable.js +22 -5
- package/dist/markdown/trailers.js +4 -1
- package/dist/pdf/images.js +142 -6
- package/dist/prosemirror/attrs/index.d.ts +14 -3
- package/dist/prosemirror/attrs/index.js +224 -2
- package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
- package/dist/prosemirror/authoredTransformAttrs.js +64 -0
- package/dist/prosemirror/commands/contentControls.js +10 -2
- package/dist/prosemirror/commands/image.d.ts +9 -2
- package/dist/prosemirror/commands/image.js +43 -28
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
- package/dist/prosemirror/commentReferenceAttrs.js +33 -0
- package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
- package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +338 -99
- package/dist/prosemirror/conversion/index.d.ts +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
- package/dist/prosemirror/conversion/toProseDoc.js +551 -327
- package/dist/prosemirror/extensions/StarterKit.js +8 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
- package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
- package/dist/prosemirror/extensions/markRegistry.js +75 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +20 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +10 -2
- package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +13 -5
- package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
- package/dist/prosemirror/inlineWrapperStack.js +75 -0
- package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
- package/dist/prosemirror/pageBreakRunProjection.js +19 -9
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
- package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
- package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
- package/dist/prosemirror/replacedAnnotations.js +165 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +25 -1
- package/dist/prosemirror/schema/nodes.d.ts +139 -3
- package/dist/prosemirror/schema/nodes.js +16 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
- package/dist/prosemirror/validation.js +16 -2
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
- package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
- package/dist/prosemirror/zeroWidthAnchors.js +2 -0
- package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
- package/dist/render-dom/commentAnchorAttributes.js +54 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +3 -1
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/base64.d.ts +36 -0
- package/dist/utils/base64.js +40 -0
- package/dist/utils/clipboard.d.ts +9 -1
- package/dist/utils/clipboard.js +29 -2
- package/dist/utils/findReplace.js +1 -1
- package/dist/utils/imageLuminance.d.ts +15 -0
- package/dist/utils/imageLuminance.js +32 -0
- package/dist/utils/mergeDocumentContent.js +7 -17
- package/dist/utils/replaceText.js +1 -0
- package/dist/utils/units.d.ts +10 -1
- package/dist/utils/units.js +12 -1
- package/dist/utils/urlSecurity.d.ts +8 -2
- package/dist/utils/urlSecurity.js +21 -3
- package/package.json +2 -2
- package/dist/docx/blockRangeMarkers.d.ts +0 -36
- package/dist/docx/blockRangeMarkers.js +0 -59
- package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
- package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { patchBreaksCommentRangeBalance } from "./commentRangeIntegrity.js";
|
|
1
2
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
2
3
|
import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findAttributeByNamespaceUri, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
|
|
3
4
|
//#region src/docx/selectiveXmlPatch.ts
|
|
@@ -237,10 +238,27 @@ function buildPatchedNoteXml(originalXml, serializedXml, changedIds) {
|
|
|
237
238
|
return spliceChangedParagraphs(originalXml, serializedXml, changedIds);
|
|
238
239
|
}
|
|
239
240
|
/**
|
|
241
|
+
* Apply `splices` to `xml`, end-to-start so earlier offsets stay valid.
|
|
242
|
+
*
|
|
243
|
+
* Every selective patch is a splice, and every splice goes through here, so
|
|
244
|
+
* the refusal below is a property of the operation rather than a check each
|
|
245
|
+
* site has to remember. A patch rewrites the regions an edit touched and keeps
|
|
246
|
+
* the rest of the part byte-for-byte, so it can write half a comment range:
|
|
247
|
+
* invalid OOXML that anchors the comment to nothing. Answers null when it
|
|
248
|
+
* would, leaving the caller to rewrite a wider region — ultimately the whole
|
|
249
|
+
* part from the model, which is balanced with itself.
|
|
250
|
+
*/
|
|
251
|
+
const spliceXml = (xml, splices) => {
|
|
252
|
+
let result = xml;
|
|
253
|
+
for (const { start, end, newXml } of [...splices].toSorted((a, b) => b.start - a.start)) result = result.slice(0, start) + newXml + result.slice(end);
|
|
254
|
+
return patchBreaksCommentRangeBalance(xml, result) ? null : result;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
240
257
|
* Replace each changed paragraph in `originalXml` with its re-serialized form
|
|
241
|
-
* extracted from `serializedXml
|
|
242
|
-
*
|
|
243
|
-
*
|
|
258
|
+
* extracted from `serializedXml`. Assumes safety has already been validated.
|
|
259
|
+
* Returns null if an offset or extraction unexpectedly fails, or if
|
|
260
|
+
* {@link spliceXml} refuses the result; the caller then falls back to a full
|
|
261
|
+
* repack, whose parts are all re-serialized from one model.
|
|
244
262
|
*/
|
|
245
263
|
function spliceChangedParagraphs(originalXml, serializedXml, changedIds) {
|
|
246
264
|
const replacements = [];
|
|
@@ -255,10 +273,7 @@ function spliceChangedParagraphs(originalXml, serializedXml, changedIds) {
|
|
|
255
273
|
newXml
|
|
256
274
|
});
|
|
257
275
|
}
|
|
258
|
-
|
|
259
|
-
let result = originalXml;
|
|
260
|
-
for (const { start, end, newXml } of replacements) result = result.slice(0, start) + newXml + result.slice(end);
|
|
261
|
-
return result;
|
|
276
|
+
return spliceXml(originalXml, replacements);
|
|
262
277
|
}
|
|
263
278
|
/**
|
|
264
279
|
* Depth-count the end of the element that opens at `start` (an `<openLiteral…>`
|
|
@@ -424,11 +439,6 @@ const collectChangedNoteParaIds = (baselineXml, currentXml) => {
|
|
|
424
439
|
}
|
|
425
440
|
return changed;
|
|
426
441
|
};
|
|
427
|
-
const replaceRanges = (xml, replacements) => {
|
|
428
|
-
let result = xml;
|
|
429
|
-
for (const { start, end, newXml } of [...replacements].toSorted((a, b) => b.start - a.start)) result = result.slice(0, start) + newXml + result.slice(end);
|
|
430
|
-
return result;
|
|
431
|
-
};
|
|
432
442
|
/**
|
|
433
443
|
* Patch an existing note part from its model serialization.
|
|
434
444
|
*
|
|
@@ -440,56 +450,79 @@ const replaceRanges = (xml, replacements) => {
|
|
|
440
450
|
* notes, and unaffected equal-shape paragraphs remain byte-exact.
|
|
441
451
|
* `replacementXml` also supplies synthesized automatic note-reference marks,
|
|
442
452
|
* which the parsed model intentionally omits.
|
|
453
|
+
*
|
|
454
|
+
* A comment can be anchored on a note's own text, so its range spans that
|
|
455
|
+
* note's paragraphs and an edit inside the span moves a range half from one
|
|
456
|
+
* paragraph to another. Replacing only the dirty paragraph then drops the half
|
|
457
|
+
* it held and leaves the other standing, so {@link spliceXml} refuses the
|
|
458
|
+
* result and the changed notes are rewritten whole instead — model content on
|
|
459
|
+
* both sides of the range, with every other note still byte-exact.
|
|
443
460
|
*/
|
|
444
461
|
function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, replacementXml, elementName, changedParaIds }) {
|
|
445
462
|
const currentElements = collectNoteElementSyntax(serializedXml, elementName);
|
|
446
463
|
const originalElements = collectNoteElementSyntax(originalXml, elementName);
|
|
447
464
|
const replacementElements = collectNoteElementSyntax(replacementXml, elementName);
|
|
448
465
|
const replacementXmlnsDeclarations = collectXmlnsFromOpeningTag(replacementXml);
|
|
449
|
-
const
|
|
466
|
+
const paragraphSplices = [];
|
|
467
|
+
/** The same edits at note granularity, should the paragraph splices be refused. */
|
|
468
|
+
const noteSplices = [];
|
|
450
469
|
const serializedParaIds = collectParaIds(serializedXml);
|
|
451
470
|
const effectiveChangedParaIds = changedParaIds ?? collectChangedNoteParaIds(baselineXml, serializedXml);
|
|
452
471
|
const unroutedChangedParaIds = new Set([...effectiveChangedParaIds].filter((paraId) => serializedParaIds.has(paraId)));
|
|
453
472
|
for (const [id, currentSyntaxEntries] of currentElements) {
|
|
454
473
|
const originalSyntaxEntries = originalElements.get(id);
|
|
455
474
|
const replacementSyntaxEntries = replacementElements.get(id);
|
|
456
|
-
if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length !== 1 || replacementSyntaxEntries?.length !== 1) return
|
|
475
|
+
if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length !== 1 || replacementSyntaxEntries?.length !== 1) return {
|
|
476
|
+
type: "refused",
|
|
477
|
+
reason: "unroutable-paragraph"
|
|
478
|
+
};
|
|
457
479
|
const currentSyntax = currentSyntaxEntries[0];
|
|
458
480
|
const originalSyntax = originalSyntaxEntries[0];
|
|
459
481
|
const replacementSyntax = replacementSyntaxEntries[0];
|
|
460
|
-
if (!currentSyntax || !originalSyntax || !replacementSyntax) return
|
|
482
|
+
if (!currentSyntax || !originalSyntax || !replacementSyntax) return {
|
|
483
|
+
type: "refused",
|
|
484
|
+
reason: "unroutable-paragraph"
|
|
485
|
+
};
|
|
461
486
|
const currentNote = extractNoteElement(serializedXml, currentSyntax, id);
|
|
462
487
|
const originalOffsets = findNoteElement(originalXml, originalSyntax, id);
|
|
463
488
|
const replacementNote = extractNoteElement(replacementXml, replacementSyntax, id);
|
|
464
|
-
if (!currentNote || !originalOffsets || !replacementNote) return
|
|
489
|
+
if (!currentNote || !originalOffsets || !replacementNote) return {
|
|
490
|
+
type: "refused",
|
|
491
|
+
reason: "unroutable-paragraph"
|
|
492
|
+
};
|
|
465
493
|
const originalNote = originalXml.slice(originalOffsets.start, originalOffsets.end);
|
|
466
494
|
const currentParagraphs = paragraphRanges(currentNote, currentSyntax.elementPrefix);
|
|
467
495
|
const originalParagraphs = paragraphRanges(originalNote, originalSyntax.elementPrefix);
|
|
468
496
|
const replacementParagraphs = paragraphRanges(replacementNote, replacementSyntax.elementPrefix);
|
|
469
497
|
const noteChangedParaIds = [...collectParaIds(currentNote).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
|
|
470
498
|
if (noteChangedParaIds.length === 0) continue;
|
|
499
|
+
const wholeNoteSplice = {
|
|
500
|
+
start: originalOffsets.start,
|
|
501
|
+
end: originalOffsets.end,
|
|
502
|
+
newXml: rewriteWordprocessingPrefixes(replacementNote, {
|
|
503
|
+
source: replacementSyntax,
|
|
504
|
+
target: originalSyntax,
|
|
505
|
+
sourceXmlnsDeclarations: replacementXmlnsDeclarations
|
|
506
|
+
})
|
|
507
|
+
};
|
|
508
|
+
noteSplices.push(wholeNoteSplice);
|
|
471
509
|
if (currentParagraphs.length !== originalParagraphs.length || currentParagraphs.length !== replacementParagraphs.length) {
|
|
472
510
|
for (const paraId of noteChangedParaIds) unroutedChangedParaIds.delete(paraId);
|
|
473
|
-
|
|
474
|
-
start: originalOffsets.start,
|
|
475
|
-
end: originalOffsets.end,
|
|
476
|
-
newXml: rewriteWordprocessingPrefixes(replacementNote, {
|
|
477
|
-
source: replacementSyntax,
|
|
478
|
-
target: originalSyntax,
|
|
479
|
-
sourceXmlnsDeclarations: replacementXmlnsDeclarations
|
|
480
|
-
})
|
|
481
|
-
});
|
|
511
|
+
paragraphSplices.push(wholeNoteSplice);
|
|
482
512
|
continue;
|
|
483
513
|
}
|
|
484
514
|
for (let index = 0; index < currentParagraphs.length; index++) {
|
|
485
515
|
const currentRange = currentParagraphs[index];
|
|
486
516
|
const originalRange = originalParagraphs[index];
|
|
487
517
|
const replacementRange = replacementParagraphs[index];
|
|
488
|
-
if (!currentRange || !originalRange || !replacementRange) return
|
|
518
|
+
if (!currentRange || !originalRange || !replacementRange) return {
|
|
519
|
+
type: "refused",
|
|
520
|
+
reason: "unroutable-paragraph"
|
|
521
|
+
};
|
|
489
522
|
const routedIds = [...collectParaIds(currentNote.slice(currentRange.start, currentRange.end)).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
|
|
490
523
|
if (routedIds.length === 0) continue;
|
|
491
524
|
for (const paraId of routedIds) unroutedChangedParaIds.delete(paraId);
|
|
492
|
-
|
|
525
|
+
paragraphSplices.push({
|
|
493
526
|
start: originalOffsets.start + originalRange.start,
|
|
494
527
|
end: originalOffsets.start + originalRange.end,
|
|
495
528
|
newXml: rewriteWordprocessingPrefixes(replacementNote.slice(replacementRange.start, replacementRange.end), {
|
|
@@ -500,8 +533,23 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
|
|
|
500
533
|
});
|
|
501
534
|
}
|
|
502
535
|
}
|
|
503
|
-
if (unroutedChangedParaIds.size > 0) return
|
|
504
|
-
|
|
536
|
+
if (unroutedChangedParaIds.size > 0) return {
|
|
537
|
+
type: "refused",
|
|
538
|
+
reason: "unroutable-paragraph"
|
|
539
|
+
};
|
|
540
|
+
const patched = spliceXml(originalXml, paragraphSplices);
|
|
541
|
+
if (patched !== null) return {
|
|
542
|
+
type: "patched",
|
|
543
|
+
xml: patched
|
|
544
|
+
};
|
|
545
|
+
const wholeNotes = spliceXml(originalXml, noteSplices);
|
|
546
|
+
return wholeNotes === null ? {
|
|
547
|
+
type: "refused",
|
|
548
|
+
reason: "comment-range-balance"
|
|
549
|
+
} : {
|
|
550
|
+
type: "patched",
|
|
551
|
+
xml: wholeNotes
|
|
552
|
+
};
|
|
505
553
|
}
|
|
506
554
|
/**
|
|
507
555
|
* The full range of the first `<openLiteral …>…</closeTag>` element, or null.
|
|
@@ -695,17 +743,19 @@ function restoreLevelNumFmts(originalDefXml, currentDefXml) {
|
|
|
695
743
|
...collectXmlnsFromOpeningTag(origLevel)
|
|
696
744
|
});
|
|
697
745
|
else replacement = reconstructCustomNumFmt(currentWidth);
|
|
698
|
-
const restoredLevel = curLevel
|
|
746
|
+
const restoredLevel = spliceXml(curLevel, [{
|
|
747
|
+
start: synthetic.index,
|
|
748
|
+
end: synthetic.index + synthetic[0].length,
|
|
749
|
+
newXml: replacement
|
|
750
|
+
}]);
|
|
751
|
+
if (restoredLevel === null) continue;
|
|
699
752
|
replacements.push({
|
|
700
753
|
start: curOffsets.start,
|
|
701
754
|
end: curOffsets.end,
|
|
702
755
|
newXml: restoredLevel
|
|
703
756
|
});
|
|
704
757
|
}
|
|
705
|
-
|
|
706
|
-
let result = currentDefXml;
|
|
707
|
-
for (const { start, end, newXml } of replacements) result = result.slice(0, start) + newXml + result.slice(end);
|
|
708
|
-
return result;
|
|
758
|
+
return spliceXml(currentDefXml, replacements) ?? currentDefXml;
|
|
709
759
|
}
|
|
710
760
|
/**
|
|
711
761
|
* Build a patched `word/numbering.xml` by splicing the changed `w:abstractNum` /
|
|
@@ -733,10 +783,7 @@ function buildPatchedNumberingXml(originalXml, currentXml, changed) {
|
|
|
733
783
|
return true;
|
|
734
784
|
};
|
|
735
785
|
if (!collect("abstractNum", changed.abstractNums) || !collect("num", changed.nums)) return null;
|
|
736
|
-
|
|
737
|
-
let result = originalXml;
|
|
738
|
-
for (const { start, end, newXml } of replacements) result = result.slice(0, start) + newXml + result.slice(end);
|
|
739
|
-
return result;
|
|
786
|
+
return spliceXml(originalXml, replacements);
|
|
740
787
|
}
|
|
741
788
|
/**
|
|
742
789
|
* Numbering definitions present in `currentXml` (the model's serialization)
|
|
@@ -872,4 +919,4 @@ function escapeRegExp(str) {
|
|
|
872
919
|
return str.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
873
920
|
}
|
|
874
921
|
//#endregion
|
|
875
|
-
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, validatePatchSafety };
|
|
922
|
+
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, spliceXml, validatePatchSafety };
|
|
@@ -1,24 +1,39 @@
|
|
|
1
1
|
import { reconcileRawSdtPr } from "../sdtPropertiesPatch.js";
|
|
2
2
|
import { isSingleWellFormedElement } from "./xmlUtils.js";
|
|
3
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
3
4
|
//#region src/docx/serializer/blockSdtSerializer.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Block-level SDT serializer.
|
|
7
|
+
*
|
|
8
|
+
* Replays `<w:sdtPr>` (and `<w:sdtEndPr>` when present) verbatim from the
|
|
9
|
+
* `rawPropertiesXml` / `rawEndPropertiesXml` strings captured by the parser.
|
|
10
|
+
* That keeps OOXML element order intact (`CT_SdtPr` is an `xsd:sequence`,
|
|
11
|
+
* ECMA-376 §17.5.2) and round-trips unmodeled features — `w:dataBinding`,
|
|
12
|
+
* `w15:repeatingSection`, `@lastValue`, custom XML mappings — without us
|
|
13
|
+
* having to enumerate them.
|
|
14
|
+
*
|
|
15
|
+
* If a `BlockSdt` was constructed programmatically (no parsed snapshot to
|
|
16
|
+
* replay), fall back to a minimal projection from the modeled fields so the
|
|
17
|
+
* result is still a valid `<w:sdt>`.
|
|
18
|
+
*
|
|
19
|
+
* Sharing the helper between the document body and the header/footer
|
|
20
|
+
* serializers keeps body↔HF parity in one place.
|
|
21
|
+
*/
|
|
7
22
|
function serializeFallbackSdtPr(props) {
|
|
8
23
|
const parts = [];
|
|
9
24
|
if (props.id !== void 0) parts.push(`<w:id w:val="${props.id}"/>`);
|
|
10
|
-
if (props.alias) parts.push(`<w:alias w:val="${
|
|
11
|
-
if (props.tag) parts.push(`<w:tag w:val="${
|
|
25
|
+
if (props.alias) parts.push(`<w:alias w:val="${escapeXmlAttribute(props.alias)}"/>`);
|
|
26
|
+
if (props.tag) parts.push(`<w:tag w:val="${escapeXmlAttribute(props.tag)}"/>`);
|
|
12
27
|
if (props.lock) parts.push(`<w:lock w:val="${props.lock}"/>`);
|
|
13
|
-
if (props.placeholder) parts.push(`<w:placeholder><w:docPart w:val="${
|
|
28
|
+
if (props.placeholder) parts.push(`<w:placeholder><w:docPart w:val="${escapeXmlAttribute(props.placeholder)}"/></w:placeholder>`);
|
|
14
29
|
if (props.showingPlaceholder) parts.push("<w:showingPlcHdr/>");
|
|
15
30
|
switch (props.sdtType) {
|
|
16
31
|
case "plainText":
|
|
17
32
|
parts.push("<w:text/>");
|
|
18
33
|
break;
|
|
19
34
|
case "date": {
|
|
20
|
-
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${
|
|
21
|
-
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${
|
|
35
|
+
const fullDateAttr = props.dateValueISO ? ` w:fullDate="${escapeXmlAttribute(props.dateValueISO)}"` : "";
|
|
36
|
+
const formatChild = props.dateFormat ? `<w:dateFormat w:val="${escapeXmlAttribute(props.dateFormat)}"/>` : "";
|
|
22
37
|
if (fullDateAttr || formatChild) parts.push(`<w:date${fullDateAttr}>${formatChild}</w:date>`);
|
|
23
38
|
else parts.push("<w:date/>");
|
|
24
39
|
break;
|
|
@@ -26,7 +41,7 @@ function serializeFallbackSdtPr(props) {
|
|
|
26
41
|
case "dropdown":
|
|
27
42
|
case "comboBox": {
|
|
28
43
|
const tag = props.sdtType === "dropdown" ? "w:dropDownList" : "w:comboBox";
|
|
29
|
-
const items = (props.listItems ?? []).map((item) => `<w:listItem w:displayText="${
|
|
44
|
+
const items = (props.listItems ?? []).map((item) => `<w:listItem w:displayText="${escapeXmlAttribute(item.displayText)}" w:value="${escapeXmlAttribute(item.value)}"/>`).join("");
|
|
30
45
|
parts.push(`<${tag}>${items}</${tag}>`);
|
|
31
46
|
break;
|
|
32
47
|
}
|
|
@@ -48,25 +63,22 @@ function serializeFallbackSdtPr(props) {
|
|
|
48
63
|
}
|
|
49
64
|
return `<w:sdtPr>${parts.join("")}</w:sdtPr>`;
|
|
50
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* `properties.dropdownLastValue` is the only record of what was selected.
|
|
68
|
+
*
|
|
69
|
+
* The XSD default of `@w:lastValue` is the empty string, so "never selected",
|
|
70
|
+
* "cleared" and "selected" are three distinguishable states, and the body's
|
|
71
|
+
* display text is evidence for none of them: it is equally the placeholder of
|
|
72
|
+
* a dropdown nobody has touched, and a displayText shared by two list items
|
|
73
|
+
* picks the wrong sibling. The parser records the authored `@w:lastValue` and
|
|
74
|
+
* `setContentControlValue` records a pick; what neither wrote is no selection.
|
|
75
|
+
*
|
|
76
|
+
* `""` is a value a producer can author (`<w:listItem w:value=""/>`), so
|
|
77
|
+
* presence is the test, not truthiness.
|
|
78
|
+
*/
|
|
51
79
|
function extractDropdownLastValue(blockSdt) {
|
|
52
80
|
if (blockSdt.properties.sdtType !== "dropdown" && blockSdt.properties.sdtType !== "comboBox") return;
|
|
53
|
-
|
|
54
|
-
if (modeled !== void 0) return modeled;
|
|
55
|
-
if (blockSdt.properties.rawPropertiesXml !== void 0) return;
|
|
56
|
-
const firstBlock = blockSdt.content[0];
|
|
57
|
-
if (!firstBlock || firstBlock.type !== "paragraph") return;
|
|
58
|
-
const parts = [];
|
|
59
|
-
for (const child of firstBlock.content) if (child.type === "run") {
|
|
60
|
-
for (const item of child.content) if (item.type === "text") parts.push(item.text);
|
|
61
|
-
}
|
|
62
|
-
const text = parts.join("");
|
|
63
|
-
if (text.length === 0) return;
|
|
64
|
-
const items = blockSdt.properties.listItems;
|
|
65
|
-
if (items) {
|
|
66
|
-
const match = items.find((item) => item.displayText === text);
|
|
67
|
-
if (match) return match.value;
|
|
68
|
-
}
|
|
69
|
-
return text;
|
|
81
|
+
return blockSdt.properties.dropdownLastValue;
|
|
70
82
|
}
|
|
71
83
|
function extractDateFullDate(blockSdt) {
|
|
72
84
|
if (blockSdt.properties.sdtType !== "date") return;
|
|
@@ -18,7 +18,7 @@ type ExhaustiveBorder = document_d_exports.ExhaustiveFields<document_d_exports.B
|
|
|
18
18
|
*
|
|
19
19
|
* `style` and the color values come straight from the parsed DOCX (the parser
|
|
20
20
|
* casts `w:val`/`w:color` without validating the enum), so they are
|
|
21
|
-
* untrusted and are `
|
|
21
|
+
* untrusted and are `escapeXmlAttribute`'d before re-entering XML attributes; for valid
|
|
22
22
|
* documents these are enum/hex values, so escaping is a no-op.
|
|
23
23
|
*/
|
|
24
24
|
declare function serializeBorder(input: ExhaustiveBorder | undefined, elementName: string): string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { intAttr } from "./xmlUtils.js";
|
|
2
|
+
import { escapeXmlAttribute } from "@stll/docx-core";
|
|
2
3
|
//#region src/docx/serializer/borderSerializer.ts
|
|
3
4
|
/**
|
|
4
5
|
* Serialize a single border element (`<w:top .../>`, `<w:left .../>`, ...).
|
|
@@ -16,30 +17,30 @@ import { escapeXml, intAttr } from "./xmlUtils.js";
|
|
|
16
17
|
*
|
|
17
18
|
* `style` and the color values come straight from the parsed DOCX (the parser
|
|
18
19
|
* casts `w:val`/`w:color` without validating the enum), so they are
|
|
19
|
-
* untrusted and are `
|
|
20
|
+
* untrusted and are `escapeXmlAttribute`'d before re-entering XML attributes; for valid
|
|
20
21
|
* documents these are enum/hex values, so escaping is a no-op.
|
|
21
22
|
*/
|
|
22
23
|
function serializeBorder(input, elementName) {
|
|
23
24
|
if (!input) return "";
|
|
24
25
|
const { style, color, size, space, shadow, frame, artRelationshipId, topLeftArtRelationshipId, topRightArtRelationshipId, bottomLeftArtRelationshipId, bottomRightArtRelationshipId } = input;
|
|
25
|
-
const attrs = [`w:val="${
|
|
26
|
+
const attrs = [`w:val="${escapeXmlAttribute(style)}"`];
|
|
26
27
|
if (size !== void 0) attrs.push(`w:sz="${intAttr(size)}"`);
|
|
27
28
|
if (space !== void 0) attrs.push(`w:space="${intAttr(space)}"`);
|
|
28
29
|
if (color) {
|
|
29
30
|
const { auto, rgb, themeColor, themeTint, themeShade } = color;
|
|
30
31
|
if (auto) attrs.push("w:color=\"auto\"");
|
|
31
|
-
else if (rgb) attrs.push(`w:color="${
|
|
32
|
-
if (themeColor) attrs.push(`w:themeColor="${
|
|
33
|
-
if (themeTint) attrs.push(`w:themeTint="${
|
|
34
|
-
if (themeShade) attrs.push(`w:themeShade="${
|
|
32
|
+
else if (rgb) attrs.push(`w:color="${escapeXmlAttribute(rgb)}"`);
|
|
33
|
+
if (themeColor) attrs.push(`w:themeColor="${escapeXmlAttribute(themeColor)}"`);
|
|
34
|
+
if (themeTint) attrs.push(`w:themeTint="${escapeXmlAttribute(themeTint)}"`);
|
|
35
|
+
if (themeShade) attrs.push(`w:themeShade="${escapeXmlAttribute(themeShade)}"`);
|
|
35
36
|
}
|
|
36
|
-
if (shadow) attrs.push(
|
|
37
|
-
if (frame) attrs.push(
|
|
38
|
-
if (artRelationshipId) attrs.push(`
|
|
39
|
-
if (topLeftArtRelationshipId) attrs.push(`
|
|
40
|
-
if (topRightArtRelationshipId) attrs.push(`
|
|
41
|
-
if (bottomLeftArtRelationshipId) attrs.push(`
|
|
42
|
-
if (bottomRightArtRelationshipId) attrs.push(`
|
|
37
|
+
if (shadow !== void 0) attrs.push(`w:shadow="${shadow ? "1" : "0"}"`);
|
|
38
|
+
if (frame !== void 0) attrs.push(`w:frame="${frame ? "1" : "0"}"`);
|
|
39
|
+
if (artRelationshipId) attrs.push(`r:id="${escapeXmlAttribute(artRelationshipId)}"`);
|
|
40
|
+
if (topLeftArtRelationshipId) attrs.push(`r:topLeft="${escapeXmlAttribute(topLeftArtRelationshipId)}"`);
|
|
41
|
+
if (topRightArtRelationshipId) attrs.push(`r:topRight="${escapeXmlAttribute(topRightArtRelationshipId)}"`);
|
|
42
|
+
if (bottomLeftArtRelationshipId) attrs.push(`r:bottomLeft="${escapeXmlAttribute(bottomLeftArtRelationshipId)}"`);
|
|
43
|
+
if (bottomRightArtRelationshipId) attrs.push(`r:bottomRight="${escapeXmlAttribute(bottomRightArtRelationshipId)}"`);
|
|
43
44
|
return `<w:${elementName} ${attrs.join(" ")}/>`;
|
|
44
45
|
}
|
|
45
46
|
//#endregion
|
|
@@ -1,30 +1,55 @@
|
|
|
1
1
|
import { Comment } from "../../types/content.js";
|
|
2
2
|
//#region src/docx/serializer/commentSerializer.d.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* What every comment part is written from: one ordered list of comment ids, and
|
|
5
|
+
* the facts each id carries.
|
|
6
|
+
*
|
|
7
|
+
* `word/comments.xml` and `word/commentsExtended.xml` describe the same
|
|
8
|
+
* comments and have to agree about which is which. They agreed by each walking
|
|
9
|
+
* the `Comment[]` they were handed and pairing entries up by position, so any
|
|
10
|
+
* disagreement about order — one part reordered, the other not — silently moved
|
|
11
|
+
* a comment's thread link, resolved state and paraId onto a different comment.
|
|
12
|
+
* Positions cannot disagree if neither part has one: both walk `order` and look
|
|
13
|
+
* every fact up by `w:id`, the only identity the package itself has.
|
|
14
|
+
*/
|
|
15
|
+
type CommentPartPlan = {
|
|
16
|
+
/** The `w:id`s to write, in order, each exactly once. */
|
|
17
|
+
readonly order: readonly number[];
|
|
18
|
+
readonly byId: ReadonlyMap<number, Comment>;
|
|
19
|
+
/** The `w14:paraId` each comment is threaded by, minted where the model had none. */
|
|
20
|
+
readonly threadParaIdById: ReadonlyMap<number, string>;
|
|
21
|
+
/** The comments needing a `commentsExtended.xml` entry: replies, their parents, resolved comments. */
|
|
22
|
+
readonly threadedIds: ReadonlySet<number>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Plan both comment parts from the model, in the model's own order.
|
|
26
|
+
*
|
|
27
|
+
* The order is the document's: the order `word/comments.xml` listed the
|
|
28
|
+
* comments in is the order it is written back in, so a save neither reshuffles
|
|
29
|
+
* a reviewer's threads nor hands the next parse a different `comments[]` than
|
|
30
|
+
* the one it read.
|
|
31
|
+
*
|
|
32
|
+
* Planning also mints the `w14:paraId` a thread needs when the model has none —
|
|
33
|
+
* a comment written in the editor has no Word-authored id, and without one the
|
|
34
|
+
* `commentsExtended.xml` link would be dropped. Minting is deterministic
|
|
35
|
+
* (comment id and text derived) so repeated saves mint the same id, and it
|
|
36
|
+
* happens here rather than at each call site because both parts must see it.
|
|
37
|
+
*/
|
|
38
|
+
declare const planCommentParts: (comments: readonly Comment[]) => CommentPartPlan;
|
|
39
|
+
/**
|
|
40
|
+
* Serialize a comment plan to comments.xml content. Returns a valid empty
|
|
41
|
+
* `<w:comments/>` document for an empty plan so callers can overwrite an
|
|
6
42
|
* existing `word/comments.xml` part when the editor has removed the last
|
|
7
43
|
* comment — leaving the previous file in place would otherwise re-emit
|
|
8
44
|
* the orphaned comment threads on every save.
|
|
9
45
|
*/
|
|
10
|
-
declare function serializeComments(
|
|
11
|
-
/**
|
|
12
|
-
* Assign a deterministic `w14:paraId` to the LAST paragraph of every comment
|
|
13
|
-
* that needs a commentsExtended entry (a reply, a reply's parent, or a resolved
|
|
14
|
-
* comment) but has none. A document authored or loaded without comment paraIds
|
|
15
|
-
* would otherwise have no stable key to thread through commentsExtended.xml, and
|
|
16
|
-
* the thread link would be silently dropped. Word-authored ids are preserved;
|
|
17
|
-
* only threaded, id-less paragraphs are filled. Deterministic (content- and
|
|
18
|
-
* id-derived) so repeated saves mint the SAME id. MUST run before serializing
|
|
19
|
-
* BOTH comments.xml and commentsExtended.xml so the two reference the same id.
|
|
20
|
-
*/
|
|
21
|
-
declare function ensureThreadedCommentParaIds(comments: readonly Comment[]): void;
|
|
46
|
+
declare function serializeComments({ order, byId }: CommentPartPlan, sourceBindings?: ReadonlyMap<string, string>): string;
|
|
22
47
|
/**
|
|
23
48
|
* Serialize `commentsExtended.xml` (`w15:commentsEx`) for reply threading and
|
|
24
49
|
* resolved state, or `null` when no comment needs an entry (see
|
|
25
50
|
* {@link buildCommentExtendedEntries}). This is the part that makes Word render
|
|
26
51
|
* a comment as a REPLY rather than a separate top-level thread.
|
|
27
52
|
*/
|
|
28
|
-
declare function serializeCommentsExtended(
|
|
53
|
+
declare function serializeCommentsExtended(plan: CommentPartPlan): string | null;
|
|
29
54
|
//#endregion
|
|
30
|
-
export {
|
|
55
|
+
export { CommentPartPlan, planCommentParts, serializeComments, serializeCommentsExtended };
|