@stll/folio-core 0.52.0 → 0.53.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 -0
- package/dist/ai-edits/apply.d.ts +26 -1
- package/dist/ai-edits/apply.js +1519 -105
- package/dist/ai-edits/batch-claims.d.ts +130 -0
- package/dist/ai-edits/batch-claims.js +244 -0
- package/dist/ai-edits/blockRange.js +9 -3
- package/dist/ai-edits/character-boundaries.d.ts +43 -0
- package/dist/ai-edits/character-boundaries.js +95 -0
- package/dist/ai-edits/clean-text.d.ts +33 -3
- package/dist/ai-edits/clean-text.js +52 -6
- package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
- package/dist/ai-edits/comment-lifecycle.js +52 -0
- package/dist/ai-edits/headless.d.ts +71 -7
- package/dist/ai-edits/headless.js +381 -41
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/minimal-replacement.d.ts +21 -6
- package/dist/ai-edits/minimal-replacement.js +134 -15
- package/dist/ai-edits/newListNumbering.d.ts +23 -0
- package/dist/ai-edits/newListNumbering.js +94 -0
- package/dist/ai-edits/note-references.d.ts +96 -0
- package/dist/ai-edits/note-references.js +139 -0
- package/dist/ai-edits/pending-suggestions.d.ts +79 -0
- package/dist/ai-edits/pending-suggestions.js +306 -0
- package/dist/ai-edits/read.d.ts +1 -1
- package/dist/ai-edits/read.js +56 -43
- package/dist/ai-edits/result-validation.d.ts +29 -0
- package/dist/ai-edits/result-validation.js +164 -0
- package/dist/ai-edits/snapshot.d.ts +36 -3
- package/dist/ai-edits/snapshot.js +177 -23
- package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
- package/dist/ai-edits/table-cell-mutations.js +4 -1
- package/dist/ai-edits/table-geometry.d.ts +1 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
- package/dist/ai-edits/table-row-column-mutations.js +61 -26
- package/dist/ai-edits/types.d.ts +155 -16
- package/dist/compare/compare.d.ts +1 -1
- package/dist/compare/compare.js +65 -16
- package/dist/compare/content-alignment.js +13 -2
- package/dist/compare/content-types.d.ts +7 -0
- package/dist/compare/content.d.ts +4 -1
- package/dist/compare/content.js +6 -1
- package/dist/compare/formatting.js +2 -1
- package/dist/compare/inline-atoms.d.ts +21 -4
- package/dist/compare/inline-atoms.js +138 -16
- package/dist/compare/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +1 -1
- package/dist/compare/types.d.ts +4 -2
- package/dist/compare/types.js +2 -1
- package/dist/compare/verification.d.ts +1 -1
- package/dist/compare/verification.js +15 -3
- package/dist/controller/committedLayoutDiff.d.ts +25 -0
- package/dist/controller/committedLayoutDiff.js +71 -0
- package/dist/controller/contentControlWidgetController.d.ts +1 -1
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/fontReadiness.d.ts +70 -3
- package/dist/controller/fontReadiness.js +100 -10
- package/dist/controller/headerFooterEditorManager.js +6 -1
- package/dist/controller/layoutPipeline.d.ts +12 -10
- package/dist/controller/layoutPipeline.js +62 -9
- package/dist/controller/layoutRunOptions.d.ts +24 -0
- package/dist/controller/layoutRunOptions.js +17 -0
- package/dist/controller/layoutScheduler.d.ts +19 -14
- package/dist/controller/layoutScheduler.js +15 -12
- package/dist/controller/layoutSession.d.ts +26 -2
- package/dist/controller/layoutSession.js +1 -1
- package/dist/controller/noteEditorManager.js +8 -3
- package/dist/document-operations.d.ts +11 -2
- package/dist/document-operations.js +230 -21
- package/dist/docx/altChunk.d.ts +8 -0
- package/dist/docx/altChunk.js +17 -0
- package/dist/docx/attributeRemainder.d.ts +1 -1
- package/dist/docx/blockContentParser.d.ts +64 -1
- package/dist/docx/blockContentParser.js +17 -4
- package/dist/docx/blockCustomXmlShell.d.ts +7 -0
- package/dist/docx/blockCustomXmlShell.js +14 -0
- package/dist/docx/blockPlainText.js +7 -0
- package/dist/docx/commentAnchorIndex.js +1 -1
- package/dist/docx/commentRangeIntegrity.js +8 -3
- package/dist/docx/commentReferenceNormalization.js +1 -1
- package/dist/docx/commentReplyMarkers.js +20 -1
- package/dist/docx/compatibility.d.ts +1 -1
- package/dist/docx/compatibility.js +34 -13
- package/dist/docx/containerChildren.gen.d.ts +1 -1
- package/dist/docx/containerChildren.gen.js +1 -0
- package/dist/docx/documentParser.js +12 -12
- package/dist/docx/drawingIdNormalization.d.ts +3 -1
- package/dist/docx/drawingIdNormalization.js +6 -1
- package/dist/docx/ensureParaIds.js +119 -97
- package/dist/docx/footnoteParser.js +8 -4
- package/dist/docx/headerFooterParser.js +3 -3
- package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
- package/dist/docx/headerFooterReferenceNormalization.js +52 -27
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/listNumberingInstances.d.ts +59 -0
- package/dist/docx/listNumberingInstances.js +249 -0
- package/dist/docx/normalizeBaseDirection.js +2 -1
- package/dist/docx/opaqueCarrier.d.ts +7 -0
- package/dist/docx/opaqueCarrier.js +38 -0
- package/dist/docx/packageParts.js +4 -1
- package/dist/docx/paragraphParser.js +91 -24
- package/dist/docx/paragraphPropertySource.d.ts +4 -2
- package/dist/docx/paragraphPropertySource.js +7 -1
- package/dist/docx/paragraphTraversal.js +1 -0
- package/dist/docx/parseWarningMessage.js +4 -1
- package/dist/docx/parser.js +173 -20
- package/dist/docx/rezip.js +27 -5
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sdtPropertiesPatch.js +3 -0
- package/dist/docx/selectiveSave.js +16 -9
- package/dist/docx/selectiveXmlPatch.js +106 -37
- package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
- package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
- package/dist/docx/serializer/documentSerializer.js +2 -0
- package/dist/docx/serializer/headerFooterSerializer.js +2 -0
- package/dist/docx/serializer/noteSerializer.js +3 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -9
- package/dist/docx/serializer/tableSerializer.js +25 -38
- package/dist/docx/server/createBilingualDocument.js +3 -3
- package/dist/docx/tableParser.d.ts +157 -1
- package/dist/docx/tableParser.js +89 -10
- package/dist/docx/textBoxParser.js +1 -1
- package/dist/docx/unzip.d.ts +2 -1
- package/dist/docx/unzip.js +1 -1
- package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
- package/dist/docx/wordprocessingPrefixes.js +286 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
- package/dist/i18n/messages/catalogs.gen.js +153 -17
- package/dist/i18n/messages/messages.gen.d.ts +8 -0
- package/dist/internal/compare/inline-presentation.d.ts +2 -1
- package/dist/internal/compare/inline-presentation.js +11 -10
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
- package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
- package/dist/internal/indexedPositionMap.d.ts +11 -0
- package/dist/internal/indexedPositionMap.js +42 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/revisionResolutionEdits.d.ts +13 -0
- package/dist/internal/revisionResolutionEdits.js +19 -0
- package/dist/internal/revisionResolutionInline.d.ts +24 -0
- package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
- package/dist/internal/revisionResolutionStep.d.ts +34 -0
- package/dist/internal/revisionResolutionStep.js +282 -0
- package/dist/internal/revisionResolutionTracking.d.ts +11 -0
- package/dist/internal/revisionResolutionTracking.js +124 -0
- package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
- package/dist/internal/wholeStoryRevisionResolution.js +530 -0
- package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
- package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
- package/dist/layout-bridge/convert/tableConversion.js +1 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
- package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
- package/dist/layout-engine/layoutInstrumentation.js +4 -1
- package/dist/layout-engine/measure/cache.d.ts +18 -1
- package/dist/layout-engine/measure/cache.js +31 -1
- package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
- package/dist/layout-engine/measure/measureContainer.js +7 -47
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
- package/dist/layout-engine/measure/measureHelpers.js +22 -2
- package/dist/layout-engine/measure/measureWorker.js +2 -3
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
- package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/managers/ContextMenuManager.js +5 -2
- package/dist/managers/types.d.ts +3 -0
- package/dist/markdown/fromMarkdown.js +2 -1
- package/dist/markdown/internals.d.ts +11 -1
- package/dist/markdown/internals.js +24 -3
- package/dist/markdown/renderBlock.js +39 -9
- package/dist/markdown/renderParagraph.d.ts +13 -1
- package/dist/markdown/renderParagraph.js +85 -38
- package/dist/markdown/renderRuns.js +3 -10
- package/dist/markdown/renderTable.js +24 -12
- package/dist/markdown/trailers.js +2 -2
- package/dist/markdown/types.d.ts +29 -7
- package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
- package/dist/paged-layout/incrementalMeasure.js +1 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
- package/dist/paged-layout/transactionDirtyRange.js +29 -41
- package/dist/panel-layout.d.ts +83 -0
- package/dist/panel-layout.js +96 -0
- package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
- package/dist/prosemirror/anchoredTextBoxes.js +156 -0
- package/dist/prosemirror/attrs/index.d.ts +6 -2
- package/dist/prosemirror/attrs/index.js +172 -1
- package/dist/prosemirror/commands/comments.d.ts +7 -8
- package/dist/prosemirror/commands/comments.js +241 -207
- package/dist/prosemirror/commands/contentControls.d.ts +1 -1
- package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
- package/dist/prosemirror/commands/formatting.d.ts +1 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +7 -1
- package/dist/prosemirror/commands/paragraph.js +9 -1
- package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
- package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
- package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
- package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
- package/dist/prosemirror/commands/sectionBreak.js +2 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
- package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
- package/dist/prosemirror/conversion/toProseDoc.js +39 -4
- package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
- package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
- package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
- package/dist/prosemirror/extensions/StarterKit.js +3 -1
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
- package/dist/prosemirror/extensions/types.d.ts +4 -1
- package/dist/prosemirror/findReplaceSelection.js +1 -1
- package/dist/prosemirror/index.d.ts +3 -2
- package/dist/prosemirror/index.js +3 -2
- package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
- package/dist/prosemirror/indexedNodeLookup.js +103 -0
- package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
- package/dist/prosemirror/listAutoformatMarkers.js +98 -0
- package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
- package/dist/prosemirror/listInstanceReferences.js +72 -0
- package/dist/prosemirror/listLabels.d.ts +24 -0
- package/dist/prosemirror/listLabels.js +111 -0
- package/dist/prosemirror/listNumbering.d.ts +110 -0
- package/dist/prosemirror/listNumbering.js +345 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
- package/dist/prosemirror/listRenderingAttrs.js +8 -1
- package/dist/prosemirror/markupViewProjection.d.ts +95 -0
- package/dist/prosemirror/markupViewProjection.js +83 -0
- package/dist/prosemirror/numberingAttr.d.ts +9 -1
- package/dist/prosemirror/numberingAttr.js +9 -1
- package/dist/prosemirror/outlineLevelAttr.js +1 -1
- package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
- package/dist/prosemirror/paragraphIndentation.js +29 -7
- package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
- package/dist/prosemirror/paragraphMarkJoin.js +24 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
- package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
- package/dist/prosemirror/plugins/documentNumbering.js +97 -11
- package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
- package/dist/prosemirror/plugins/documentStyles.js +14 -1
- package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
- package/dist/prosemirror/plugins/suggestionMode.js +116 -52
- package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
- package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
- package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
- package/dist/prosemirror/positionSweep.d.ts +24 -0
- package/dist/prosemirror/positionSweep.js +222 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
- package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
- package/dist/prosemirror/replacedAnnotations.js +170 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/index.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +42 -11
- package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
- package/dist/prosemirror/sectionMarkEdits.js +59 -0
- package/dist/prosemirror/storyListNumbering.d.ts +17 -0
- package/dist/prosemirror/storyListNumbering.js +102 -0
- package/dist/prosemirror/tableGridMutation.d.ts +48 -2
- package/dist/prosemirror/tableGridMutation.js +106 -1
- package/dist/prosemirror/validation.js +5 -1
- package/dist/redline.js +93 -9
- package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
- package/dist/render-dom/BodySelectionOverlay.js +3 -2
- package/dist/utils/findReplace.js +2 -2
- package/dist/utils/mergeDocumentContent.js +1 -1
- package/dist/utils/noteReferenceLabels.d.ts +36 -0
- package/dist/utils/noteReferenceLabels.js +32 -0
- package/dist/utils/replaceText.js +1 -1
- package/dist/version-comparison.d.ts +1 -0
- package/dist/version-comparison.js +1 -0
- package/package.json +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { FolioAIEditAppliedOperation, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
|
|
2
|
+
import { FolioDocumentStoryHandle } from "./headless.js";
|
|
3
|
+
import { EditorState } from "prosemirror-state";
|
|
4
|
+
//#region src/ai-edits/pending-suggestions.d.ts
|
|
5
|
+
declare const FOLIO_PENDING_SUGGESTION_VERSION: 1;
|
|
6
|
+
/** A host-owned, JSON-serializable proposal. It is never written into the DOCX. */
|
|
7
|
+
type FolioPendingSuggestionRecord = {
|
|
8
|
+
version: typeof FOLIO_PENDING_SUGGESTION_VERSION;
|
|
9
|
+
suggestionId: string;
|
|
10
|
+
operation: FolioAIEditOperation;
|
|
11
|
+
story: FolioDocumentStoryHandle;
|
|
12
|
+
anchor: {
|
|
13
|
+
blockId: string;
|
|
14
|
+
paraId: string | null;
|
|
15
|
+
originalTextHash: string;
|
|
16
|
+
selectedTextHash?: string;
|
|
17
|
+
startOffset?: number;
|
|
18
|
+
endOffset?: number;
|
|
19
|
+
};
|
|
20
|
+
author: string;
|
|
21
|
+
provenance: "suggested";
|
|
22
|
+
sourceDocumentFingerprint: string;
|
|
23
|
+
commentId?: number;
|
|
24
|
+
};
|
|
25
|
+
type FolioPendingSuggestionStaleReason = "missingAnchor" | "ambiguousAnchor" | "textChanged" | "unsupportedVersion" | "documentChanged" | "invalidRecord" | "applyFailed";
|
|
26
|
+
type FolioPendingSuggestionLoadResult = {
|
|
27
|
+
status: "restaged";
|
|
28
|
+
suggestionId: string;
|
|
29
|
+
} | {
|
|
30
|
+
status: "stale";
|
|
31
|
+
suggestionId: string | null;
|
|
32
|
+
reason: FolioPendingSuggestionStaleReason;
|
|
33
|
+
};
|
|
34
|
+
type RecordAppliedOptions = {
|
|
35
|
+
snapshot: FolioAIEditSnapshot;
|
|
36
|
+
story: FolioDocumentStoryHandle;
|
|
37
|
+
operations: readonly FolioAIEditOperation[];
|
|
38
|
+
applied: readonly FolioAIEditAppliedOperation[];
|
|
39
|
+
author: string;
|
|
40
|
+
activeSuggestionIds: ReadonlySet<string>;
|
|
41
|
+
};
|
|
42
|
+
type ReplaceRestagedStoryOptions = {
|
|
43
|
+
story: FolioDocumentStoryHandle;
|
|
44
|
+
restaged: readonly {
|
|
45
|
+
snapshot: FolioAIEditSnapshot;
|
|
46
|
+
operation: FolioAIEditOperation;
|
|
47
|
+
applied: FolioAIEditAppliedOperation;
|
|
48
|
+
author: string;
|
|
49
|
+
}[];
|
|
50
|
+
activeSuggestionIds: ReadonlySet<string>;
|
|
51
|
+
};
|
|
52
|
+
type LoadPendingSuggestionsOptions = {
|
|
53
|
+
records: readonly unknown[];
|
|
54
|
+
snapshotForStory: (story: FolioDocumentStoryHandle) => FolioAIEditSnapshot | null;
|
|
55
|
+
sourceSnapshotForStory: (story: FolioDocumentStoryHandle, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot | null;
|
|
56
|
+
apply: (record: FolioPendingSuggestionRecord, snapshot: FolioAIEditSnapshot) => FolioAIEditApplyResult;
|
|
57
|
+
activeSuggestionIds: () => ReadonlySet<string>;
|
|
58
|
+
};
|
|
59
|
+
type ExportPendingSuggestionsOptions = {
|
|
60
|
+
activeSuggestionIds: ReadonlySet<string>;
|
|
61
|
+
snapshotForStory: (story: FolioDocumentStoryHandle, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot | null;
|
|
62
|
+
};
|
|
63
|
+
/** Drop only comments attached to proposals that have not been accepted. */
|
|
64
|
+
declare const withoutPendingSuggestionCommentMarks: (state: EditorState, commentIds: ReadonlySet<number>) => EditorState;
|
|
65
|
+
/** The saved package's story, with all pending proposals removed. */
|
|
66
|
+
declare const createPendingSuggestionSourceSnapshot: (state: EditorState, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot;
|
|
67
|
+
/** Tracks only operations that successfully staged in `suggested` mode. */
|
|
68
|
+
declare class FolioPendingSuggestionRegistry {
|
|
69
|
+
private readonly records;
|
|
70
|
+
private readonly sourceFingerprints;
|
|
71
|
+
clear(): void;
|
|
72
|
+
recordApplied({ snapshot, story, operations, applied, author, activeSuggestionIds }: RecordAppliedOptions): void;
|
|
73
|
+
/** Rebase records after ordinary revisions change their source story. */
|
|
74
|
+
replaceRestagedStory({ story, restaged, activeSuggestionIds }: ReplaceRestagedStoryOptions): void;
|
|
75
|
+
exportPendingSuggestions({ activeSuggestionIds, snapshotForStory }: ExportPendingSuggestionsOptions): FolioPendingSuggestionRecord[];
|
|
76
|
+
loadPendingSuggestions({ records, snapshotForStory, sourceSnapshotForStory, apply, activeSuggestionIds }: LoadPendingSuggestionsOptions): FolioPendingSuggestionLoadResult[];
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { FOLIO_PENDING_SUGGESTION_VERSION, FolioPendingSuggestionLoadResult, FolioPendingSuggestionRecord, FolioPendingSuggestionRegistry, FolioPendingSuggestionStaleReason, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks };
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { parseFolioDocumentOperationBatch } from "../document-operations.js";
|
|
2
|
+
import { rejectAllSuggestions } from "../prosemirror/commands/comments.js";
|
|
3
|
+
import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
|
|
4
|
+
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
5
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
6
|
+
import { createFolioAIEditSnapshotWithStyleResolver, hashFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
|
|
7
|
+
//#region src/ai-edits/pending-suggestions.ts
|
|
8
|
+
const FOLIO_PENDING_SUGGESTION_VERSION = 1;
|
|
9
|
+
/** Drop only comments attached to proposals that have not been accepted. */
|
|
10
|
+
const withoutPendingSuggestionCommentMarks = (state, commentIds) => {
|
|
11
|
+
if (commentIds.size === 0) return state;
|
|
12
|
+
let transaction = state.tr;
|
|
13
|
+
state.doc.descendants((node, position) => {
|
|
14
|
+
for (const mark of node.marks) if (mark.type.name === "comment" && commentIds.has(mark.attrs["commentId"])) transaction = transaction.removeMark(position, position + node.nodeSize, mark);
|
|
15
|
+
});
|
|
16
|
+
return transaction.docChanged ? state.apply(transaction) : state;
|
|
17
|
+
};
|
|
18
|
+
/** The saved package's story, with all pending proposals removed. */
|
|
19
|
+
const createPendingSuggestionSourceSnapshot = (state, commentIds) => {
|
|
20
|
+
let source = state;
|
|
21
|
+
rejectAllSuggestions()(state, (transaction) => {
|
|
22
|
+
source = state.apply(transaction);
|
|
23
|
+
});
|
|
24
|
+
source = withoutPendingSuggestionCommentMarks(source, commentIds);
|
|
25
|
+
return createFolioAIEditSnapshotWithStyleResolver(source.doc, getDocumentStyleResolver(source));
|
|
26
|
+
};
|
|
27
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
+
const operationBlockId = (operation) => {
|
|
29
|
+
switch (operation.type) {
|
|
30
|
+
case "replaceRange":
|
|
31
|
+
case "commentOnRange":
|
|
32
|
+
case "formatRange": return operation.range.blockId;
|
|
33
|
+
default: return operation.blockId;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const storyFromUnknown = (value) => {
|
|
37
|
+
if (!isObject(value)) return null;
|
|
38
|
+
switch (value["type"]) {
|
|
39
|
+
case "main": return { type: "main" };
|
|
40
|
+
case "header":
|
|
41
|
+
case "footer": return typeof value["relationshipId"] === "string" ? {
|
|
42
|
+
type: value["type"],
|
|
43
|
+
relationshipId: value["relationshipId"]
|
|
44
|
+
} : null;
|
|
45
|
+
case "footnote":
|
|
46
|
+
case "endnote": return typeof value["noteId"] === "number" && Number.isSafeInteger(value["noteId"]) ? {
|
|
47
|
+
type: value["type"],
|
|
48
|
+
noteId: value["noteId"]
|
|
49
|
+
} : null;
|
|
50
|
+
default: return null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const fingerprintOf = (snapshot, story) => hashFolioAIBlockText(canonicalJson([story, snapshot.blocks.map((block) => [block, snapshot.anchors[block.id]?.structuralBoundaryHash])]));
|
|
54
|
+
const paraIdCountsOf = (snapshot) => {
|
|
55
|
+
const counts = /* @__PURE__ */ new Map();
|
|
56
|
+
sourceDocumentOf(snapshot).descendants((node) => {
|
|
57
|
+
const paraId = node.attrs["paraId"];
|
|
58
|
+
if (node.type.name === "paragraph" && typeof paraId === "string") counts.set(paraId, (counts.get(paraId) ?? 0) + 1);
|
|
59
|
+
});
|
|
60
|
+
return counts;
|
|
61
|
+
};
|
|
62
|
+
const recordKey = (record) => canonicalJson([
|
|
63
|
+
record.story,
|
|
64
|
+
record.suggestionId,
|
|
65
|
+
record.operation.id
|
|
66
|
+
]);
|
|
67
|
+
const parseRecord = (value) => {
|
|
68
|
+
if (!isObject(value) || !isObject(value["anchor"])) return null;
|
|
69
|
+
const story = storyFromUnknown(value["story"]);
|
|
70
|
+
const anchor = value["anchor"];
|
|
71
|
+
if (story === null || typeof value["suggestionId"] !== "string" || typeof value["author"] !== "string" || value["provenance"] !== "suggested" || typeof value["sourceDocumentFingerprint"] !== "string" || value["commentId"] !== void 0 && (typeof value["commentId"] !== "number" || !Number.isSafeInteger(value["commentId"])) || typeof anchor["blockId"] !== "string" || anchor["paraId"] !== null && typeof anchor["paraId"] !== "string" || typeof anchor["originalTextHash"] !== "string") return null;
|
|
72
|
+
let operation;
|
|
73
|
+
try {
|
|
74
|
+
const parsed = parseFolioDocumentOperationBatch({
|
|
75
|
+
version: 1,
|
|
76
|
+
operations: [value["operation"]],
|
|
77
|
+
mode: "suggested"
|
|
78
|
+
}).operations.at(0);
|
|
79
|
+
if (!parsed) return null;
|
|
80
|
+
operation = parsed;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (operationBlockId(operation) !== anchor["blockId"] || (operation.suggestionId ?? operation.id) !== value["suggestionId"] || getFolioParaIdFromBlockId(anchor["blockId"]) !== anchor["paraId"]) return null;
|
|
85
|
+
if (operation.type === "replaceRange" || operation.type === "formatRange") {
|
|
86
|
+
if (anchor["selectedTextHash"] !== operation.range.selectedTextHash || anchor["startOffset"] !== operation.range.startOffset || anchor["endOffset"] !== operation.range.endOffset) return null;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
version: 1,
|
|
90
|
+
suggestionId: value["suggestionId"],
|
|
91
|
+
operation,
|
|
92
|
+
story,
|
|
93
|
+
anchor: {
|
|
94
|
+
blockId: anchor["blockId"],
|
|
95
|
+
paraId: anchor["paraId"],
|
|
96
|
+
originalTextHash: anchor["originalTextHash"],
|
|
97
|
+
...typeof anchor["selectedTextHash"] === "string" && { selectedTextHash: anchor["selectedTextHash"] },
|
|
98
|
+
...typeof anchor["startOffset"] === "number" && { startOffset: anchor["startOffset"] },
|
|
99
|
+
...typeof anchor["endOffset"] === "number" && { endOffset: anchor["endOffset"] }
|
|
100
|
+
},
|
|
101
|
+
author: value["author"],
|
|
102
|
+
provenance: "suggested",
|
|
103
|
+
sourceDocumentFingerprint: value["sourceDocumentFingerprint"],
|
|
104
|
+
...typeof value["commentId"] === "number" && Number.isSafeInteger(value["commentId"]) && { commentId: value["commentId"] }
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/** Tracks only operations that successfully staged in `suggested` mode. */
|
|
108
|
+
var FolioPendingSuggestionRegistry = class {
|
|
109
|
+
records = /* @__PURE__ */ new Map();
|
|
110
|
+
sourceFingerprints = /* @__PURE__ */ new Map();
|
|
111
|
+
clear() {
|
|
112
|
+
this.records.clear();
|
|
113
|
+
this.sourceFingerprints.clear();
|
|
114
|
+
}
|
|
115
|
+
recordApplied({ snapshot, story, operations, applied, author, activeSuggestionIds }) {
|
|
116
|
+
if (applied.length === 0) return;
|
|
117
|
+
const storyKey = canonicalJson(story);
|
|
118
|
+
for (const [key, record] of this.records) if (!activeSuggestionIds.has(record.suggestionId)) this.records.delete(key);
|
|
119
|
+
if (![...this.records.values()].some((record) => canonicalJson(record.story) === storyKey)) this.sourceFingerprints.delete(storyKey);
|
|
120
|
+
const fingerprint = this.sourceFingerprints.get(storyKey) ?? fingerprintOf(snapshot, story);
|
|
121
|
+
this.sourceFingerprints.set(storyKey, fingerprint);
|
|
122
|
+
const appliedById = new Map(applied.map((entry) => [entry.id, entry]));
|
|
123
|
+
for (const operation of operations) {
|
|
124
|
+
const outcome = appliedById.get(operation.id);
|
|
125
|
+
if (!outcome?.suggestionId) continue;
|
|
126
|
+
const blockId = operationBlockId(operation);
|
|
127
|
+
const anchor = snapshot.anchors[blockId];
|
|
128
|
+
if (!anchor) continue;
|
|
129
|
+
const range = operation.type === "replaceRange" || operation.type === "formatRange" ? operation.range : null;
|
|
130
|
+
const record = {
|
|
131
|
+
version: 1,
|
|
132
|
+
suggestionId: outcome.suggestionId,
|
|
133
|
+
operation: structuredClone(operation),
|
|
134
|
+
story: structuredClone(story),
|
|
135
|
+
anchor: {
|
|
136
|
+
blockId,
|
|
137
|
+
paraId: getFolioParaIdFromBlockId(blockId),
|
|
138
|
+
originalTextHash: anchor.textHash,
|
|
139
|
+
...range !== null && {
|
|
140
|
+
selectedTextHash: range.selectedTextHash,
|
|
141
|
+
startOffset: range.startOffset,
|
|
142
|
+
endOffset: range.endOffset
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
author,
|
|
146
|
+
provenance: "suggested",
|
|
147
|
+
sourceDocumentFingerprint: fingerprint,
|
|
148
|
+
...outcome.commentId !== void 0 && { commentId: outcome.commentId }
|
|
149
|
+
};
|
|
150
|
+
this.records.set(recordKey(record), record);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Rebase records after ordinary revisions change their source story. */
|
|
154
|
+
replaceRestagedStory({ story, restaged, activeSuggestionIds }) {
|
|
155
|
+
if (restaged.length === 0) return;
|
|
156
|
+
const storyKey = canonicalJson(story);
|
|
157
|
+
for (const [key, record] of this.records) if (canonicalJson(record.story) === storyKey) this.records.delete(key);
|
|
158
|
+
const first = restaged.at(0);
|
|
159
|
+
if (!first) return;
|
|
160
|
+
this.sourceFingerprints.set(storyKey, fingerprintOf(first.snapshot, story));
|
|
161
|
+
for (const { snapshot, operation, applied, author } of restaged) this.recordApplied({
|
|
162
|
+
snapshot,
|
|
163
|
+
story,
|
|
164
|
+
operations: [operation],
|
|
165
|
+
applied: [applied],
|
|
166
|
+
author,
|
|
167
|
+
activeSuggestionIds
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
exportPendingSuggestions({ activeSuggestionIds, snapshotForStory }) {
|
|
171
|
+
const pending = [];
|
|
172
|
+
const fingerprints = /* @__PURE__ */ new Map();
|
|
173
|
+
const commentIdsByStory = /* @__PURE__ */ new Map();
|
|
174
|
+
for (const record of this.records.values()) {
|
|
175
|
+
if (!activeSuggestionIds.has(record.suggestionId) || record.commentId === void 0) continue;
|
|
176
|
+
const storyKey = canonicalJson(record.story);
|
|
177
|
+
const commentIds = commentIdsByStory.get(storyKey) ?? /* @__PURE__ */ new Set();
|
|
178
|
+
commentIds.add(record.commentId);
|
|
179
|
+
commentIdsByStory.set(storyKey, commentIds);
|
|
180
|
+
}
|
|
181
|
+
for (const [key, record] of this.records) if (activeSuggestionIds.has(record.suggestionId)) {
|
|
182
|
+
const storyKey = canonicalJson(record.story);
|
|
183
|
+
let fingerprint = fingerprints.get(storyKey);
|
|
184
|
+
if (fingerprint === void 0) {
|
|
185
|
+
const snapshot = snapshotForStory(record.story, commentIdsByStory.get(storyKey) ?? /* @__PURE__ */ new Set());
|
|
186
|
+
if (!snapshot) continue;
|
|
187
|
+
fingerprint = fingerprintOf(snapshot, record.story);
|
|
188
|
+
fingerprints.set(storyKey, fingerprint);
|
|
189
|
+
}
|
|
190
|
+
const current = {
|
|
191
|
+
...record,
|
|
192
|
+
sourceDocumentFingerprint: fingerprint
|
|
193
|
+
};
|
|
194
|
+
this.records.set(key, current);
|
|
195
|
+
pending.push(structuredClone(current));
|
|
196
|
+
} else this.records.delete(key);
|
|
197
|
+
for (const [storyKey, fingerprint] of fingerprints) this.sourceFingerprints.set(storyKey, fingerprint);
|
|
198
|
+
if (pending.length === 0) this.sourceFingerprints.clear();
|
|
199
|
+
return pending;
|
|
200
|
+
}
|
|
201
|
+
loadPendingSuggestions({ records, snapshotForStory, sourceSnapshotForStory, apply, activeSuggestionIds }) {
|
|
202
|
+
const baselines = /* @__PURE__ */ new Map();
|
|
203
|
+
const changedStories = /* @__PURE__ */ new Set();
|
|
204
|
+
return records.map((value) => {
|
|
205
|
+
const suggestionId = isObject(value) && typeof value["suggestionId"] === "string" ? value["suggestionId"] : null;
|
|
206
|
+
if (!isObject(value) || value["version"] !== 1) return {
|
|
207
|
+
status: "stale",
|
|
208
|
+
suggestionId,
|
|
209
|
+
reason: "unsupportedVersion"
|
|
210
|
+
};
|
|
211
|
+
const record = parseRecord(value);
|
|
212
|
+
if (!record) return {
|
|
213
|
+
status: "stale",
|
|
214
|
+
suggestionId,
|
|
215
|
+
reason: "invalidRecord"
|
|
216
|
+
};
|
|
217
|
+
const key = recordKey(record);
|
|
218
|
+
const existing = this.records.get(key);
|
|
219
|
+
if (existing && activeSuggestionIds().has(record.suggestionId)) {
|
|
220
|
+
const { commentId: _existingCommentId, ...existingProposal } = existing;
|
|
221
|
+
const { commentId: _loadedCommentId, ...loadedProposal } = record;
|
|
222
|
+
return canonicalJson(existingProposal) === canonicalJson(loadedProposal) ? {
|
|
223
|
+
status: "restaged",
|
|
224
|
+
suggestionId: record.suggestionId
|
|
225
|
+
} : {
|
|
226
|
+
status: "stale",
|
|
227
|
+
suggestionId,
|
|
228
|
+
reason: "invalidRecord"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const storyKey = canonicalJson(record.story);
|
|
232
|
+
if (!baselines.has(storyKey)) {
|
|
233
|
+
const snapshot = snapshotForStory(record.story);
|
|
234
|
+
const active = activeSuggestionIds();
|
|
235
|
+
const commentIds = /* @__PURE__ */ new Set();
|
|
236
|
+
for (const existingRecord of this.records.values()) if (canonicalJson(existingRecord.story) === storyKey && active.has(existingRecord.suggestionId) && existingRecord.commentId !== void 0) commentIds.add(existingRecord.commentId);
|
|
237
|
+
const source = sourceSnapshotForStory(record.story, commentIds);
|
|
238
|
+
baselines.set(storyKey, snapshot && source ? {
|
|
239
|
+
snapshot,
|
|
240
|
+
fingerprint: fingerprintOf(source, record.story)
|
|
241
|
+
} : null);
|
|
242
|
+
}
|
|
243
|
+
const baseline = baselines.get(storyKey);
|
|
244
|
+
if (!baseline || record.anchor.paraId === null) return {
|
|
245
|
+
status: "stale",
|
|
246
|
+
suggestionId,
|
|
247
|
+
reason: "missingAnchor"
|
|
248
|
+
};
|
|
249
|
+
const snapshot = changedStories.has(storyKey) ? snapshotForStory(record.story) : baseline.snapshot;
|
|
250
|
+
if (!snapshot) return {
|
|
251
|
+
status: "stale",
|
|
252
|
+
suggestionId,
|
|
253
|
+
reason: "missingAnchor"
|
|
254
|
+
};
|
|
255
|
+
const multiplicity = paraIdCountsOf(snapshot).get(record.anchor.paraId) ?? 0;
|
|
256
|
+
if (multiplicity === 0 || !snapshot.anchors[record.anchor.paraId]) return {
|
|
257
|
+
status: "stale",
|
|
258
|
+
suggestionId,
|
|
259
|
+
reason: "missingAnchor"
|
|
260
|
+
};
|
|
261
|
+
if (multiplicity > 1) return {
|
|
262
|
+
status: "stale",
|
|
263
|
+
suggestionId,
|
|
264
|
+
reason: "ambiguousAnchor"
|
|
265
|
+
};
|
|
266
|
+
const anchor = snapshot.anchors[record.anchor.paraId];
|
|
267
|
+
if (!anchor || anchor.textHash !== record.anchor.originalTextHash) return {
|
|
268
|
+
status: "stale",
|
|
269
|
+
suggestionId,
|
|
270
|
+
reason: "textChanged"
|
|
271
|
+
};
|
|
272
|
+
const { startOffset, endOffset, selectedTextHash } = record.anchor;
|
|
273
|
+
if (startOffset !== void 0 && endOffset !== void 0 && selectedTextHash !== void 0 && hashFolioAIBlockText(anchor.text.slice(startOffset, endOffset)) !== selectedTextHash) return {
|
|
274
|
+
status: "stale",
|
|
275
|
+
suggestionId,
|
|
276
|
+
reason: "textChanged"
|
|
277
|
+
};
|
|
278
|
+
if (baseline.fingerprint !== record.sourceDocumentFingerprint) return {
|
|
279
|
+
status: "stale",
|
|
280
|
+
suggestionId,
|
|
281
|
+
reason: "documentChanged"
|
|
282
|
+
};
|
|
283
|
+
const applied = apply(record, snapshot).applied.find((entry) => entry.id === record.operation.id);
|
|
284
|
+
if (!applied) return {
|
|
285
|
+
status: "stale",
|
|
286
|
+
suggestionId,
|
|
287
|
+
reason: "applyFailed"
|
|
288
|
+
};
|
|
289
|
+
this.sourceFingerprints.set(storyKey, record.sourceDocumentFingerprint);
|
|
290
|
+
if (!this.records.has(key)) {
|
|
291
|
+
const { commentId: _oldCommentId, ...rest } = record;
|
|
292
|
+
this.records.set(key, structuredClone({
|
|
293
|
+
...rest,
|
|
294
|
+
...applied.commentId !== void 0 && { commentId: applied.commentId }
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
changedStories.add(storyKey);
|
|
298
|
+
return {
|
|
299
|
+
status: "restaged",
|
|
300
|
+
suggestionId: record.suggestionId
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
//#endregion
|
|
306
|
+
export { FOLIO_PENDING_SUGGESTION_VERSION, FolioPendingSuggestionRegistry, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks };
|
package/dist/ai-edits/read.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ declare const getTrackedChangeStatsFromDoc: (doc: Node) => {
|
|
|
78
78
|
/**
|
|
79
79
|
* The comment anchors present in the body, read from the `comment` mark the
|
|
80
80
|
* editor renders. Each entry carries the anchored text and containing block id;
|
|
81
|
-
*
|
|
81
|
+
* ranges for one comment id within the body fold into a single anchor.
|
|
82
82
|
*/
|
|
83
83
|
declare const getCommentAnchorsFromDoc: (doc: Node) => FolioCommentAnchor[];
|
|
84
84
|
//#endregion
|
package/dist/ai-edits/read.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
1
|
+
import { expectRunPropertyChangeMarkAttrs, expectTrackedChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
2
2
|
import { getFolioNodeRevisionCarriers } from "../prosemirror/revisionCarriers.js";
|
|
3
3
|
import { expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
4
4
|
import { getTableCellMergeChange } from "../prosemirror/tableCellMergeRevision.js";
|
|
5
|
-
import {
|
|
5
|
+
import { readerTextOf } from "./note-references.js";
|
|
6
|
+
import { collectNoteReferenceLabels, createFolioAIEditSnapshot, sourceDocumentOf } from "./snapshot.js";
|
|
6
7
|
//#region src/ai-edits/read.ts
|
|
7
8
|
/**
|
|
8
9
|
* Runtime census of every tracked-change discriminator the reviewer exposes.
|
|
@@ -50,12 +51,6 @@ const firstBlockIdWithin = ({ node, nodePos, blockStarts }) => {
|
|
|
50
51
|
});
|
|
51
52
|
return blockId;
|
|
52
53
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Whether a run's revision inside a marked row is that row's own. Folio writes
|
|
55
|
-
* both halves under one revision id; Word mints a fresh `w:id` per element, so
|
|
56
|
-
* an author + timestamp match counts too. A third party's edit inside the row
|
|
57
|
-
* matches neither and keeps its own entry.
|
|
58
|
-
*/
|
|
59
54
|
const belongsToRowRevision = (scope, revision) => revision.id === scope.change.id || revision.author === scope.change.author && revision.date === scope.change.date;
|
|
60
55
|
/** Shared revision interpreter; a null block map omits unrelated block-id projection. */
|
|
61
56
|
const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
|
|
@@ -65,6 +60,7 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
|
|
|
65
60
|
const structuralChangeCellPositions = /* @__PURE__ */ new Map();
|
|
66
61
|
const rowRevisionScopes = [];
|
|
67
62
|
const derivedFormattingRepresentationPositions = /* @__PURE__ */ new Set();
|
|
63
|
+
const noteReferences = collectNoteReferenceLabels(doc);
|
|
68
64
|
let currentBlockId = null;
|
|
69
65
|
doc.descendants((node, pos) => {
|
|
70
66
|
while (pos >= (rowRevisionScopes.at(-1)?.end ?? Number.POSITIVE_INFINITY)) rowRevisionScopes.pop();
|
|
@@ -207,7 +203,7 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
|
|
|
207
203
|
}
|
|
208
204
|
}
|
|
209
205
|
}
|
|
210
|
-
const text = node
|
|
206
|
+
const text = readerTextOf(node, noteReferences);
|
|
211
207
|
for (const mark of node.marks) {
|
|
212
208
|
if (mark.type.name === "runPropertyChange") continue;
|
|
213
209
|
if (typeof mark.attrs["revisionId"] !== "number") continue;
|
|
@@ -215,28 +211,39 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
|
|
|
215
211
|
if (mark.type === insertionType) kind = "insertion";
|
|
216
212
|
else if (mark.type === deletionType) kind = "deletion";
|
|
217
213
|
else continue;
|
|
218
|
-
const revisionId = mark.attrs["revisionId"];
|
|
219
214
|
const author = mark.attrs["author"];
|
|
220
215
|
const date = mark.attrs["date"];
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
216
|
+
const layers = [...(expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors ?? []).map((ancestor) => ({
|
|
217
|
+
kind: ancestor.type === "insertion" || ancestor.type === "moveTo" ? "insertion" : "deletion",
|
|
218
|
+
revision: {
|
|
219
|
+
id: ancestor.revisionId,
|
|
220
|
+
author: ancestor.author,
|
|
221
|
+
date: ancestor.date ?? null
|
|
222
|
+
}
|
|
223
|
+
})), {
|
|
224
|
+
kind,
|
|
225
|
+
revision: {
|
|
226
|
+
id: mark.attrs["revisionId"],
|
|
227
|
+
author: typeof author === "string" ? author : "",
|
|
228
|
+
date: typeof date === "string" ? date : null
|
|
229
|
+
}
|
|
230
|
+
}];
|
|
231
|
+
for (const { kind: layerKind, revision } of layers) {
|
|
232
|
+
const rowScope = rowRevisionScopes.at(-1);
|
|
233
|
+
if (rowScope?.kind === layerKind && belongsToRowRevision(rowScope, revision)) continue;
|
|
234
|
+
const key = `${currentBlockId ?? ""}:${layerKind}:${revision.id}`;
|
|
235
|
+
const existing = grouped.get(key);
|
|
236
|
+
if (existing) {
|
|
237
|
+
existing.text += text;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
grouped.set(key, {
|
|
241
|
+
...revision,
|
|
242
|
+
type: layerKind,
|
|
243
|
+
text,
|
|
244
|
+
blockId: currentBlockId
|
|
245
|
+
});
|
|
233
246
|
}
|
|
234
|
-
grouped.set(key, {
|
|
235
|
-
...revision,
|
|
236
|
-
type: kind,
|
|
237
|
-
text,
|
|
238
|
-
blockId: currentBlockId
|
|
239
|
-
});
|
|
240
247
|
}
|
|
241
248
|
});
|
|
242
249
|
return [...grouped.values()];
|
|
@@ -266,38 +273,44 @@ const getTrackedChangeStatsFromDoc = (doc) => {
|
|
|
266
273
|
/**
|
|
267
274
|
* The comment anchors present in the body, read from the `comment` mark the
|
|
268
275
|
* editor renders. Each entry carries the anchored text and containing block id;
|
|
269
|
-
*
|
|
276
|
+
* ranges for one comment id within the body fold into a single anchor.
|
|
270
277
|
*/
|
|
271
278
|
const getCommentAnchorsFromDoc = (doc) => {
|
|
272
279
|
const commentType = doc.type.schema.marks["comment"];
|
|
273
280
|
if (!commentType) return [];
|
|
274
281
|
const blockStarts = blockStartIdsFromDoc(doc);
|
|
275
|
-
const
|
|
282
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
283
|
+
const noteReferences = collectNoteReferenceLabels(doc);
|
|
276
284
|
let currentBlockId = null;
|
|
285
|
+
let bodyText = "";
|
|
277
286
|
doc.descendants((node, pos) => {
|
|
278
287
|
if (node.isTextblock) {
|
|
279
288
|
currentBlockId = blockStarts.get(pos) ?? null;
|
|
280
289
|
return true;
|
|
281
290
|
}
|
|
282
291
|
if (!node.isInline || node.text === void 0) return;
|
|
283
|
-
const text = node
|
|
292
|
+
const text = readerTextOf(node, noteReferences);
|
|
293
|
+
const end = bodyText.length + text.length;
|
|
284
294
|
for (const mark of node.marks) {
|
|
285
295
|
if (mark.type !== commentType || typeof mark.attrs["commentId"] !== "number") continue;
|
|
286
296
|
const commentId = mark.attrs["commentId"];
|
|
287
|
-
const
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
existing.quote += text;
|
|
297
|
-
existing.blockId ??= currentBlockId;
|
|
297
|
+
const range = ranges.get(commentId);
|
|
298
|
+
if (range) {
|
|
299
|
+
range.end = end;
|
|
300
|
+
range.blockId ??= currentBlockId;
|
|
301
|
+
} else ranges.set(commentId, {
|
|
302
|
+
blockId: currentBlockId,
|
|
303
|
+
start: bodyText.length,
|
|
304
|
+
end
|
|
305
|
+
});
|
|
298
306
|
}
|
|
307
|
+
bodyText += text;
|
|
299
308
|
});
|
|
300
|
-
return [...
|
|
309
|
+
return [...ranges].map(([commentId, { blockId, start, end }]) => ({
|
|
310
|
+
commentId,
|
|
311
|
+
blockId,
|
|
312
|
+
quote: bodyText.slice(start, end)
|
|
313
|
+
}));
|
|
301
314
|
};
|
|
302
315
|
//#endregion
|
|
303
316
|
export { FOLIO_REVIEW_CHANGE_KINDS, getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { Node } from "prosemirror-model";
|
|
3
|
+
import { ValidateDocumentModelIssue } from "@stll/docx-core";
|
|
4
|
+
//#region src/ai-edits/result-validation.d.ts
|
|
5
|
+
/** What the story's package knows that the story itself does not. */
|
|
6
|
+
type FolioOperationResultValidationContext = {
|
|
7
|
+
/**
|
|
8
|
+
* The document's numbering definitions; `null` when the story states none,
|
|
9
|
+
* `undefined` when it cannot say (no numbering plugin), which leaves
|
|
10
|
+
* numbering references unchecked.
|
|
11
|
+
*/
|
|
12
|
+
numbering: document_d_exports.NumberingDefinitions | null | undefined;
|
|
13
|
+
/** Comment ids the batch allocated; the host holds the comments themselves. */
|
|
14
|
+
createdCommentIds: readonly number[];
|
|
15
|
+
/**
|
|
16
|
+
* The author the host stamps on those comments, when the batch names one;
|
|
17
|
+
* the host writes the batch's author, so a blank one fails there too.
|
|
18
|
+
*/
|
|
19
|
+
commentAuthor?: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The save-validator errors the batch introduced: errors in the changed window
|
|
23
|
+
* of `after` that the same window of `before` did not already report. Empty
|
|
24
|
+
* when the result is as saveable as the story it started from.
|
|
25
|
+
*/
|
|
26
|
+
declare const findIntroducedModelErrors: (before: Node, after: Node, context: FolioOperationResultValidationContext) => ValidateDocumentModelIssue[];
|
|
27
|
+
declare const describeModelError: ({ path, message }: ValidateDocumentModelIssue) => string;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { FolioOperationResultValidationContext, describeModelError, findIntroducedModelErrors };
|