@stll/folio-core 0.55.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +214 -52
- package/dist/ai-edits/batch-claims.d.ts +3 -1
- package/dist/ai-edits/batch-claims.js +25 -1
- package/dist/ai-edits/headless.js +2 -2
- package/dist/ai-edits/table-row-column-mutations.js +17 -3
- package/dist/ai-edits/types.d.ts +7 -1
- package/dist/ai-suggestions/apply.js +20 -1
- package/dist/compare/compare.js +1 -0
- package/dist/compare/inline-atoms.js +40 -16
- package/dist/compare/inline-provenance.js +12 -5
- package/dist/compare/reproducible-package.d.ts +0 -17
- package/dist/compare/reproducible-package.js +12 -18
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/controller/canonicalClipboard.d.ts +23 -0
- package/dist/controller/canonicalClipboard.js +482 -0
- package/dist/controller/canonicalClipboardResources.d.ts +21 -0
- package/dist/controller/canonicalClipboardResources.js +382 -0
- package/dist/controller/canonicalComments.d.ts +41 -0
- package/dist/controller/canonicalComments.js +152 -0
- package/dist/controller/canonicalComposition.d.ts +24 -3
- package/dist/controller/canonicalComposition.js +208 -48
- package/dist/controller/canonicalInlineProjection.d.ts +118 -0
- package/dist/controller/canonicalInlineProjection.js +476 -0
- package/dist/controller/canonicalInput.d.ts +13 -4
- package/dist/controller/canonicalInput.js +62 -14
- package/dist/controller/canonicalInputTimer.d.ts +16 -0
- package/dist/controller/canonicalInputTimer.js +34 -0
- package/dist/controller/canonicalPublicOperations.d.ts +48 -0
- package/dist/controller/canonicalPublicOperations.js +648 -0
- package/dist/controller/canonicalSelection.d.ts +18 -0
- package/dist/controller/canonicalSelection.js +32 -0
- package/dist/controller/canonicalSession.d.ts +27 -6
- package/dist/controller/canonicalSession.js +298 -116
- package/dist/controller/canonicalStoryEditor.d.ts +7 -3
- package/dist/controller/canonicalStoryEditor.js +27 -2
- package/dist/controller/canonicalStructure.js +57 -4
- package/dist/controller/canonicalTocSelection.d.ts +11 -0
- package/dist/controller/canonicalTocSelection.js +43 -0
- package/dist/controller/folioEditor.d.ts +2 -9
- package/dist/controller/folioEditor.js +18 -4
- package/dist/controller/headerFooterEditorManager.d.ts +2 -1
- package/dist/controller/headerFooterEditorManager.js +26 -11
- package/dist/controller/hiddenEditorApi.d.ts +24 -3
- package/dist/controller/hiddenEditorApi.js +8 -0
- package/dist/controller/hiddenEditorManager.d.ts +5 -1
- package/dist/controller/hiddenEditorManager.js +378 -43
- package/dist/controller/noteEditorManager.d.ts +2 -1
- package/dist/controller/noteEditorManager.js +29 -13
- package/dist/document-operations.d.ts +8 -1
- package/dist/document-operations.js +6 -4
- package/dist/docx/blockPlainText.d.ts +1 -1
- package/dist/docx/blockPlainText.js +11 -3
- package/dist/docx/builtInStyles.js +13 -6
- package/dist/docx/canonicalResourceSave.d.ts +16 -0
- package/dist/docx/canonicalResourceSave.js +27 -0
- package/dist/docx/canonicalSave.d.ts +29 -0
- package/dist/docx/canonicalSave.js +101 -0
- package/dist/docx/canonicalStoryRepack.d.ts +1 -1
- package/dist/docx/canonicalStoryRepack.js +38 -5
- package/dist/docx/containerChildren.gen.d.ts +3 -1
- package/dist/docx/containerChildren.gen.js +33 -1
- package/dist/docx/documentParser.d.ts +1 -13
- package/dist/docx/documentParser.js +1 -47
- package/dist/docx/ensureParaIds.js +10 -4
- package/dist/docx/fieldParser.js +14 -2
- package/dist/docx/footnoteParser.d.ts +1 -11
- package/dist/docx/footnoteParser.js +1 -16
- package/dist/docx/headerFooterParser.d.ts +1 -9
- package/dist/docx/headerFooterParser.js +1 -12
- package/dist/docx/headerFooterVerbatim.d.ts +26 -1
- package/dist/docx/headerFooterVerbatim.js +81 -3
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/hyperlinkParser.js +20 -1
- package/dist/docx/imageParser.d.ts +13 -1
- package/dist/docx/imageParser.js +20 -11
- package/dist/docx/inlineWrapperContent.d.ts +4 -4
- package/dist/docx/inlineWrapperContent.js +4 -4
- package/dist/docx/linkedSdtPreservation.d.ts +10 -0
- package/dist/docx/linkedSdtPreservation.js +37 -0
- package/dist/docx/metadataPrivacy.js +6 -1
- package/dist/docx/packageParts.js +9 -3
- package/dist/docx/paragraphParser.js +88 -67
- package/dist/docx/paragraphPropertySource.d.ts +22 -1
- package/dist/docx/paragraphPropertySource.js +109 -2
- package/dist/docx/parser.js +2 -1
- package/dist/docx/preservedRunContent.js +2 -1
- package/dist/docx/relsParser.js +26 -2
- package/dist/docx/removeHeaderFooterParts.js +17 -6
- package/dist/docx/rezip.d.ts +49 -5
- package/dist/docx/rezip.js +754 -235
- package/dist/docx/saveDiagnostics.d.ts +11 -0
- package/dist/docx/selectiveSave.d.ts +2 -1
- package/dist/docx/selectiveSave.js +42 -11
- package/dist/docx/selectiveXmlPatch.js +7 -5
- package/dist/docx/serializer/commentSerializer.d.ts +4 -1
- package/dist/docx/serializer/commentSerializer.js +7 -3
- package/dist/docx/serializer/documentSerializer.d.ts +9 -2
- package/dist/docx/serializer/documentSerializer.js +29 -10
- package/dist/docx/serializer/paragraphSerializer.js +207 -94
- package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
- package/dist/docx/server/materializeYjsDocx.js +14 -6
- package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
- package/dist/docx/storyBlockReplay.d.ts +21 -1
- package/dist/docx/storyBlockReplay.js +113 -9
- package/dist/docx/storyPlainText.d.ts +20 -0
- package/dist/docx/storyPlainText.js +23 -0
- package/dist/docx/structuralXmlPatch.js +10 -22
- package/dist/docx/styleParser.d.ts +5 -1
- package/dist/docx/styleParser.js +1 -1
- package/dist/docx/tableParser.d.ts +1 -8
- package/dist/docx/tableParser.js +1 -28
- package/dist/docx/textBoxParser.d.ts +1 -5
- package/dist/docx/textBoxParser.js +1 -20
- package/dist/docx/unzip.js +6 -1
- package/dist/i18n/messages/ar.gen.js +4 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
- package/dist/i18n/messages/catalogs.gen.js +68 -0
- package/dist/i18n/messages/cs.gen.js +4 -0
- package/dist/i18n/messages/de.gen.js +4 -0
- package/dist/i18n/messages/en.gen.js +4 -0
- package/dist/i18n/messages/es.gen.js +4 -0
- package/dist/i18n/messages/et.gen.js +4 -0
- package/dist/i18n/messages/fr.gen.js +4 -0
- package/dist/i18n/messages/he.gen.js +4 -0
- package/dist/i18n/messages/hi.gen.js +4 -0
- package/dist/i18n/messages/hu.gen.js +4 -0
- package/dist/i18n/messages/lt.gen.js +4 -0
- package/dist/i18n/messages/lv.gen.js +4 -0
- package/dist/i18n/messages/messages.gen.d.ts +4 -0
- package/dist/i18n/messages/pl.gen.js +4 -0
- package/dist/i18n/messages/pt-BR.gen.js +4 -0
- package/dist/i18n/messages/sk.gen.js +4 -0
- package/dist/i18n/messages/tr.gen.js +4 -0
- package/dist/i18n/messages/zh-CN.gen.js +4 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/acceptedBlockProjection.d.ts +12 -0
- package/dist/internal/acceptedBlockProjection.js +45 -0
- package/dist/internal/revisionResolutionInline.js +25 -3
- package/dist/managers/DocumentLoaderManager.js +2 -1
- package/dist/markdown/escape.d.ts +3 -1
- package/dist/markdown/escape.js +9 -2
- package/dist/markdown/renderBlock.js +5 -120
- package/dist/markdown/renderParagraph.js +4 -2
- package/dist/markdown/renderRuns.d.ts +7 -1
- package/dist/markdown/renderRuns.js +33 -24
- package/dist/panel-layout.d.ts +11 -9
- package/dist/panel-layout.js +13 -9
- package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
- package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
- package/dist/prosemirror/attrs/index.js +5 -0
- package/dist/prosemirror/canonicalCommands.d.ts +28 -4
- package/dist/prosemirror/canonicalCommands.js +11 -4
- package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
- package/dist/prosemirror/canonicalSelectionRange.js +15 -0
- package/dist/prosemirror/clipboardIntent.d.ts +7 -0
- package/dist/prosemirror/clipboardIntent.js +9 -0
- package/dist/prosemirror/commands/comments.js +60 -35
- package/dist/prosemirror/commands/pageBreak.js +2 -2
- package/dist/prosemirror/commands/pastePlainText.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
- package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
- package/dist/prosemirror/commentOps.js +15 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
- package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
- package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
- package/dist/prosemirror/conversion/toProseDoc.js +124 -22
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +61 -35
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +65 -29
- package/dist/prosemirror/extensions/marks/markUtils.js +11 -8
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
- package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
- package/dist/prosemirror/fieldRepresentation.js +18 -0
- package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
- package/dist/prosemirror/hyperlinkRemoval.js +28 -9
- package/dist/prosemirror/insertOperations.js +2 -1
- package/dist/prosemirror/listNumbering.js +11 -5
- package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
- package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
- package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
- package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
- package/dist/prosemirror/noteReferenceReview.js +14 -1
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
- package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
- package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
- package/dist/prosemirror/plugins/documentStyles.js +17 -10
- package/dist/prosemirror/plugins/suggestionMode.js +48 -48
- package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
- package/dist/prosemirror/preservedXmlReview.js +35 -0
- package/dist/prosemirror/runFormattingFromMarks.js +2 -2
- package/dist/prosemirror/runFormattingReconciliation.js +5 -4
- package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
- package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
- package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
- package/dist/prosemirror/runStyleFormatting.js +3 -1
- package/dist/prosemirror/schema/marks.d.ts +1 -0
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
- package/dist/prosemirror/textInput.d.ts +5 -1
- package/dist/prosemirror/textInput.js +3 -3
- package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +39 -1
- package/dist/prosemirror/validation.js +3 -5
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
- package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
- package/dist/types/canonicalCapabilities.d.ts +201 -0
- package/dist/types/canonicalCapabilities.js +198 -0
- package/dist/types/canonicalComments.d.ts +51 -0
- package/dist/types/canonicalComments.js +0 -0
- package/dist/types/canonicalSave.d.ts +11 -0
- package/dist/types/canonicalSave.js +0 -0
- package/dist/types/canonicalSections.d.ts +12 -0
- package/dist/types/canonicalSections.js +0 -0
- package/dist/types/docxSerialization.d.ts +14 -0
- package/dist/types/docxSerialization.js +7 -0
- package/dist/utils/headingCollector.js +45 -4
- package/dist/utils/outlineDepth.d.ts +9 -0
- package/dist/utils/outlineDepth.js +9 -0
- package/package.json +4 -4
|
@@ -1,31 +1,52 @@
|
|
|
1
1
|
import { splitsGraphemeCluster, splitsSurrogatePair } from "../ai-edits/character-boundaries.js";
|
|
2
|
+
import { rangedCommentIds, visitCommentMarkers } from "../docx/commentAnchorIndex.js";
|
|
2
3
|
import { cloneDocumentWithParagraphPropertySources, copyDocumentParagraphPropertySourceContract, copyParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
3
|
-
import {
|
|
4
|
+
import { canonicalSelectionRange } from "../prosemirror/canonicalSelectionRange.js";
|
|
5
|
+
import { collectPairedBookmarkIds, footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
4
6
|
import { markPackageChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
5
7
|
import { runFormattingPatchFromMarks } from "../prosemirror/runFormattingFromMarks.js";
|
|
8
|
+
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
9
|
+
import { canonicalInlineSourceIssue, canonicalNativeCells, projectCanonicalInline } from "./canonicalInlineProjection.js";
|
|
10
|
+
import { restoreCanonicalSelection } from "./canonicalSelection.js";
|
|
11
|
+
import { mapTocSelection } from "./canonicalTocSelection.js";
|
|
6
12
|
import { Result, TaggedError, panic } from "better-result";
|
|
7
13
|
import { hasIllegalXmlCharacters } from "@stll/docx-core";
|
|
8
14
|
import { Fragment } from "prosemirror-model";
|
|
9
|
-
import { DOCUMENT_OP_TYPES, OP_STORIES, applyDocumentOps, combineEdits, compileEditorIntent, createEditorIntentIdAllocator, documentStories, editorParagraphGroups, findStoryBody, normalizeForOps, packageParagraphIds, sameStory, validateOpsDocument } from "@stll/docx-core/ops";
|
|
10
|
-
import { TextSelection } from "prosemirror-state";
|
|
15
|
+
import { DOCUMENT_OP_TYPES, OP_STORIES, applyDocumentOps, combineEdits, commentDocumentIssue, compileEditorIntent, createEditorIntentIdAllocator, documentStories, editorParagraphGroups, findStoryBody, normalizeForOps, packageParagraphIds, sameStory, validateOpsDocument } from "@stll/docx-core/ops";
|
|
16
|
+
import { AllSelection, TextSelection } from "prosemirror-state";
|
|
11
17
|
//#region src/controller/canonicalSession.ts
|
|
12
18
|
var CanonicalSessionError = class extends TaggedError("CanonicalSessionError") {};
|
|
13
19
|
const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
20
|
+
gap: CANONICAL_GAP.dispatch,
|
|
14
21
|
message,
|
|
15
22
|
reason: "refused"
|
|
16
23
|
}));
|
|
17
24
|
const noChange = (message) => Result.err(new CanonicalSessionError({
|
|
25
|
+
gap: CANONICAL_GAP.dispatch,
|
|
18
26
|
message,
|
|
19
27
|
reason: "noChange"
|
|
20
28
|
}));
|
|
29
|
+
const COMMENT_OPERATION_TYPES = /* @__PURE__ */ new Set([
|
|
30
|
+
DOCUMENT_OP_TYPES.CREATE_COMMENT,
|
|
31
|
+
DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT,
|
|
32
|
+
DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION,
|
|
33
|
+
DOCUMENT_OP_TYPES.DELETE_COMMENT,
|
|
34
|
+
DOCUMENT_OP_TYPES.RESTORE_COMMENT_STATE
|
|
35
|
+
]);
|
|
21
36
|
const operationChangesPackage = (op) => {
|
|
22
37
|
switch (op.type) {
|
|
38
|
+
case DOCUMENT_OP_TYPES.CREATE_COMMENT:
|
|
39
|
+
case DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT:
|
|
40
|
+
case DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION:
|
|
41
|
+
case DOCUMENT_OP_TYPES.DELETE_COMMENT:
|
|
42
|
+
case DOCUMENT_OP_TYPES.RESTORE_COMMENT_STATE:
|
|
23
43
|
case DOCUMENT_OP_TYPES.CREATE_HEADER_FOOTER:
|
|
24
44
|
case DOCUMENT_OP_TYPES.REMOVE_HEADER_FOOTER:
|
|
25
45
|
case DOCUMENT_OP_TYPES.ADD_NOTE:
|
|
26
46
|
case DOCUMENT_OP_TYPES.REMOVE_NOTE:
|
|
27
47
|
case DOCUMENT_OP_TYPES.SET_SECTION_PROPS:
|
|
28
48
|
case DOCUMENT_OP_TYPES.RESTORE_STORY_PARTS:
|
|
49
|
+
case DOCUMENT_OP_TYPES.SET_PACKAGE_RESOURCES:
|
|
29
50
|
case DOCUMENT_OP_TYPES.CREATE_NUMBERING_INSTANCE:
|
|
30
51
|
case DOCUMENT_OP_TYPES.DELETE_NUMBERING_INSTANCE:
|
|
31
52
|
case DOCUMENT_OP_TYPES.SET_SECTION_ENDPOINT: return true;
|
|
@@ -36,14 +57,70 @@ const operationChangesPackage = (op) => {
|
|
|
36
57
|
return op;
|
|
37
58
|
}
|
|
38
59
|
};
|
|
60
|
+
const operationChangesStructure = (op) => {
|
|
61
|
+
switch (op.type) {
|
|
62
|
+
case DOCUMENT_OP_TYPES.DELETE_BLOCKS:
|
|
63
|
+
case DOCUMENT_OP_TYPES.INSERT_BLOCKS:
|
|
64
|
+
case DOCUMENT_OP_TYPES.SPLIT_BLOCK:
|
|
65
|
+
case DOCUMENT_OP_TYPES.JOIN_BLOCKS:
|
|
66
|
+
case DOCUMENT_OP_TYPES.REPLACE_BLOCKS:
|
|
67
|
+
case DOCUMENT_OP_TYPES.INSERT_TABLE:
|
|
68
|
+
case DOCUMENT_OP_TYPES.DELETE_TABLE:
|
|
69
|
+
case DOCUMENT_OP_TYPES.SET_CONTAINER_BLOCKS:
|
|
70
|
+
case DOCUMENT_OP_TYPES.INSERT_ROW:
|
|
71
|
+
case DOCUMENT_OP_TYPES.DELETE_ROW:
|
|
72
|
+
case DOCUMENT_OP_TYPES.SET_TABLE_ROWS:
|
|
73
|
+
case DOCUMENT_OP_TYPES.INSERT_COLUMN:
|
|
74
|
+
case DOCUMENT_OP_TYPES.DELETE_COLUMN:
|
|
75
|
+
case DOCUMENT_OP_TYPES.MERGE_CELLS:
|
|
76
|
+
case DOCUMENT_OP_TYPES.SPLIT_CELL:
|
|
77
|
+
case DOCUMENT_OP_TYPES.SET_TABLE:
|
|
78
|
+
case DOCUMENT_OP_TYPES.RESOLVE_REVISION: return true;
|
|
79
|
+
case DOCUMENT_OP_TYPES.CREATE_HEADER_FOOTER:
|
|
80
|
+
case DOCUMENT_OP_TYPES.REMOVE_HEADER_FOOTER:
|
|
81
|
+
case DOCUMENT_OP_TYPES.ADD_NOTE:
|
|
82
|
+
case DOCUMENT_OP_TYPES.REMOVE_NOTE:
|
|
83
|
+
case DOCUMENT_OP_TYPES.SET_SECTION_PROPS:
|
|
84
|
+
case DOCUMENT_OP_TYPES.RESTORE_STORY_PARTS:
|
|
85
|
+
case DOCUMENT_OP_TYPES.SET_PACKAGE_RESOURCES:
|
|
86
|
+
case DOCUMENT_OP_TYPES.CREATE_NUMBERING_INSTANCE:
|
|
87
|
+
case DOCUMENT_OP_TYPES.DELETE_NUMBERING_INSTANCE:
|
|
88
|
+
case DOCUMENT_OP_TYPES.SET_SECTION_ENDPOINT: return true;
|
|
89
|
+
case DOCUMENT_OP_TYPES.INSERT_TEXT:
|
|
90
|
+
case DOCUMENT_OP_TYPES.INSERT_CONTENT:
|
|
91
|
+
case DOCUMENT_OP_TYPES.DELETE_RANGE:
|
|
92
|
+
case DOCUMENT_OP_TYPES.SPLIT_INLINE:
|
|
93
|
+
case DOCUMENT_OP_TYPES.JOIN_INLINE:
|
|
94
|
+
case DOCUMENT_OP_TYPES.SET_RUN_PROPS:
|
|
95
|
+
case DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS:
|
|
96
|
+
case DOCUMENT_OP_TYPES.SET_PARAGRAPH_REVIEW:
|
|
97
|
+
case DOCUMENT_OP_TYPES.REPLACE_INLINE:
|
|
98
|
+
case DOCUMENT_OP_TYPES.SET_TABLE_GRID:
|
|
99
|
+
case DOCUMENT_OP_TYPES.SET_CELL_PROPS:
|
|
100
|
+
case DOCUMENT_OP_TYPES.SET_ROW_PROPS:
|
|
101
|
+
case DOCUMENT_OP_TYPES.SET_TABLE_PROPS:
|
|
102
|
+
case DOCUMENT_OP_TYPES.CREATE_COMMENT:
|
|
103
|
+
case DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT:
|
|
104
|
+
case DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION:
|
|
105
|
+
case DOCUMENT_OP_TYPES.DELETE_COMMENT:
|
|
106
|
+
case DOCUMENT_OP_TYPES.RESTORE_COMMENT_STATE: return false;
|
|
107
|
+
default: return op;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
39
110
|
const operationChangesBodyProjection = (op) => {
|
|
40
111
|
switch (op.type) {
|
|
112
|
+
case DOCUMENT_OP_TYPES.CREATE_COMMENT:
|
|
113
|
+
case DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT:
|
|
114
|
+
case DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION:
|
|
115
|
+
case DOCUMENT_OP_TYPES.DELETE_COMMENT:
|
|
116
|
+
case DOCUMENT_OP_TYPES.RESTORE_COMMENT_STATE:
|
|
41
117
|
case DOCUMENT_OP_TYPES.CREATE_HEADER_FOOTER:
|
|
42
118
|
case DOCUMENT_OP_TYPES.REMOVE_HEADER_FOOTER:
|
|
43
119
|
case DOCUMENT_OP_TYPES.ADD_NOTE:
|
|
44
120
|
case DOCUMENT_OP_TYPES.REMOVE_NOTE:
|
|
45
121
|
case DOCUMENT_OP_TYPES.SET_SECTION_PROPS:
|
|
46
122
|
case DOCUMENT_OP_TYPES.RESTORE_STORY_PARTS:
|
|
123
|
+
case DOCUMENT_OP_TYPES.SET_PACKAGE_RESOURCES:
|
|
47
124
|
case DOCUMENT_OP_TYPES.CREATE_NUMBERING_INSTANCE:
|
|
48
125
|
case DOCUMENT_OP_TYPES.DELETE_NUMBERING_INSTANCE:
|
|
49
126
|
case DOCUMENT_OP_TYPES.SET_SECTION_ENDPOINT: return true;
|
|
@@ -55,7 +132,7 @@ const operationChangesBodyProjection = (op) => {
|
|
|
55
132
|
}
|
|
56
133
|
};
|
|
57
134
|
const CANONICAL_PROJECTION_META = "folioCanonicalProjection";
|
|
58
|
-
/** Paragraph gaps
|
|
135
|
+
/** Paragraph gaps bind logical units and source zero-width ordinals to PM positions. */
|
|
59
136
|
var CanonicalProjection = class {
|
|
60
137
|
doc;
|
|
61
138
|
story;
|
|
@@ -72,15 +149,16 @@ var CanonicalProjection = class {
|
|
|
72
149
|
const paragraph = this.paragraphs.find(({ start, node }) => position >= start && position <= start + node.content.size);
|
|
73
150
|
if (paragraph === void 0) return refuse("The input is outside a plain paragraph.");
|
|
74
151
|
const relative = position - paragraph.start;
|
|
75
|
-
const offset = paragraph.boundaries.findIndex((gaps) => gaps.
|
|
76
|
-
if (offset < 0) return refuse("The input splits
|
|
152
|
+
const offset = paragraph.boundaries.findIndex((gaps) => gaps.some((gap) => gap.position === relative));
|
|
153
|
+
if (offset < 0) return refuse("The input splits an indivisible inline element.");
|
|
77
154
|
if (splitsSurrogatePair(paragraph.text, offset)) return refuse("The input would split a surrogate pair.");
|
|
78
155
|
const gaps = paragraph.boundaries.at(offset) ?? panic("Missing canonical boundary");
|
|
156
|
+
const gap = gaps.find((entry) => entry.position === relative) ?? panic("Missing canonical native gap");
|
|
79
157
|
return Result.ok({
|
|
80
158
|
story: this.story,
|
|
81
159
|
blockId: paragraph.blockId,
|
|
82
160
|
offset,
|
|
83
|
-
...gaps.length > 1 ? { zeroWidthBefore:
|
|
161
|
+
...gaps.length > 1 || gap.zeroWidthBefore > 0 ? { zeroWidthBefore: gap.zeroWidthBefore } : {}
|
|
84
162
|
});
|
|
85
163
|
}
|
|
86
164
|
inputAddressAt(position) {
|
|
@@ -88,30 +166,32 @@ var CanonicalProjection = class {
|
|
|
88
166
|
if (address.isErr()) return address;
|
|
89
167
|
const paragraph = this.paragraph(address.value.blockId);
|
|
90
168
|
if (paragraph === void 0) panic("Input lost its paragraph.");
|
|
91
|
-
let
|
|
169
|
+
let destination = position;
|
|
92
170
|
paragraph.node.forEach((node, start) => {
|
|
93
|
-
if (node.marks.some((mark) => mark.type.name === "deletion") &&
|
|
94
|
-
});
|
|
95
|
-
return Result.ok({
|
|
96
|
-
...address.value,
|
|
97
|
-
offset
|
|
171
|
+
if (node.marks.some((mark) => mark.type.name === "deletion") && position > paragraph.start + start && position < paragraph.start + start + node.nodeSize) destination = paragraph.start + start + node.nodeSize;
|
|
98
172
|
});
|
|
173
|
+
return destination === position ? address : this.addressAt(destination);
|
|
99
174
|
}
|
|
100
175
|
positionAt(address) {
|
|
101
176
|
const paragraph = this.paragraph(address.blockId);
|
|
102
177
|
if (!sameStory(address.story, this.story) || paragraph === void 0 || !Number.isInteger(address.offset) || address.offset < 0 || address.offset > paragraph.text.length || splitsSurrogatePair(paragraph.text, address.offset)) return refuse("The canonical selection is outside a plain paragraph.");
|
|
103
178
|
const gaps = paragraph.boundaries.at(address.offset) ?? panic("Missing canonical boundary");
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
179
|
+
const ordinal = address.zeroWidthBefore ?? (gaps.at(-1) ?? panic("Missing canonical gap")).zeroWidthBefore;
|
|
180
|
+
const gap = gaps.find(({ zeroWidthBefore }) => zeroWidthBefore === ordinal);
|
|
181
|
+
if (!Number.isInteger(ordinal) || ordinal < 0 || ordinal > (gaps.at(-1)?.zeroWidthBefore ?? 0)) return refuse("The canonical selection is outside a paragraph gap.");
|
|
182
|
+
if (gap === void 0) return refuse("This source gap is inside a collapsed or erased inline boundary and has no editor position.");
|
|
183
|
+
return Result.ok(paragraph.start + gap.position);
|
|
107
184
|
}
|
|
108
185
|
selectionAt(state) {
|
|
109
|
-
if (!(state.selection instanceof TextSelection)) return refuse("Canonical input requires a text selection.");
|
|
110
|
-
const
|
|
186
|
+
if (!(state.selection instanceof TextSelection) && !(state.selection instanceof AllSelection)) return refuse("Canonical input requires a text selection.");
|
|
187
|
+
const range = canonicalSelectionRange(state);
|
|
188
|
+
const type = state.selection instanceof AllSelection ? "all" : "text";
|
|
189
|
+
const anchor = this.addressAt(type === "all" ? range.from : state.selection.anchor);
|
|
111
190
|
if (anchor.isErr()) return anchor;
|
|
112
|
-
const head = this.addressAt(state.selection.head);
|
|
191
|
+
const head = this.addressAt(type === "all" ? range.to : state.selection.head);
|
|
113
192
|
if (head.isErr()) return head;
|
|
114
193
|
return Result.ok({
|
|
194
|
+
type,
|
|
115
195
|
anchor: anchor.value,
|
|
116
196
|
head: head.value
|
|
117
197
|
});
|
|
@@ -162,31 +242,31 @@ const preservePropertySources = ({ target, source, stories = documentStories(sou
|
|
|
162
242
|
}
|
|
163
243
|
copyDocumentParagraphPropertySourceContract(target, source);
|
|
164
244
|
};
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
case "deletion": return supportsTextContent(item.content);
|
|
170
|
-
default: return false;
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
const supportsSeed = (document, stories = documentStories(document)) => {
|
|
174
|
-
if ((document.package.document.comments?.length ?? 0) > 0) return false;
|
|
175
|
-
return stories.every((story) => {
|
|
245
|
+
const unsupportedSeedReason = (document, stories = documentStories(document)) => {
|
|
246
|
+
const commentIssue = commentDocumentIssue(document, "all");
|
|
247
|
+
if (commentIssue !== void 0) return commentIssue;
|
|
248
|
+
for (const story of stories) {
|
|
176
249
|
const body = findStoryBody(document, story);
|
|
177
250
|
if (story !== OP_STORIES.MAIN && (story.kind === "footnote" || story.kind === "endnote")) {
|
|
178
251
|
const note = (story.kind === "footnote" ? document.package.footnotes : document.package.endnotes)?.find(({ id }) => id === story.id);
|
|
179
|
-
if (note?.noteType !== void 0 && note.noteType !== "normal")
|
|
252
|
+
if (note?.noteType !== void 0 && note.noteType !== "normal") continue;
|
|
180
253
|
}
|
|
181
254
|
const content = body?.content;
|
|
182
|
-
|
|
183
|
-
|
|
255
|
+
if (!content || content.length === 0) return "Canonical sessions require a nonempty paragraph story.";
|
|
256
|
+
for (const paragraph of content) {
|
|
257
|
+
if (paragraph.type !== "paragraph" || paragraph.reviewCarrier !== void 0) return "Canonical sessions currently require plain paragraphs and supported inline atoms.";
|
|
258
|
+
const issue = canonicalInlineSourceIssue(paragraph.content);
|
|
259
|
+
if (issue !== null) return issue;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
184
263
|
};
|
|
185
264
|
const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
186
265
|
const body = findStoryBody(document, story);
|
|
187
266
|
if (!body) return refuse("The canonical story no longer exists.");
|
|
267
|
+
const currentStyles = Object.hasOwn(document.package, "styles") ? document.package.styles : styles;
|
|
188
268
|
const options = {
|
|
189
|
-
...
|
|
269
|
+
...currentStyles == null ? {} : { styles: currentStyles },
|
|
190
270
|
...document.package.theme === void 0 ? {} : { theme: document.package.theme }
|
|
191
271
|
};
|
|
192
272
|
const converted = Result.try({
|
|
@@ -196,70 +276,66 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
196
276
|
return footnoteToProseDoc(body.content, options);
|
|
197
277
|
},
|
|
198
278
|
catch: (cause) => new CanonicalSessionError({
|
|
279
|
+
gap: CANONICAL_GAP.dispatch,
|
|
199
280
|
reason: "refused",
|
|
200
281
|
message: `Canonical projection failed: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
201
282
|
})
|
|
202
283
|
});
|
|
203
284
|
if (converted.isErr()) return converted;
|
|
285
|
+
const pairedBookmarkIds = collectPairedBookmarkIds(body.content);
|
|
286
|
+
const commentContext = {
|
|
287
|
+
rangedIds: rangedCommentIds(body.content),
|
|
288
|
+
pointIds: /* @__PURE__ */ new Set(),
|
|
289
|
+
openIds: /* @__PURE__ */ new Set()
|
|
290
|
+
};
|
|
291
|
+
visitCommentMarkers(body.content, ({ item }) => {
|
|
292
|
+
if (item.type === "commentReference" && !commentContext.rangedIds.has(item.id)) commentContext.pointIds.add(item.id);
|
|
293
|
+
});
|
|
204
294
|
const paragraphs = [];
|
|
205
295
|
let failure;
|
|
206
296
|
converted.value.forEach((node, offset, index) => {
|
|
207
297
|
const source = body.content.at(index);
|
|
208
298
|
if (source?.type !== "paragraph" || source.paraId === void 0) {
|
|
209
299
|
failure = new CanonicalSessionError({
|
|
300
|
+
gap: CANONICAL_GAP.dispatch,
|
|
210
301
|
reason: "refused",
|
|
211
302
|
message: "The projection changed paragraph structure."
|
|
212
303
|
});
|
|
213
304
|
return;
|
|
214
305
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
boundaries.push([renderedSize]);
|
|
231
|
-
}
|
|
232
|
-
else if (child.type === "footnoteRef" || child.type === "endnoteRef") {
|
|
233
|
-
text += "";
|
|
234
|
-
renderedText += String(child.id);
|
|
235
|
-
renderedSize += String(child.id).length;
|
|
236
|
-
boundaries.push([renderedSize]);
|
|
237
|
-
} else if (child.type === "tab" || child.type === "break") {
|
|
238
|
-
text += "";
|
|
239
|
-
renderedSize += 1;
|
|
240
|
-
boundaries.push([renderedSize]);
|
|
241
|
-
} else if (child.type === "noteMarker") {
|
|
242
|
-
renderedSize += 1;
|
|
243
|
-
(boundaries.at(-1) ?? panic("Missing canonical note gap")).push(renderedSize);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
appendContent(source.content);
|
|
248
|
-
if (node.type.name !== "paragraph" || node.attrs["paraId"] !== source.paraId || node.textContent !== renderedText || node.content.size !== renderedSize) {
|
|
306
|
+
if (node.type.name !== "paragraph" || node.attrs["paraId"] !== source.paraId) {
|
|
307
|
+
failure = new CanonicalSessionError({
|
|
308
|
+
gap: CANONICAL_GAP.dispatch,
|
|
309
|
+
reason: "refused",
|
|
310
|
+
message: "The projection changed paragraph structure."
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const inline = projectCanonicalInline({
|
|
315
|
+
source,
|
|
316
|
+
paragraph: node,
|
|
317
|
+
pairedBookmarkIds,
|
|
318
|
+
commentContext
|
|
319
|
+
});
|
|
320
|
+
if (inline.isErr()) {
|
|
249
321
|
failure = new CanonicalSessionError({
|
|
322
|
+
gap: CANONICAL_GAP.dispatch,
|
|
250
323
|
reason: "refused",
|
|
251
|
-
message:
|
|
324
|
+
message: inline.error.message
|
|
252
325
|
});
|
|
253
326
|
return;
|
|
254
327
|
}
|
|
255
328
|
paragraphs.push({
|
|
256
329
|
blockId: source.paraId,
|
|
257
330
|
start: offset + 1,
|
|
258
|
-
|
|
259
|
-
boundaries,
|
|
331
|
+
...inline.value,
|
|
260
332
|
node,
|
|
261
333
|
source
|
|
262
334
|
});
|
|
335
|
+
visitCommentMarkers([source], ({ item }) => {
|
|
336
|
+
if (item.type === "commentRangeStart") commentContext.openIds.add(item.id);
|
|
337
|
+
if (item.type === "commentRangeEnd") commentContext.openIds.delete(item.id);
|
|
338
|
+
});
|
|
263
339
|
});
|
|
264
340
|
if (failure !== void 0) return Result.err(failure);
|
|
265
341
|
if (paragraphs.length !== body.content.length) return refuse("The projection changed paragraph structure.");
|
|
@@ -271,9 +347,15 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
271
347
|
};
|
|
272
348
|
const intentStory = (intent) => {
|
|
273
349
|
switch (intent.type) {
|
|
350
|
+
case "setHyperlink":
|
|
351
|
+
case "removeHyperlink":
|
|
352
|
+
case "insertHyperlink":
|
|
353
|
+
case "replaceFragment":
|
|
354
|
+
case "moveFragment":
|
|
274
355
|
case "replaceText":
|
|
275
356
|
case "formatRun":
|
|
276
357
|
case "insertAtom": return intent.from.story;
|
|
358
|
+
case "generateTOC":
|
|
277
359
|
case "splitParagraph":
|
|
278
360
|
case "formatParagraph": return intent.at.story;
|
|
279
361
|
case "setList": return intent.items.at(0)?.at.story ?? panic("A list intent requires a paragraph.");
|
|
@@ -284,14 +366,14 @@ const intentStory = (intent) => {
|
|
|
284
366
|
};
|
|
285
367
|
const UNDO_GROUP_WINDOW_MS = 500;
|
|
286
368
|
const samePosition = (left, right) => sameStory(left.story, right.story) && left.blockId === right.blockId && left.offset === right.offset && (left.zeroWidthBefore ?? 0) === (right.zeroWidthBefore ?? 0);
|
|
287
|
-
const sameSelection = (left, right) => samePosition(left.anchor, right.anchor) && samePosition(left.head, right.head);
|
|
369
|
+
const sameSelection = (left, right) => left.type === right.type && samePosition(left.anchor, right.anchor) && samePosition(left.head, right.head);
|
|
288
370
|
const continuesGroup = (previous, next) => {
|
|
289
371
|
switch (next.semantic) {
|
|
290
372
|
case "typing":
|
|
291
373
|
case "deleteBackward":
|
|
292
374
|
case "deleteForward": return previous.grouping === "run" && next.grouping === "run" && previous.semantic === next.semantic && previous.boundary === next.boundary && next.time >= previous.time && next.time - previous.time <= UNDO_GROUP_WINDOW_MS && sameSelection(previous.postSelection, next.preSelection) && samePosition(next.preSelection.anchor, next.preSelection.head);
|
|
375
|
+
case "composition": return next.grouping === "compositionCorrection" && previous.semantic === "composition" && previous.boundary === next.boundary && sameSelection(previous.postSelection, next.preSelection);
|
|
293
376
|
case "paste":
|
|
294
|
-
case "composition":
|
|
295
377
|
case "replacement":
|
|
296
378
|
case "structure": return false;
|
|
297
379
|
default: {
|
|
@@ -312,6 +394,7 @@ const publishCanonicalProjection = ({ state, commit, session }) => {
|
|
|
312
394
|
const applied = Result.try({
|
|
313
395
|
try: () => state.applyTransaction(commit.transaction),
|
|
314
396
|
catch: (cause) => new CanonicalSessionError({
|
|
397
|
+
gap: CANONICAL_GAP.dispatch,
|
|
315
398
|
reason: "refused",
|
|
316
399
|
message: `Canonical plugin staging failed: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
317
400
|
})
|
|
@@ -336,7 +419,10 @@ const storyProjectionKey = (story) => {
|
|
|
336
419
|
var CanonicalSession = class {
|
|
337
420
|
currentDocument;
|
|
338
421
|
currentProjection;
|
|
422
|
+
sourceBlockIds;
|
|
339
423
|
currentVersion = 0;
|
|
424
|
+
saveTouched = /* @__PURE__ */ new Set();
|
|
425
|
+
saveStructure = "stable";
|
|
340
426
|
allocateIntentIds = createEditorIntentIdAllocator();
|
|
341
427
|
mode = { type: "editing" };
|
|
342
428
|
allocatedBlockIds = /* @__PURE__ */ new Set();
|
|
@@ -352,11 +438,16 @@ var CanonicalSession = class {
|
|
|
352
438
|
constructor({ document, projection, styles }) {
|
|
353
439
|
this.currentDocument = document;
|
|
354
440
|
this.currentProjection = projection;
|
|
441
|
+
this.sourceBlockIds = new Set(packageParagraphIds(document.package));
|
|
355
442
|
for (const blockId of packageParagraphIds(document.package)) this.allocatedBlockIds.add(blockId.toUpperCase());
|
|
356
443
|
this.advanceBlockId();
|
|
357
444
|
this.styles = styles == null ? styles : structuredClone(styles);
|
|
358
445
|
this.rememberSources(document);
|
|
359
446
|
}
|
|
447
|
+
/** Render only the committed comment projection while provisional IME blocks document snapshots. */
|
|
448
|
+
getCommittedComments() {
|
|
449
|
+
return structuredClone(this.currentDocument.package.document.comments ?? []);
|
|
450
|
+
}
|
|
360
451
|
setMode(mode) {
|
|
361
452
|
if (this.mode.type !== mode.type || this.mode.type === "suggesting" && mode.type === "suggesting" && this.mode.author !== mode.author) this.breakUndoGroup();
|
|
362
453
|
this.mode = mode.type === "editing" ? { type: "editing" } : {
|
|
@@ -375,13 +466,18 @@ var CanonicalSession = class {
|
|
|
375
466
|
return Result.ok(this.nextBlockId.toString(16).padStart(8, "0").toUpperCase());
|
|
376
467
|
}
|
|
377
468
|
intentMode(document, intent) {
|
|
378
|
-
if (this.mode.type === "editing" && !this.intentNeedsIdentityIds(document, intent)) return {
|
|
469
|
+
if (this.mode.type === "editing" && !this.intentNeedsIdentityIds(document, intent)) return {
|
|
470
|
+
type: "editing",
|
|
471
|
+
reservedBlockIds: this.allocatedBlockIds
|
|
472
|
+
};
|
|
379
473
|
const ids = this.allocateIntentIds(document, intent);
|
|
380
474
|
return this.mode.type === "editing" ? {
|
|
381
475
|
type: "editing",
|
|
382
|
-
newIds: ids.newIds
|
|
476
|
+
newIds: ids.newIds,
|
|
477
|
+
reservedBlockIds: this.allocatedBlockIds
|
|
383
478
|
} : {
|
|
384
479
|
type: "suggesting",
|
|
480
|
+
reservedBlockIds: this.allocatedBlockIds,
|
|
385
481
|
revision: {
|
|
386
482
|
id: ids.revisionId,
|
|
387
483
|
author: this.mode.author,
|
|
@@ -394,8 +490,14 @@ var CanonicalSession = class {
|
|
|
394
490
|
let blockIds;
|
|
395
491
|
switch (intent.type) {
|
|
396
492
|
case "table": return true;
|
|
493
|
+
case "generateTOC":
|
|
397
494
|
case "setList":
|
|
398
495
|
case "formatParagraph": return false;
|
|
496
|
+
case "setHyperlink":
|
|
497
|
+
case "removeHyperlink":
|
|
498
|
+
case "insertHyperlink":
|
|
499
|
+
case "replaceFragment":
|
|
500
|
+
case "moveFragment": return true;
|
|
399
501
|
case "replaceText":
|
|
400
502
|
case "insertAtom":
|
|
401
503
|
case "formatRun":
|
|
@@ -428,11 +530,26 @@ var CanonicalSession = class {
|
|
|
428
530
|
}
|
|
429
531
|
get document() {
|
|
430
532
|
if (this.isComposing) throw new CanonicalSessionError({
|
|
533
|
+
gap: CANONICAL_GAP.dispatch,
|
|
431
534
|
reason: "refused",
|
|
432
535
|
message: "Composition must finish before taking a snapshot."
|
|
433
536
|
});
|
|
434
537
|
return this.currentDocument;
|
|
435
538
|
}
|
|
539
|
+
captureSaveSnapshot() {
|
|
540
|
+
if (this.isComposing) throw new CanonicalSessionError({
|
|
541
|
+
gap: CANONICAL_GAP.save,
|
|
542
|
+
reason: "refused",
|
|
543
|
+
message: "Composition must finish before saving the canonical document."
|
|
544
|
+
});
|
|
545
|
+
const retainedBlockIdKeys = new Set([...this.sourceBlockIds, ...packageParagraphIds(this.currentDocument.package)].map((blockId) => blockId.toUpperCase()));
|
|
546
|
+
return {
|
|
547
|
+
document: cloneDocumentWithParagraphPropertySources(this.currentDocument),
|
|
548
|
+
version: this.currentVersion,
|
|
549
|
+
changedBlockIds: [...this.saveTouched].filter((blockId) => retainedBlockIdKeys.has(blockId.toUpperCase())),
|
|
550
|
+
structure: this.saveStructure
|
|
551
|
+
};
|
|
552
|
+
}
|
|
436
553
|
get projection() {
|
|
437
554
|
return this.currentProjection;
|
|
438
555
|
}
|
|
@@ -469,7 +586,7 @@ var CanonicalSession = class {
|
|
|
469
586
|
if (!state.doc.eq(projection.doc)) return refuse("The input uses a stale canonical projection.");
|
|
470
587
|
return Result.ok(void 0);
|
|
471
588
|
}
|
|
472
|
-
prepareReplace(state, { from, to, text, story = OP_STORIES.MAIN, semantic = "replacement", time = Date.now() }) {
|
|
589
|
+
prepareReplace(state, { from, to, text, story = OP_STORIES.MAIN, semantic = "replacement", compositionPhase, time = Date.now() }) {
|
|
473
590
|
const projected = this.projectStory(story);
|
|
474
591
|
if (projected.isErr()) return projected;
|
|
475
592
|
const projection = projected.value;
|
|
@@ -498,11 +615,14 @@ var CanonicalSession = class {
|
|
|
498
615
|
if (compiled.isErr()) return refuse(compiled.error.message);
|
|
499
616
|
const { ops, selection: caret } = compiled.value;
|
|
500
617
|
const postSelection = {
|
|
618
|
+
type: "text",
|
|
501
619
|
anchor: caret,
|
|
502
620
|
head: caret
|
|
503
621
|
};
|
|
504
622
|
const preDocument = this.currentDocument;
|
|
505
|
-
const
|
|
623
|
+
const isCaret = state.selection.empty;
|
|
624
|
+
const deletion = isCaret && text.length === 0 && (semantic === "deleteBackward" || semantic === "deleteForward") ? deletionRange(state, semantic === "deleteBackward" ? "backward" : "forward") : null;
|
|
625
|
+
const grouping = isCaret && (semantic === "typing" && from === to && from === state.selection.head || deletion?.isOk() && deletion.value.from === from && deletion.value.to === to) ? "run" : "isolated";
|
|
506
626
|
return this.stage({
|
|
507
627
|
state,
|
|
508
628
|
story,
|
|
@@ -521,7 +641,7 @@ var CanonicalSession = class {
|
|
|
521
641
|
version,
|
|
522
642
|
origin: "input",
|
|
523
643
|
semantic,
|
|
524
|
-
grouping:
|
|
644
|
+
grouping: compositionPhase === "correction" ? "compositionCorrection" : grouping,
|
|
525
645
|
time,
|
|
526
646
|
boundary: this.groupingBoundary
|
|
527
647
|
};
|
|
@@ -541,9 +661,9 @@ var CanonicalSession = class {
|
|
|
541
661
|
});
|
|
542
662
|
}
|
|
543
663
|
prepareIntent(state, intent) {
|
|
544
|
-
return this.prepareIntents(state, [intent]);
|
|
664
|
+
return this.prepareIntents(state, { intents: [intent] });
|
|
545
665
|
}
|
|
546
|
-
prepareIntents(state, intents) {
|
|
666
|
+
prepareIntents(state, { intents, resourceOps = [], semantic = "structure" }) {
|
|
547
667
|
const first = intents.at(0);
|
|
548
668
|
if (first === void 0) return noChange("The intent batch is empty.");
|
|
549
669
|
const story = intentStory(first);
|
|
@@ -556,12 +676,24 @@ var CanonicalSession = class {
|
|
|
556
676
|
if (preSelection.isErr()) return preSelection;
|
|
557
677
|
let document = this.currentDocument;
|
|
558
678
|
let postSelection = preSelection.value;
|
|
559
|
-
const ops = [];
|
|
679
|
+
const ops = [...resourceOps];
|
|
560
680
|
const edits = [];
|
|
681
|
+
if (resourceOps.length > 0) {
|
|
682
|
+
const imported = applyDocumentOps(document, resourceOps);
|
|
683
|
+
if (imported.isErr()) return refuse(imported.error.message);
|
|
684
|
+
document = imported.value.document;
|
|
685
|
+
edits.push(imported.value);
|
|
686
|
+
}
|
|
561
687
|
for (const intent of intents) {
|
|
688
|
+
if (this.mode.type === "suggesting" && (intent.type === "setHyperlink" || intent.type === "removeHyperlink" || intent.type === "insertHyperlink" || intent.type === "generateTOC")) return Result.err(new CanonicalSessionError({
|
|
689
|
+
gap: CANONICAL_GAP.trackedHyperlinkResolution,
|
|
690
|
+
reason: "refused",
|
|
691
|
+
message: "Hyperlink and TOC suggestions require serializable wrapper review provenance."
|
|
692
|
+
}));
|
|
562
693
|
const compiled = compileEditorIntent(document, {
|
|
563
694
|
intent,
|
|
564
|
-
mode: this.intentMode(document, intent)
|
|
695
|
+
mode: this.intentMode(document, intent),
|
|
696
|
+
firstBlockId: this.nextBlockId
|
|
565
697
|
});
|
|
566
698
|
if (compiled.isErr()) return refuse(compiled.error.message);
|
|
567
699
|
const applied = applyDocumentOps(document, compiled.value.ops);
|
|
@@ -569,7 +701,18 @@ var CanonicalSession = class {
|
|
|
569
701
|
document = applied.value.document;
|
|
570
702
|
edits.push(applied.value);
|
|
571
703
|
ops.push(...compiled.value.ops);
|
|
572
|
-
if (intent.type
|
|
704
|
+
if (intent.type === "generateTOC") {
|
|
705
|
+
const mapping = {
|
|
706
|
+
at: intent.at,
|
|
707
|
+
after: compiled.value.selection,
|
|
708
|
+
ops: compiled.value.ops
|
|
709
|
+
};
|
|
710
|
+
postSelection = Object.assign({}, postSelection, {
|
|
711
|
+
anchor: mapTocSelection(postSelection.anchor, mapping),
|
|
712
|
+
head: mapTocSelection(postSelection.head, mapping)
|
|
713
|
+
});
|
|
714
|
+
} else if (intent.type !== "setHyperlink" && intent.type !== "removeHyperlink" && intent.type !== "formatRun" && intent.type !== "formatParagraph" && intent.type !== "setList") postSelection = {
|
|
715
|
+
type: "text",
|
|
573
716
|
anchor: compiled.value.selection,
|
|
574
717
|
head: compiled.value.selection
|
|
575
718
|
};
|
|
@@ -582,7 +725,8 @@ var CanonicalSession = class {
|
|
|
582
725
|
state,
|
|
583
726
|
ops,
|
|
584
727
|
postSelection,
|
|
585
|
-
stagedApplied
|
|
728
|
+
stagedApplied,
|
|
729
|
+
semantic
|
|
586
730
|
});
|
|
587
731
|
}
|
|
588
732
|
prepareOps(state, ops, postSelection) {
|
|
@@ -592,7 +736,7 @@ var CanonicalSession = class {
|
|
|
592
736
|
postSelection
|
|
593
737
|
});
|
|
594
738
|
}
|
|
595
|
-
prepareJournalledOps({ state, ops, postSelection, stagedApplied }) {
|
|
739
|
+
prepareJournalledOps({ state, ops, postSelection, stagedApplied, semantic = "structure" }) {
|
|
596
740
|
const story = postSelection.anchor.story;
|
|
597
741
|
const projected = this.projectStory(story);
|
|
598
742
|
if (projected.isErr()) return projected;
|
|
@@ -618,7 +762,7 @@ var CanonicalSession = class {
|
|
|
618
762
|
postDocument: this.currentDocument,
|
|
619
763
|
version,
|
|
620
764
|
origin: "input",
|
|
621
|
-
semantic
|
|
765
|
+
semantic,
|
|
622
766
|
grouping: "isolated",
|
|
623
767
|
time: Date.now(),
|
|
624
768
|
boundary: this.groupingBoundary
|
|
@@ -656,7 +800,7 @@ var CanonicalSession = class {
|
|
|
656
800
|
},
|
|
657
801
|
patch: { styleId: nextStyle }
|
|
658
802
|
});
|
|
659
|
-
return this.prepareIntents(state, intents);
|
|
803
|
+
return this.prepareIntents(state, { intents });
|
|
660
804
|
}
|
|
661
805
|
prepareJoin(state, direction) {
|
|
662
806
|
const checked = this.checkState(state);
|
|
@@ -712,6 +856,7 @@ var CanonicalSession = class {
|
|
|
712
856
|
state,
|
|
713
857
|
ops,
|
|
714
858
|
postSelection: {
|
|
859
|
+
type: "text",
|
|
715
860
|
anchor: caret,
|
|
716
861
|
head: caret
|
|
717
862
|
},
|
|
@@ -836,7 +981,8 @@ var CanonicalSession = class {
|
|
|
836
981
|
document: applied.value.document,
|
|
837
982
|
previous: this.currentDocument
|
|
838
983
|
});
|
|
839
|
-
|
|
984
|
+
const unsupported = unsupportedSeedReason(applied.value.document, changed);
|
|
985
|
+
if (unsupported !== null) return refuse(unsupported);
|
|
840
986
|
preservePropertySources({
|
|
841
987
|
target: applied.value.document,
|
|
842
988
|
source: this.currentDocument,
|
|
@@ -891,12 +1037,15 @@ var CanonicalSession = class {
|
|
|
891
1037
|
transaction.replaceWith(from, to, Fragment.fromArray(replacement));
|
|
892
1038
|
}
|
|
893
1039
|
if (!transaction.doc.eq(projected.value.doc)) transaction.replaceWith(0, transaction.doc.content.size, projected.value.doc.content);
|
|
894
|
-
if (sameStory(selection.anchor.story,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1040
|
+
if (sameStory(selection.anchor.story, projectedStory) && sameStory(selection.head.story, projectedStory)) restoreCanonicalSelection({
|
|
1041
|
+
transaction,
|
|
1042
|
+
projection: projected.value,
|
|
1043
|
+
selection,
|
|
1044
|
+
unavailable: {
|
|
1045
|
+
type: "near",
|
|
1046
|
+
anchor: state.selection.anchor
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
900
1049
|
transaction.setMeta(CANONICAL_PROJECTION_META, {
|
|
901
1050
|
type: "canonical",
|
|
902
1051
|
origin,
|
|
@@ -916,6 +1065,7 @@ var CanonicalSession = class {
|
|
|
916
1065
|
document: applied.value.document,
|
|
917
1066
|
projection: projected.value,
|
|
918
1067
|
bodyProjection: bodyProjection.value,
|
|
1068
|
+
selection,
|
|
919
1069
|
touched: applied.value.touched,
|
|
920
1070
|
version: baseVersion + 1,
|
|
921
1071
|
origin,
|
|
@@ -939,6 +1089,13 @@ var CanonicalSession = class {
|
|
|
939
1089
|
this.currentSelection = selection;
|
|
940
1090
|
this.currentDocument = applied.value.document;
|
|
941
1091
|
this.currentProjection = bodyProjection.value;
|
|
1092
|
+
for (const ids of [
|
|
1093
|
+
applied.value.touched.modified,
|
|
1094
|
+
applied.value.touched.inserted,
|
|
1095
|
+
applied.value.touched.removed
|
|
1096
|
+
]) for (const id of ids) this.saveTouched.add(id);
|
|
1097
|
+
const bodyOps = ops.filter((op) => !COMMENT_OPERATION_TYPES.has(op.type));
|
|
1098
|
+
if (bodyOps.some(operationChangesStructure) || bodyOps.some(operationChangesPackage) || bodyOps.length > 0 && (applied.value.touched.inserted.length > 0 || applied.value.touched.removed.length > 0)) this.saveStructure = "changed";
|
|
942
1099
|
this.currentVersion = baseVersion + 1;
|
|
943
1100
|
onPublish(applied.value.inverse, this.currentVersion);
|
|
944
1101
|
return Result.ok(void 0);
|
|
@@ -947,7 +1104,8 @@ var CanonicalSession = class {
|
|
|
947
1104
|
}
|
|
948
1105
|
};
|
|
949
1106
|
const createCanonicalSession = (document, styles) => {
|
|
950
|
-
|
|
1107
|
+
const unsupported = unsupportedSeedReason(document);
|
|
1108
|
+
if (unsupported !== null) return refuse(unsupported);
|
|
951
1109
|
const owned = cloneDocumentWithParagraphPropertySources(document);
|
|
952
1110
|
const normalized = normalizeForOps(owned);
|
|
953
1111
|
preservePropertySources({
|
|
@@ -980,31 +1138,55 @@ const visibleDeletionContext = (state) => {
|
|
|
980
1138
|
let last = index;
|
|
981
1139
|
while (last + 1 < state.doc.childCount && hasDeletedParagraphMark(state.doc.child(last))) last += 1;
|
|
982
1140
|
let text = "";
|
|
983
|
-
const
|
|
1141
|
+
const physicalStarts = [];
|
|
1142
|
+
const physicalEnds = [];
|
|
984
1143
|
let visibleOffset = 0;
|
|
1144
|
+
let issue;
|
|
985
1145
|
state.doc.forEach((paragraph, paragraphOffset, paragraphIndex) => {
|
|
986
1146
|
if (paragraphIndex < first || paragraphIndex > last) return;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1147
|
+
const native = canonicalNativeCells(paragraph);
|
|
1148
|
+
if (native.isErr()) {
|
|
1149
|
+
issue = new CanonicalSessionError({
|
|
1150
|
+
gap: CANONICAL_GAP.dispatch,
|
|
1151
|
+
reason: "refused",
|
|
1152
|
+
message: native.error.message
|
|
1153
|
+
});
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
for (const cell of native.value) {
|
|
1157
|
+
if (cell.type === "zeroWidth" || cell.node.marks.some((mark) => mark.type.name === "deletion")) continue;
|
|
1158
|
+
const value = cell.type === "text" ? cell.text : "";
|
|
1159
|
+
const from = paragraphOffset + 1 + cell.from;
|
|
1160
|
+
const to = paragraphOffset + 1 + cell.to;
|
|
1161
|
+
if (cell.type !== "text" && selection.from > from && selection.from < to) {
|
|
1162
|
+
issue = new CanonicalSessionError({
|
|
1163
|
+
gap: CANONICAL_GAP.dispatch,
|
|
1164
|
+
reason: "refused",
|
|
1165
|
+
message: "Character deletion cannot start inside an inline atom."
|
|
1166
|
+
});
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
990
1169
|
for (let unit = 0; unit < value.length; unit += 1) {
|
|
991
|
-
const position =
|
|
992
|
-
|
|
1170
|
+
const position = from + (cell.type === "text" ? unit : 0);
|
|
1171
|
+
physicalStarts.push(position);
|
|
993
1172
|
text += value.charAt(unit);
|
|
994
|
-
|
|
1173
|
+
physicalEnds.push(cell.type === "text" ? position + 1 : to);
|
|
995
1174
|
if (position < selection.from) visibleOffset += 1;
|
|
996
1175
|
}
|
|
997
|
-
}
|
|
1176
|
+
}
|
|
998
1177
|
});
|
|
999
|
-
return {
|
|
1178
|
+
return issue === void 0 ? Result.ok({
|
|
1000
1179
|
text,
|
|
1001
|
-
|
|
1180
|
+
physicalStarts,
|
|
1181
|
+
physicalEnds,
|
|
1002
1182
|
visibleOffset
|
|
1003
|
-
};
|
|
1183
|
+
}) : Result.err(issue);
|
|
1004
1184
|
};
|
|
1005
1185
|
const isCanonicalJoinBoundary = (state, direction) => {
|
|
1006
1186
|
if (!(state.selection instanceof TextSelection) || !state.selection.empty) return false;
|
|
1007
|
-
const
|
|
1187
|
+
const context = visibleDeletionContext(state);
|
|
1188
|
+
if (context.isErr()) return false;
|
|
1189
|
+
const { text, visibleOffset } = context.value;
|
|
1008
1190
|
return visibleOffset === (direction === "backward" ? 0 : text.length);
|
|
1009
1191
|
};
|
|
1010
1192
|
/** Native character deletion consumes one whole visible grapheme. */
|
|
@@ -1018,20 +1200,20 @@ const deletionRange = (state, direction) => {
|
|
|
1018
1200
|
const parent = selection.$from.parent;
|
|
1019
1201
|
const offset = selection.$from.parentOffset;
|
|
1020
1202
|
if ((direction === "backward" ? parent.childBefore(offset).node : parent.childAfter(offset).node)?.type.name === "noteMarker") return refuse("Automatic note marks cannot be deleted as characters.");
|
|
1021
|
-
const
|
|
1203
|
+
const context = visibleDeletionContext(state);
|
|
1204
|
+
if (context.isErr()) return context;
|
|
1205
|
+
const { text, physicalStarts, physicalEnds, visibleOffset } = context.value;
|
|
1022
1206
|
if (splitsSurrogatePair(text, visibleOffset)) return refuse("The deletion would split a surrogate pair.");
|
|
1023
1207
|
if (splitsGraphemeCluster(text, visibleOffset)) return refuse("Character deletion requires a caret at a grapheme boundary.");
|
|
1024
1208
|
if (direction === "backward" && visibleOffset === 0 || direction === "forward" && visibleOffset === text.length) return refuse("There is no visible character to delete in this paragraph.");
|
|
1025
1209
|
let boundary = visibleOffset + (direction === "backward" ? -1 : 1);
|
|
1026
1210
|
while (splitsGraphemeCluster(text, boundary)) boundary += direction === "backward" ? -1 : 1;
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
from: selection.from,
|
|
1034
|
-
to: position
|
|
1211
|
+
const from = physicalStarts.at(direction === "backward" ? boundary : visibleOffset);
|
|
1212
|
+
const to = physicalEnds.at((direction === "backward" ? visibleOffset : boundary) - 1);
|
|
1213
|
+
if (from === void 0 || to === void 0) panic("Canonical deletion lost a grapheme boundary.");
|
|
1214
|
+
return Result.ok({
|
|
1215
|
+
from,
|
|
1216
|
+
to
|
|
1035
1217
|
});
|
|
1036
1218
|
};
|
|
1037
1219
|
//#endregion
|