@stll/folio-core 0.59.0 → 0.61.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/README.md +23 -1
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +57 -63
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +4 -2
- package/dist/compat/eigenpal.js +2 -2
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/canonicalSession.js +3 -2
- package/dist/controller/canonicalStructure.js +10 -7
- package/dist/controller/fontReadiness.js +1 -1
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +19 -4
- package/dist/controller/layoutSession.d.ts +4 -1
- package/dist/display-list/build/fontTable.js +7 -5
- package/dist/display-list/build/furniture.d.ts +1 -0
- package/dist/display-list/build/furniture.js +1 -0
- package/dist/display-list/build/imagePrimitives.js +3 -6
- package/dist/display-list/build/watermarkPrimitives.d.ts +2 -1
- package/dist/display-list/build/watermarkPrimitives.js +9 -3
- package/dist/display-list/editor/pageRenderer.js +1 -0
- package/dist/docx/bookmarkIdNormalization.d.ts +10 -0
- package/dist/docx/bookmarkIdNormalization.js +100 -0
- package/dist/docx/bookmarkParser.js +4 -2
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +6 -1
- package/dist/docx/importedIdCollisions.d.ts +136 -0
- package/dist/docx/importedIdCollisions.js +215 -0
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/numberingParser.js +2 -2
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/paragraphPropertySource.js +1 -2
- package/dist/docx/parseContext.d.ts +2 -1
- package/dist/docx/parseContext.js +1 -0
- package/dist/docx/parseWarningMessage.js +1 -0
- package/dist/docx/parser.js +10 -5
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/relsParser.js +1 -1
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +90 -90
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectedXmlIdentityScan.d.ts +10 -0
- package/dist/docx/selectedXmlIdentityScan.js +36 -0
- package/dist/docx/selectiveSave.js +19 -17
- package/dist/docx/selectiveXmlPatch.js +44 -52
- package/dist/docx/serializer/commentSerializer.d.ts +3 -1
- package/dist/docx/serializer/commentSerializer.js +11 -2
- package/dist/docx/serializer/runSerializer.js +5 -4
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +1 -3
- package/dist/docx/serializer/trackedChangeAttributes.js +1 -11
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/embeddedFontFingerprint.d.ts +5 -0
- package/dist/fonts/embeddedFontFingerprint.js +18 -0
- package/dist/fonts/embeddedFonts.d.ts +1 -1
- package/dist/fonts/embeddedFonts.js +1 -1
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/headless-layout.js +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -2
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +5 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +16 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +6 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +13 -14
- package/dist/layout-bridge/convert/paragraphConversion.js +8 -1
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +25 -1
- package/dist/layout-engine/measure/cache.js +39 -7
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/layout-engine/measure/lineTypography.js +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +1 -0
- package/dist/layout-engine/measure/measureContainer.js +3 -4
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -9
- package/dist/layout-engine/measure/measureHelpers.js +21 -18
- package/dist/layout-engine/measure/measureParagraph.js +2 -0
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -0
- package/dist/layout-engine/types.d.ts +4 -1
- package/dist/layout-painter/renderPage.d.ts +1 -0
- package/dist/layout-painter/renderPage.js +11 -3
- package/dist/layout-painter/renderParagraph.js +13 -3
- package/dist/layout-painter/renderWatermark.d.ts +1 -0
- package/dist/layout-painter/renderWatermark.js +7 -3
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/editorShortcuts.d.ts +2 -1
- package/dist/managers/editorShortcuts.js +2 -1
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +3 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/canonicalSelectionRange.d.ts +4 -1
- package/dist/prosemirror/canonicalSelectionRange.js +9 -3
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +9 -4
- package/dist/prosemirror/conversion/toProseDoc.js +4 -4
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +39 -3
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ImageDragExtension.js +4 -3
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +6 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +11 -3
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +1 -1
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +11 -1
- package/dist/prosemirror/findReplaceSelection.js +15 -1
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/pluginKeyOwner.d.ts +13 -0
- package/dist/prosemirror/pluginKeyOwner.js +35 -0
- package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiCitationDecorations.js +16 -3
- package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiSuggestionDecorations.js +18 -3
- package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +11 -11
- package/dist/prosemirror/plugins/anonymizationDecorations.js +23 -13
- package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +8 -2
- package/dist/prosemirror/plugins/autocompleteSuggestion.js +27 -5
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +8 -2
- package/dist/prosemirror/plugins/contentControlWidgets.js +4 -3
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +6 -5
- package/dist/prosemirror/plugins/createDocScanPlugin.js +1 -1
- package/dist/prosemirror/plugins/documentNumbering.js +5 -3
- package/dist/prosemirror/plugins/documentStyleState.d.ts +8 -2
- package/dist/prosemirror/plugins/documentStyleState.js +2 -2
- package/dist/prosemirror/plugins/documentStyles.js +1 -1
- package/dist/prosemirror/plugins/selectionTracker.d.ts +8 -2
- package/dist/prosemirror/plugins/selectionTracker.js +4 -3
- package/dist/prosemirror/plugins/suggestionMode.d.ts +8 -2
- package/dist/prosemirror/plugins/suggestionMode.js +4 -3
- package/dist/prosemirror/plugins/templateDirectives.d.ts +8 -2
- package/dist/prosemirror/plugins/templateDirectives.js +19 -4
- package/dist/prosemirror/plugins/templatePreviewValues.d.ts +8 -2
- package/dist/prosemirror/plugins/templatePreviewValues.js +27 -5
- package/dist/prosemirror/plugins/templateSlashMenu.d.ts +8 -2
- package/dist/prosemirror/plugins/templateSlashMenu.js +23 -5
- package/dist/prosemirror/plugins/transactionInvariants.js +29 -4
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findReplace.d.ts +5 -0
- package/dist/utils/fontLoader.d.ts +2 -2
- package/dist/utils/fontLoader.js +2 -7
- package/dist/utils/fontResolver.d.ts +7 -3
- package/dist/utils/fontResolver.js +6 -8
- package/dist/utils/replaceText.js +21 -4
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +73 -31
package/README.md
CHANGED
|
@@ -20,9 +20,18 @@ Part of [stella](https://github.com/stella/stella), an open-source legal workspa
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
|
-
bun add @stll/folio-core
|
|
23
|
+
bun add @stll/folio-core \
|
|
24
|
+
prosemirror-commands@^1.7.1 prosemirror-dropcursor@^1.8.2 prosemirror-gapcursor@^1.4.1 prosemirror-history@^1.5.0 \
|
|
25
|
+
prosemirror-inputrules@^1.5.0 prosemirror-keymap@^1.2.3 prosemirror-model@^1.25.9 prosemirror-state@^1.4.4 \
|
|
26
|
+
prosemirror-tables@^1.8.5 prosemirror-transform@^1.12.0 prosemirror-view@^1.42.6 y-prosemirror@^1.3.7 \
|
|
27
|
+
y-protocols@^1.0.6 yjs@^13.6.31
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
ProseMirror and Yjs packages are required peers, including the collaboration
|
|
31
|
+
packages `y-prosemirror` and `y-protocols`. Install one compatible copy of each in the host
|
|
32
|
+
application so schemas, plugins, editor states, and collaborative documents share
|
|
33
|
+
the same runtime. The command above includes the complete editor peer closure.
|
|
34
|
+
|
|
26
35
|
## Exports
|
|
27
36
|
|
|
28
37
|
| Import | What it is |
|
|
@@ -33,6 +42,19 @@ bun add @stll/folio-core
|
|
|
33
42
|
| `@stll/folio-core/redline` | Compare two `.docx` buffers and generate a native Word redline |
|
|
34
43
|
| `@stll/folio-core/*` | the source-mirrored module tree (e.g. `@stll/folio-core/types/document`, `@stll/folio-core/prosemirror/schema`) for adapters that need lower-level building blocks |
|
|
35
44
|
|
|
45
|
+
## Review display configuration
|
|
46
|
+
|
|
47
|
+
`ReviewDisplayMode` is `all-markup | final | original`.
|
|
48
|
+
`reviewDisplayModeToDisplayMode` in `managers/reviewDisplayMode` maps it to the
|
|
49
|
+
layout pipeline's `markupView` input. The shared projection reuses the
|
|
50
|
+
accept-all/reject-all resolver without changing editor state. Layout and
|
|
51
|
+
search consume that projection; saved DOCX always uses the authored state.
|
|
52
|
+
`getEditorReadOnlyReason` in `managers/EditorModeManager` makes projected review
|
|
53
|
+
views read-only. Both adapters expose the resulting `EditorReadOnlyReason`
|
|
54
|
+
through `getReadOnlyReason()` and `onReadonlyEditAttempt(reason)`.
|
|
55
|
+
`showCommentHighlights` on the layout pipeline defaults to `true`; final
|
|
56
|
+
(`no-markup`) and original suppress comment highlights regardless of it.
|
|
57
|
+
|
|
36
58
|
## New documents and reusable style sets
|
|
37
59
|
|
|
38
60
|
Create a legal-shaped blank document with the built-in preset:
|
package/dist/ai-edits/apply.js
CHANGED
|
@@ -11,6 +11,7 @@ import { encloseWholeControls } from "../prosemirror/contentControlRevisions.js"
|
|
|
11
11
|
import { JOINED_RUNS_RESTYLED_META } from "../prosemirror/extensions/features/JoinedRunStyleExtension.js";
|
|
12
12
|
import { requestDeterministicParaIds } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
13
13
|
import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
14
|
+
import "../prosemirror/extensions/nodes/RangeAnchorExtension.js";
|
|
14
15
|
import "../prosemirror/extensions/nodes/TextBoxAnchorExtension.js";
|
|
15
16
|
import { CLEARED_LIST_RENDERING_ATTRS } from "../prosemirror/listMarker.js";
|
|
16
17
|
import { effectiveParagraphNumbering, paragraphNumberingAttr, paragraphNumberingSourcesFromBoundary, readParagraphNumberingAttr, removedNumberingAttr } from "../prosemirror/numberingAttr.js";
|
|
@@ -23,11 +24,12 @@ import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqua
|
|
|
23
24
|
import { getDocumentNumbering, getDocumentNumberingInstanceIds } from "../prosemirror/plugins/documentNumbering.js";
|
|
24
25
|
import { getDocumentStyleDefinitions, getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
|
|
25
26
|
import { claimRevisionIds, nextRevisionId, nextRevisionIdRange, reserveRevisionIds, seedRevisionIdsFromDoc } from "../prosemirror/plugins/revisionIds.js";
|
|
27
|
+
import { readRangeAnchorAttrs } from "../prosemirror/rangeAnchorAttrs.js";
|
|
26
28
|
import { rebaseParagraphRuns } from "../prosemirror/rebaseParagraphRuns.js";
|
|
27
|
-
import { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, placeTrackedInsertion, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
|
|
29
|
+
import { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, placeTrackedInsertion, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
|
|
28
30
|
import { applyMarksToRunFormattingRepresentation, runFormattingInlineControlNodeName, selectRunFormattingCarrierRepresentations } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
29
31
|
import { RUN_FORMATTING_MARK_NAMES } from "../prosemirror/runFormattingMarkNames.js";
|
|
30
|
-
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
32
|
+
import { readAuthoredRunFormatting, readAuthoredRunPropertySnapshot, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
31
33
|
import { paragraphRunStyleContext, paragraphRunStyleContextAt, resolveParagraphBodyRunFormatting } from "../prosemirror/runStyleFormatting.js";
|
|
32
34
|
import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
|
|
33
35
|
import { listAttrsFromNumbering, listLevelAttrPatch, listLevelIndentRemovalPatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
@@ -788,12 +790,12 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
|
|
|
788
790
|
pos: representation.from,
|
|
789
791
|
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
790
792
|
});
|
|
791
|
-
const previousFormatting =
|
|
793
|
+
const previousFormatting = readAuthoredRunPropertySnapshot({
|
|
792
794
|
context: styleContext,
|
|
793
795
|
marks: node.marks,
|
|
794
796
|
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
795
797
|
});
|
|
796
|
-
const includedProperties = formattingChangesForAuthoredFormatting(previousFormatting, formatting);
|
|
798
|
+
const includedProperties = formattingChangesForAuthoredFormatting(previousFormatting ?? {}, formatting);
|
|
797
799
|
if (includedProperties.length === 0) continue;
|
|
798
800
|
const existingMark = node.marks.find((mark) => mark.type === propertyChangeType);
|
|
799
801
|
if (existingMark && expectRunPropertyChangeMarkAttrs(existingMark).changes.length > 0) {
|
|
@@ -802,7 +804,7 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
|
|
|
802
804
|
}
|
|
803
805
|
const formattingMarks = reconcileRunFormattingMarks({
|
|
804
806
|
authoredFormatting: patchAuthoredRunFormatting({
|
|
805
|
-
authoredFormatting: previousFormatting,
|
|
807
|
+
authoredFormatting: previousFormatting ?? {},
|
|
806
808
|
formatting,
|
|
807
809
|
includedProperties
|
|
808
810
|
}),
|
|
@@ -845,7 +847,7 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
|
|
|
845
847
|
date,
|
|
846
848
|
...initials ? { initials } : {}
|
|
847
849
|
},
|
|
848
|
-
...
|
|
850
|
+
...segment.previousFormatting !== void 0 ? { previousFormatting: segment.previousFormatting } : {}
|
|
849
851
|
};
|
|
850
852
|
const marks = propertyChangeType.create({
|
|
851
853
|
changes: [change],
|
|
@@ -1353,12 +1355,12 @@ const isPendingDeletion = (doc, blockFrom, paragraph, cleanText) => {
|
|
|
1353
1355
|
return false;
|
|
1354
1356
|
};
|
|
1355
1357
|
/** Whether every piece of the paragraph's content is pending inserted text. */
|
|
1356
|
-
const holdsOnlyInsertedContent = (paragraph) => {
|
|
1358
|
+
const holdsOnlyInsertedContent = (paragraph, revisions) => {
|
|
1357
1359
|
let whollyInserted = true;
|
|
1358
1360
|
paragraph.descendants((node) => {
|
|
1359
1361
|
if (!whollyInserted) return false;
|
|
1360
1362
|
if (!node.isInline || isZeroWidthAnchor(node)) return true;
|
|
1361
|
-
whollyInserted = node.marks.some((mark) => mark.type.name === "insertion" && mark.attrs["moveKind"] !== "moveTo");
|
|
1363
|
+
whollyInserted = node.marks.some((mark) => mark.type.name === "insertion" && (revisions === "including-moves" || mark.attrs["moveKind"] !== "moveTo"));
|
|
1362
1364
|
return false;
|
|
1363
1365
|
});
|
|
1364
1366
|
return whollyInserted;
|
|
@@ -1376,11 +1378,16 @@ const holdsOnlyInsertedContent = (paragraph) => {
|
|
|
1376
1378
|
*/
|
|
1377
1379
|
const pendingParagraphRetraction = (doc, position) => {
|
|
1378
1380
|
const paragraph = doc.nodeAt(position);
|
|
1379
|
-
if (paragraph?.type.name !== "paragraph"
|
|
1381
|
+
if (paragraph?.type.name !== "paragraph") return null;
|
|
1380
1382
|
const at = doc.resolve(position);
|
|
1381
1383
|
const before = at.nodeBefore;
|
|
1382
1384
|
const endsItsContainer = paragraphEndsItsContainer(at, paragraph.type.name);
|
|
1383
1385
|
if (at.parent.childCount < 2 || endsItsContainer && before?.type.name !== paragraph.type.name) return null;
|
|
1386
|
+
let followingIndex = at.index() + 1;
|
|
1387
|
+
while (at.parent.maybeChild(followingIndex)?.type.name === "blockBookmarkBoundary") followingIndex++;
|
|
1388
|
+
const following = at.parent.maybeChild(followingIndex);
|
|
1389
|
+
if (expectParagraphAttrs(paragraph).pPrMark?.kind === "moveTo" && following?.type.spec["tableRole"] === "table" && holdsOnlyInsertedContent(paragraph, "including-moves")) return { clearBreakAt: null };
|
|
1390
|
+
if (!holdsOnlyInsertedContent(paragraph, "ordinary")) return null;
|
|
1384
1391
|
if (isInsertedPPrMark(paragraph.attrs["pPrMark"]) || isSuggestedParagraphInsert(paragraph)) return { clearBreakAt: null };
|
|
1385
1392
|
if (endsItsContainer && before && paragraph.attrs["pPrMark"] == null) return isInsertedPPrMark(before.attrs["pPrMark"]) ? { clearBreakAt: position - before.nodeSize } : null;
|
|
1386
1393
|
return null;
|
|
@@ -1596,7 +1603,7 @@ const hasDeferredParagraphPropertyWrite = ({ tr, deletions, targets, mergedPrope
|
|
|
1596
1603
|
})) continue;
|
|
1597
1604
|
const writeTarget = mark == null ? target : tr.doc.nodeAt(predecessor.position + predecessor.node.nodeSize);
|
|
1598
1605
|
if (!writeTarget || !targets.includes(writeTarget)) continue;
|
|
1599
|
-
if (mark != null && !(
|
|
1606
|
+
if (mark != null && !(addedBreakRevisionId(mark) !== null && canRetractFinalBreak({
|
|
1600
1607
|
predecessor: predecessor.node,
|
|
1601
1608
|
target: writeTarget
|
|
1602
1609
|
}))) continue;
|
|
@@ -1629,7 +1636,7 @@ const paragraphPropertyRevisionTargets = ({ item, tr, deletedFinalParagraphs })
|
|
|
1629
1636
|
for (const deletion of deletedFinalParagraphs) {
|
|
1630
1637
|
const at = tr.mapping.slice(deletion.mappedFrom).map(deletion.position);
|
|
1631
1638
|
const predecessor = finalParagraphPredecessor(tr.doc, at);
|
|
1632
|
-
if (!predecessor ||
|
|
1639
|
+
if (!predecessor || addedBreakRevisionId(predecessor.node.attrs["pPrMark"]) === null) continue;
|
|
1633
1640
|
const target = tr.doc.nodeAt(predecessor.position + predecessor.node.nodeSize);
|
|
1634
1641
|
if (!target) continue;
|
|
1635
1642
|
if (canRetractFinalBreak({
|
|
@@ -1727,7 +1734,7 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1727
1734
|
});
|
|
1728
1735
|
continue;
|
|
1729
1736
|
}
|
|
1730
|
-
if (
|
|
1737
|
+
if (addedBreakRevisionId(mark) !== null) {
|
|
1731
1738
|
const following = tr.doc.nodeAt(position + previous.nodeSize);
|
|
1732
1739
|
if (!following || !canRetractFinalBreak({
|
|
1733
1740
|
predecessor: previous,
|
|
@@ -1777,10 +1784,10 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1777
1784
|
/**
|
|
1778
1785
|
* Move an ADDED paragraph break off every paragraph its container ends with.
|
|
1779
1786
|
*
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1782
|
-
*
|
|
1783
|
-
*
|
|
1787
|
+
* Prefer a preceding carrier so rejection can join the inserted content back
|
|
1788
|
+
* over its added boundary. When no carrier is free, the insertion keeps its
|
|
1789
|
+
* own mark: the resolver can remove that whole paragraph when its text is
|
|
1790
|
+
* rejected, while a markless empty paragraph would have no insertion owner.
|
|
1784
1791
|
*
|
|
1785
1792
|
* Appending at a container's end therefore rotates exactly as removing from it
|
|
1786
1793
|
* does. The first inserted break moves onto the paragraph the run was appended
|
|
@@ -1806,7 +1813,9 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1806
1813
|
* paragraph's words run into the one after it, which ends with the free mark,
|
|
1807
1814
|
* and neither break is left to resolve.
|
|
1808
1815
|
*/
|
|
1809
|
-
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials, styleResolver }) => {
|
|
1816
|
+
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials, styleResolver, numbering }) => {
|
|
1817
|
+
let nextRevisionId = revisionSeed;
|
|
1818
|
+
const synthesizedRevisions = [];
|
|
1810
1819
|
const supersededDeletedBreaks = [];
|
|
1811
1820
|
const collectSupersededDeletedBreaks = (parent, contentStart) => {
|
|
1812
1821
|
let previous = null;
|
|
@@ -1824,7 +1833,7 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1824
1833
|
const trackedInsertion = insertedRevisionId !== null && batchRevisionIds.has(insertedRevisionId);
|
|
1825
1834
|
const suggestedInsertion = isSuggestedParagraphInsert(node);
|
|
1826
1835
|
const deletedBreakRevisionId = addedOrDeletedBreakRevisionId(deletedMark);
|
|
1827
|
-
if (isPlainDeletedPPrMark(deletedMark) && deletedBreakRevisionId !== null && !batchRevisionIds.has(deletedBreakRevisionId) && !holdsOnlyDeletedContent(previous.node) && (trackedInsertion || suggestedInsertion) && holdsOnlyInsertedContent(node)) supersededDeletedBreaks.push({
|
|
1836
|
+
if (isPlainDeletedPPrMark(deletedMark) && deletedBreakRevisionId !== null && !batchRevisionIds.has(deletedBreakRevisionId) && !holdsOnlyDeletedContent(previous.node) && (trackedInsertion || suggestedInsertion) && holdsOnlyInsertedContent(node, "ordinary")) supersededDeletedBreaks.push({
|
|
1828
1837
|
preceding: previous.position,
|
|
1829
1838
|
inserted: position,
|
|
1830
1839
|
deletedMark,
|
|
@@ -1859,6 +1868,33 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1859
1868
|
}
|
|
1860
1869
|
tr.setNodeAttribute(preceding, "pPrMark", precedingMark);
|
|
1861
1870
|
tr.setNodeAttribute(inserted, "pPrMark", deletedMark);
|
|
1871
|
+
if (insertionRevisionId === null) continue;
|
|
1872
|
+
const source = tr.doc.nodeAt(inserted);
|
|
1873
|
+
const survivor = paragraphLeftAfter({
|
|
1874
|
+
doc: tr.doc,
|
|
1875
|
+
paragraphPos: inserted
|
|
1876
|
+
});
|
|
1877
|
+
if (!source || survivor === null) continue;
|
|
1878
|
+
const revisionId = nextRevisionId;
|
|
1879
|
+
if (carryParagraphProperties({
|
|
1880
|
+
tr,
|
|
1881
|
+
position: survivor,
|
|
1882
|
+
source,
|
|
1883
|
+
styleResolver,
|
|
1884
|
+
numbering,
|
|
1885
|
+
revision: {
|
|
1886
|
+
id: revisionId,
|
|
1887
|
+
author,
|
|
1888
|
+
date,
|
|
1889
|
+
...initials ? { initials } : {}
|
|
1890
|
+
}
|
|
1891
|
+
}).tracked) {
|
|
1892
|
+
nextRevisionId++;
|
|
1893
|
+
synthesizedRevisions.push({
|
|
1894
|
+
ownerRevisionId: insertionRevisionId,
|
|
1895
|
+
revisionId
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1862
1898
|
}
|
|
1863
1899
|
const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
|
|
1864
1900
|
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).flatMap(({ node, position }) => {
|
|
@@ -1872,16 +1908,11 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1872
1908
|
}] : [];
|
|
1873
1909
|
}).toSorted((left, right) => right.position - left.position);
|
|
1874
1910
|
let next = tr;
|
|
1875
|
-
let nextRevisionId = revisionSeed;
|
|
1876
|
-
const synthesizedRevisions = [];
|
|
1877
1911
|
const retractedRevisionIds = [];
|
|
1878
1912
|
for (const { position: finalPosition, node: final, mark: finalMark } of rotations) {
|
|
1879
1913
|
const resolved = next.doc.resolve(finalPosition);
|
|
1880
1914
|
const carrier = finalBreakCarrierBefore(resolved, final.type.name, batchRevisionIds);
|
|
1881
|
-
if (!carrier)
|
|
1882
|
-
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
1883
|
-
continue;
|
|
1884
|
-
}
|
|
1915
|
+
if (!carrier) continue;
|
|
1885
1916
|
const path = [{
|
|
1886
1917
|
node: final,
|
|
1887
1918
|
position: finalPosition
|
|
@@ -1897,10 +1928,10 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1897
1928
|
});
|
|
1898
1929
|
if (position === carrier.position) break;
|
|
1899
1930
|
}
|
|
1900
|
-
if (path.at(-1)?.position !== carrier.position) {
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
}
|
|
1931
|
+
if (path.at(-1)?.position !== carrier.position) panic("A final-mark rotation lost its carrier", {
|
|
1932
|
+
finalPosition,
|
|
1933
|
+
carrier: carrier.position
|
|
1934
|
+
});
|
|
1904
1935
|
if (path.slice(0, -1).some(({ node }) => {
|
|
1905
1936
|
const revisionId = addedBreakRevisionId(node.attrs["pPrMark"]);
|
|
1906
1937
|
return revisionId === null || !batchRevisionIds.has(revisionId);
|
|
@@ -2581,6 +2612,17 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2581
2612
|
date,
|
|
2582
2613
|
...trackedRevisionExtras
|
|
2583
2614
|
} : null;
|
|
2615
|
+
const commentSource = tr.doc;
|
|
2616
|
+
const commentStepsBefore = tr.steps.length;
|
|
2617
|
+
const commentReplacement = replacementText(item.operation);
|
|
2618
|
+
const commentPlan = mode === "direct" && commentReplacement !== null && surveyReplacedAnnotations(commentSource, item.from, item.to).carried.some((mark) => mark.type.name === "comment") ? planDocumentReplacement({
|
|
2619
|
+
doc: commentSource,
|
|
2620
|
+
item,
|
|
2621
|
+
replacement: stripInlineEmphasisMarkers(commentReplacement),
|
|
2622
|
+
noteReferences,
|
|
2623
|
+
planChanges: (before, after) => changesFromSegments(diffText(before, after)),
|
|
2624
|
+
keepFieldsOf: planTextChanges
|
|
2625
|
+
}) : null;
|
|
2584
2626
|
switch (item.operation.type) {
|
|
2585
2627
|
case "replaceInBlock":
|
|
2586
2628
|
case "replaceRange": {
|
|
@@ -3259,8 +3301,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3259
3301
|
const markPosition = tr.mapping.map(item.blockFrom);
|
|
3260
3302
|
const endsItsContainer = paragraphEndsItsContainer(tr.doc.resolve(markPosition), item.blockNode.type.name);
|
|
3261
3303
|
const deleted = tr.doc.nodeAt(markPosition);
|
|
3262
|
-
const following = deleted && tr.doc
|
|
3263
|
-
if (deleted && following?.type === deleted.type && !isPairedMove(item.operation.moveId) &&
|
|
3304
|
+
const following = deleted && paragraphJoinAfter(tr.doc, markPosition + deleted.nodeSize)?.next;
|
|
3305
|
+
if (deleted && following?.type === deleted.type && !isPairedMove(item.operation.moveId) && addedBreakRevisionId(deleted.attrs["pPrMark"]) !== null) {
|
|
3264
3306
|
const restored = paragraphPropertiesOnReject(deleted);
|
|
3265
3307
|
const followingChanges = expectParagraphAttrs(following)._propertyChanges;
|
|
3266
3308
|
const recordsRestored = isInsertedPPrMark(following.attrs["pPrMark"]) && !hasSerializableParagraphPropertyChange(followingChanges) && JSON.stringify(restored) !== JSON.stringify(paragraphPropertiesSnapshot(following));
|
|
@@ -3784,6 +3826,14 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3784
3826
|
break;
|
|
3785
3827
|
default: break;
|
|
3786
3828
|
}
|
|
3829
|
+
if (commentPlan !== null) tr = applyAlignedReplacementComments({
|
|
3830
|
+
tr,
|
|
3831
|
+
source: commentSource,
|
|
3832
|
+
item,
|
|
3833
|
+
plan: commentPlan,
|
|
3834
|
+
stepsBefore: commentStepsBefore,
|
|
3835
|
+
noteReferences
|
|
3836
|
+
});
|
|
3787
3837
|
if (tr.steps.length === stepsBefore) {
|
|
3788
3838
|
if (!deletedFinalParagraphs.some(({ operationId }) => operationId === item.operation.id)) {
|
|
3789
3839
|
skipped.push({
|
|
@@ -3838,7 +3888,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3838
3888
|
author,
|
|
3839
3889
|
date,
|
|
3840
3890
|
initials,
|
|
3841
|
-
styleResolver: styleResolver ?? null
|
|
3891
|
+
styleResolver: styleResolver ?? null,
|
|
3892
|
+
numbering
|
|
3842
3893
|
});
|
|
3843
3894
|
tr = rotated.transaction;
|
|
3844
3895
|
revisionSeed = rotated.nextRevisionId;
|
|
@@ -4353,6 +4404,57 @@ const directReplacementMarks = ({ doc, plan, replacementBackground }) => {
|
|
|
4353
4404
|
keepUntil(plan.spanEnd - plan.spanStart);
|
|
4354
4405
|
return allocated;
|
|
4355
4406
|
};
|
|
4407
|
+
const replacementText = (operation) => {
|
|
4408
|
+
switch (operation.type) {
|
|
4409
|
+
case "replaceBlock": return operation.text;
|
|
4410
|
+
case "replaceInBlock":
|
|
4411
|
+
case "replaceRange": return operation.replace;
|
|
4412
|
+
default: return null;
|
|
4413
|
+
}
|
|
4414
|
+
};
|
|
4415
|
+
/** Both modes project comment coverage through the same text alignment. */
|
|
4416
|
+
const applyAlignedReplacementComments = ({ tr, source, item, plan, stepsBefore, noteReferences }) => {
|
|
4417
|
+
const comments = surveyReplacedAnnotations(source, item.from, item.to).carried.filter((mark) => mark.type.name === "comment");
|
|
4418
|
+
if (comments.length === 0) return tr;
|
|
4419
|
+
const marks = directReplacementMarks({
|
|
4420
|
+
doc: source,
|
|
4421
|
+
plan,
|
|
4422
|
+
replacementBackground: "keep"
|
|
4423
|
+
});
|
|
4424
|
+
const blockFrom = tr.mapping.slice(stepsBefore).map(item.blockFrom, -1);
|
|
4425
|
+
const block = tr.doc.nodeAt(blockFrom);
|
|
4426
|
+
if (!block) panic("Replacement lost its paragraph");
|
|
4427
|
+
const clean = buildCleanBlockText(block, blockFrom, {
|
|
4428
|
+
fieldResults: "text",
|
|
4429
|
+
noteReferences
|
|
4430
|
+
});
|
|
4431
|
+
for (const [index, expected] of marks.entries()) {
|
|
4432
|
+
const from = clean.offsets[plan.spanStart + index];
|
|
4433
|
+
if (from === void 0) panic("Replacement lost an aligned character");
|
|
4434
|
+
const node = tr.doc.nodeAt(from);
|
|
4435
|
+
if (!node?.isText || noteReferenceOf(node) !== null) continue;
|
|
4436
|
+
for (const comment of comments) if (comment.isInSet(expected)) tr.addMark(from, from + 1, comment);
|
|
4437
|
+
else tr.removeMark(from, from + 1, comment);
|
|
4438
|
+
}
|
|
4439
|
+
const visible = /* @__PURE__ */ new Set();
|
|
4440
|
+
const points = /* @__PURE__ */ new Set();
|
|
4441
|
+
tr.doc.descendants((node) => {
|
|
4442
|
+
if (node.type.name === "rangeAnchor") {
|
|
4443
|
+
const attrs = readRangeAnchorAttrs(node);
|
|
4444
|
+
if (attrs.ok && attrs.value.start.type === "commentRangeStart") points.add(attrs.value.start.id);
|
|
4445
|
+
}
|
|
4446
|
+
if (!node.isInline || isZeroWidthAnchor(node) || node.marks.some((mark) => mark.type.name === "deletion")) return;
|
|
4447
|
+
for (const mark of node.marks) if (mark.type.name === "comment") visible.add(mark.attrs["commentId"]);
|
|
4448
|
+
});
|
|
4449
|
+
const at = clean.offsets[plan.spanStart];
|
|
4450
|
+
if (at === void 0) panic("Replacement lost its start gap");
|
|
4451
|
+
for (const comment of comments) {
|
|
4452
|
+
const id = comment.attrs["commentId"];
|
|
4453
|
+
if (visible.has(id) || points.has(id)) continue;
|
|
4454
|
+
tr.insert(at, commentPoint(tr.doc.type.schema, id));
|
|
4455
|
+
}
|
|
4456
|
+
return tr;
|
|
4457
|
+
};
|
|
4356
4458
|
/**
|
|
4357
4459
|
* Apply a direct-mode text replacement as the difference between the matched
|
|
4358
4460
|
* text and its replacement; `minimal-replacement.ts` states the contract.
|
|
@@ -4474,11 +4576,16 @@ const applyMinimalTrackedReplacement = ({ tr, item, noteReferences, replacement,
|
|
|
4474
4576
|
noteReferences,
|
|
4475
4577
|
planChanges: planTextChanges
|
|
4476
4578
|
});
|
|
4579
|
+
const annotationMarks = directReplacementMarks({
|
|
4580
|
+
doc: tr.doc,
|
|
4581
|
+
plan,
|
|
4582
|
+
replacementBackground: "keep"
|
|
4583
|
+
});
|
|
4477
4584
|
const allocatedMarks = directPlan === null ? null : directReplacementMarks({
|
|
4478
4585
|
doc: tr.doc,
|
|
4479
4586
|
plan: directPlan,
|
|
4480
4587
|
replacementBackground
|
|
4481
|
-
});
|
|
4588
|
+
}).map((marks, index) => addCarriedMarks(marks.filter((mark) => mark.type.name !== "comment"), (annotationMarks[index] ?? []).filter((mark) => mark.type.name === "comment")));
|
|
4482
4589
|
const unifiesFormatting = directPlan === null ? false : replacementUnifiesFormatting(tr.doc, directPlan);
|
|
4483
4590
|
let nextTr = tr;
|
|
4484
4591
|
let backgroundRevisionIds = [];
|
|
@@ -591,6 +591,8 @@ declare class FolioDocxReviewer {
|
|
|
591
591
|
private noteKeysReferencedOutsideBody;
|
|
592
592
|
private resolveEveryStoryOnce;
|
|
593
593
|
private acceptOrdinaryChangesWithSuggestions;
|
|
594
|
+
/** Resolve ordinary revisions on a detached in-memory document. */
|
|
595
|
+
toResolvedDocument(decision: "accept" | "reject"): document_d_exports.Document;
|
|
594
596
|
/** The current document model with edits merged back in. */
|
|
595
597
|
toDocument(): document_d_exports.Document;
|
|
596
598
|
private captureReviewerState;
|
|
@@ -625,6 +627,7 @@ declare class FolioDocxReviewer {
|
|
|
625
627
|
* state for a story nothing edits.
|
|
626
628
|
*/
|
|
627
629
|
private readSecondaryStoryDoc;
|
|
630
|
+
private secondaryStorySource;
|
|
628
631
|
private secondaryStoryDoc;
|
|
629
632
|
private requireEditableStoryState;
|
|
630
633
|
private setEditableStoryState;
|
|
@@ -4,8 +4,11 @@ import { stageSectionBoundaryProperties } from "../compare/section-boundary-prop
|
|
|
4
4
|
import { importReferencedStyleDefinitions, styleClosureNumberingReferences } from "../compare/style-resources.js";
|
|
5
5
|
import { applyFolioDocumentOperations } from "../document-operations.js";
|
|
6
6
|
import { applyReplyThreadMarkers } from "../docx/commentReplyMarkers.js";
|
|
7
|
+
import { commentTextParagraph } from "../docx/commentTextParagraph.js";
|
|
8
|
+
import { buildSections } from "../docx/documentParser.js";
|
|
7
9
|
import { rebindDrawingImageRelationship } from "../docx/drawingRelationships.js";
|
|
8
10
|
import { isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
11
|
+
import { normalizeListModelRendering } from "../docx/listModelNormalization.js";
|
|
9
12
|
import { createNumberingIdAllocator } from "../docx/numberingIds.js";
|
|
10
13
|
import { getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
11
14
|
import { recreateProseNodeWithParagraphPropertySource, transferProseParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
@@ -40,6 +43,7 @@ import { deterministicHexId } from "../utils/hexId.js";
|
|
|
40
43
|
import { buildAnnotatedBlockTextWithNoteReferences } from "./clean-text.js";
|
|
41
44
|
import { anchoredCommentIdsInBlocks, anchoredCommentIdsInProseDoc, withoutLostCommentThreads } from "./comment-lifecycle.js";
|
|
42
45
|
import { noteReferenceEditsAllowed } from "./note-references.js";
|
|
46
|
+
import { noteStoryInventory } from "./note-story-inventory.js";
|
|
43
47
|
import { FolioPendingSuggestionRegistry, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks } from "./pending-suggestions.js";
|
|
44
48
|
import { getCommentAnchorsFromDoc, getTrackedChangeGroupIdsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
|
|
45
49
|
import { classifyResourceReference } from "./referenceClassification.js";
|
|
@@ -49,6 +53,7 @@ import { tableTemplateCanCrossPackageLosslessly } from "./table-template.js";
|
|
|
49
53
|
import { TaggedError, panic } from "better-result";
|
|
50
54
|
import { REVIEW_CARRIERS } from "@stll/docx-core/model";
|
|
51
55
|
import { Fragment } from "prosemirror-model";
|
|
56
|
+
import { reconcileDocumentPartRelationships } from "@stll/docx-core/ops";
|
|
52
57
|
import { EditorState } from "prosemirror-state";
|
|
53
58
|
import JSZip from "jszip";
|
|
54
59
|
//#region src/ai-edits/headless.ts
|
|
@@ -76,18 +81,7 @@ const createReviewerComment = ({ id, text, author, date }) => ({
|
|
|
76
81
|
id,
|
|
77
82
|
author,
|
|
78
83
|
date: date ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
79
|
-
content: [
|
|
80
|
-
type: "paragraph",
|
|
81
|
-
formatting: {},
|
|
82
|
-
content: [{
|
|
83
|
-
type: "run",
|
|
84
|
-
formatting: {},
|
|
85
|
-
content: [{
|
|
86
|
-
type: "text",
|
|
87
|
-
text
|
|
88
|
-
}]
|
|
89
|
-
}]
|
|
90
|
-
}]
|
|
84
|
+
content: [commentTextParagraph(text)]
|
|
91
85
|
});
|
|
92
86
|
/**
|
|
93
87
|
* Assign a stable `w14:paraId` to every body paragraph that lacks one (or
|
|
@@ -1080,42 +1074,28 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1080
1074
|
* with the sections that show it: `[header first (sections 1, 3)] …`.
|
|
1081
1075
|
*/
|
|
1082
1076
|
getNotesAsText() {
|
|
1083
|
-
const pkg = this.baseDocument.package;
|
|
1084
1077
|
const lines = [];
|
|
1085
1078
|
const placements = this.headerFooterPlacements();
|
|
1086
|
-
const
|
|
1087
|
-
if (
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
const roles = placements.byPart.get(`${
|
|
1096
|
-
|
|
1079
|
+
for (const handle of this.listStoryHandlesInternal()) {
|
|
1080
|
+
if (handle.type === "main") continue;
|
|
1081
|
+
const text = this.getStoryText(handle);
|
|
1082
|
+
if (text.length === 0) continue;
|
|
1083
|
+
switch (handle.type) {
|
|
1084
|
+
case "header":
|
|
1085
|
+
case "footer": {
|
|
1086
|
+
const source = this.getHeaderFooterStory(handle);
|
|
1087
|
+
if (!source) panic("A listed document story no longer exists", { story: handle });
|
|
1088
|
+
const roles = placements.byPart.get(`${handle.type}:${handle.relationshipId}`);
|
|
1089
|
+
const label = roles ? describePlacements(roles, placements.sectionCount) : source.hdrFtrType;
|
|
1090
|
+
lines.push(`[${handle.type} ${label}] ${text}`);
|
|
1091
|
+
break;
|
|
1097
1092
|
}
|
|
1093
|
+
case "footnote":
|
|
1094
|
+
case "endnote":
|
|
1095
|
+
lines.push(`[${handle.type} #${handle.noteId}] ${text}`);
|
|
1096
|
+
break;
|
|
1097
|
+
default: return handle;
|
|
1098
1098
|
}
|
|
1099
|
-
};
|
|
1100
|
-
pushHeaderFooter(pkg.headers, "header");
|
|
1101
|
-
pushHeaderFooter(pkg.footers, "footer");
|
|
1102
|
-
for (const footnote of pkg.footnotes ?? []) {
|
|
1103
|
-
if (isSeparatorFootnote(footnote)) continue;
|
|
1104
|
-
const handle = {
|
|
1105
|
-
type: "footnote",
|
|
1106
|
-
noteId: footnote.id
|
|
1107
|
-
};
|
|
1108
|
-
const text = this.getNoteStoryText(handle, footnote);
|
|
1109
|
-
if (text.length > 0) lines.push(`[footnote #${footnote.id}] ${text}`);
|
|
1110
|
-
}
|
|
1111
|
-
for (const endnote of pkg.endnotes ?? []) {
|
|
1112
|
-
if (isSeparatorEndnote(endnote)) continue;
|
|
1113
|
-
const handle = {
|
|
1114
|
-
type: "endnote",
|
|
1115
|
-
noteId: endnote.id
|
|
1116
|
-
};
|
|
1117
|
-
const text = this.getNoteStoryText(handle, endnote);
|
|
1118
|
-
if (text.length > 0) lines.push(`[endnote #${endnote.id}] ${text}`);
|
|
1119
1099
|
}
|
|
1120
1100
|
return lines.join("\n");
|
|
1121
1101
|
}
|
|
@@ -1138,17 +1118,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1138
1118
|
type: "footer",
|
|
1139
1119
|
relationshipId
|
|
1140
1120
|
});
|
|
1141
|
-
|
|
1121
|
+
const notes = noteStoryInventory(pkg, this.removedNoteStories);
|
|
1122
|
+
for (const footnote of notes.footnotes ?? []) if (!isSeparatorFootnote(footnote)) handles.push({
|
|
1142
1123
|
type: "footnote",
|
|
1143
1124
|
noteId: footnote.id
|
|
1144
1125
|
});
|
|
1145
|
-
for (const endnote of
|
|
1126
|
+
for (const endnote of notes.endnotes ?? []) if (!isSeparatorEndnote(endnote)) handles.push({
|
|
1146
1127
|
type: "endnote",
|
|
1147
1128
|
noteId: endnote.id
|
|
1148
1129
|
});
|
|
1149
1130
|
return handles.filter((handle) => {
|
|
1150
1131
|
if (handle.type === "header" || handle.type === "footer") return !this.removedHeaderFooterStories.has(headerFooterStoryKey(handle));
|
|
1151
|
-
return
|
|
1132
|
+
return true;
|
|
1152
1133
|
});
|
|
1153
1134
|
}
|
|
1154
1135
|
/** Discover every readable document story through a typed, serializable handle. */
|
|
@@ -1676,6 +1657,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1676
1657
|
})
|
|
1677
1658
|
};
|
|
1678
1659
|
}
|
|
1660
|
+
/** Resolve ordinary revisions on a detached in-memory document. */
|
|
1661
|
+
toResolvedDocument(decision) {
|
|
1662
|
+
const copy = new FolioDocxReviewer({
|
|
1663
|
+
baseDocument: this.toDocument(),
|
|
1664
|
+
originalBuffer: this.originalBuffer,
|
|
1665
|
+
state: this.state,
|
|
1666
|
+
author: this.author
|
|
1667
|
+
});
|
|
1668
|
+
if (decision === "accept") copy.acceptAll();
|
|
1669
|
+
else copy.rejectAll();
|
|
1670
|
+
return copy.toDocument();
|
|
1671
|
+
}
|
|
1679
1672
|
/** The current document model with edits merged back in. */
|
|
1680
1673
|
toDocument() {
|
|
1681
1674
|
const document = this.documentFromStateSnapshot(this.captureReviewerState());
|
|
@@ -1702,7 +1695,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1702
1695
|
importedFooters: new Map(this.importedFooters),
|
|
1703
1696
|
createdComments: [...this.createdComments],
|
|
1704
1697
|
resolvedOverrides: new Map(this.resolvedOverrides),
|
|
1705
|
-
resolvedStoryExpectations: [...this.resolvedStoryExpectations.values()]
|
|
1698
|
+
resolvedStoryExpectations: [...this.resolvedStoryExpectations.values()].filter(({ story }) => story.type === "main" || this.secondaryStorySource(story) !== void 0)
|
|
1706
1699
|
};
|
|
1707
1700
|
}
|
|
1708
1701
|
documentFromStateSnapshot(snapshot) {
|
|
@@ -1717,7 +1710,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1717
1710
|
type: "supplied",
|
|
1718
1711
|
styles: snapshot.importedStyles
|
|
1719
1712
|
} });
|
|
1720
|
-
if (snapshot.finalSectionPropertiesOverride !== void 0)
|
|
1713
|
+
if (snapshot.finalSectionPropertiesOverride !== void 0) {
|
|
1714
|
+
document.package.document.finalSectionProperties = snapshot.finalSectionPropertiesOverride;
|
|
1715
|
+
if (document.package.document.sections !== void 0) document.package.document.sections = buildSections(document.package.document.content, snapshot.finalSectionPropertiesOverride);
|
|
1716
|
+
}
|
|
1721
1717
|
if (snapshot.importedStyles !== void 0) document.package.styles = snapshot.importedStyles;
|
|
1722
1718
|
if (snapshot.importedMedia.size > 0) document.package.media = new Map([...document.package.media ?? [], ...snapshot.importedMedia]);
|
|
1723
1719
|
if (snapshot.importedHeaders.size > 0) document.package.headers = new Map([...document.package.headers ?? [], ...snapshot.importedHeaders]);
|
|
@@ -1734,20 +1730,12 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1734
1730
|
const candidates = new Set(snapshot.removedNoteStories.map(noteStoryKey));
|
|
1735
1731
|
const referenced = referencedNotes(document, candidates);
|
|
1736
1732
|
const removed = new Set([...candidates].filter((key) => !referenced.has(key)));
|
|
1737
|
-
|
|
1738
|
-
if (footnotes) document.package.footnotes = footnotes.filter((note) => !removed.has(noteStoryKey({
|
|
1739
|
-
type: "footnote",
|
|
1740
|
-
noteId: note.id
|
|
1741
|
-
})));
|
|
1742
|
-
if (endnotes) document.package.endnotes = endnotes.filter((note) => !removed.has(noteStoryKey({
|
|
1743
|
-
type: "endnote",
|
|
1744
|
-
noteId: note.id
|
|
1745
|
-
})));
|
|
1733
|
+
Object.assign(document.package, noteStoryInventory(document.package, removed));
|
|
1746
1734
|
}
|
|
1747
1735
|
const definitions = [...document.package.document.comments ?? [], ...snapshot.createdComments];
|
|
1748
1736
|
const kept = this.keptCommentThreads(definitions, snapshot.mainState, snapshot.secondaryStoryStates);
|
|
1749
1737
|
if (kept.length !== definitions.length || snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides(kept, snapshot.resolvedOverrides);
|
|
1750
|
-
return document;
|
|
1738
|
+
return normalizeListModelRendering(reconcileDocumentPartRelationships(document)).document;
|
|
1751
1739
|
}
|
|
1752
1740
|
captureSaveSnapshot() {
|
|
1753
1741
|
const snapshot = this.captureReviewerState();
|
|
@@ -1895,6 +1883,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1895
1883
|
}
|
|
1896
1884
|
getEditableStoryState(story) {
|
|
1897
1885
|
if (story.type === "main") return this.state;
|
|
1886
|
+
if (!this.secondaryStorySource(story)) return null;
|
|
1898
1887
|
const key = secondaryStoryKey(story);
|
|
1899
1888
|
const existing = this.secondaryStoryStates.get(key);
|
|
1900
1889
|
if (existing) return existing.state;
|
|
@@ -1919,10 +1908,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1919
1908
|
* state for a story nothing edits.
|
|
1920
1909
|
*/
|
|
1921
1910
|
readSecondaryStoryDoc(story) {
|
|
1911
|
+
if (!this.secondaryStorySource(story)) return null;
|
|
1922
1912
|
return this.secondaryStoryStates.get(secondaryStoryKey(story))?.state.doc ?? this.secondaryStoryDoc(story);
|
|
1923
1913
|
}
|
|
1914
|
+
secondaryStorySource(story) {
|
|
1915
|
+
return story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
|
|
1916
|
+
}
|
|
1924
1917
|
secondaryStoryDoc(story) {
|
|
1925
|
-
const source =
|
|
1918
|
+
const source = this.secondaryStorySource(story);
|
|
1926
1919
|
if (!source) return null;
|
|
1927
1920
|
const conversionOptions = {
|
|
1928
1921
|
...this.baseDocument.package.numbering !== void 0 && { numbering: this.baseDocument.package.numbering },
|
|
@@ -2041,8 +2034,9 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
2041
2034
|
return snapshot !== null && detachFolioAIEditSnapshotExternalHyperlinks(snapshot) !== null;
|
|
2042
2035
|
}
|
|
2043
2036
|
getNoteStory(story) {
|
|
2044
|
-
|
|
2045
|
-
return
|
|
2037
|
+
const notes = noteStoryInventory(this.baseDocument.package, this.removedNoteStories);
|
|
2038
|
+
if (story.type === "footnote") return notes.footnotes?.find((note) => note.id === story.noteId && !isSeparatorFootnote(note));
|
|
2039
|
+
return notes.endnotes?.find((note) => note.id === story.noteId && !isSeparatorEndnote(note));
|
|
2046
2040
|
}
|
|
2047
2041
|
/**
|
|
2048
2042
|
* A loaded story's blocks, so its text comes from the same walk an unloaded
|
|
@@ -2178,7 +2172,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
2178
2172
|
}
|
|
2179
2173
|
for (const handle of this.removedNoteStories.values()) {
|
|
2180
2174
|
const entry = loaded.get(secondaryStoryKey(handle));
|
|
2181
|
-
addAll(anchoredBefore, entry ? anchoredCommentIdsInProseDoc(entry.initialState.doc) : anchoredCommentIdsInBlocks(this.
|
|
2175
|
+
addAll(anchoredBefore, entry ? anchoredCommentIdsInProseDoc(entry.initialState.doc) : anchoredCommentIdsInBlocks((handle.type === "footnote" ? this.baseDocument.package.footnotes : this.baseDocument.package.endnotes)?.find((note) => note.id === handle.noteId)?.content ?? []));
|
|
2182
2176
|
}
|
|
2183
2177
|
return withoutLostCommentThreads(comments, {
|
|
2184
2178
|
anchoredBefore,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/ai-edits/note-story-inventory.d.ts
|
|
3
|
+
type NoteStories = Pick<document_d_exports.Document["package"], "footnotes" | "endnotes">;
|
|
4
|
+
/** The note collections visible to a reviewer or its captured save state. */
|
|
5
|
+
declare const noteStoryInventory: (source: NoteStories, removed: Pick<ReadonlySet<string>, "has" | "size">) => NoteStories;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { noteStoryInventory };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/ai-edits/note-story-inventory.ts
|
|
2
|
+
/** The note collections visible to a reviewer or its captured save state. */
|
|
3
|
+
const noteStoryInventory = (source, removed) => {
|
|
4
|
+
if (removed.size === 0) return source;
|
|
5
|
+
return {
|
|
6
|
+
...source.footnotes && { footnotes: source.footnotes.filter((note) => !removed.has(`footnote:${note.id}`)) },
|
|
7
|
+
...source.endnotes && { endnotes: source.endnotes.filter((note) => !removed.has(`endnote:${note.id}`)) }
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { noteStoryInventory };
|