@stll/folio-core 0.56.0 → 0.58.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 +122 -22
- package/dist/ai-edits/batch-claims.d.ts +3 -1
- package/dist/ai-edits/batch-claims.js +25 -1
- package/dist/ai-edits/table-row-column-mutations.js +17 -3
- package/dist/ai-suggestions/apply.js +20 -1
- package/dist/compare/inline-atoms.js +40 -16
- package/dist/compare/inline-provenance.js +12 -5
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/controller/canonicalClipboard.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +31 -13
- package/dist/controller/canonicalComments.d.ts +41 -0
- package/dist/controller/canonicalComments.js +152 -0
- package/dist/controller/canonicalComposition.d.ts +17 -4
- package/dist/controller/canonicalComposition.js +125 -20
- package/dist/controller/canonicalInlineProjection.d.ts +6 -1
- package/dist/controller/canonicalInlineProjection.js +86 -3
- package/dist/controller/canonicalInput.d.ts +1 -1
- package/dist/controller/canonicalInput.js +14 -4
- package/dist/controller/canonicalInputTimer.d.ts +10 -1
- package/dist/controller/canonicalInputTimer.js +24 -1
- package/dist/controller/canonicalPublicOperations.d.ts +2 -2
- package/dist/controller/canonicalPublicOperations.js +107 -15
- package/dist/controller/canonicalSelection.d.ts +18 -0
- package/dist/controller/canonicalSelection.js +41 -0
- package/dist/controller/canonicalSession.d.ts +4 -1
- package/dist/controller/canonicalSession.js +106 -36
- package/dist/controller/canonicalStoryEditor.d.ts +3 -1
- package/dist/controller/canonicalStoryEditor.js +21 -1
- package/dist/controller/canonicalStructure.js +8 -3
- package/dist/controller/folioEditor.js +13 -0
- package/dist/controller/headerFooterEditorManager.d.ts +1 -1
- package/dist/controller/headerFooterEditorManager.js +25 -11
- package/dist/controller/hiddenEditorApi.d.ts +13 -2
- package/dist/controller/hiddenEditorApi.js +5 -1
- package/dist/controller/hiddenEditorManager.d.ts +3 -1
- package/dist/controller/hiddenEditorManager.js +167 -14
- package/dist/controller/noteEditorManager.d.ts +1 -1
- package/dist/controller/noteEditorManager.js +28 -13
- package/dist/docx/builtInStyles.js +13 -6
- package/dist/docx/containerChildren.gen.d.ts +3 -1
- package/dist/docx/containerChildren.gen.js +33 -1
- package/dist/docx/fieldParser.js +14 -2
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/hyperlinkParser.js +20 -1
- 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/listRendering.d.ts +18 -0
- package/dist/{prosemirror/conversion/listRenderingDefinition.js → docx/listRendering.js} +17 -3
- package/dist/docx/paragraphParser.js +88 -67
- package/dist/docx/paragraphPropertySource.d.ts +22 -1
- package/dist/docx/paragraphPropertySource.js +107 -1
- package/dist/docx/preservedRunContent.js +2 -1
- package/dist/docx/relsParser.js +26 -2
- package/dist/docx/rezip.js +9 -6
- package/dist/docx/selectiveSave.js +10 -8
- package/dist/docx/serializer/commentSerializer.d.ts +4 -1
- package/dist/docx/serializer/commentSerializer.js +7 -3
- package/dist/docx/serializer/paragraphSerializer.js +207 -94
- package/dist/docx/server/materializeYjsDocx.js +15 -7
- package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
- 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/revisionResolutionInline.js +25 -3
- package/dist/markdown/internals.js +7 -0
- package/dist/markdown/renderParagraph.js +11 -12
- package/dist/markdown/renderRuns.js +3 -13
- package/dist/markdown/types.d.ts +3 -0
- 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 +9 -0
- package/dist/prosemirror/commands/comments.js +60 -35
- 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.d.ts +8 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +150 -30
- package/dist/prosemirror/conversion/toProseDoc.js +128 -40
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +22 -5
- 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 +22 -14
- package/dist/prosemirror/extensions/marks/markUtils.js +8 -7
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +8 -1
- 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/imagePictureNames.d.ts +12 -0
- package/dist/prosemirror/imagePictureNames.js +20 -0
- 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/paragraphFormattingProvenance.js +1 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
- package/dist/prosemirror/plugins/documentStyles.js +17 -10
- package/dist/prosemirror/plugins/suggestionMode.js +47 -49
- 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 +74 -2
- package/dist/prosemirror/runStyleFormatting.js +20 -1
- package/dist/prosemirror/schema/marks.d.ts +1 -0
- package/dist/prosemirror/schema/nodes.d.ts +3 -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/styles/styleResolver.d.ts +4 -1
- package/dist/prosemirror/styles/styleResolver.js +32 -11
- 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 +18 -18
- package/dist/types/canonicalCapabilities.js +18 -18
- package/dist/types/canonicalComments.d.ts +51 -0
- package/dist/types/canonicalComments.js +0 -0
- package/dist/types/canonicalSections.d.ts +12 -0
- package/dist/types/canonicalSections.js +0 -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 +2 -2
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +0 -6
|
@@ -1,4 +1,5 @@
|
|
|
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
4
|
import { canonicalSelectionRange } from "../prosemirror/canonicalSelectionRange.js";
|
|
4
5
|
import { collectPairedBookmarkIds, footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
@@ -6,12 +7,13 @@ import { markPackageChange } from "../prosemirror/extensions/features/ParagraphC
|
|
|
6
7
|
import { runFormattingPatchFromMarks } from "../prosemirror/runFormattingFromMarks.js";
|
|
7
8
|
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
8
9
|
import { canonicalInlineSourceIssue, canonicalNativeCells, projectCanonicalInline } from "./canonicalInlineProjection.js";
|
|
10
|
+
import { restoreCanonicalSelection } from "./canonicalSelection.js";
|
|
9
11
|
import { mapTocSelection } from "./canonicalTocSelection.js";
|
|
10
12
|
import { Result, TaggedError, panic } from "better-result";
|
|
11
13
|
import { hasIllegalXmlCharacters } from "@stll/docx-core";
|
|
12
14
|
import { Fragment } from "prosemirror-model";
|
|
13
|
-
import { DOCUMENT_OP_TYPES, OP_STORIES, applyDocumentOps, combineEdits, compileEditorIntent, createEditorIntentIdAllocator, documentStories, editorParagraphGroups, findStoryBody, normalizeForOps, packageParagraphIds, sameStory, validateOpsDocument } from "@stll/docx-core/ops";
|
|
14
|
-
import { AllSelection, TextSelection } from "prosemirror-state";
|
|
15
|
+
import { DOCUMENT_OP_REFUSAL_REASONS, 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, NodeSelection, TextSelection } from "prosemirror-state";
|
|
15
17
|
//#region src/controller/canonicalSession.ts
|
|
16
18
|
var CanonicalSessionError = class extends TaggedError("CanonicalSessionError") {};
|
|
17
19
|
const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
@@ -24,8 +26,20 @@ const noChange = (message) => Result.err(new CanonicalSessionError({
|
|
|
24
26
|
message,
|
|
25
27
|
reason: "noChange"
|
|
26
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
|
+
]);
|
|
27
36
|
const operationChangesPackage = (op) => {
|
|
28
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:
|
|
29
43
|
case DOCUMENT_OP_TYPES.CREATE_HEADER_FOOTER:
|
|
30
44
|
case DOCUMENT_OP_TYPES.REMOVE_HEADER_FOOTER:
|
|
31
45
|
case DOCUMENT_OP_TYPES.ADD_NOTE:
|
|
@@ -71,7 +85,7 @@ const operationChangesStructure = (op) => {
|
|
|
71
85
|
case DOCUMENT_OP_TYPES.SET_PACKAGE_RESOURCES:
|
|
72
86
|
case DOCUMENT_OP_TYPES.CREATE_NUMBERING_INSTANCE:
|
|
73
87
|
case DOCUMENT_OP_TYPES.DELETE_NUMBERING_INSTANCE:
|
|
74
|
-
case DOCUMENT_OP_TYPES.SET_SECTION_ENDPOINT:
|
|
88
|
+
case DOCUMENT_OP_TYPES.SET_SECTION_ENDPOINT: return true;
|
|
75
89
|
case DOCUMENT_OP_TYPES.INSERT_TEXT:
|
|
76
90
|
case DOCUMENT_OP_TYPES.INSERT_CONTENT:
|
|
77
91
|
case DOCUMENT_OP_TYPES.DELETE_RANGE:
|
|
@@ -84,12 +98,22 @@ const operationChangesStructure = (op) => {
|
|
|
84
98
|
case DOCUMENT_OP_TYPES.SET_TABLE_GRID:
|
|
85
99
|
case DOCUMENT_OP_TYPES.SET_CELL_PROPS:
|
|
86
100
|
case DOCUMENT_OP_TYPES.SET_ROW_PROPS:
|
|
87
|
-
case DOCUMENT_OP_TYPES.SET_TABLE_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;
|
|
88
107
|
default: return op;
|
|
89
108
|
}
|
|
90
109
|
};
|
|
91
110
|
const operationChangesBodyProjection = (op) => {
|
|
92
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:
|
|
93
117
|
case DOCUMENT_OP_TYPES.CREATE_HEADER_FOOTER:
|
|
94
118
|
case DOCUMENT_OP_TYPES.REMOVE_HEADER_FOOTER:
|
|
95
119
|
case DOCUMENT_OP_TYPES.ADD_NOTE:
|
|
@@ -159,12 +183,21 @@ var CanonicalProjection = class {
|
|
|
159
183
|
return Result.ok(paragraph.start + gap.position);
|
|
160
184
|
}
|
|
161
185
|
selectionAt(state) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
186
|
+
const selection = state.selection;
|
|
187
|
+
let type;
|
|
188
|
+
let anchorPosition = selection.anchor;
|
|
189
|
+
let headPosition = selection.head;
|
|
190
|
+
if (selection instanceof TextSelection) type = "text";
|
|
191
|
+
else if (selection instanceof NodeSelection && selection.node.isInline && selection.node.isAtom && selection.node.type.name !== "noteMarker") type = "inlineNode";
|
|
192
|
+
else if (selection instanceof AllSelection) {
|
|
193
|
+
type = "all";
|
|
194
|
+
const range = canonicalSelectionRange(state);
|
|
195
|
+
anchorPosition = range.from;
|
|
196
|
+
headPosition = range.to;
|
|
197
|
+
} else return refuse("Canonical input requires text, an inline atom, or the whole document selection.");
|
|
198
|
+
const anchor = this.addressAt(anchorPosition);
|
|
166
199
|
if (anchor.isErr()) return anchor;
|
|
167
|
-
const head = this.addressAt(
|
|
200
|
+
const head = this.addressAt(headPosition);
|
|
168
201
|
if (head.isErr()) return head;
|
|
169
202
|
return Result.ok({
|
|
170
203
|
type,
|
|
@@ -219,6 +252,8 @@ const preservePropertySources = ({ target, source, stories = documentStories(sou
|
|
|
219
252
|
copyDocumentParagraphPropertySourceContract(target, source);
|
|
220
253
|
};
|
|
221
254
|
const unsupportedSeedReason = (document, stories = documentStories(document)) => {
|
|
255
|
+
const commentIssue = commentDocumentIssue(document, "all");
|
|
256
|
+
if (commentIssue !== void 0) return commentIssue;
|
|
222
257
|
for (const story of stories) {
|
|
223
258
|
const body = findStoryBody(document, story);
|
|
224
259
|
if (story !== OP_STORIES.MAIN && (story.kind === "footnote" || story.kind === "endnote")) {
|
|
@@ -257,9 +292,18 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
257
292
|
});
|
|
258
293
|
if (converted.isErr()) return converted;
|
|
259
294
|
const pairedBookmarkIds = collectPairedBookmarkIds(body.content);
|
|
295
|
+
const commentContext = {
|
|
296
|
+
rangedIds: rangedCommentIds(body.content),
|
|
297
|
+
pointIds: /* @__PURE__ */ new Set(),
|
|
298
|
+
openIds: /* @__PURE__ */ new Set()
|
|
299
|
+
};
|
|
300
|
+
visitCommentMarkers(body.content, ({ item }) => {
|
|
301
|
+
if (item.type === "commentReference" && !commentContext.rangedIds.has(item.id)) commentContext.pointIds.add(item.id);
|
|
302
|
+
});
|
|
260
303
|
const paragraphs = [];
|
|
261
304
|
let failure;
|
|
262
305
|
converted.value.forEach((node, offset, index) => {
|
|
306
|
+
if (failure !== void 0) return;
|
|
263
307
|
const source = body.content.at(index);
|
|
264
308
|
if (source?.type !== "paragraph" || source.paraId === void 0) {
|
|
265
309
|
failure = new CanonicalSessionError({
|
|
@@ -271,7 +315,7 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
271
315
|
}
|
|
272
316
|
if (node.type.name !== "paragraph" || node.attrs["paraId"] !== source.paraId) {
|
|
273
317
|
failure = new CanonicalSessionError({
|
|
274
|
-
gap: CANONICAL_GAP.
|
|
318
|
+
gap: CANONICAL_GAP.storyContentProjection,
|
|
275
319
|
reason: "refused",
|
|
276
320
|
message: "The projection changed paragraph structure."
|
|
277
321
|
});
|
|
@@ -280,7 +324,8 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
280
324
|
const inline = projectCanonicalInline({
|
|
281
325
|
source,
|
|
282
326
|
paragraph: node,
|
|
283
|
-
pairedBookmarkIds
|
|
327
|
+
pairedBookmarkIds,
|
|
328
|
+
commentContext
|
|
284
329
|
});
|
|
285
330
|
if (inline.isErr()) {
|
|
286
331
|
failure = new CanonicalSessionError({
|
|
@@ -297,6 +342,10 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
297
342
|
node,
|
|
298
343
|
source
|
|
299
344
|
});
|
|
345
|
+
visitCommentMarkers([source], ({ item }) => {
|
|
346
|
+
if (item.type === "commentRangeStart") commentContext.openIds.add(item.id);
|
|
347
|
+
if (item.type === "commentRangeEnd") commentContext.openIds.delete(item.id);
|
|
348
|
+
});
|
|
300
349
|
});
|
|
301
350
|
if (failure !== void 0) return Result.err(failure);
|
|
302
351
|
if (paragraphs.length !== body.content.length) return refuse("The projection changed paragraph structure.");
|
|
@@ -405,6 +454,10 @@ var CanonicalSession = class {
|
|
|
405
454
|
this.styles = styles == null ? styles : structuredClone(styles);
|
|
406
455
|
this.rememberSources(document);
|
|
407
456
|
}
|
|
457
|
+
/** Render only the committed comment projection while provisional IME blocks document snapshots. */
|
|
458
|
+
getCommittedComments() {
|
|
459
|
+
return structuredClone(this.currentDocument.package.document.comments ?? []);
|
|
460
|
+
}
|
|
408
461
|
setMode(mode) {
|
|
409
462
|
if (this.mode.type !== mode.type || this.mode.type === "suggesting" && mode.type === "suggesting" && this.mode.author !== mode.author) this.breakUndoGroup();
|
|
410
463
|
this.mode = mode.type === "editing" ? { type: "editing" } : {
|
|
@@ -552,9 +605,13 @@ var CanonicalSession = class {
|
|
|
552
605
|
if (!Number.isFinite(time)) return refuse("The input time is invalid.");
|
|
553
606
|
if (from > to || hasIllegalXmlCharacters(text) || /[\t\r\n]/u.test(text)) return refuse("Canonical input accepts a well-formed text replacement.");
|
|
554
607
|
if (from === to && text.length === 0) return noChange("The input makes no text change.");
|
|
555
|
-
const
|
|
608
|
+
const replacesAll = state.selection instanceof AllSelection && from === state.selection.from && to === state.selection.to;
|
|
609
|
+
const range = canonicalSelectionRange(state);
|
|
610
|
+
const inputFrom = replacesAll ? range.from : from;
|
|
611
|
+
const inputTo = replacesAll ? range.to : to;
|
|
612
|
+
const start = projection.inputAddressAt(inputFrom);
|
|
556
613
|
if (start.isErr()) return start;
|
|
557
|
-
const end = projection.inputAddressAt(
|
|
614
|
+
const end = projection.inputAddressAt(inputTo);
|
|
558
615
|
if (end.isErr()) return end;
|
|
559
616
|
const preSelection = projection.selectionAt(state);
|
|
560
617
|
if (preSelection.isErr()) return preSelection;
|
|
@@ -652,7 +709,14 @@ var CanonicalSession = class {
|
|
|
652
709
|
mode: this.intentMode(document, intent),
|
|
653
710
|
firstBlockId: this.nextBlockId
|
|
654
711
|
});
|
|
655
|
-
if (compiled.isErr())
|
|
712
|
+
if (compiled.isErr()) {
|
|
713
|
+
if (compiled.error.reason === DOCUMENT_OP_REFUSAL_REASONS.UNTRACKABLE && (intent.type === "setHyperlink" || intent.type === "removeHyperlink" || intent.type === "insertHyperlink")) return Result.err(new CanonicalSessionError({
|
|
714
|
+
gap: CANONICAL_GAP.trackedHyperlinkResolution,
|
|
715
|
+
reason: "refused",
|
|
716
|
+
message: compiled.error.message
|
|
717
|
+
}));
|
|
718
|
+
return refuse(compiled.error.message);
|
|
719
|
+
}
|
|
656
720
|
const applied = applyDocumentOps(document, compiled.value.ops);
|
|
657
721
|
if (applied.isErr()) return refuse(applied.error.message);
|
|
658
722
|
document = applied.value.document;
|
|
@@ -938,8 +1002,11 @@ var CanonicalSession = class {
|
|
|
938
1002
|
document: applied.value.document,
|
|
939
1003
|
previous: this.currentDocument
|
|
940
1004
|
});
|
|
941
|
-
|
|
942
|
-
|
|
1005
|
+
if (unsupportedSeedReason(applied.value.document, changed) !== null) return Result.err(new CanonicalSessionError({
|
|
1006
|
+
gap: CANONICAL_GAP.storyContentProjection,
|
|
1007
|
+
reason: "refused",
|
|
1008
|
+
message: "The operations produce unsupported canonical story content."
|
|
1009
|
+
}));
|
|
943
1010
|
preservePropertySources({
|
|
944
1011
|
target: applied.value.document,
|
|
945
1012
|
source: this.currentDocument,
|
|
@@ -994,25 +1061,16 @@ var CanonicalSession = class {
|
|
|
994
1061
|
transaction.replaceWith(from, to, Fragment.fromArray(replacement));
|
|
995
1062
|
}
|
|
996
1063
|
if (!transaction.doc.eq(projected.value.doc)) transaction.replaceWith(0, transaction.doc.content.size, projected.value.doc.content);
|
|
997
|
-
if (sameStory(selection.anchor.story,
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (anchor.isOk() && head.isOk()) {
|
|
1006
|
-
transaction.setSelection(TextSelection.create(transaction.doc, anchor.value, head.value));
|
|
1007
|
-
break;
|
|
1008
|
-
}
|
|
1009
|
-
transaction.setSelection(TextSelection.near(transaction.doc.resolve(Math.min(state.selection.anchor, transaction.doc.content.size))));
|
|
1010
|
-
break;
|
|
1011
|
-
default: {
|
|
1012
|
-
const exhaustive = selection.type;
|
|
1013
|
-
return panic(`Unknown canonical selection type: ${exhaustive}`);
|
|
1064
|
+
if (sameStory(selection.anchor.story, projectedStory) && sameStory(selection.head.story, projectedStory)) {
|
|
1065
|
+
if (!restoreCanonicalSelection({
|
|
1066
|
+
transaction,
|
|
1067
|
+
projection: projected.value,
|
|
1068
|
+
selection,
|
|
1069
|
+
unavailable: {
|
|
1070
|
+
type: "near",
|
|
1071
|
+
anchor: state.selection.anchor
|
|
1014
1072
|
}
|
|
1015
|
-
}
|
|
1073
|
+
})) return refuse("Canonical history cannot restore the selected inline atom.");
|
|
1016
1074
|
}
|
|
1017
1075
|
transaction.setMeta(CANONICAL_PROJECTION_META, {
|
|
1018
1076
|
type: "canonical",
|
|
@@ -1033,6 +1091,7 @@ var CanonicalSession = class {
|
|
|
1033
1091
|
document: applied.value.document,
|
|
1034
1092
|
projection: projected.value,
|
|
1035
1093
|
bodyProjection: bodyProjection.value,
|
|
1094
|
+
selection,
|
|
1036
1095
|
touched: applied.value.touched,
|
|
1037
1096
|
version: baseVersion + 1,
|
|
1038
1097
|
origin,
|
|
@@ -1061,7 +1120,8 @@ var CanonicalSession = class {
|
|
|
1061
1120
|
applied.value.touched.inserted,
|
|
1062
1121
|
applied.value.touched.removed
|
|
1063
1122
|
]) for (const id of ids) this.saveTouched.add(id);
|
|
1064
|
-
|
|
1123
|
+
const bodyOps = ops.filter((op) => !COMMENT_OPERATION_TYPES.has(op.type));
|
|
1124
|
+
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";
|
|
1065
1125
|
this.currentVersion = baseVersion + 1;
|
|
1066
1126
|
onPublish(applied.value.inverse, this.currentVersion);
|
|
1067
1127
|
return Result.ok(void 0);
|
|
@@ -1070,6 +1130,11 @@ var CanonicalSession = class {
|
|
|
1070
1130
|
}
|
|
1071
1131
|
};
|
|
1072
1132
|
const createCanonicalSession = (document, styles) => {
|
|
1133
|
+
if (documentStories(document).some((story) => findStoryBody(document, story)?.content.some((block) => block.type === "table"))) return Result.err(new CanonicalSessionError({
|
|
1134
|
+
gap: CANONICAL_GAP.tableActivation,
|
|
1135
|
+
reason: "refused",
|
|
1136
|
+
message: "Canonical sessions cannot activate documents containing tables."
|
|
1137
|
+
}));
|
|
1073
1138
|
const unsupported = unsupportedSeedReason(document);
|
|
1074
1139
|
if (unsupported !== null) return refuse(unsupported);
|
|
1075
1140
|
const owned = cloneDocumentWithParagraphPropertySources(document);
|
|
@@ -1158,7 +1223,12 @@ const isCanonicalJoinBoundary = (state, direction) => {
|
|
|
1158
1223
|
/** Native character deletion consumes one whole visible grapheme. */
|
|
1159
1224
|
const deletionRange = (state, direction) => {
|
|
1160
1225
|
const { selection } = state;
|
|
1161
|
-
if (
|
|
1226
|
+
if (selection instanceof NodeSelection && selection.node.isInline && selection.node.isAtom && selection.node.type.name !== "noteMarker") return Result.ok({
|
|
1227
|
+
from: selection.from,
|
|
1228
|
+
to: selection.to
|
|
1229
|
+
});
|
|
1230
|
+
if (selection instanceof AllSelection) return Result.ok(canonicalSelectionRange(state));
|
|
1231
|
+
if (!(selection instanceof TextSelection)) return refuse("Canonical deletion requires text, an inline atom, or the whole document selection.");
|
|
1162
1232
|
if (!selection.empty) return Result.ok({
|
|
1163
1233
|
from: selection.from,
|
|
1164
1234
|
to: selection.to
|
|
@@ -20,10 +20,12 @@ declare const createCanonicalStoryEditor: ({ story, getView, getApi, enabled, on
|
|
|
20
20
|
handleTextInput: (view: EditorView, from: number, to: number, text: string) => boolean;
|
|
21
21
|
handleDOMEvents: {
|
|
22
22
|
keydown: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
23
|
+
keyup: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
24
|
+
mouseup: (view: EditorView) => boolean;
|
|
23
25
|
beforeinput: (view: EditorView, event: InputEvent) => boolean;
|
|
24
26
|
mousedown: (view: EditorView) => boolean;
|
|
25
27
|
compositionstart: (view: EditorView) => boolean;
|
|
26
|
-
compositionend: (view: EditorView) => boolean;
|
|
28
|
+
compositionend: (view: EditorView, event: Event) => boolean;
|
|
27
29
|
input: (view: EditorView) => boolean;
|
|
28
30
|
blur: (view: EditorView) => boolean;
|
|
29
31
|
paste: (view: EditorView, event: ClipboardEvent) => boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
2
2
|
import { createCanonicalInputBoundary } from "./canonicalInput.js";
|
|
3
3
|
import { CanonicalSessionError } from "./canonicalSession.js";
|
|
4
|
+
import { AllSelection, TextSelection } from "prosemirror-state";
|
|
4
5
|
//#region src/controller/canonicalStoryEditor.ts
|
|
5
6
|
/** Secondary views compile classified native input into the shared Document journal. */
|
|
6
7
|
const createCanonicalStoryEditor = ({ story, getView, getApi, enabled, onRefusal, onSelectionChange }) => {
|
|
@@ -24,6 +25,17 @@ const createCanonicalStoryEditor = ({ story, getView, getApi, enabled, onRefusal
|
|
|
24
25
|
if (accepted && !view.isDestroyed) onSelectionChange();
|
|
25
26
|
return accepted;
|
|
26
27
|
};
|
|
28
|
+
const syncNativeSelection = (view) => {
|
|
29
|
+
if (boundary.isComposing || view.composing) return;
|
|
30
|
+
const selection = view.dom.ownerDocument.getSelection();
|
|
31
|
+
if (!selection?.anchorNode || !selection.focusNode || !view.dom.contains(selection.anchorNode) || !view.dom.contains(selection.focusNode)) return;
|
|
32
|
+
const anchor = view.posAtDOM(selection.anchorNode, selection.anchorOffset);
|
|
33
|
+
const head = view.posAtDOM(selection.focusNode, selection.focusOffset);
|
|
34
|
+
if (view.state.selection instanceof AllSelection && Math.min(anchor, head) === 0 && Math.max(anchor, head) === view.state.doc.content.size) return;
|
|
35
|
+
if (anchor === view.state.selection.anchor && head === view.state.selection.head) return;
|
|
36
|
+
if (!view.state.doc.resolve(anchor).parent.inlineContent || !view.state.doc.resolve(head).parent.inlineContent) return;
|
|
37
|
+
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, anchor, head)));
|
|
38
|
+
};
|
|
27
39
|
const boundary = createCanonicalInputBoundary({
|
|
28
40
|
breakUndoGroup: () => {
|
|
29
41
|
getApi()?.updateCanonicalInputLifecycle("breakUndoGroup");
|
|
@@ -70,10 +82,18 @@ const createCanonicalStoryEditor = ({ story, getView, getApi, enabled, onRefusal
|
|
|
70
82
|
handleTextInput: (view, from, to, text) => enabled() && boundary.handleTextInput(view, from, to, text),
|
|
71
83
|
handleDOMEvents: {
|
|
72
84
|
keydown: (view, event) => enabled() && boundary.handleDOMEvents.keydown(view, event),
|
|
85
|
+
keyup: (view, event) => {
|
|
86
|
+
if (enabled() && /^(?:Arrow|Home$|End$|Page)/u.test(event.key)) syncNativeSelection(view);
|
|
87
|
+
return false;
|
|
88
|
+
},
|
|
89
|
+
mouseup: (view) => {
|
|
90
|
+
if (enabled()) syncNativeSelection(view);
|
|
91
|
+
return false;
|
|
92
|
+
},
|
|
73
93
|
beforeinput: (view, event) => enabled() && boundary.handleDOMEvents.beforeinput(view, event),
|
|
74
94
|
mousedown: (view) => enabled() && boundary.handleDOMEvents.mousedown(view),
|
|
75
95
|
compositionstart: (view) => enabled() && boundary.handleDOMEvents.compositionstart(view),
|
|
76
|
-
compositionend: (view) => enabled() && boundary.handleDOMEvents.compositionend(view),
|
|
96
|
+
compositionend: (view, event) => enabled() && boundary.handleDOMEvents.compositionend(view, event),
|
|
77
97
|
input: (view) => enabled() && boundary.handleDOMEvents.input(view),
|
|
78
98
|
blur: (view) => enabled() && boundary.handleDOMEvents.blur(view),
|
|
79
99
|
paste: (view, event) => enabled() && boundary.handleDOMEvents.paste(view, event),
|
|
@@ -15,6 +15,11 @@ const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
|
15
15
|
message,
|
|
16
16
|
reason: "refused"
|
|
17
17
|
}));
|
|
18
|
+
const noChange = (message) => Result.err(new CanonicalSessionError({
|
|
19
|
+
gap: CANONICAL_GAP.commands,
|
|
20
|
+
message,
|
|
21
|
+
reason: "noChange"
|
|
22
|
+
}));
|
|
18
23
|
const selectedParagraphs = (session, state) => {
|
|
19
24
|
const ids = /* @__PURE__ */ new Set();
|
|
20
25
|
state.doc.nodesBetween(state.selection.from, state.selection.to, (node) => {
|
|
@@ -218,7 +223,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
218
223
|
offset: 0
|
|
219
224
|
};
|
|
220
225
|
const level = (numPr.ilvl ?? 0) + (command.direction === "increase" ? 1 : -1);
|
|
221
|
-
if (level > 8) return
|
|
226
|
+
if (level > 8) return noChange("The list is already at its deepest level.");
|
|
222
227
|
intents.push({
|
|
223
228
|
type: "formatParagraph",
|
|
224
229
|
at,
|
|
@@ -234,7 +239,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
234
239
|
const source = paragraphs.at(0);
|
|
235
240
|
const numPr = source?.formatting?.numPr;
|
|
236
241
|
const definitions = session.document.package.numbering;
|
|
237
|
-
if (source === void 0 || numPr?.kind !== "reference" || definitions === void 0) return
|
|
242
|
+
if (source === void 0 || numPr?.kind !== "reference" || definitions === void 0) return noChange("Numbering changes require a list paragraph.");
|
|
238
243
|
const body = session.document.package.document.content;
|
|
239
244
|
const index = body.findIndex((item) => item === source);
|
|
240
245
|
const affected = body.slice(index).filter((item) => item.type === "paragraph" && item.formatting?.numPr?.kind === "reference" && item.formatting.numPr.numId === numPr.numId);
|
|
@@ -244,7 +249,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
244
249
|
const map = getCachedNumberingMap(definitions);
|
|
245
250
|
const bullet = numberingLevelUsesBulletMarker(map.getLevel(numPr.numId, numPr.ilvl ?? 0));
|
|
246
251
|
const earlier = body.slice(0, index).findLast((item) => item.type === "paragraph" && item.formatting?.numPr?.kind === "reference" && item.formatting.numPr.numId !== numPr.numId && numberingLevelUsesBulletMarker(map.getLevel(item.formatting.numPr.numId, item.formatting.numPr.ilvl ?? 0)) === bullet);
|
|
247
|
-
if (earlier?.type !== "paragraph" || earlier.formatting?.numPr?.kind !== "reference") return
|
|
252
|
+
if (earlier?.type !== "paragraph" || earlier.formatting?.numPr?.kind !== "reference") return noChange("There is no preceding compatible list.");
|
|
248
253
|
targetId = earlier.formatting.numPr.numId;
|
|
249
254
|
target = {
|
|
250
255
|
type: "existing",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
1
2
|
import { FOLIO_DOCX_SERIALIZATION_MODE } from "../types/docxSerialization.js";
|
|
2
3
|
import { panic } from "better-result";
|
|
3
4
|
//#region src/controller/folioEditor.ts
|
|
@@ -14,6 +15,16 @@ const createFolioEditor = (deps) => {
|
|
|
14
15
|
getDocument: () => deps.getEditorApi()?.getDocument() ?? null,
|
|
15
16
|
updateCanonicalInputLifecycle: (action) => deps.getEditorApi()?.updateCanonicalInputLifecycle(action) ?? false,
|
|
16
17
|
applyCanonicalStoryHistory: (options) => deps.getEditorApi()?.applyCanonicalStoryHistory(options) ?? false,
|
|
18
|
+
applyCanonicalComment: (request) => deps.getEditorApi()?.applyCanonicalComment(request) ?? null,
|
|
19
|
+
applyCanonicalSectionProperties: (patch) => {
|
|
20
|
+
const api = deps.getEditorApi();
|
|
21
|
+
if (!api) return {
|
|
22
|
+
status: "refused",
|
|
23
|
+
gap: CANONICAL_GAP.sectionProperties,
|
|
24
|
+
message: "The editor is not ready for section changes."
|
|
25
|
+
};
|
|
26
|
+
return api.applyCanonicalSectionProperties(patch);
|
|
27
|
+
},
|
|
17
28
|
applyCanonicalOperations: (ops) => deps.getEditorApi()?.applyCanonicalOperations(ops) ?? false,
|
|
18
29
|
getCanonicalStorySelection: (story) => deps.getEditorApi()?.getCanonicalStorySelection(story) ?? null,
|
|
19
30
|
getCanonicalStoryProjection: (story) => deps.getEditorApi()?.getCanonicalStoryProjection(story) ?? null,
|
|
@@ -22,6 +33,8 @@ const createFolioEditor = (deps) => {
|
|
|
22
33
|
captureCanonicalSave: () => deps.getEditorApi()?.captureCanonicalSave() ?? null,
|
|
23
34
|
applyCanonicalDocumentOperations: (options) => deps.getEditorApi()?.applyCanonicalDocumentOperations(options) ?? null,
|
|
24
35
|
undoCanonicalDocumentOperations: (handle) => deps.getEditorApi()?.undoCanonicalDocumentOperations(handle) ?? null,
|
|
36
|
+
getCanonicalComments: () => deps.getEditorApi()?.getCanonicalComments() ?? null,
|
|
37
|
+
getCanonicalCommittedVersion: () => deps.getEditorApi()?.getCanonicalCommittedVersion() ?? null,
|
|
25
38
|
getCanonicalDocument: () => deps.getEditorApi()?.getCanonicalDocument() ?? null,
|
|
26
39
|
setCanonicalMode: (mode) => deps.getEditorApi()?.setCanonicalMode(mode) ?? false,
|
|
27
40
|
resolveCanonicalRevisions: (revisionIds, resolution) => deps.getEditorApi()?.resolveCanonicalRevisions(revisionIds, resolution) ?? false,
|
|
@@ -20,7 +20,7 @@ type HeaderFooterEditorManagerDeps = {
|
|
|
20
20
|
getDocument: () => document_d_exports.Document | null;
|
|
21
21
|
getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
|
|
22
22
|
getExperimentalSession?: (() => "canonical" | undefined) | undefined;
|
|
23
|
-
onSessionRefusal?: ((reason: string, gap: CanonicalGap) => void) | undefined;
|
|
23
|
+
onSessionRefusal?: ((reason: string, gap: CanonicalGap, error?: Error) => void) | undefined;
|
|
24
24
|
getHost: () => HTMLElement | null;
|
|
25
25
|
getStyles: () => document_d_exports.StyleDefinitions | null | undefined;
|
|
26
26
|
getTheme: () => document_d_exports.Theme | null | undefined;
|
|
@@ -6,11 +6,13 @@ import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js"
|
|
|
6
6
|
import { createStarterKit } from "../prosemirror/extensions/StarterKit.js";
|
|
7
7
|
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
8
8
|
import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
9
|
+
import { noteReferenceTransactionIssue } from "../prosemirror/noteReferenceOccurrences.js";
|
|
9
10
|
import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
|
|
10
|
-
import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
11
|
+
import { createDocumentStyleContextPlugin, createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
11
12
|
import { schema } from "../prosemirror/schema/index.js";
|
|
12
13
|
import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
|
|
13
14
|
import { CANONICAL_GAP, usesCanonicalSession } from "../types/canonicalCapabilities.js";
|
|
15
|
+
import { CanonicalSessionError } from "./canonicalSession.js";
|
|
14
16
|
import { createCanonicalStoryEditor } from "./canonicalStoryEditor.js";
|
|
15
17
|
import { OP_STORIES } from "@stll/docx-core/ops";
|
|
16
18
|
import { EditorState, TextSelection } from "prosemirror-state";
|
|
@@ -85,11 +87,12 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
85
87
|
for (const part of mounted.values()) destroyMounted(part);
|
|
86
88
|
mounted.clear();
|
|
87
89
|
};
|
|
90
|
+
const getOwnedDocument = () => usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) ? deps.getCanonicalApi?.()?.getCanonicalDocument() ?? null : deps.getDocument();
|
|
88
91
|
const sync = () => {
|
|
89
92
|
const host = deps.getHost();
|
|
90
93
|
if (!host) return;
|
|
91
|
-
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
92
|
-
const document =
|
|
94
|
+
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
|
|
95
|
+
const document = getOwnedDocument();
|
|
93
96
|
const styles = deps.getStyles();
|
|
94
97
|
const theme = deps.getTheme();
|
|
95
98
|
const numbering = document?.package.numbering;
|
|
@@ -105,7 +108,7 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
105
108
|
const existing = mounted.get(part.rId);
|
|
106
109
|
if (existing) {
|
|
107
110
|
if (existing.mountNode.parentElement !== host) host.append(existing.mountNode);
|
|
108
|
-
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
111
|
+
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) {
|
|
109
112
|
const projected = deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
|
|
110
113
|
kind: part.kind,
|
|
111
114
|
rId: part.rId
|
|
@@ -155,7 +158,7 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
155
158
|
},
|
|
156
159
|
getView: () => view,
|
|
157
160
|
getApi: () => deps.getCanonicalApi?.() ?? null,
|
|
158
|
-
enabled: () => usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
161
|
+
enabled: () => usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting),
|
|
159
162
|
onRefusal: deps.onSessionRefusal,
|
|
160
163
|
onSelectionChange: () => deps.onTransaction?.({
|
|
161
164
|
kind: part.kind,
|
|
@@ -165,11 +168,11 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
165
168
|
selectionChanged: true
|
|
166
169
|
})
|
|
167
170
|
});
|
|
168
|
-
const canonicalProjection = usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
171
|
+
const canonicalProjection = usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) ? deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
|
|
169
172
|
kind: part.kind,
|
|
170
173
|
rId: part.rId
|
|
171
174
|
}) : null;
|
|
172
|
-
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
175
|
+
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !canonicalProjection) {
|
|
173
176
|
manager.destroy();
|
|
174
177
|
mountNode.remove();
|
|
175
178
|
continue;
|
|
@@ -178,16 +181,19 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
178
181
|
...canonical.props,
|
|
179
182
|
state: canonicalProjection ? EditorState.create({
|
|
180
183
|
doc: canonicalProjection,
|
|
181
|
-
plugins: [
|
|
184
|
+
plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
|
|
182
185
|
}) : buildInitialState(headerFooter, styles, theme, numbering, manager),
|
|
183
186
|
dispatchTransaction(transaction) {
|
|
184
187
|
if (canonical.dispatch(transaction)) return;
|
|
185
188
|
const nextState = view.state.apply(transaction);
|
|
189
|
+
const docChanged = !nextState.doc.eq(view.state.doc);
|
|
190
|
+
const noteIssue = noteReferenceTransactionIssue(transaction);
|
|
191
|
+
if (noteIssue) deps.onSessionRefusal?.(noteIssue.message, CANONICAL_GAP.dispatch, noteIssue);
|
|
186
192
|
view.updateState(nextState);
|
|
187
193
|
const mountedPart = mounted.get(part.rId);
|
|
188
|
-
if (mountedPart &&
|
|
194
|
+
if (mountedPart && docChanged) mountedPart.dirty = true;
|
|
189
195
|
deps.onTransaction?.({
|
|
190
|
-
docChanged
|
|
196
|
+
docChanged,
|
|
191
197
|
kind: part.kind,
|
|
192
198
|
rId: part.rId,
|
|
193
199
|
selectionChanged: transaction.selectionSet,
|
|
@@ -218,7 +224,15 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
218
224
|
rId
|
|
219
225
|
})),
|
|
220
226
|
snapshotDocument: (document) => {
|
|
221
|
-
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.
|
|
227
|
+
if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) {
|
|
228
|
+
const canonical = deps.getCanonicalApi?.()?.getCanonicalDocument();
|
|
229
|
+
if (canonical) return canonical;
|
|
230
|
+
throw new CanonicalSessionError({
|
|
231
|
+
gap: CANONICAL_GAP.authorityRouting,
|
|
232
|
+
reason: "refused",
|
|
233
|
+
message: "Canonical story snapshot is unavailable."
|
|
234
|
+
});
|
|
235
|
+
}
|
|
222
236
|
let headers = document.package.headers;
|
|
223
237
|
let footers = document.package.footers;
|
|
224
238
|
let headersChanged = false;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { FolioDocumentOperationResult, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult } from "../document-operations.js";
|
|
3
|
+
import { Comment } from "../types/content.js";
|
|
3
4
|
import { CanonicalSaveSnapshot } from "../types/canonicalSave.js";
|
|
4
5
|
import { CanonicalSessionMode } from "./canonicalSession.js";
|
|
5
6
|
import { createCanonicalInputBoundary } from "./canonicalInput.js";
|
|
6
7
|
import { CanonicalPublicOperationOptions } from "./canonicalPublicOperations.js";
|
|
8
|
+
import { CanonicalSectionPropertiesResult } from "../types/canonicalSections.js";
|
|
9
|
+
import { CanonicalCommentRequest, CanonicalCommentResult } from "../types/canonicalComments.js";
|
|
7
10
|
import { Node } from "prosemirror-model";
|
|
8
11
|
import { Command, EditorState, Transaction } from "prosemirror-state";
|
|
9
12
|
import { EditorView } from "prosemirror-view";
|
|
10
|
-
import { DocumentOp, OpStory } from "@stll/docx-core/ops";
|
|
13
|
+
import { DocumentOp, FormattingPatch, OpStory } from "@stll/docx-core/ops";
|
|
11
14
|
//#region src/controller/hiddenEditorApi.d.ts
|
|
12
15
|
type CanonicalStoryHistoryOptions = {
|
|
13
16
|
view: EditorView;
|
|
@@ -31,6 +34,9 @@ type HiddenEditorApi = {
|
|
|
31
34
|
/** Get the current Document from PM state */
|
|
32
35
|
getDocument: () => document_d_exports.Document | null;
|
|
33
36
|
/** Canonical snapshot, or null in the default session. */
|
|
37
|
+
getCanonicalComments: () => Comment[] | null;
|
|
38
|
+
/** Committed session/version identity; safe during provisional composition. */
|
|
39
|
+
getCanonicalCommittedVersion: () => string | null;
|
|
34
40
|
getCanonicalDocument: () => document_d_exports.Document | null;
|
|
35
41
|
captureCanonicalSave: () => CanonicalSaveSnapshot | null;
|
|
36
42
|
/** A save cannot acknowledge newer edits or an unfinished composition. */
|
|
@@ -42,6 +48,9 @@ type HiddenEditorApi = {
|
|
|
42
48
|
updateCanonicalInputLifecycle: (action: "beginComposition" | "endComposition" | "breakUndoGroup") => boolean;
|
|
43
49
|
applyCanonicalStoryHistory: (options: CanonicalStoryHistoryOptions) => boolean;
|
|
44
50
|
applyCanonicalOperations: (ops: readonly DocumentOp[]) => boolean;
|
|
51
|
+
applyCanonicalComment: (request: CanonicalCommentRequest) => CanonicalCommentResult | null;
|
|
52
|
+
/** Apply a final-section patch; null is reserved for default sessions. */
|
|
53
|
+
applyCanonicalSectionProperties: (patch: FormattingPatch<document_d_exports.SectionProperties>) => CanonicalSectionPropertiesResult | null;
|
|
45
54
|
getCanonicalStorySelection: (story: OpStory) => {
|
|
46
55
|
anchor: number;
|
|
47
56
|
head: number;
|
|
@@ -80,6 +89,8 @@ type HiddenEditorApi = {
|
|
|
80
89
|
type HiddenEditorApiDeps = {
|
|
81
90
|
getView: () => EditorView | null;
|
|
82
91
|
getDocumentContext: () => document_d_exports.Document | null;
|
|
92
|
+
getCanonicalComments?: () => Comment[] | null;
|
|
93
|
+
getCanonicalCommittedVersion?: HiddenEditorApi["getCanonicalCommittedVersion"];
|
|
83
94
|
getCanonicalDocument?: () => document_d_exports.Document | null;
|
|
84
95
|
captureCanonicalSave?: HiddenEditorApi["captureCanonicalSave"];
|
|
85
96
|
isCanonicalSaveCurrent?: HiddenEditorApi["isCanonicalSaveCurrent"];
|
|
@@ -87,7 +98,7 @@ type HiddenEditorApiDeps = {
|
|
|
87
98
|
resolveCanonicalRevisions?: HiddenEditorApi["resolveCanonicalRevisions"];
|
|
88
99
|
executeCanonicalCommand?: (command: Command) => boolean | undefined;
|
|
89
100
|
getCanonicalHistory?: () => Pick<HiddenEditorApi, "undo" | "redo" | "canUndo" | "canRedo"> | null;
|
|
90
|
-
canonicalOperations?: Pick<HiddenEditorApi, "updateCanonicalInputLifecycle" | "applyCanonicalOperations" | "getCanonicalStoryProjection" | "replaceCanonicalStoryText" | "applyCanonicalStoryHistory" | "getCanonicalStorySelection" | "applyCanonicalDocumentOperations" | "undoCanonicalDocumentOperations">;
|
|
101
|
+
canonicalOperations?: Pick<HiddenEditorApi, "updateCanonicalInputLifecycle" | "applyCanonicalOperations" | "applyCanonicalComment" | "applyCanonicalSectionProperties" | "getCanonicalStoryProjection" | "replaceCanonicalStoryText" | "applyCanonicalStoryHistory" | "getCanonicalStorySelection" | "applyCanonicalDocumentOperations" | "undoCanonicalDocumentOperations">;
|
|
91
102
|
isDestroying: () => boolean;
|
|
92
103
|
ensureView: () => void;
|
|
93
104
|
isViewRequested: () => boolean;
|
|
@@ -9,7 +9,7 @@ import { redo, undo } from "prosemirror-history";
|
|
|
9
9
|
*/
|
|
10
10
|
const stateToDocument = (state, originalDoc) => {
|
|
11
11
|
if (!originalDoc) return null;
|
|
12
|
-
return fromProseDoc(state.doc, originalDoc);
|
|
12
|
+
return fromProseDoc(state.doc, originalDoc, { stylesheetSource: { type: "package" } });
|
|
13
13
|
};
|
|
14
14
|
const createHiddenEditorApi = (deps) => {
|
|
15
15
|
const dispatchTr = (view, tr) => {
|
|
@@ -34,6 +34,8 @@ const createHiddenEditorApi = (deps) => {
|
|
|
34
34
|
getView: () => deps.getView() ?? null,
|
|
35
35
|
setCanonicalMode: (mode) => !deps.isDestroying() && (deps.setCanonicalMode?.(mode) ?? false),
|
|
36
36
|
resolveCanonicalRevisions: (revisionIds, resolution) => !deps.isDestroying() && (deps.resolveCanonicalRevisions?.(revisionIds, resolution) ?? false),
|
|
37
|
+
getCanonicalComments: () => deps.getCanonicalComments?.() ?? null,
|
|
38
|
+
getCanonicalCommittedVersion: () => deps.getCanonicalCommittedVersion?.() ?? null,
|
|
37
39
|
captureCanonicalSave: () => deps.captureCanonicalSave?.() ?? null,
|
|
38
40
|
isCanonicalSaveCurrent: (version) => deps.isCanonicalSaveCurrent?.(version) ?? false,
|
|
39
41
|
getCanonicalDocument: () => {
|
|
@@ -49,6 +51,8 @@ const createHiddenEditorApi = (deps) => {
|
|
|
49
51
|
},
|
|
50
52
|
updateCanonicalInputLifecycle: (action) => deps.canonicalOperations?.updateCanonicalInputLifecycle(action) ?? false,
|
|
51
53
|
applyCanonicalStoryHistory: (options) => deps.canonicalOperations?.applyCanonicalStoryHistory(options) ?? false,
|
|
54
|
+
applyCanonicalComment: (request) => deps.canonicalOperations?.applyCanonicalComment(request) ?? null,
|
|
55
|
+
applyCanonicalSectionProperties: (patch) => deps.canonicalOperations?.applyCanonicalSectionProperties(patch) ?? null,
|
|
52
56
|
applyCanonicalOperations: (ops) => deps.canonicalOperations?.applyCanonicalOperations(ops) ?? false,
|
|
53
57
|
getCanonicalStorySelection: (story) => deps.canonicalOperations?.getCanonicalStorySelection(story) ?? null,
|
|
54
58
|
getCanonicalStoryProjection: (story) => deps.canonicalOperations?.getCanonicalStoryProjection(story) ?? null,
|
|
@@ -65,7 +65,7 @@ type HiddenEditorManagerDeps = {
|
|
|
65
65
|
getExperimentalSession?: () => "canonical" | undefined;
|
|
66
66
|
getEditingMode?: () => EditorMode;
|
|
67
67
|
getSuggestionAuthor?: () => string;
|
|
68
|
-
onSessionRefusal?: (reason: string, gap: CanonicalGap) => void;
|
|
68
|
+
onSessionRefusal?: (reason: string, gap: CanonicalGap, error?: Error) => void;
|
|
69
69
|
/**
|
|
70
70
|
* Identity of the loaded document as tracked by the adapter's loader: the
|
|
71
71
|
* same value across internal edits (so typing does not trigger an external
|
|
@@ -99,6 +99,8 @@ declare const createHiddenEditorClipboardHandlers: (deps: Pick<HiddenEditorManag
|
|
|
99
99
|
paste: (_view: unknown, event: PreventableDomEvent) => boolean;
|
|
100
100
|
};
|
|
101
101
|
type HiddenEditorManager = {
|
|
102
|
+
/** Whether the canonical owner still holds a provisional composition. */
|
|
103
|
+
isCanonicalComposing: () => boolean;
|
|
102
104
|
/** Request the view (sets the requested flag, then attempts creation). */
|
|
103
105
|
ensureView: () => void;
|
|
104
106
|
/** Re-attempt a previously-requested-but-deferred creation (no-op otherwise). */
|