@stll/folio-core 0.54.2 → 0.55.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 +22 -1
- package/dist/ai-edits/headless.js +1 -0
- package/dist/compare/content-types.d.ts +3 -15
- package/dist/controller/canonicalComposition.d.ts +36 -0
- package/dist/controller/canonicalComposition.js +168 -0
- package/dist/controller/canonicalInput.d.ts +45 -0
- package/dist/controller/canonicalInput.js +338 -0
- package/dist/controller/canonicalOperations.d.ts +22 -0
- package/dist/controller/canonicalOperations.js +85 -0
- package/dist/controller/canonicalReview.d.ts +12 -0
- package/dist/controller/canonicalReview.js +19 -0
- package/dist/controller/canonicalSession.d.ts +149 -0
- package/dist/controller/canonicalSession.js +1038 -0
- package/dist/controller/canonicalStoryEditor.d.ts +34 -0
- package/dist/controller/canonicalStoryEditor.js +82 -0
- package/dist/controller/canonicalStructure.d.ts +16 -0
- package/dist/controller/canonicalStructure.js +269 -0
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/folioEditor.js +9 -0
- package/dist/controller/headerFooterEditorManager.d.ts +4 -0
- package/dist/controller/headerFooterEditorManager.js +71 -3
- package/dist/controller/hiddenEditorApi.d.ts +35 -0
- package/dist/controller/hiddenEditorApi.js +25 -8
- package/dist/controller/hiddenEditorManager.d.ts +11 -2
- package/dist/controller/hiddenEditorManager.js +456 -30
- package/dist/controller/layoutPipeline.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +2 -0
- package/dist/controller/noteEditorManager.d.ts +4 -0
- package/dist/controller/noteEditorManager.js +113 -15
- package/dist/docx/attributeRemainder.js +1 -0
- package/dist/docx/canonicalSessionInput.d.ts +11 -0
- package/dist/docx/canonicalSessionInput.js +27 -0
- package/dist/docx/canonicalStoryRepack.d.ts +10 -0
- package/dist/docx/canonicalStoryRepack.js +41 -0
- package/dist/docx/contentTypeRegistry.d.ts +8 -0
- package/dist/docx/contentTypeRegistry.js +64 -0
- package/dist/docx/ensureParaIds.d.ts +7 -1
- package/dist/docx/ensureParaIds.js +163 -211
- package/dist/docx/foldedListNumberFields.d.ts +85 -0
- package/dist/docx/foldedListNumberFields.js +267 -0
- package/dist/docx/headerFooterVerbatim.d.ts +14 -1
- package/dist/docx/headerFooterVerbatim.js +95 -2
- package/dist/docx/noteIds.d.ts +5 -0
- package/dist/docx/noteIds.js +6 -2
- package/dist/docx/noteReferenceMark.d.ts +3 -1
- package/dist/docx/noteReferenceMark.js +15 -14
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +17 -3
- package/dist/docx/packageParts.d.ts +3 -1
- package/dist/docx/packageParts.js +65 -16
- package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
- package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
- package/dist/docx/paragraphParser.js +101 -53
- package/dist/docx/paragraphPropertySource.d.ts +15 -1
- package/dist/docx/paragraphPropertySource.js +76 -22
- package/dist/docx/parser.js +2 -1
- package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
- package/dist/docx/reviewHistoryNamespace.js +4 -0
- package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
- package/dist/docx/reviewResolutionProvenance.js +153 -0
- package/dist/docx/rezip.d.ts +7 -3
- package/dist/docx/rezip.js +107 -45
- package/dist/docx/runConsolidator.d.ts +2 -58
- package/dist/docx/runConsolidator.js +3 -175
- package/dist/docx/runParser.d.ts +2 -1
- package/dist/docx/runParser.js +156 -116
- package/dist/docx/saveDiagnostics.d.ts +11 -0
- package/dist/docx/saveDiagnostics.js +0 -0
- package/dist/docx/selectiveSave.d.ts +2 -1
- package/dist/docx/selectiveSave.js +4 -1
- package/dist/docx/selectiveXmlPatch.d.ts +16 -8
- package/dist/docx/selectiveXmlPatch.js +74 -2
- package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
- package/dist/docx/serializer/headerFooterSerializer.js +19 -11
- package/dist/docx/serializer/noteSerializer.d.ts +2 -2
- package/dist/docx/serializer/noteSerializer.js +24 -8
- package/dist/docx/serializer/paragraphSerializer.js +41 -4
- package/dist/docx/serializer/partNamespaces.js +2 -1
- package/dist/docx/serializer/runSerializer.js +3 -1
- package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
- package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
- package/dist/docx/settingsParser.d.ts +1 -2
- package/dist/docx/settingsParser.js +5 -6
- package/dist/docx/storyBlockReplay.d.ts +11 -0
- package/dist/docx/storyBlockReplay.js +367 -0
- package/dist/docx/streamingXmlParser.d.ts +22 -1
- package/dist/docx/streamingXmlParser.js +49 -3
- package/dist/docx/structuralXmlPatch.js +8 -1
- package/dist/docx/verbatimCapture.d.ts +3 -1
- package/dist/docx/verbatimCapture.js +49 -6
- package/dist/i18n/messages/ar.gen.d.ts +5 -0
- package/dist/i18n/messages/ar.gen.js +656 -0
- package/dist/i18n/messages/cs.gen.d.ts +5 -0
- package/dist/i18n/messages/cs.gen.js +656 -0
- package/dist/i18n/messages/de.gen.d.ts +5 -0
- package/dist/i18n/messages/de.gen.js +656 -0
- package/dist/i18n/messages/en.gen.d.ts +5 -0
- package/dist/i18n/messages/en.gen.js +656 -0
- package/dist/i18n/messages/es.gen.d.ts +5 -0
- package/dist/i18n/messages/es.gen.js +656 -0
- package/dist/i18n/messages/et.gen.d.ts +5 -0
- package/dist/i18n/messages/et.gen.js +656 -0
- package/dist/i18n/messages/fr.gen.d.ts +5 -0
- package/dist/i18n/messages/fr.gen.js +656 -0
- package/dist/i18n/messages/he.gen.d.ts +5 -0
- package/dist/i18n/messages/he.gen.js +656 -0
- package/dist/i18n/messages/hi.gen.d.ts +5 -0
- package/dist/i18n/messages/hi.gen.js +656 -0
- package/dist/i18n/messages/hu.gen.d.ts +5 -0
- package/dist/i18n/messages/hu.gen.js +656 -0
- package/dist/i18n/messages/locales.d.ts +5 -0
- package/dist/i18n/messages/locales.js +22 -0
- package/dist/i18n/messages/lt.gen.d.ts +5 -0
- package/dist/i18n/messages/lt.gen.js +656 -0
- package/dist/i18n/messages/lv.gen.d.ts +5 -0
- package/dist/i18n/messages/lv.gen.js +656 -0
- package/dist/i18n/messages/pl.gen.d.ts +5 -0
- package/dist/i18n/messages/pl.gen.js +656 -0
- package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
- package/dist/i18n/messages/pt-BR.gen.js +656 -0
- package/dist/i18n/messages/sk.gen.d.ts +5 -0
- package/dist/i18n/messages/sk.gen.js +656 -0
- package/dist/i18n/messages/tr.gen.d.ts +5 -0
- package/dist/i18n/messages/tr.gen.js +656 -0
- package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
- package/dist/i18n/messages/zh-CN.gen.js +656 -0
- package/dist/i18n/messages.d.ts +2 -3
- package/dist/i18n/messages.js +1 -19
- package/dist/internal/noteMarkerAttrs.d.ts +8 -0
- package/dist/internal/noteMarkerAttrs.js +16 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
- package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
- package/dist/internal/prosemirrorAttrBoundary.js +50 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
- package/dist/layout-bridge/convert/noteReferences.js +1 -6
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
- package/dist/managers/DocumentLoaderManager.d.ts +1 -0
- package/dist/managers/DocumentLoaderManager.js +8 -1
- package/dist/markdown/renderBlock.js +15 -1
- package/dist/markdown/renderParagraph.js +9 -13
- package/dist/markdown/renderRuns.js +3 -2
- package/dist/markdown/renderTable.js +2 -0
- package/dist/markdown/types.d.ts +3 -9
- package/dist/prosemirror/attrs/index.d.ts +2 -12
- package/dist/prosemirror/attrs/index.js +22 -48
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/canonicalCommands.d.ts +97 -0
- package/dist/prosemirror/canonicalCommands.js +28 -0
- package/dist/prosemirror/commands/pageBreak.js +15 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
- package/dist/prosemirror/conversion/toProseDoc.js +51 -11
- package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
- package/dist/prosemirror/executeEditorCommand.js +26 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
- package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +16 -7
- package/dist/prosemirror/listNumbering.js +26 -5
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/markupViewNotes.js +3 -2
- package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/noteReferenceReview.js +5 -3
- package/dist/prosemirror/plugins/suggestionMode.js +50 -2
- package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
- package/dist/prosemirror/runFormattingFromMarks.js +15 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
- package/dist/prosemirror/schema/marks.d.ts +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +10 -2
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/textInput.js +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +2 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
- package/dist/prosemirror/validation.js +4 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +2 -2
- package/package.json +14 -2
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1485,6 +1485,22 @@ const finalParagraphPredecessor = (doc, at) => {
|
|
|
1485
1485
|
}
|
|
1486
1486
|
return null;
|
|
1487
1487
|
};
|
|
1488
|
+
/**
|
|
1489
|
+
* Whether a sibling between a final carrier's predecessor and the carrier
|
|
1490
|
+
* has its break deleted by this batch.
|
|
1491
|
+
*/
|
|
1492
|
+
const crossesBatchDeletion = ({ doc, from, to, batchRevisionIds }) => {
|
|
1493
|
+
const $to = doc.resolve(to);
|
|
1494
|
+
let position = to;
|
|
1495
|
+
for (let index = $to.index() - 1; index >= 0 && position > from; index--) {
|
|
1496
|
+
const node = $to.parent.child(index);
|
|
1497
|
+
position -= node.nodeSize;
|
|
1498
|
+
const mark = node.attrs["pPrMark"];
|
|
1499
|
+
const revisionId = isPlainDeletedPPrMark(mark) ? addedOrDeletedBreakRevisionId(mark) : null;
|
|
1500
|
+
if (revisionId !== null && batchRevisionIds.has(revisionId)) return true;
|
|
1501
|
+
}
|
|
1502
|
+
return false;
|
|
1503
|
+
};
|
|
1488
1504
|
/** Pending retirements, read after the merge has mapped any inserted separator. */
|
|
1489
1505
|
const pendingRemovedFinalBreakPositions = (tr, deletions) => {
|
|
1490
1506
|
const positions = /* @__PURE__ */ new Set();
|
|
@@ -1569,7 +1585,12 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1569
1585
|
date,
|
|
1570
1586
|
...revisionExtras
|
|
1571
1587
|
},
|
|
1572
|
-
keep:
|
|
1588
|
+
keep: crossesBatchDeletion({
|
|
1589
|
+
doc: tr.doc,
|
|
1590
|
+
from: position + previous.nodeSize,
|
|
1591
|
+
to: at,
|
|
1592
|
+
batchRevisionIds
|
|
1593
|
+
}) ? /* @__PURE__ */ new Set() : propertiesSetInBatch(emptied, batchRevisionIds)
|
|
1573
1594
|
});
|
|
1574
1595
|
resolvedOperationIds.add(operationId);
|
|
1575
1596
|
addedRevisions.push({
|
|
@@ -1895,6 +1895,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1895
1895
|
const source = story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
|
|
1896
1896
|
if (!source) return null;
|
|
1897
1897
|
const conversionOptions = {
|
|
1898
|
+
...this.baseDocument.package.numbering !== void 0 && { numbering: this.baseDocument.package.numbering },
|
|
1898
1899
|
...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
|
|
1899
1900
|
...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
|
|
1900
1901
|
};
|
|
@@ -83,14 +83,9 @@ type FolioContentTableLocation = {
|
|
|
83
83
|
*/
|
|
84
84
|
type FolioContentParagraphAlignment = document_d_exports.ParagraphAlignment;
|
|
85
85
|
/** Line-height interpretation understood by the neutral comparison model. */
|
|
86
|
-
type FolioContentLineSpacingRule = "
|
|
86
|
+
type FolioContentLineSpacingRule = NonNullable<document_d_exports.ParagraphFormatting["lineSpacingRule"]>;
|
|
87
87
|
/** The complete modeled attribute set of direct paragraph indentation. */
|
|
88
|
-
type FolioContentParagraphIndentation =
|
|
89
|
-
indentLeft?: number;
|
|
90
|
-
indentRight?: number;
|
|
91
|
-
indentFirstLine?: number;
|
|
92
|
-
hangingIndent?: boolean;
|
|
93
|
-
};
|
|
88
|
+
type FolioContentParagraphIndentation = Pick<document_d_exports.ParagraphFormatting, "indentLeft" | "indentRight" | "indentFirstLine" | "hangingIndent">;
|
|
94
89
|
/** A concrete numbering instance and its zero-based level. */
|
|
95
90
|
type FolioContentListReference = {
|
|
96
91
|
numId: number;
|
|
@@ -100,14 +95,7 @@ type FolioContentListReference = {
|
|
|
100
95
|
* The complete modeled attribute set of direct paragraph spacing. Optional
|
|
101
96
|
* fields distinguish an absent attribute from an explicit zero or false value.
|
|
102
97
|
*/
|
|
103
|
-
type FolioContentParagraphSpacing =
|
|
104
|
-
spaceBefore?: number;
|
|
105
|
-
spaceAfter?: number;
|
|
106
|
-
lineSpacing?: number;
|
|
107
|
-
lineSpacingRule?: FolioContentLineSpacingRule;
|
|
108
|
-
beforeAutospacing?: boolean;
|
|
109
|
-
afterAutospacing?: boolean;
|
|
110
|
-
};
|
|
98
|
+
type FolioContentParagraphSpacing = Pick<document_d_exports.ParagraphFormatting, "spaceBefore" | "spaceAfter" | "lineSpacing" | "lineSpacingRule" | "beforeAutospacing" | "afterAutospacing">;
|
|
111
99
|
/** A representation-neutral block in one ordered document story. */
|
|
112
100
|
type FolioContentBlock<Kind extends string = string> = {
|
|
113
101
|
id: string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { EditorState, Transaction } from "prosemirror-state";
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
//#region src/controller/canonicalComposition.d.ts
|
|
4
|
+
type CompositionOptions = {
|
|
5
|
+
begin: () => boolean;
|
|
6
|
+
end: () => void;
|
|
7
|
+
replace: (input: {
|
|
8
|
+
from: number;
|
|
9
|
+
to: number;
|
|
10
|
+
text: string;
|
|
11
|
+
semantic: "composition";
|
|
12
|
+
}) => void;
|
|
13
|
+
refuse: (reason: string) => void;
|
|
14
|
+
};
|
|
15
|
+
type CompositionState = {
|
|
16
|
+
type: "committed";
|
|
17
|
+
} | {
|
|
18
|
+
type: "provisional" | "refused";
|
|
19
|
+
baseline: EditorState;
|
|
20
|
+
phase: "active" | "ended";
|
|
21
|
+
};
|
|
22
|
+
/** Native IME owns the provisional view; the captured canonical projection stays unchanged. */
|
|
23
|
+
declare const createCanonicalComposition: (options: CompositionOptions) => {
|
|
24
|
+
readonly status: CompositionState["type"];
|
|
25
|
+
readonly active: boolean;
|
|
26
|
+
start: (view: EditorView) => void;
|
|
27
|
+
ended: (view: EditorView) => void;
|
|
28
|
+
authorizeNative: (view: EditorView) => void;
|
|
29
|
+
flushed: (view: EditorView) => void;
|
|
30
|
+
recover: (view: EditorView) => void;
|
|
31
|
+
cancel: (view: EditorView) => void;
|
|
32
|
+
reset: () => void;
|
|
33
|
+
accept: (view: EditorView, transaction: Transaction) => boolean;
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { createCanonicalComposition };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { splitsSurrogatePair } from "../ai-edits/character-boundaries.js";
|
|
2
|
+
import { CanonicalSessionError } from "./canonicalSession.js";
|
|
3
|
+
import { Result } from "better-result";
|
|
4
|
+
//#region src/controller/canonicalComposition.ts
|
|
5
|
+
const COMPOSITION_FLUSH_DELAY_MS = 25;
|
|
6
|
+
/** Native IME owns the provisional view; the captured canonical projection stays unchanged. */
|
|
7
|
+
const createCanonicalComposition = (options) => {
|
|
8
|
+
let state = { type: "committed" };
|
|
9
|
+
let authorizedNativeState = null;
|
|
10
|
+
let timer;
|
|
11
|
+
const clearTimer = () => {
|
|
12
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
13
|
+
timer = void 0;
|
|
14
|
+
};
|
|
15
|
+
const replacement = (baseline, proposed) => {
|
|
16
|
+
const { $from, $to } = baseline.selection;
|
|
17
|
+
if (!$from.parent.isTextblock || !$to.parent.isTextblock) return null;
|
|
18
|
+
if (!$from.sameParent($to)) {
|
|
19
|
+
const from = baseline.selection.from;
|
|
20
|
+
const to = baseline.selection.to;
|
|
21
|
+
const insertedSize = proposed.doc.content.size - (baseline.doc.content.size - (to - from));
|
|
22
|
+
if (insertedSize < 0 || from + insertedSize > proposed.doc.content.size) return null;
|
|
23
|
+
const text = proposed.doc.textBetween(from, from + insertedSize, "", "");
|
|
24
|
+
if (text.length !== insertedSize) return null;
|
|
25
|
+
if (!baseline.tr.insertText(text, from, to).doc.eq(proposed.doc)) return null;
|
|
26
|
+
return {
|
|
27
|
+
from,
|
|
28
|
+
to,
|
|
29
|
+
text,
|
|
30
|
+
semantic: "composition"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const start = $from.start();
|
|
34
|
+
const next = proposed.doc.nodeAt(start - 1);
|
|
35
|
+
if (!next || next.type !== $from.parent.type || next.content.size !== next.textContent.length) return null;
|
|
36
|
+
const before = $from.parent.textContent;
|
|
37
|
+
const after = next.textContent;
|
|
38
|
+
let from = 0;
|
|
39
|
+
while (from < before.length && from < after.length && before[from] === after[from]) from++;
|
|
40
|
+
while (splitsSurrogatePair(before, from) || splitsSurrogatePair(after, from)) from--;
|
|
41
|
+
let oldEnd = before.length;
|
|
42
|
+
let newEnd = after.length;
|
|
43
|
+
while (oldEnd > from && newEnd > from && before[oldEnd - 1] === after[newEnd - 1]) {
|
|
44
|
+
oldEnd--;
|
|
45
|
+
newEnd--;
|
|
46
|
+
}
|
|
47
|
+
while (splitsSurrogatePair(before, oldEnd) || splitsSurrogatePair(after, newEnd)) {
|
|
48
|
+
oldEnd++;
|
|
49
|
+
newEnd++;
|
|
50
|
+
}
|
|
51
|
+
const text = after.slice(from, newEnd);
|
|
52
|
+
if (!baseline.tr.insertText(text, start + from, start + oldEnd).doc.eq(proposed.doc)) return null;
|
|
53
|
+
return {
|
|
54
|
+
from: start + from,
|
|
55
|
+
to: start + oldEnd,
|
|
56
|
+
text,
|
|
57
|
+
semantic: "composition"
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const finish = (view, commit) => {
|
|
61
|
+
clearTimer();
|
|
62
|
+
authorizedNativeState = null;
|
|
63
|
+
const pending = state;
|
|
64
|
+
if (pending.type === "committed") return;
|
|
65
|
+
const input = commit && pending.type === "provisional" ? replacement(pending.baseline, view.state) : null;
|
|
66
|
+
state = { type: "committed" };
|
|
67
|
+
if (!view.isDestroyed) view.updateState(pending.baseline);
|
|
68
|
+
options.end();
|
|
69
|
+
if (!commit || view.isDestroyed || pending.type === "refused") return;
|
|
70
|
+
if (input?.from === input?.to && input?.text === "") return;
|
|
71
|
+
if (!input) {
|
|
72
|
+
options.refuse("Composition changed unsupported content; the canonical document was restored.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
options.replace(input);
|
|
76
|
+
};
|
|
77
|
+
const schedule = (view) => {
|
|
78
|
+
clearTimer();
|
|
79
|
+
timer = setTimeout(() => finish(view, true), COMPOSITION_FLUSH_DELAY_MS);
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
get status() {
|
|
83
|
+
return state.type;
|
|
84
|
+
},
|
|
85
|
+
get active() {
|
|
86
|
+
return state.type !== "committed";
|
|
87
|
+
},
|
|
88
|
+
start: (view) => {
|
|
89
|
+
if (state.type !== "committed") return;
|
|
90
|
+
if (!options.begin()) return;
|
|
91
|
+
state = {
|
|
92
|
+
type: "provisional",
|
|
93
|
+
baseline: view.state,
|
|
94
|
+
phase: "active"
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
ended: (view) => {
|
|
98
|
+
if (state.type === "committed") return;
|
|
99
|
+
state = {
|
|
100
|
+
type: state.type,
|
|
101
|
+
baseline: state.baseline,
|
|
102
|
+
phase: "ended"
|
|
103
|
+
};
|
|
104
|
+
schedule(view);
|
|
105
|
+
},
|
|
106
|
+
authorizeNative: (view) => {
|
|
107
|
+
authorizedNativeState = view.state;
|
|
108
|
+
},
|
|
109
|
+
flushed: (view) => {
|
|
110
|
+
const authorized = authorizedNativeState;
|
|
111
|
+
queueMicrotask(() => {
|
|
112
|
+
if (authorizedNativeState === authorized) authorizedNativeState = null;
|
|
113
|
+
});
|
|
114
|
+
if (state.type !== "committed" && state.phase === "ended") schedule(view);
|
|
115
|
+
},
|
|
116
|
+
recover: (view) => finish(view, true),
|
|
117
|
+
cancel: (view) => finish(view, false),
|
|
118
|
+
reset: () => {
|
|
119
|
+
clearTimer();
|
|
120
|
+
authorizedNativeState = null;
|
|
121
|
+
if (state.type !== "committed") options.end();
|
|
122
|
+
state = { type: "committed" };
|
|
123
|
+
},
|
|
124
|
+
accept: (view, transaction) => {
|
|
125
|
+
if (state.type === "committed") return false;
|
|
126
|
+
if (state.type === "refused") {
|
|
127
|
+
view.updateState(state.baseline);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (typeof transaction.getMeta("composition") !== "number" && authorizedNativeState !== view.state) {
|
|
131
|
+
options.refuse("Other edits must wait until composition finishes.");
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
authorizedNativeState = null;
|
|
135
|
+
const applied = Result.try({
|
|
136
|
+
try: () => view.state.applyTransaction(transaction),
|
|
137
|
+
catch: (cause) => new CanonicalSessionError({
|
|
138
|
+
reason: "refused",
|
|
139
|
+
message: `Composition projection failed: ${String(cause)}`
|
|
140
|
+
})
|
|
141
|
+
});
|
|
142
|
+
if (applied.isErr()) {
|
|
143
|
+
view.updateState(state.baseline);
|
|
144
|
+
state = {
|
|
145
|
+
type: "refused",
|
|
146
|
+
baseline: state.baseline,
|
|
147
|
+
phase: state.phase
|
|
148
|
+
};
|
|
149
|
+
options.refuse(applied.error.message);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
if (!replacement(state.baseline, applied.value.state)) {
|
|
153
|
+
view.updateState(state.baseline);
|
|
154
|
+
state = {
|
|
155
|
+
type: "refused",
|
|
156
|
+
baseline: state.baseline,
|
|
157
|
+
phase: state.phase
|
|
158
|
+
};
|
|
159
|
+
options.refuse("Composition changed unsupported content; the canonical document was restored.");
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
view.updateState(applied.value.state);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
//#endregion
|
|
168
|
+
export { createCanonicalComposition };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EditorState, Transaction } from "prosemirror-state";
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
//#region src/controller/canonicalInput.d.ts
|
|
4
|
+
type ReplaceTextInput = {
|
|
5
|
+
from: number;
|
|
6
|
+
to: number;
|
|
7
|
+
text: string;
|
|
8
|
+
semantic?: "typing" | "deleteBackward" | "deleteForward" | "composition";
|
|
9
|
+
};
|
|
10
|
+
type CanonicalInputOptions = {
|
|
11
|
+
replace: (input: ReplaceTextInput) => void;
|
|
12
|
+
structure?: (type: "split" | "joinBackward" | "joinForward" | "lineBreak" | "pageBreak" | "tab" | "indent" | "outdent") => void;
|
|
13
|
+
command?: (name: "toggleBold" | "toggleItalic" | "toggleUnderline") => void;
|
|
14
|
+
breakUndoGroup?: () => void;
|
|
15
|
+
beginComposition?: () => boolean;
|
|
16
|
+
endComposition?: () => void;
|
|
17
|
+
undo: () => boolean;
|
|
18
|
+
redo: () => boolean;
|
|
19
|
+
refuse: (reason: string) => void;
|
|
20
|
+
};
|
|
21
|
+
/** Native changes require a preceding, classified input event at the same state. */
|
|
22
|
+
declare const createCanonicalInputBoundary: (options: CanonicalInputOptions) => {
|
|
23
|
+
reset: () => void;
|
|
24
|
+
readonly isComposing: boolean;
|
|
25
|
+
acceptComposition: (view: EditorView, transaction: Transaction) => boolean;
|
|
26
|
+
handleTextInput: (view: EditorView, from: number, to: number, text: string) => boolean;
|
|
27
|
+
handleKeyDown: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
28
|
+
handleDOMEvents: {
|
|
29
|
+
compositionstart: (view: EditorView) => boolean;
|
|
30
|
+
compositionend: (view: EditorView) => boolean;
|
|
31
|
+
mousedown: (view: EditorView) => boolean;
|
|
32
|
+
blur: (view: EditorView) => boolean;
|
|
33
|
+
beforeinput: (view: EditorView, event: InputEvent) => boolean;
|
|
34
|
+
input: (view: EditorView) => boolean;
|
|
35
|
+
paste: (_view: EditorView, event: Event) => boolean;
|
|
36
|
+
cut: (_view: EditorView, event: Event) => boolean;
|
|
37
|
+
drop: (_view: EditorView, event: Event) => boolean;
|
|
38
|
+
};
|
|
39
|
+
/** Returns a classified intent only; the proposed PM document is never authoritative. */
|
|
40
|
+
takeNativeProposal: (state: EditorState, transaction: Transaction) => ReplaceTextInput | null;
|
|
41
|
+
refuseNativeMutation: (view: EditorView) => void;
|
|
42
|
+
commitNativeProposal: (view: EditorView, transaction: Transaction) => boolean;
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { createCanonicalInputBoundary };
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { handleEditorBeforeInput } from "../prosemirror/textInput.js";
|
|
2
|
+
import { createCanonicalComposition } from "./canonicalComposition.js";
|
|
3
|
+
import { deletionRange, isCanonicalJoinBoundary } from "./canonicalSession.js";
|
|
4
|
+
import { ReplaceStep } from "prosemirror-transform";
|
|
5
|
+
import { Selection, TextSelection } from "prosemirror-state";
|
|
6
|
+
//#region src/controller/canonicalInput.ts
|
|
7
|
+
/** Native changes require a preceding, classified input event at the same state. */
|
|
8
|
+
const createCanonicalInputBoundary = (options) => {
|
|
9
|
+
const composition = createCanonicalComposition({
|
|
10
|
+
begin: () => options.beginComposition?.() ?? true,
|
|
11
|
+
end: () => options.endComposition?.(),
|
|
12
|
+
replace: options.replace,
|
|
13
|
+
refuse: options.refuse
|
|
14
|
+
});
|
|
15
|
+
let proposal = { type: "idle" };
|
|
16
|
+
let authorizedInput = null;
|
|
17
|
+
let groupClosed = false;
|
|
18
|
+
const beginGesture = () => {
|
|
19
|
+
proposal = { type: "idle" };
|
|
20
|
+
authorizedInput = null;
|
|
21
|
+
groupClosed = false;
|
|
22
|
+
};
|
|
23
|
+
const closeGroup = () => {
|
|
24
|
+
if (groupClosed) return;
|
|
25
|
+
groupClosed = true;
|
|
26
|
+
options.breakUndoGroup?.();
|
|
27
|
+
};
|
|
28
|
+
const repaint = (view) => {
|
|
29
|
+
queueMicrotask(() => {
|
|
30
|
+
if (!view.isDestroyed) view.updateState(view.state);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const refuseEvent = (event, reason) => {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
closeGroup();
|
|
36
|
+
if (!composition.active && proposal.type !== "refused") options.refuse(reason);
|
|
37
|
+
if (!composition.active) proposal = { type: "refused" };
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
40
|
+
const refuseNativeMutation = (view) => {
|
|
41
|
+
if (!composition.active && proposal.type !== "refused") {
|
|
42
|
+
options.refuse("Unclassified native text is unavailable in this session.");
|
|
43
|
+
proposal = { type: "refused" };
|
|
44
|
+
}
|
|
45
|
+
repaint(view);
|
|
46
|
+
};
|
|
47
|
+
const takeNativeProposal = (state, transaction) => {
|
|
48
|
+
const pending = proposal;
|
|
49
|
+
if (pending.type !== "replacement") return null;
|
|
50
|
+
proposal = { type: "idle" };
|
|
51
|
+
if (pending.state !== state || transaction.steps.length !== 1) return null;
|
|
52
|
+
const step = transaction.steps.at(0);
|
|
53
|
+
if (!(step instanceof ReplaceStep) || "structure" in step && step.structure === true || step.from !== pending.input.from || step.to !== pending.input.to) return null;
|
|
54
|
+
if (step.slice.openStart !== 0 || step.slice.openEnd !== 0) return null;
|
|
55
|
+
let text = "";
|
|
56
|
+
let plain = true;
|
|
57
|
+
step.slice.content.forEach((node) => {
|
|
58
|
+
if (!node.isText) plain = false;
|
|
59
|
+
text += node.text ?? "";
|
|
60
|
+
});
|
|
61
|
+
if (!plain || text !== pending.input.text) return null;
|
|
62
|
+
return state.tr.insertText(text, step.from, step.to).doc.eq(transaction.doc) ? pending.input : null;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
reset: () => {
|
|
66
|
+
proposal = { type: "idle" };
|
|
67
|
+
authorizedInput = null;
|
|
68
|
+
composition.reset();
|
|
69
|
+
},
|
|
70
|
+
get isComposing() {
|
|
71
|
+
return composition.active;
|
|
72
|
+
},
|
|
73
|
+
acceptComposition: composition.accept,
|
|
74
|
+
handleTextInput: (view, from, to, text) => {
|
|
75
|
+
if (composition.active) return false;
|
|
76
|
+
const pending = proposal;
|
|
77
|
+
if (authorizedInput?.view === view && authorizedInput.state === view.state) {
|
|
78
|
+
options.replace({
|
|
79
|
+
from,
|
|
80
|
+
to,
|
|
81
|
+
text,
|
|
82
|
+
semantic: "typing"
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (pending.type === "replacement" && pending.state === view.state && pending.input.from === from && pending.input.to === to && pending.input.text === text) {
|
|
87
|
+
proposal = { type: "idle" };
|
|
88
|
+
options.replace(pending.input);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
refuseNativeMutation(view);
|
|
92
|
+
return true;
|
|
93
|
+
},
|
|
94
|
+
handleKeyDown: (view, event) => {
|
|
95
|
+
beginGesture();
|
|
96
|
+
if (composition.active) {
|
|
97
|
+
if (event.key === "Escape") {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
composition.cancel(view);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (event.metaKey || event.ctrlKey) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const modifier = event.metaKey || event.ctrlKey;
|
|
109
|
+
if (modifier || event.altKey || /^(?:Arrow|Home$|End$|Page|Escape$|Tab$)/u.test(event.key)) closeGroup();
|
|
110
|
+
if (modifier && event.key.toLowerCase() === "z") {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
if (event.shiftKey) options.redo();
|
|
113
|
+
else options.undo();
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (modifier && event.key.toLowerCase() === "y") {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
options.redo();
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (modifier && event.key.toLowerCase() === "a") {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, Selection.atStart(view.state.doc).from, Selection.atEnd(view.state.doc).to)));
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (modifier && (event.key.toLowerCase() === "b" || event.key.toLowerCase() === "i" || event.key.toLowerCase() === "u") && options.command !== void 0) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
switch (event.key.toLowerCase()) {
|
|
129
|
+
case "b":
|
|
130
|
+
options.command("toggleBold");
|
|
131
|
+
break;
|
|
132
|
+
case "i":
|
|
133
|
+
options.command("toggleItalic");
|
|
134
|
+
break;
|
|
135
|
+
case "u":
|
|
136
|
+
options.command("toggleUnderline");
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
if (event.key === "Enter") {
|
|
142
|
+
if (view.composing) return refuseEvent(event, "Paragraph structure edits are unavailable during composition.");
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
closeGroup();
|
|
145
|
+
if (options.structure === void 0) return refuseEvent(event, "Paragraph structure edits are unavailable in this session.");
|
|
146
|
+
if (modifier) options.structure("pageBreak");
|
|
147
|
+
else options.structure(event.shiftKey ? "lineBreak" : "split");
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
if (event.key === "Tab" && !modifier && !event.altKey && options.structure !== void 0) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
if (view.state.selection.$from.parent.attrs["numPr"]?.kind === "reference") options.structure(event.shiftKey ? "outdent" : "indent");
|
|
153
|
+
else if (!event.shiftKey) options.structure("tab");
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (event.key !== "Backspace" && event.key !== "Delete") return false;
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
if (modifier || event.altKey || view.composing || composition.active) {
|
|
159
|
+
closeGroup();
|
|
160
|
+
if (!composition.active) options.refuse("Only plain character deletion is available in this session.");
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
if (view.state.selection.empty && options.structure !== void 0 && isCanonicalJoinBoundary(view.state, event.key === "Backspace" ? "backward" : "forward")) {
|
|
164
|
+
closeGroup();
|
|
165
|
+
options.structure(event.key === "Backspace" ? "joinBackward" : "joinForward");
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
const range = deletionRange(view.state, event.key === "Backspace" ? "backward" : "forward");
|
|
169
|
+
if (range.isErr()) {
|
|
170
|
+
closeGroup();
|
|
171
|
+
options.refuse(range.error.message);
|
|
172
|
+
} else options.replace({
|
|
173
|
+
...range.value,
|
|
174
|
+
text: "",
|
|
175
|
+
semantic: event.key === "Backspace" ? "deleteBackward" : "deleteForward"
|
|
176
|
+
});
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
handleDOMEvents: {
|
|
180
|
+
compositionstart: (view) => {
|
|
181
|
+
beginGesture();
|
|
182
|
+
composition.start(view);
|
|
183
|
+
return false;
|
|
184
|
+
},
|
|
185
|
+
compositionend: (view) => {
|
|
186
|
+
composition.ended(view);
|
|
187
|
+
return false;
|
|
188
|
+
},
|
|
189
|
+
mousedown: (view) => {
|
|
190
|
+
composition.recover(view);
|
|
191
|
+
beginGesture();
|
|
192
|
+
closeGroup();
|
|
193
|
+
return false;
|
|
194
|
+
},
|
|
195
|
+
blur: (view) => {
|
|
196
|
+
composition.recover(view);
|
|
197
|
+
beginGesture();
|
|
198
|
+
closeGroup();
|
|
199
|
+
repaint(view);
|
|
200
|
+
return false;
|
|
201
|
+
},
|
|
202
|
+
beforeinput: (view, event) => {
|
|
203
|
+
beginGesture();
|
|
204
|
+
if (event.isComposing || event.inputType === "insertCompositionText" || event.inputType === "insertFromComposition" || event.inputType === "deleteCompositionText" || event.inputType === "deleteByComposition") {
|
|
205
|
+
if (composition.active) {
|
|
206
|
+
composition.authorizeNative(view);
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return refuseEvent(event, "Composition has no captured canonical baseline.");
|
|
210
|
+
}
|
|
211
|
+
if (composition.active) {
|
|
212
|
+
const refusedNativeCommit = composition.status === "refused" && view.composing;
|
|
213
|
+
composition.recover(view);
|
|
214
|
+
proposal = { type: "idle" };
|
|
215
|
+
if (refusedNativeCommit) {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (event.inputType === "insertText" || event.inputType === "insertReplacementText") {
|
|
221
|
+
if (event.cancelable) {
|
|
222
|
+
if (view.composing && event.inputType === "insertText" && event.data !== null) {
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
proposal = { type: "idle" };
|
|
225
|
+
options.replace({
|
|
226
|
+
from: view.state.selection.from,
|
|
227
|
+
to: view.state.selection.to,
|
|
228
|
+
text: event.data,
|
|
229
|
+
semantic: "typing"
|
|
230
|
+
});
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
proposal = { type: "idle" };
|
|
234
|
+
authorizedInput = {
|
|
235
|
+
view,
|
|
236
|
+
state: view.state
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
if (handleEditorBeforeInput(view, event)) return true;
|
|
240
|
+
return refuseEvent(event, "This native replacement cannot be addressed safely.");
|
|
241
|
+
} finally {
|
|
242
|
+
authorizedInput = null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (event.inputType !== "insertText" || event.data === null) return refuseEvent(event, "This native replacement cannot be addressed safely.");
|
|
246
|
+
proposal = {
|
|
247
|
+
type: "replacement",
|
|
248
|
+
state: view.state,
|
|
249
|
+
input: {
|
|
250
|
+
from: view.state.selection.from,
|
|
251
|
+
to: view.state.selection.to,
|
|
252
|
+
text: event.data,
|
|
253
|
+
semantic: "typing"
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
if ((event.inputType === "insertParagraph" || event.inputType === "insertLineBreak") && event.cancelable && options.structure !== void 0) {
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
closeGroup();
|
|
261
|
+
options.structure(event.inputType === "insertParagraph" ? "split" : "lineBreak");
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
if (event.inputType === "deleteContentBackward" || event.inputType === "deleteContentForward") {
|
|
265
|
+
const selection = view.state.selection;
|
|
266
|
+
const backward = event.inputType === "deleteContentBackward";
|
|
267
|
+
if (event.cancelable && selection.empty && options.structure !== void 0 && isCanonicalJoinBoundary(view.state, backward ? "backward" : "forward")) {
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
closeGroup();
|
|
270
|
+
options.structure(backward ? "joinBackward" : "joinForward");
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
const range = deletionRange(view.state, event.inputType === "deleteContentBackward" ? "backward" : "forward");
|
|
274
|
+
if (range.isErr()) return refuseEvent(event, range.error.message);
|
|
275
|
+
const input = {
|
|
276
|
+
...range.value,
|
|
277
|
+
text: "",
|
|
278
|
+
semantic: event.inputType === "deleteContentBackward" ? "deleteBackward" : "deleteForward"
|
|
279
|
+
};
|
|
280
|
+
if (event.cancelable) {
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
options.replace(input);
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
proposal = {
|
|
286
|
+
type: "replacement",
|
|
287
|
+
state: view.state,
|
|
288
|
+
input
|
|
289
|
+
};
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
if (event.inputType === "historyUndo" || event.inputType === "historyRedo") {
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
if (event.inputType === "historyUndo") options.undo();
|
|
295
|
+
else options.redo();
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
return refuseEvent(event, `Input ${event.inputType} is unavailable in this session.`);
|
|
299
|
+
},
|
|
300
|
+
input: (view) => {
|
|
301
|
+
if (composition.active) {
|
|
302
|
+
composition.flushed(view);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const pending = proposal;
|
|
306
|
+
queueMicrotask(() => {
|
|
307
|
+
if (view.isDestroyed) return;
|
|
308
|
+
view.updateState(view.state);
|
|
309
|
+
if (proposal === pending && !composition.active) proposal = { type: "idle" };
|
|
310
|
+
});
|
|
311
|
+
return false;
|
|
312
|
+
},
|
|
313
|
+
paste: (_view, event) => {
|
|
314
|
+
beginGesture();
|
|
315
|
+
return refuseEvent(event, "Paste is unavailable in this session.");
|
|
316
|
+
},
|
|
317
|
+
cut: (_view, event) => {
|
|
318
|
+
beginGesture();
|
|
319
|
+
return refuseEvent(event, "Cut is unavailable in this session.");
|
|
320
|
+
},
|
|
321
|
+
drop: (_view, event) => {
|
|
322
|
+
beginGesture();
|
|
323
|
+
return refuseEvent(event, "Drop is unavailable in this session.");
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
/** Returns a classified intent only; the proposed PM document is never authoritative. */
|
|
327
|
+
takeNativeProposal,
|
|
328
|
+
refuseNativeMutation,
|
|
329
|
+
commitNativeProposal: (view, transaction) => {
|
|
330
|
+
const input = takeNativeProposal(view.state, transaction);
|
|
331
|
+
if (!input) return false;
|
|
332
|
+
options.replace(input);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
//#endregion
|
|
338
|
+
export { createCanonicalInputBoundary };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { CreateHeaderFooterOp, DOCUMENT_OP_TYPES, DocumentOp, FormattingPatch, FormattingPatch as FormattingPatch$1, OpStory, RemoveHeaderFooterOp, SetSectionPropsOp, TextPosition } from "@stll/docx-core/ops";
|
|
3
|
+
//#region src/controller/canonicalOperations.d.ts
|
|
4
|
+
declare const canonicalFinalSectionIndex: (document: document_d_exports.Document) => number;
|
|
5
|
+
declare const withCanonicalParagraphIds: (content: document_d_exports.BlockContent[], document: document_d_exports.Document) => document_d_exports.BlockContent[];
|
|
6
|
+
type CreateCanonicalHeaderFooterOptions = {
|
|
7
|
+
document: document_d_exports.Document;
|
|
8
|
+
position: "header" | "footer";
|
|
9
|
+
referenceType: document_d_exports.HeaderFooterType;
|
|
10
|
+
sectionIndex?: number;
|
|
11
|
+
};
|
|
12
|
+
declare const createCanonicalHeaderFooterOperation: ({ document, position, referenceType, sectionIndex }: CreateCanonicalHeaderFooterOptions) => CreateHeaderFooterOp;
|
|
13
|
+
type RemoveCanonicalHeaderFooterOptions = {
|
|
14
|
+
document: document_d_exports.Document;
|
|
15
|
+
position: "header" | "footer";
|
|
16
|
+
rId: string;
|
|
17
|
+
};
|
|
18
|
+
/** Remove each explicit binding, leaving inherited sections to inherit the remaining parts. */
|
|
19
|
+
declare const removeCanonicalHeaderFooterOperations: ({ document, position, rId }: RemoveCanonicalHeaderFooterOptions) => RemoveHeaderFooterOp[];
|
|
20
|
+
declare const createCanonicalSectionPropertiesOperation: (document: document_d_exports.Document, patch: FormattingPatch$1<document_d_exports.SectionProperties>) => SetSectionPropsOp;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { DOCUMENT_OP_TYPES, type DocumentOp, type FormattingPatch, type OpStory, type TextPosition, canonicalFinalSectionIndex, createCanonicalHeaderFooterOperation, createCanonicalSectionPropertiesOperation, removeCanonicalHeaderFooterOperations, withCanonicalParagraphIds };
|