@stll/folio-core 0.51.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/inline-provenance.js +18 -22
- package/dist/compare/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +14 -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/generated/text_shaper.js +26 -0
- package/dist/generated/text_shaper_bg.wasm +0 -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/runStyleFormatting.d.ts +7 -1
- package/dist/prosemirror/runStyleFormatting.js +13 -7
- 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/shaping/shaper.d.ts +47 -4
- package/dist/shaping/shaper.js +46 -15
- package/dist/text-shaping.d.ts +4 -0
- package/dist/text-shaping.js +4 -0
- 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 +8 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
1
2
|
import { encloseWholeControls } from "../contentControlRevisions.js";
|
|
2
3
|
import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
|
|
4
|
+
import { expandNoteReferenceDeletionRange } from "../extensions/marks/noteReferenceDeletion.js";
|
|
3
5
|
import { handleEditorBeforeInput } from "../textInput.js";
|
|
4
6
|
import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
|
|
5
7
|
import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
|
|
6
8
|
import { Mapping } from "prosemirror-transform";
|
|
7
9
|
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
8
10
|
import { isHistoryTransaction } from "prosemirror-history";
|
|
11
|
+
import { undoInputRule } from "prosemirror-inputrules";
|
|
9
12
|
//#region src/prosemirror/plugins/suggestionMode.ts
|
|
10
13
|
/**
|
|
11
14
|
* Suggestion Mode Plugin
|
|
@@ -22,6 +25,12 @@ import { isHistoryTransaction } from "prosemirror-history";
|
|
|
22
25
|
const suggestionModeKey = new PluginKey("suggestionMode");
|
|
23
26
|
const SUGGESTION_META = "suggestionModeApplied";
|
|
24
27
|
const SUGGESTION_BYPASS_META = "suggestionModeBypass";
|
|
28
|
+
/**
|
|
29
|
+
* The text a tracked text-input transaction typed. Rules that react to typing
|
|
30
|
+
* (list autoformat) read it, because this plugin claims text input before any
|
|
31
|
+
* input rule sees it.
|
|
32
|
+
*/
|
|
33
|
+
const SUGGESTED_TEXT_INPUT_META = "suggestionModeTextInput";
|
|
25
34
|
function makeMarkAttrs(pluginState) {
|
|
26
35
|
return {
|
|
27
36
|
revisionId: mintRevisionId(),
|
|
@@ -69,7 +78,8 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
|
|
|
69
78
|
return findAdjacentRevision(doc, from, markTypeName, author) ?? findAdjacentRevision(doc, to, markTypeName, author);
|
|
70
79
|
}
|
|
71
80
|
/**
|
|
72
|
-
* Walk a
|
|
81
|
+
* Walk a selected range and track its text, paragraph breaks, and table rows.
|
|
82
|
+
* Retract the current author's own inserted text.
|
|
73
83
|
* Processes in reverse order to maintain position validity.
|
|
74
84
|
*/
|
|
75
85
|
function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState) {
|
|
@@ -87,12 +97,25 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
87
97
|
isOwnInsert
|
|
88
98
|
});
|
|
89
99
|
});
|
|
90
|
-
if (ranges.length === 0) return;
|
|
91
100
|
const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
101
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
102
|
+
if (node.type.name === "tableRow" && pos >= from && pos + node.nodeSize <= to && node.attrs["trIns"] == null && node.attrs["trDel"] == null) tr.setNodeAttribute(pos, "trDel", delAttrs);
|
|
103
|
+
if (node.type.name !== "paragraph") return;
|
|
104
|
+
const boundary = pos + node.nodeSize;
|
|
105
|
+
if (boundary > from && boundary <= to && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
|
|
106
|
+
kind: "del",
|
|
107
|
+
info: {
|
|
108
|
+
id: delAttrs.revisionId,
|
|
109
|
+
author: delAttrs.author,
|
|
110
|
+
date: delAttrs.date
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
if (ranges.length === 0) return;
|
|
92
115
|
encloseWholeControls({
|
|
93
116
|
tr,
|
|
94
|
-
from
|
|
95
|
-
to
|
|
117
|
+
from,
|
|
118
|
+
to,
|
|
96
119
|
revisionId: delAttrs.revisionId
|
|
97
120
|
});
|
|
98
121
|
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
@@ -102,9 +125,8 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
102
125
|
}
|
|
103
126
|
}
|
|
104
127
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* left untouched so another author's attribution is not overwritten.
|
|
128
|
+
* Track every inserted run, paragraph break, and table row in `[from, to)`.
|
|
129
|
+
* Content already carrying a revision keeps its attribution.
|
|
108
130
|
*/
|
|
109
131
|
function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, attrs) {
|
|
110
132
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
@@ -115,6 +137,20 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
|
|
|
115
137
|
if (start >= end) return;
|
|
116
138
|
tr.addMark(start, end, insertionType.create(attrs));
|
|
117
139
|
});
|
|
140
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
141
|
+
if (node.type.name === "paragraph") {
|
|
142
|
+
const boundary = pos + node.nodeSize;
|
|
143
|
+
if (boundary > from && boundary < to && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
|
|
144
|
+
kind: "ins",
|
|
145
|
+
info: {
|
|
146
|
+
id: attrs.revisionId,
|
|
147
|
+
author: attrs.author,
|
|
148
|
+
date: attrs.date
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (node.type.name === "tableRow" && pos >= from && pos + node.nodeSize <= to && node.attrs["trIns"] == null && node.attrs["trDel"] == null) tr.setNodeAttribute(pos, "trIns", attrs);
|
|
153
|
+
});
|
|
118
154
|
encloseWholeControls({
|
|
119
155
|
tr,
|
|
120
156
|
from,
|
|
@@ -159,6 +195,7 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
|
|
|
159
195
|
if (!insertionType) return false;
|
|
160
196
|
const tr = view.state.tr;
|
|
161
197
|
tr.setMeta(SUGGESTION_META, true);
|
|
198
|
+
tr.setMeta(SUGGESTED_TEXT_INPUT_META, text);
|
|
162
199
|
const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
163
200
|
if (from !== to) {
|
|
164
201
|
const deletionType = view.state.schema.marks["deletion"];
|
|
@@ -173,6 +210,19 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
|
|
|
173
210
|
return true;
|
|
174
211
|
}
|
|
175
212
|
/**
|
|
213
|
+
* Remove `from`..`to` of `tr.doc` as a suggestion: text the current author
|
|
214
|
+
* inserted is retracted, anything else is marked deleted. `false` when the
|
|
215
|
+
* editor is not suggesting.
|
|
216
|
+
*/
|
|
217
|
+
function suggestRangeDeletion(state, tr, from, to) {
|
|
218
|
+
const pluginState = suggestionModeKey.getState(state);
|
|
219
|
+
const insertionType = state.schema.marks["insertion"];
|
|
220
|
+
const deletionType = state.schema.marks["deletion"];
|
|
221
|
+
if (!pluginState?.active || !insertionType || !deletionType) return false;
|
|
222
|
+
markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
176
226
|
* Track-changes Enter: split the paragraph and stamp `pPrMark = 'ins'` on the
|
|
177
227
|
* FIRST half of the split (the upper paragraph), per ECMA-376 §17.13.5. The
|
|
178
228
|
* actual splitting reuses `splitBlockClearBorders` so border/style/stored-mark
|
|
@@ -291,7 +341,11 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
291
341
|
const tr = state.tr;
|
|
292
342
|
tr.setMeta(SUGGESTION_META, true);
|
|
293
343
|
if (!empty) {
|
|
294
|
-
|
|
344
|
+
const ranges = state.selection.ranges.map(({ $from: rangeFrom, $to: rangeTo }) => expandNoteReferenceDeletionRange(state.doc, rangeFrom.pos, rangeTo.pos) ?? {
|
|
345
|
+
from: rangeFrom.pos,
|
|
346
|
+
to: rangeTo.pos
|
|
347
|
+
}).sort((a, b) => b.from - a.from);
|
|
348
|
+
for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, insertionType, deletionType, pluginState);
|
|
295
349
|
const cursorPos = tr.mapping.map($to.pos);
|
|
296
350
|
tr.setSelection(TextSelection.near(tr.doc.resolve(cursorPos)));
|
|
297
351
|
dispatch(tr.scrollIntoView());
|
|
@@ -301,22 +355,27 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
301
355
|
const deletePos = isBackward ? $from.pos - 1 : $from.pos;
|
|
302
356
|
const deleteEnd = isBackward ? $from.pos : $from.pos + 1;
|
|
303
357
|
if (deletePos < 0 || deleteEnd > state.doc.content.size) return true;
|
|
304
|
-
const
|
|
358
|
+
const noteRange = expandNoteReferenceDeletionRange(state.doc, deletePos, deleteEnd);
|
|
359
|
+
const rangeFrom = noteRange?.from ?? deletePos;
|
|
360
|
+
const rangeTo = noteRange?.to ?? deleteEnd;
|
|
361
|
+
const nodeAfter = state.doc.resolve(rangeFrom).nodeAfter;
|
|
305
362
|
if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
|
|
306
363
|
const hasOwnInsertion = nodeAfter.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
|
|
307
364
|
if (nodeAfter.marks.some((m) => m.type === deletionType)) {
|
|
308
|
-
const newPos = isBackward ?
|
|
365
|
+
const newPos = isBackward ? rangeFrom : rangeTo;
|
|
309
366
|
tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
|
|
310
|
-
} else if (hasOwnInsertion) tr.delete(
|
|
367
|
+
} else if (hasOwnInsertion) tr.delete(rangeFrom, rangeTo);
|
|
311
368
|
else {
|
|
312
|
-
const delAttrs = findAdjacentRevisionForRange(state.doc,
|
|
313
|
-
tr.addMark(
|
|
314
|
-
const newPos = isBackward ?
|
|
369
|
+
const delAttrs = findAdjacentRevisionForRange(state.doc, rangeFrom, rangeTo, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
370
|
+
tr.addMark(rangeFrom, rangeTo, deletionType.create(delAttrs));
|
|
371
|
+
const newPos = isBackward ? rangeFrom : rangeTo;
|
|
315
372
|
tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
|
|
316
373
|
}
|
|
317
374
|
dispatch(tr.scrollIntoView());
|
|
318
375
|
return true;
|
|
319
376
|
}
|
|
377
|
+
/** Apply the editor's selected-content deletion path for a host cut action. */
|
|
378
|
+
const deleteSelectionAsSuggestion = (state, dispatch) => state.selection.empty ? false : handleSuggestionDelete(state, dispatch, "forward");
|
|
320
379
|
/**
|
|
321
380
|
* Mark the text committed by an IME composition as a tracked insertion.
|
|
322
381
|
*
|
|
@@ -329,18 +388,28 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
329
388
|
* committed range safely. `markRangeAsInserted` skips nodes that already carry a
|
|
330
389
|
* tracked-change mark, so this is idempotent. eigenpal/docx-editor#938.
|
|
331
390
|
*/
|
|
332
|
-
function markComposedAsInsertion(view, from, pluginState) {
|
|
391
|
+
function markComposedAsInsertion(view, from, pluginState, replaced) {
|
|
333
392
|
const insertionType = view.state.schema.marks["insertion"];
|
|
334
393
|
const deletionType = view.state.schema.marks["deletion"];
|
|
335
394
|
if (!insertionType || !deletionType) return;
|
|
336
395
|
const to = view.state.selection.to;
|
|
337
|
-
if (to <= from) return;
|
|
396
|
+
if (to <= from && !replaced) return;
|
|
338
397
|
const tr = view.state.tr;
|
|
339
398
|
tr.setMeta(SUGGESTION_META, true);
|
|
340
|
-
|
|
341
|
-
|
|
399
|
+
if (replaced) {
|
|
400
|
+
tr.insert(from, replaced.content);
|
|
401
|
+
markRangeAsDeleted(tr, tr.doc, from, from + replaced.content.size, insertionType, deletionType, pluginState);
|
|
402
|
+
}
|
|
403
|
+
const insertionFrom = tr.mapping.map(from, 1);
|
|
404
|
+
const insertionTo = tr.mapping.map(to, 1);
|
|
405
|
+
if (insertionTo <= insertionFrom) {
|
|
406
|
+
if (tr.steps.length > 0) view.dispatch(tr);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const markAttrs = findAdjacentRevisionForRange(tr.doc, insertionFrom, insertionTo, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
410
|
+
markRangeAsInserted(tr, tr.doc, insertionFrom, insertionTo, insertionType, deletionType, markAttrs);
|
|
342
411
|
if (tr.steps.length === 0) return;
|
|
343
|
-
const caret = tr.mapping.map(to);
|
|
412
|
+
const caret = tr.mapping.map(to, 1);
|
|
344
413
|
tr.setSelection(TextSelection.create(tr.doc, caret));
|
|
345
414
|
view.dispatch(tr);
|
|
346
415
|
}
|
|
@@ -351,6 +420,7 @@ function markComposedAsInsertion(view, from, pluginState) {
|
|
|
351
420
|
function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
352
421
|
let composing = false;
|
|
353
422
|
let compositionFrom = null;
|
|
423
|
+
let compositionReplaced = null;
|
|
354
424
|
return new Plugin({
|
|
355
425
|
key: suggestionModeKey,
|
|
356
426
|
state: {
|
|
@@ -372,32 +442,32 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
372
442
|
},
|
|
373
443
|
props: {
|
|
374
444
|
handleDOMEvents: {
|
|
445
|
+
cut(view, event) {
|
|
446
|
+
if (!suggestionModeKey.getState(view.state)?.active || view.state.selection.empty) return false;
|
|
447
|
+
const data = event.clipboardData;
|
|
448
|
+
if (!data) return false;
|
|
449
|
+
const { dom, text } = view.serializeForClipboard(view.state.selection.content());
|
|
450
|
+
data.clearData();
|
|
451
|
+
data.setData("text/html", dom.innerHTML);
|
|
452
|
+
data.setData("text/plain", text);
|
|
453
|
+
event.preventDefault();
|
|
454
|
+
return handleSuggestionDelete(view.state, view.dispatch, "forward");
|
|
455
|
+
},
|
|
375
456
|
compositionstart(view) {
|
|
376
|
-
|
|
377
|
-
if (
|
|
457
|
+
if (!suggestionModeKey.getState(view.state)?.active) return false;
|
|
458
|
+
if (composing) return false;
|
|
378
459
|
composing = true;
|
|
379
460
|
const { from, to } = view.state.selection;
|
|
380
|
-
if (from !== to) {
|
|
381
|
-
const insertionType = view.state.schema.marks["insertion"];
|
|
382
|
-
const deletionType = view.state.schema.marks["deletion"];
|
|
383
|
-
if (insertionType && deletionType) {
|
|
384
|
-
const tr = view.state.tr;
|
|
385
|
-
tr.setMeta(SUGGESTION_META, true);
|
|
386
|
-
markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState);
|
|
387
|
-
const caret = tr.mapping.map(to);
|
|
388
|
-
tr.setSelection(TextSelection.create(tr.doc, caret));
|
|
389
|
-
view.dispatch(tr);
|
|
390
|
-
compositionFrom = caret;
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
461
|
compositionFrom = from;
|
|
462
|
+
compositionReplaced = from === to ? null : view.state.doc.slice(from, to);
|
|
395
463
|
return false;
|
|
396
464
|
},
|
|
397
465
|
compositionend(view) {
|
|
398
466
|
const pluginState = suggestionModeKey.getState(view.state);
|
|
399
467
|
const from = compositionFrom;
|
|
468
|
+
const replaced = compositionReplaced;
|
|
400
469
|
compositionFrom = null;
|
|
470
|
+
compositionReplaced = null;
|
|
401
471
|
if (!pluginState?.active || from == null) {
|
|
402
472
|
composing = false;
|
|
403
473
|
return false;
|
|
@@ -405,7 +475,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
405
475
|
queueMicrotask(() => {
|
|
406
476
|
try {
|
|
407
477
|
const current = suggestionModeKey.getState(view.state);
|
|
408
|
-
if (current?.active) markComposedAsInsertion(view, from, current);
|
|
478
|
+
if (current?.active) markComposedAsInsertion(view, from, current, replaced);
|
|
409
479
|
} finally {
|
|
410
480
|
composing = false;
|
|
411
481
|
}
|
|
@@ -420,8 +490,15 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
420
490
|
handleKeyDown(view, event) {
|
|
421
491
|
const pluginState = suggestionModeKey.getState(view.state);
|
|
422
492
|
if (!pluginState?.active) return false;
|
|
423
|
-
if (event.key === "Enter")
|
|
493
|
+
if (event.key === "Enter") {
|
|
494
|
+
const { $from, empty } = view.state.selection;
|
|
495
|
+
if (empty && $from.parent.type.name === "paragraph" && expectParagraphAttrs($from.parent).numPr?.kind === "reference") return false;
|
|
496
|
+
return handleSuggestionEnter(view, pluginState);
|
|
497
|
+
}
|
|
498
|
+
if (event.key === "Backspace" && undoInputRule(view.state, view.dispatch)) return true;
|
|
424
499
|
if (event.key === "Backspace" || event.key === "Delete") {
|
|
500
|
+
const { $from, empty } = view.state.selection;
|
|
501
|
+
if (event.key === "Backspace" && empty && $from.parentOffset === 0 && $from.parent.type.name === "paragraph" && expectParagraphAttrs($from.parent).numPr?.kind === "reference") return false;
|
|
425
502
|
const boundaryTarget = paragraphBoundaryTarget(view.state, event.key === "Backspace" ? "backward" : "forward");
|
|
426
503
|
if (boundaryTarget !== null) return applyPPrDel(view, boundaryTarget, pluginState);
|
|
427
504
|
const direction = event.key === "Backspace" ? "backward" : "forward";
|
|
@@ -461,20 +538,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
461
538
|
const newFrom = following.map(stepFrom, -1);
|
|
462
539
|
const newTo = following.map(stepTo, 1);
|
|
463
540
|
if (newTo > newFrom) {
|
|
464
|
-
newState.doc
|
|
465
|
-
if (!canCarryTrackedRunMark(node)) return;
|
|
466
|
-
if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
|
|
467
|
-
const nodeStart = Math.max(pos, newFrom);
|
|
468
|
-
const nodeEnd = Math.min(pos + node.nodeSize, newTo);
|
|
469
|
-
tr.addMark(nodeStart, nodeEnd, insertionType.create(markAttrs));
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
encloseWholeControls({
|
|
473
|
-
tr,
|
|
474
|
-
from: newFrom,
|
|
475
|
-
to: newTo,
|
|
476
|
-
revisionId: markAttrs.revisionId
|
|
477
|
-
});
|
|
541
|
+
if (deletionType) markRangeAsInserted(tr, newState.doc, newFrom, newTo, insertionType, deletionType, markAttrs);
|
|
478
542
|
}
|
|
479
543
|
});
|
|
480
544
|
}
|
|
@@ -509,4 +573,4 @@ function isSuggestionModeActive(state) {
|
|
|
509
573
|
return suggestionModeKey.getState(state)?.active ?? false;
|
|
510
574
|
}
|
|
511
575
|
//#endregion
|
|
512
|
-
export { SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, handleSuggestionEnter, handleSuggestionPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestionModeKey, toggleSuggestionMode };
|
|
576
|
+
export { SUGGESTED_TEXT_INPUT_META, SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, deleteSelectionAsSuggestion, handleSuggestionEnter, handleSuggestionPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestRangeDeletion, suggestionModeKey, toggleSuggestionMode };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DocScanState } from "./createDocScanPlugin.js";
|
|
2
|
-
import { EditorState, PluginKey } from "prosemirror-state";
|
|
3
2
|
import { Node } from "prosemirror-model";
|
|
3
|
+
import { EditorState, PluginKey } from "prosemirror-state";
|
|
4
4
|
import { DirectiveKind } from "@stll/template-conditions";
|
|
5
5
|
//#region src/prosemirror/plugins/templateDirectives.d.ts
|
|
6
6
|
type DirectiveRange = {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EditorState, Plugin, Transaction } from "prosemirror-state";
|
|
2
|
+
//#region src/prosemirror/plugins/transactionInvariants.d.ts
|
|
3
|
+
type TransactionInvariantFlags = {
|
|
4
|
+
/** Install the invariant plugin in every extension-manager runtime. */
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare global {
|
|
8
|
+
var __folioTransactionInvariants: TransactionInvariantFlags | undefined;
|
|
9
|
+
}
|
|
10
|
+
/** Whether new extension-manager runtimes install the invariant plugin. */
|
|
11
|
+
declare const areTransactionInvariantsEnabled: () => boolean;
|
|
12
|
+
type TransactionInvariantIssue = {
|
|
13
|
+
path: string;
|
|
14
|
+
message: string;
|
|
15
|
+
/** Position of the node the issue sits on, when the check knows it. */
|
|
16
|
+
pos?: number;
|
|
17
|
+
};
|
|
18
|
+
declare class TransactionInvariantError extends Error {
|
|
19
|
+
readonly issues: readonly TransactionInvariantIssue[];
|
|
20
|
+
readonly steps: readonly unknown[];
|
|
21
|
+
constructor(issues: readonly TransactionInvariantIssue[], transactions: readonly Transaction[]);
|
|
22
|
+
}
|
|
23
|
+
/** Every invariant the state breaks; empty when it is sound. */
|
|
24
|
+
declare const checkEditorStateInvariants: (state: EditorState) => TransactionInvariantIssue[];
|
|
25
|
+
/**
|
|
26
|
+
* Throw a {@link TransactionInvariantError} naming the offending steps when a
|
|
27
|
+
* document-changing transaction leaves the state with an issue it did not
|
|
28
|
+
* have before.
|
|
29
|
+
*/
|
|
30
|
+
declare const createTransactionInvariantPlugin: () => Plugin;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { TransactionInvariantError, TransactionInvariantFlags, TransactionInvariantIssue, areTransactionInvariantsEnabled, checkEditorStateInvariants, createTransactionInvariantPlugin };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { paragraphNumberingReferenceId } from "../../docx/numberingReference.js";
|
|
2
|
+
import { validateProseMirrorDocument } from "../validation.js";
|
|
3
|
+
import { getDocumentNumbering, hasDocumentNumbering } from "./documentNumbering.js";
|
|
4
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
5
|
+
//#region src/prosemirror/plugins/transactionInvariants.ts
|
|
6
|
+
/** Whether new extension-manager runtimes install the invariant plugin. */
|
|
7
|
+
const areTransactionInvariantsEnabled = () => globalThis.__folioTransactionInvariants?.enabled === true;
|
|
8
|
+
const MAX_REPORTED_STEPS_LENGTH = 4e3;
|
|
9
|
+
const describeSteps = (transactions) => {
|
|
10
|
+
const steps = transactions.flatMap((transaction) => transaction.steps.map((step) => step.toJSON()));
|
|
11
|
+
const json = JSON.stringify(steps);
|
|
12
|
+
return json.length > MAX_REPORTED_STEPS_LENGTH ? `${json.slice(0, MAX_REPORTED_STEPS_LENGTH)}… (${json.length} characters)` : json;
|
|
13
|
+
};
|
|
14
|
+
var TransactionInvariantError = class extends Error {
|
|
15
|
+
issues;
|
|
16
|
+
steps;
|
|
17
|
+
constructor(issues, transactions) {
|
|
18
|
+
super(`A transaction left the editor document invalid:\n${issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")}\nSteps: ${describeSteps(transactions)}`);
|
|
19
|
+
this.name = "TransactionInvariantError";
|
|
20
|
+
this.issues = issues;
|
|
21
|
+
this.steps = transactions.flatMap((transaction) => transaction.steps.map((step) => step.toJSON()));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const numberingReferenceIssues = (state) => {
|
|
25
|
+
if (!hasDocumentNumbering(state)) return [];
|
|
26
|
+
const numbering = getDocumentNumbering(state);
|
|
27
|
+
const issues = [];
|
|
28
|
+
state.doc.descendants((node, pos) => {
|
|
29
|
+
if (node.type.name !== "paragraph") return true;
|
|
30
|
+
const numId = paragraphNumberingReferenceId(node.attrs["numPr"]);
|
|
31
|
+
if (numId !== void 0 && !numbering?.hasNumbering(numId)) issues.push({
|
|
32
|
+
path: `paragraph at ${pos}.numPr.numId`,
|
|
33
|
+
message: `Numbering definition ${numId} is missing.`,
|
|
34
|
+
pos
|
|
35
|
+
});
|
|
36
|
+
return false;
|
|
37
|
+
});
|
|
38
|
+
return issues;
|
|
39
|
+
};
|
|
40
|
+
/** Every invariant the state breaks; empty when it is sound. */
|
|
41
|
+
const checkEditorStateInvariants = (state) => [...validateProseMirrorDocument(state.doc).issues, ...numberingReferenceIssues(state)];
|
|
42
|
+
/**
|
|
43
|
+
* The issues `after` has that `before` did not. A document can arrive broken
|
|
44
|
+
* (a malformed attribute the editor tolerates); only what a transaction adds
|
|
45
|
+
* is the transaction's fault. An issue that names its node persists only on
|
|
46
|
+
* that node, followed through the transactions' mappings, so moving a broken
|
|
47
|
+
* reference from one paragraph to another still counts as new. Issues without
|
|
48
|
+
* a position are matched by message, since their paths move with the edit.
|
|
49
|
+
*/
|
|
50
|
+
const introducedIssues = (before, after, transactions) => {
|
|
51
|
+
const mapPosition = (pos) => {
|
|
52
|
+
let mapped = pos;
|
|
53
|
+
for (const transaction of transactions) mapped = transaction.mapping.map(mapped, 1);
|
|
54
|
+
return mapped;
|
|
55
|
+
};
|
|
56
|
+
const persisting = /* @__PURE__ */ new Set();
|
|
57
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const issue of before) if (issue.pos === void 0) remaining.set(issue.message, (remaining.get(issue.message) ?? 0) + 1);
|
|
59
|
+
else persisting.add(`${mapPosition(issue.pos)}:${issue.message}`);
|
|
60
|
+
return after.filter((issue) => {
|
|
61
|
+
if (issue.pos !== void 0) return !persisting.has(`${issue.pos}:${issue.message}`);
|
|
62
|
+
const count = remaining.get(issue.message) ?? 0;
|
|
63
|
+
if (count === 0) return true;
|
|
64
|
+
remaining.set(issue.message, count - 1);
|
|
65
|
+
return false;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const transactionInvariantsKey = new PluginKey("transactionInvariants");
|
|
69
|
+
/**
|
|
70
|
+
* Throw a {@link TransactionInvariantError} naming the offending steps when a
|
|
71
|
+
* document-changing transaction leaves the state with an issue it did not
|
|
72
|
+
* have before.
|
|
73
|
+
*/
|
|
74
|
+
const createTransactionInvariantPlugin = () => new Plugin({
|
|
75
|
+
key: transactionInvariantsKey,
|
|
76
|
+
appendTransaction(transactions, oldState, newState) {
|
|
77
|
+
if (!transactions.some((transaction) => transaction.docChanged)) return null;
|
|
78
|
+
const issues = checkEditorStateInvariants(newState);
|
|
79
|
+
if (issues.length === 0) return null;
|
|
80
|
+
const introduced = introducedIssues(checkEditorStateInvariants(oldState), issues, transactions);
|
|
81
|
+
if (introduced.length > 0) throw new TransactionInvariantError(introduced, transactions);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
//#endregion
|
|
86
|
+
export { TransactionInvariantError, areTransactionInvariantsEnabled, checkEditorStateInvariants, createTransactionInvariantPlugin };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Mapping } from "prosemirror-transform";
|
|
2
|
+
//#region src/prosemirror/positionSweep.d.ts
|
|
3
|
+
type PositionQuery = {
|
|
4
|
+
pos: number;
|
|
5
|
+
assoc: -1 | 1;
|
|
6
|
+
/**
|
|
7
|
+
* Index of the first step map to apply, counting across every mapping
|
|
8
|
+
* passed in order: the position is in the document that step read.
|
|
9
|
+
*/
|
|
10
|
+
from: number;
|
|
11
|
+
};
|
|
12
|
+
type SweptPosition = {
|
|
13
|
+
pos: number;
|
|
14
|
+
deletedAcross: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Map every query through `mappings` (one per transaction, in order), each
|
|
18
|
+
* entering at its own step. O((positions + steps) x log positions) plus the
|
|
19
|
+
* positions a step's changed ranges actually cover, rather than
|
|
20
|
+
* O(positions x steps).
|
|
21
|
+
*/
|
|
22
|
+
declare const sweepPositions: (mappings: readonly Mapping[], queries: readonly PositionQuery[]) => SweptPosition[];
|
|
23
|
+
//#endregion
|
|
24
|
+
export { PositionQuery, SweptPosition, sweepPositions };
|