@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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
2
|
+
import { Transaction } from "prosemirror-state";
|
|
3
|
+
import { EditorView } from "prosemirror-view";
|
|
4
|
+
import { OpStory } from "@stll/docx-core/ops";
|
|
5
|
+
//#region src/controller/canonicalStoryEditor.d.ts
|
|
6
|
+
type CanonicalStoryEditorOptions = {
|
|
7
|
+
story: OpStory;
|
|
8
|
+
getView: () => EditorView;
|
|
9
|
+
getApi: () => HiddenEditorApi | null;
|
|
10
|
+
enabled: () => boolean;
|
|
11
|
+
onSelectionChange: () => void;
|
|
12
|
+
onRefusal: ((reason: string) => void) | undefined;
|
|
13
|
+
};
|
|
14
|
+
/** Secondary views compile classified native input into the shared Document journal. */
|
|
15
|
+
declare const createCanonicalStoryEditor: ({ story, getView, getApi, enabled, onRefusal, onSelectionChange }: CanonicalStoryEditorOptions) => {
|
|
16
|
+
dispatch: (transaction: Transaction) => boolean;
|
|
17
|
+
props: {
|
|
18
|
+
handleKeyDown: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
19
|
+
handleTextInput: (view: EditorView, from: number, to: number, text: string) => boolean;
|
|
20
|
+
handleDOMEvents: {
|
|
21
|
+
beforeinput: (view: EditorView, event: InputEvent) => boolean;
|
|
22
|
+
mousedown: (view: EditorView) => boolean;
|
|
23
|
+
compositionstart: (view: EditorView) => boolean;
|
|
24
|
+
compositionend: (view: EditorView) => boolean;
|
|
25
|
+
input: (view: EditorView) => boolean;
|
|
26
|
+
blur: (view: EditorView) => boolean;
|
|
27
|
+
paste: (view: EditorView, event: Event) => boolean;
|
|
28
|
+
cut: (view: EditorView, event: Event) => boolean;
|
|
29
|
+
drop: (view: EditorView, event: Event) => boolean;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createCanonicalStoryEditor };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createCanonicalInputBoundary } from "./canonicalInput.js";
|
|
2
|
+
import { CanonicalSessionError } from "./canonicalSession.js";
|
|
3
|
+
//#region src/controller/canonicalStoryEditor.ts
|
|
4
|
+
/** Secondary views compile classified native input into the shared Document journal. */
|
|
5
|
+
const createCanonicalStoryEditor = ({ story, getView, getApi, enabled, onRefusal, onSelectionChange }) => {
|
|
6
|
+
const refuse = (message) => {
|
|
7
|
+
if (onRefusal) onRefusal(message);
|
|
8
|
+
else throw new CanonicalSessionError({
|
|
9
|
+
message,
|
|
10
|
+
reason: "refused"
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
const history = (direction) => {
|
|
14
|
+
const view = getView();
|
|
15
|
+
const accepted = getApi()?.applyCanonicalStoryHistory({
|
|
16
|
+
view,
|
|
17
|
+
story,
|
|
18
|
+
direction
|
|
19
|
+
}) ?? false;
|
|
20
|
+
if (accepted && !view.isDestroyed) onSelectionChange();
|
|
21
|
+
return accepted;
|
|
22
|
+
};
|
|
23
|
+
const boundary = createCanonicalInputBoundary({
|
|
24
|
+
breakUndoGroup: () => {
|
|
25
|
+
getApi()?.updateCanonicalInputLifecycle("breakUndoGroup");
|
|
26
|
+
},
|
|
27
|
+
beginComposition: () => getApi()?.updateCanonicalInputLifecycle("beginComposition") ?? false,
|
|
28
|
+
endComposition: () => {
|
|
29
|
+
getApi()?.updateCanonicalInputLifecycle("endComposition");
|
|
30
|
+
},
|
|
31
|
+
replace: (intent) => {
|
|
32
|
+
const view = getView();
|
|
33
|
+
if (getApi()?.replaceCanonicalStoryText({
|
|
34
|
+
view,
|
|
35
|
+
story,
|
|
36
|
+
intent
|
|
37
|
+
}) && !view.isDestroyed) onSelectionChange();
|
|
38
|
+
},
|
|
39
|
+
undo: () => history("undo"),
|
|
40
|
+
redo: () => history("redo"),
|
|
41
|
+
refuse
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
dispatch: (transaction) => {
|
|
45
|
+
if (!enabled()) return false;
|
|
46
|
+
const view = getView();
|
|
47
|
+
if (transaction.docChanged) {
|
|
48
|
+
if (boundary.acceptComposition(view, transaction)) return true;
|
|
49
|
+
if (!boundary.commitNativeProposal(view, transaction)) boundary.refuseNativeMutation(view);
|
|
50
|
+
view.updateState(view.state);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (transaction.selectionSet && !boundary.isComposing) getApi()?.updateCanonicalInputLifecycle("breakUndoGroup");
|
|
54
|
+
const applied = view.state.applyTransaction(transaction);
|
|
55
|
+
if (!applied.state.doc.eq(view.state.doc)) {
|
|
56
|
+
refuse("A plugin attempted an unclassified canonical story mutation.");
|
|
57
|
+
view.updateState(view.state);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
view.updateState(applied.state);
|
|
61
|
+
if (applied.transactions.some((step) => step.selectionSet)) onSelectionChange();
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
props: {
|
|
65
|
+
handleKeyDown: (view, event) => enabled() && boundary.handleKeyDown(view, event),
|
|
66
|
+
handleTextInput: (view, from, to, text) => enabled() && boundary.handleTextInput(view, from, to, text),
|
|
67
|
+
handleDOMEvents: {
|
|
68
|
+
beforeinput: (view, event) => enabled() && boundary.handleDOMEvents.beforeinput(view, event),
|
|
69
|
+
mousedown: (view) => enabled() && boundary.handleDOMEvents.mousedown(view),
|
|
70
|
+
compositionstart: (view) => enabled() && boundary.handleDOMEvents.compositionstart(view),
|
|
71
|
+
compositionend: (view) => enabled() && boundary.handleDOMEvents.compositionend(view),
|
|
72
|
+
input: (view) => enabled() && boundary.handleDOMEvents.input(view),
|
|
73
|
+
blur: (view) => enabled() && boundary.handleDOMEvents.blur(view),
|
|
74
|
+
paste: (view, event) => enabled() && boundary.handleDOMEvents.paste(view, event),
|
|
75
|
+
cut: (view, event) => enabled() && boundary.handleDOMEvents.cut(view, event),
|
|
76
|
+
drop: (view, event) => enabled() && boundary.handleDOMEvents.drop(view, event)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { createCanonicalStoryEditor };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CanonicalCommit, CanonicalSession, CanonicalSessionError } from "./canonicalSession.js";
|
|
2
|
+
import { CanonicalCommandIntent } from "../prosemirror/canonicalCommands.js";
|
|
3
|
+
import { EditorState } from "prosemirror-state";
|
|
4
|
+
import { Result } from "better-result";
|
|
5
|
+
//#region src/controller/canonicalStructure.d.ts
|
|
6
|
+
/** Toolbar meanings enter the same operation journal as native input. */
|
|
7
|
+
declare const prepareCanonicalCommands: (session: CanonicalSession, state: EditorState, commands: readonly CanonicalCommandIntent[]) => Result<CanonicalCommit, CanonicalSessionError>;
|
|
8
|
+
type CanonicalAutoformatInput = {
|
|
9
|
+
from: number;
|
|
10
|
+
to: number;
|
|
11
|
+
text: string;
|
|
12
|
+
};
|
|
13
|
+
/** Rules compile their replacement and paragraph change as one atomic edit. */
|
|
14
|
+
declare const prepareCanonicalAutoformat: (session: CanonicalSession, state: EditorState, { from, to, text }: CanonicalAutoformatInput) => Result<CanonicalCommit, CanonicalSessionError> | undefined;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { prepareCanonicalAutoformat, prepareCanonicalCommands };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { getCachedNumberingMap, numberingLevelUsesBulletMarker } from "../docx/numberingParser.js";
|
|
2
|
+
import { listRequestsForMarker } from "../prosemirror/listAutoformatMarkers.js";
|
|
3
|
+
import { CanonicalSessionError } from "./canonicalSession.js";
|
|
4
|
+
import { Result } from "better-result";
|
|
5
|
+
import { paragraphNumberingReference } from "@stll/docx-core/model";
|
|
6
|
+
import { OP_STORIES } from "@stll/docx-core/ops";
|
|
7
|
+
//#region src/controller/canonicalStructure.ts
|
|
8
|
+
const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
9
|
+
message,
|
|
10
|
+
reason: "refused"
|
|
11
|
+
}));
|
|
12
|
+
const selectedParagraphs = (session, state) => {
|
|
13
|
+
const ids = /* @__PURE__ */ new Set();
|
|
14
|
+
state.doc.nodesBetween(state.selection.from, state.selection.to, (node) => {
|
|
15
|
+
if (node.type.name === "paragraph" && typeof node.attrs["paraId"] === "string") ids.add(node.attrs["paraId"]);
|
|
16
|
+
});
|
|
17
|
+
return session.document.package.document.content.filter((paragraph) => paragraph.type === "paragraph" && ids.has(paragraph.paraId ?? ""));
|
|
18
|
+
};
|
|
19
|
+
const numberingIntent = (session, paragraphs, request) => {
|
|
20
|
+
const definitions = session.document.package.numbering;
|
|
21
|
+
const map = definitions === void 0 ? void 0 : getCachedNumberingMap(definitions);
|
|
22
|
+
const body = session.document.package.document.content;
|
|
23
|
+
const first = body.findIndex((item) => item === paragraphs.at(0));
|
|
24
|
+
const last = body.findIndex((item) => item === paragraphs.at(-1));
|
|
25
|
+
const neighbor = [first > 0 ? body.at(first - 1) : void 0, body.at(last + 1)].find((item) => {
|
|
26
|
+
if (item?.type !== "paragraph" || item.formatting?.numPr?.kind !== "reference" || item.formatting.numPrFromStyle !== void 0) return false;
|
|
27
|
+
const level = map?.getLevel(item.formatting.numPr.numId, item.formatting.numPr.ilvl ?? 0);
|
|
28
|
+
if (level == null || numberingLevelUsesBulletMarker(level) !== (request.kind === "bullet")) return false;
|
|
29
|
+
return request.format === void 0 || level.numFmt === request.format.numFmt && level.lvlText === request.format.lvlText;
|
|
30
|
+
});
|
|
31
|
+
let numId;
|
|
32
|
+
let ilvl = 0;
|
|
33
|
+
let target;
|
|
34
|
+
if (neighbor?.type === "paragraph" && neighbor.formatting?.numPr?.kind === "reference") {
|
|
35
|
+
numId = neighbor.formatting.numPr.numId;
|
|
36
|
+
ilvl = neighbor.formatting.numPr.ilvl ?? 0;
|
|
37
|
+
target = {
|
|
38
|
+
type: "existing",
|
|
39
|
+
numId
|
|
40
|
+
};
|
|
41
|
+
} else {
|
|
42
|
+
numId = Math.max(0, ...definitions?.nums.map((num) => num.numId) ?? []) + 1;
|
|
43
|
+
const abstractNumId = Math.max(-1, ...definitions?.abstractNums.map((num) => num.abstractNumId) ?? []) + 1;
|
|
44
|
+
const levels = Array.from({ length: 9 }, (_, level) => ({
|
|
45
|
+
ilvl: level,
|
|
46
|
+
start: level === 0 ? request.start ?? 1 : 1,
|
|
47
|
+
numFmt: request.kind === "bullet" ? "bullet" : request.format?.numFmt ?? "decimal",
|
|
48
|
+
lvlText: request.kind === "bullet" ? "•" : (request.format?.lvlText ?? "%1.").replaceAll("%1", `%${level + 1}`),
|
|
49
|
+
suffix: "tab",
|
|
50
|
+
pPr: {
|
|
51
|
+
indentLeft: (level + 1) * 720,
|
|
52
|
+
indentFirstLine: 360,
|
|
53
|
+
hangingIndent: true
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
target = {
|
|
57
|
+
type: "new",
|
|
58
|
+
num: {
|
|
59
|
+
numId,
|
|
60
|
+
abstractNumId
|
|
61
|
+
},
|
|
62
|
+
abstractNum: {
|
|
63
|
+
abstractNumId,
|
|
64
|
+
multiLevelType: "multilevel",
|
|
65
|
+
levels
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
type: "setList",
|
|
71
|
+
target,
|
|
72
|
+
items: paragraphs.map((paragraph) => ({
|
|
73
|
+
at: {
|
|
74
|
+
story: OP_STORIES.MAIN,
|
|
75
|
+
blockId: paragraph.paraId ?? "",
|
|
76
|
+
offset: 0
|
|
77
|
+
},
|
|
78
|
+
ilvl
|
|
79
|
+
}))
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
/** Toolbar meanings enter the same operation journal as native input. */
|
|
83
|
+
const prepareCanonicalCommands = (session, state, commands) => {
|
|
84
|
+
const intents = [];
|
|
85
|
+
const paragraphs = selectedParagraphs(session, state);
|
|
86
|
+
for (const command of commands) switch (command.type) {
|
|
87
|
+
case "insertBreak": {
|
|
88
|
+
const from = session.projection.addressAt(command.from);
|
|
89
|
+
if (from.isErr()) return from;
|
|
90
|
+
const to = session.projection.addressAt(command.to);
|
|
91
|
+
if (to.isErr()) return to;
|
|
92
|
+
intents.push({
|
|
93
|
+
type: "insertAtom",
|
|
94
|
+
from: from.value,
|
|
95
|
+
to: to.value,
|
|
96
|
+
atom: {
|
|
97
|
+
type: "break",
|
|
98
|
+
breakType: command.breakType
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "formatRun": {
|
|
104
|
+
const from = session.projection.addressAt(command.from);
|
|
105
|
+
if (from.isErr()) return from;
|
|
106
|
+
const to = session.projection.addressAt(command.to);
|
|
107
|
+
if (to.isErr()) return to;
|
|
108
|
+
intents.push({
|
|
109
|
+
type: "formatRun",
|
|
110
|
+
from: from.value,
|
|
111
|
+
to: to.value,
|
|
112
|
+
patch: command.patch
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "formatParagraph": {
|
|
117
|
+
const at = session.projection.addressAt(command.at);
|
|
118
|
+
if (at.isErr()) return at;
|
|
119
|
+
intents.push({
|
|
120
|
+
type: "formatParagraph",
|
|
121
|
+
at: at.value,
|
|
122
|
+
patch: command.patch
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case "toggleList": {
|
|
127
|
+
const definitions = session.document.package.numbering;
|
|
128
|
+
const map = definitions === void 0 ? void 0 : getCachedNumberingMap(definitions);
|
|
129
|
+
if (!(paragraphs.length > 0 && paragraphs.every(({ formatting }) => {
|
|
130
|
+
const numPr = formatting?.numPr;
|
|
131
|
+
return numPr?.kind === "reference" && numberingLevelUsesBulletMarker(map?.getLevel(numPr.numId, numPr.ilvl ?? 0)) === (command.kind === "bullet");
|
|
132
|
+
}))) {
|
|
133
|
+
intents.push(numberingIntent(session, paragraphs, { kind: command.kind === "bullet" ? "bullet" : "numbered" }));
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
for (const paragraph of paragraphs) intents.push({
|
|
137
|
+
type: "formatParagraph",
|
|
138
|
+
at: {
|
|
139
|
+
story: OP_STORIES.MAIN,
|
|
140
|
+
blockId: paragraph.paraId ?? "",
|
|
141
|
+
offset: 0
|
|
142
|
+
},
|
|
143
|
+
patch: { numPr: { kind: "none" } }
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "removeList":
|
|
148
|
+
for (const paragraph of paragraphs) intents.push({
|
|
149
|
+
type: "formatParagraph",
|
|
150
|
+
at: {
|
|
151
|
+
story: OP_STORIES.MAIN,
|
|
152
|
+
blockId: paragraph.paraId ?? "",
|
|
153
|
+
offset: 0
|
|
154
|
+
},
|
|
155
|
+
patch: { numPr: { kind: "none" } }
|
|
156
|
+
});
|
|
157
|
+
break;
|
|
158
|
+
case "changeListLevel":
|
|
159
|
+
for (const paragraph of paragraphs) {
|
|
160
|
+
const numPr = paragraph.formatting?.numPr;
|
|
161
|
+
if (numPr?.kind !== "reference") continue;
|
|
162
|
+
const at = {
|
|
163
|
+
story: OP_STORIES.MAIN,
|
|
164
|
+
blockId: paragraph.paraId ?? "",
|
|
165
|
+
offset: 0
|
|
166
|
+
};
|
|
167
|
+
const level = (numPr.ilvl ?? 0) + (command.direction === "increase" ? 1 : -1);
|
|
168
|
+
if (level > 8) return refuse("The list is already at its deepest level.");
|
|
169
|
+
intents.push({
|
|
170
|
+
type: "formatParagraph",
|
|
171
|
+
at,
|
|
172
|
+
patch: { numPr: level < 0 ? { kind: "none" } : paragraphNumberingReference({
|
|
173
|
+
numId: numPr.numId,
|
|
174
|
+
ilvl: level
|
|
175
|
+
}) }
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case "restartNumbering":
|
|
180
|
+
case "continueNumbering": {
|
|
181
|
+
const source = paragraphs.at(0);
|
|
182
|
+
const numPr = source?.formatting?.numPr;
|
|
183
|
+
const definitions = session.document.package.numbering;
|
|
184
|
+
if (source === void 0 || numPr?.kind !== "reference" || definitions === void 0) return refuse("Numbering changes require a list paragraph.");
|
|
185
|
+
const body = session.document.package.document.content;
|
|
186
|
+
const index = body.findIndex((item) => item === source);
|
|
187
|
+
const affected = body.slice(index).filter((item) => item.type === "paragraph" && item.formatting?.numPr?.kind === "reference" && item.formatting.numPr.numId === numPr.numId);
|
|
188
|
+
let targetId;
|
|
189
|
+
let target;
|
|
190
|
+
if (command.type === "continueNumbering") {
|
|
191
|
+
const map = getCachedNumberingMap(definitions);
|
|
192
|
+
const bullet = numberingLevelUsesBulletMarker(map.getLevel(numPr.numId, numPr.ilvl ?? 0));
|
|
193
|
+
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);
|
|
194
|
+
if (earlier?.type !== "paragraph" || earlier.formatting?.numPr?.kind !== "reference") return refuse("There is no preceding compatible list.");
|
|
195
|
+
targetId = earlier.formatting.numPr.numId;
|
|
196
|
+
target = {
|
|
197
|
+
type: "existing",
|
|
198
|
+
numId: targetId
|
|
199
|
+
};
|
|
200
|
+
} else {
|
|
201
|
+
const start = command.start ?? 1;
|
|
202
|
+
if (!Number.isSafeInteger(start) || start < 0) return refuse("The numbering start must be a non-negative integer.");
|
|
203
|
+
const original = definitions.nums.find((num) => num.numId === numPr.numId);
|
|
204
|
+
if (original === void 0) return refuse("The numbering instance does not exist.");
|
|
205
|
+
targetId = Math.max(0, ...definitions.nums.map((num) => num.numId)) + 1;
|
|
206
|
+
const level = numPr.ilvl ?? 0;
|
|
207
|
+
target = {
|
|
208
|
+
type: "new",
|
|
209
|
+
num: {
|
|
210
|
+
...original,
|
|
211
|
+
numId: targetId,
|
|
212
|
+
levelOverrides: [...(original.levelOverrides ?? []).filter(({ ilvl }) => ilvl !== level), {
|
|
213
|
+
...original.levelOverrides?.find(({ ilvl }) => ilvl === level),
|
|
214
|
+
ilvl: level,
|
|
215
|
+
startOverride: start
|
|
216
|
+
}]
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
intents.push({
|
|
221
|
+
type: "setList",
|
|
222
|
+
target,
|
|
223
|
+
items: affected.map((paragraph) => ({
|
|
224
|
+
at: {
|
|
225
|
+
story: OP_STORIES.MAIN,
|
|
226
|
+
blockId: paragraph.paraId ?? "",
|
|
227
|
+
offset: 0
|
|
228
|
+
},
|
|
229
|
+
ilvl: paragraph.formatting?.numPr?.kind === "reference" ? paragraph.formatting.numPr.ilvl ?? 0 : 0
|
|
230
|
+
}))
|
|
231
|
+
});
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
default: return command;
|
|
235
|
+
}
|
|
236
|
+
return session.prepareIntents(state, intents);
|
|
237
|
+
};
|
|
238
|
+
/** Rules compile their replacement and paragraph change as one atomic edit. */
|
|
239
|
+
const prepareCanonicalAutoformat = (session, state, { from, to, text }) => {
|
|
240
|
+
if (text !== " " || from !== to) return void 0;
|
|
241
|
+
const at = session.projection.addressAt(from);
|
|
242
|
+
if (at.isErr()) return void 0;
|
|
243
|
+
const address = session.projection.paragraph(at.value.blockId);
|
|
244
|
+
if (address === void 0 || address.source.formatting?.numPr?.kind === "reference") return void 0;
|
|
245
|
+
const source = address.source;
|
|
246
|
+
const prefix = address.text.slice(0, at.value.offset) + text;
|
|
247
|
+
const requests = listRequestsForMarker(prefix);
|
|
248
|
+
const heading = /^(?<hashes>#{1,6}) $/u.exec(prefix)?.groups?.["hashes"];
|
|
249
|
+
if (requests === null && heading === void 0) return void 0;
|
|
250
|
+
const start = {
|
|
251
|
+
...at.value,
|
|
252
|
+
offset: 0
|
|
253
|
+
};
|
|
254
|
+
const request = requests?.at(0);
|
|
255
|
+
const styleId = `Heading${heading?.length ?? 1}`;
|
|
256
|
+
if (request === void 0 && !session.hasStyle(styleId)) return void 0;
|
|
257
|
+
return session.prepareIntents(state, [{
|
|
258
|
+
type: "replaceText",
|
|
259
|
+
from: start,
|
|
260
|
+
to: at.value,
|
|
261
|
+
text: ""
|
|
262
|
+
}, request === void 0 ? {
|
|
263
|
+
type: "formatParagraph",
|
|
264
|
+
at: start,
|
|
265
|
+
patch: { styleId }
|
|
266
|
+
} : numberingIntent(session, [source], request)]);
|
|
267
|
+
};
|
|
268
|
+
//#endregion
|
|
269
|
+
export { prepareCanonicalAutoformat, prepareCanonicalCommands };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { DocxInput } from "../utils/docxInput.js";
|
|
3
3
|
import { Layout } from "../layout-engine/types.js";
|
|
4
|
-
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
5
4
|
import { FolioEditorEmitter } from "./folioEditorEvents.js";
|
|
5
|
+
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
6
6
|
import { LayoutRunOptions } from "./layoutRunOptions.js";
|
|
7
7
|
import { EditorState } from "prosemirror-state";
|
|
8
8
|
//#region src/controller/folioEditor.d.ts
|
|
@@ -15,6 +15,15 @@ const createFolioEditor = (deps) => {
|
|
|
15
15
|
getState: () => deps.getEditorApi()?.getState() ?? null,
|
|
16
16
|
getView: () => deps.getEditorApi()?.getView() ?? null,
|
|
17
17
|
getDocument: () => deps.getEditorApi()?.getDocument() ?? null,
|
|
18
|
+
updateCanonicalInputLifecycle: (action) => deps.getEditorApi()?.updateCanonicalInputLifecycle(action) ?? false,
|
|
19
|
+
applyCanonicalStoryHistory: (options) => deps.getEditorApi()?.applyCanonicalStoryHistory(options) ?? false,
|
|
20
|
+
applyCanonicalOperations: (ops) => deps.getEditorApi()?.applyCanonicalOperations(ops) ?? false,
|
|
21
|
+
getCanonicalStorySelection: (story) => deps.getEditorApi()?.getCanonicalStorySelection(story) ?? null,
|
|
22
|
+
getCanonicalStoryProjection: (story) => deps.getEditorApi()?.getCanonicalStoryProjection(story) ?? null,
|
|
23
|
+
replaceCanonicalStoryText: (options) => deps.getEditorApi()?.replaceCanonicalStoryText(options) ?? false,
|
|
24
|
+
getCanonicalDocument: () => deps.getEditorApi()?.getCanonicalDocument() ?? null,
|
|
25
|
+
setCanonicalMode: (mode) => deps.getEditorApi()?.setCanonicalMode(mode) ?? false,
|
|
26
|
+
resolveCanonicalRevisions: (revisionIds, resolution) => deps.getEditorApi()?.resolveCanonicalRevisions(revisionIds, resolution) ?? false,
|
|
18
27
|
focus: () => deps.getEditorApi()?.focus(),
|
|
19
28
|
blur: () => deps.getEditorApi()?.blur(),
|
|
20
29
|
isFocused: () => deps.getEditorApi()?.isFocused() ?? false,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { HiddenEditorApi } from "./hiddenEditorApi.js";
|
|
2
3
|
import { DirectEditorProps, EditorView } from "prosemirror-view";
|
|
3
4
|
//#region src/controller/headerFooterEditorManager.d.ts
|
|
4
5
|
type HeaderFooterPartKind = "header" | "footer";
|
|
@@ -16,6 +17,9 @@ type HeaderFooterEditorTransaction = {
|
|
|
16
17
|
type HeaderFooterEditorManagerDeps = {
|
|
17
18
|
createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
|
|
18
19
|
getDocument: () => document_d_exports.Document | null;
|
|
20
|
+
getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
|
|
21
|
+
getExperimentalSession?: (() => "canonical" | undefined) | undefined;
|
|
22
|
+
onSessionRefusal?: ((reason: string) => void) | undefined;
|
|
19
23
|
getHost: () => HTMLElement | null;
|
|
20
24
|
getStyles: () => document_d_exports.StyleDefinitions | null | undefined;
|
|
21
25
|
getTheme: () => document_d_exports.Theme | null | undefined;
|
|
@@ -10,9 +10,19 @@ import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNu
|
|
|
10
10
|
import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
11
11
|
import { schema } from "../prosemirror/schema/index.js";
|
|
12
12
|
import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
|
|
13
|
-
import {
|
|
13
|
+
import { createCanonicalStoryEditor } from "./canonicalStoryEditor.js";
|
|
14
|
+
import { OP_STORIES } from "@stll/docx-core/ops";
|
|
15
|
+
import { EditorState, TextSelection } from "prosemirror-state";
|
|
14
16
|
import { EditorView } from "prosemirror-view";
|
|
15
17
|
//#region src/controller/headerFooterEditorManager.ts
|
|
18
|
+
/**
|
|
19
|
+
* Framework-neutral lifecycle owner for persistent header/footer EditorViews.
|
|
20
|
+
*
|
|
21
|
+
* Each distinct relationship id owns one off-screen ProseMirror view. Painted
|
|
22
|
+
* header/footer instances that share that relationship therefore share live
|
|
23
|
+
* editor state, and adapters only decide when to synchronize or destroy the
|
|
24
|
+
* manager.
|
|
25
|
+
*/
|
|
16
26
|
const createEditorView = (mountNode, props) => new EditorView(mountNode, props);
|
|
17
27
|
const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
|
|
18
28
|
const headerFooterToProseDocWithDetachedWatermarkHost = (headerFooter, options) => {
|
|
@@ -27,6 +37,7 @@ const buildInitialState = (headerFooter, styles, theme, numbering, manager) => {
|
|
|
27
37
|
const proseDocOptions = {};
|
|
28
38
|
if (styles) proseDocOptions.styles = styles;
|
|
29
39
|
if (theme !== void 0) proseDocOptions.theme = theme;
|
|
40
|
+
if (numbering != null) proseDocOptions.numbering = numbering;
|
|
30
41
|
const document = headerFooter.watermarkBlockIndex === void 0 ? headerFooterToProseDoc(headerFooter.content, proseDocOptions) : headerFooterToProseDocWithDetachedWatermarkHost(headerFooter, proseDocOptions);
|
|
31
42
|
return ensureBaseDirectionInState(ensureParaIdsInState(EditorState.create({
|
|
32
43
|
doc: document,
|
|
@@ -76,7 +87,8 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
76
87
|
const sync = () => {
|
|
77
88
|
const host = deps.getHost();
|
|
78
89
|
if (!host) return;
|
|
79
|
-
|
|
90
|
+
if (deps.getExperimentalSession?.() === "canonical" && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
|
|
91
|
+
const document = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalDocument() ?? deps.getDocument() : deps.getDocument();
|
|
80
92
|
const styles = deps.getStyles();
|
|
81
93
|
const theme = deps.getTheme();
|
|
82
94
|
const numbering = document?.package.numbering;
|
|
@@ -92,6 +104,30 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
92
104
|
const existing = mounted.get(part.rId);
|
|
93
105
|
if (existing) {
|
|
94
106
|
if (existing.mountNode.parentElement !== host) host.append(existing.mountNode);
|
|
107
|
+
if (deps.getExperimentalSession?.() === "canonical") {
|
|
108
|
+
const projected = deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
|
|
109
|
+
kind: part.kind,
|
|
110
|
+
rId: part.rId
|
|
111
|
+
});
|
|
112
|
+
if (projected && !existing.view.state.doc.eq(projected)) {
|
|
113
|
+
const transaction = existing.view.state.tr.replaceWith(0, existing.view.state.doc.content.size, projected.content);
|
|
114
|
+
const restored = deps.getCanonicalApi?.()?.getCanonicalStorySelection({
|
|
115
|
+
kind: part.kind,
|
|
116
|
+
rId: part.rId
|
|
117
|
+
});
|
|
118
|
+
const anchor = Math.min(existing.view.state.selection.anchor, projected.content.size);
|
|
119
|
+
transaction.setSelection(restored ? TextSelection.create(transaction.doc, restored.anchor, restored.head) : TextSelection.near(transaction.doc.resolve(anchor)));
|
|
120
|
+
transaction.setMeta("addToHistory", false);
|
|
121
|
+
existing.view.updateState(existing.view.state.apply(transaction));
|
|
122
|
+
}
|
|
123
|
+
existing.appliedHeaderFooter = headerFooter;
|
|
124
|
+
existing.appliedContent = headerFooter.content;
|
|
125
|
+
existing.appliedStyles = styles;
|
|
126
|
+
existing.appliedTheme = theme;
|
|
127
|
+
existing.appliedNumbering = numbering;
|
|
128
|
+
existing.dirty = false;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
95
131
|
const contentIsCurrent = existing.appliedHeaderFooter === headerFooter && existing.appliedContent === headerFooter.content;
|
|
96
132
|
const contextIsCurrent = existing.appliedStyles === styles && existing.appliedTheme === theme && existing.appliedNumbering === numbering;
|
|
97
133
|
if (contentIsCurrent && contextIsCurrent) continue;
|
|
@@ -111,9 +147,40 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
111
147
|
mountNode.dataset["hfRId"] = part.rId;
|
|
112
148
|
mountNode.dataset["hfKind"] = part.kind;
|
|
113
149
|
host.append(mountNode);
|
|
150
|
+
const canonical = createCanonicalStoryEditor({
|
|
151
|
+
story: {
|
|
152
|
+
kind: part.kind,
|
|
153
|
+
rId: part.rId
|
|
154
|
+
},
|
|
155
|
+
getView: () => view,
|
|
156
|
+
getApi: () => deps.getCanonicalApi?.() ?? null,
|
|
157
|
+
enabled: () => deps.getExperimentalSession?.() === "canonical",
|
|
158
|
+
onRefusal: deps.onSessionRefusal,
|
|
159
|
+
onSelectionChange: () => deps.onTransaction?.({
|
|
160
|
+
kind: part.kind,
|
|
161
|
+
rId: part.rId,
|
|
162
|
+
view,
|
|
163
|
+
docChanged: false,
|
|
164
|
+
selectionChanged: true
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
const canonicalProjection = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
|
|
168
|
+
kind: part.kind,
|
|
169
|
+
rId: part.rId
|
|
170
|
+
}) : null;
|
|
171
|
+
if (deps.getExperimentalSession?.() === "canonical" && !canonicalProjection) {
|
|
172
|
+
manager.destroy();
|
|
173
|
+
mountNode.remove();
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
114
176
|
const view = (deps.createView ?? createEditorView)(mountNode, {
|
|
115
|
-
|
|
177
|
+
...canonical.props,
|
|
178
|
+
state: canonicalProjection ? EditorState.create({
|
|
179
|
+
doc: canonicalProjection,
|
|
180
|
+
plugins: [createDocumentStylesPlugin(styles), createDocumentNumberingPlugin(numbering)]
|
|
181
|
+
}) : buildInitialState(headerFooter, styles, theme, numbering, manager),
|
|
116
182
|
dispatchTransaction(transaction) {
|
|
183
|
+
if (canonical.dispatch(transaction)) return;
|
|
117
184
|
const nextState = view.state.apply(transaction);
|
|
118
185
|
view.updateState(nextState);
|
|
119
186
|
const mountedPart = mounted.get(part.rId);
|
|
@@ -150,6 +217,7 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
150
217
|
rId
|
|
151
218
|
})),
|
|
152
219
|
snapshotDocument: (document) => {
|
|
220
|
+
if (deps.getExperimentalSession?.() === "canonical") return deps.getCanonicalApi?.()?.getCanonicalDocument() ?? document;
|
|
153
221
|
let headers = document.package.headers;
|
|
154
222
|
let footers = document.package.footers;
|
|
155
223
|
let headersChanged = false;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { createCanonicalInputBoundary } from "./canonicalInput.js";
|
|
3
|
+
import { CanonicalSessionMode } from "./canonicalSession.js";
|
|
4
|
+
import { Node } from "prosemirror-model";
|
|
2
5
|
import { Command, EditorState, Transaction } from "prosemirror-state";
|
|
3
6
|
import { EditorView } from "prosemirror-view";
|
|
7
|
+
import { DocumentOp, OpStory } from "@stll/docx-core/ops";
|
|
4
8
|
//#region src/controller/hiddenEditorApi.d.ts
|
|
9
|
+
type CanonicalStoryHistoryOptions = {
|
|
10
|
+
view: EditorView;
|
|
11
|
+
story: OpStory;
|
|
12
|
+
direction: "undo" | "redo";
|
|
13
|
+
};
|
|
14
|
+
type CanonicalStoryTextOptions = {
|
|
15
|
+
view: EditorView;
|
|
16
|
+
story: OpStory;
|
|
17
|
+
intent: Parameters<Parameters<typeof createCanonicalInputBoundary>[0]["replace"]>[0];
|
|
18
|
+
};
|
|
5
19
|
type HiddenEditorApi = {
|
|
6
20
|
/** Request the off-screen EditorView (idempotent; creates it when possible). */
|
|
7
21
|
ensureView: () => void;
|
|
@@ -13,6 +27,21 @@ type HiddenEditorApi = {
|
|
|
13
27
|
getView: () => EditorView | null;
|
|
14
28
|
/** Get the current Document from PM state */
|
|
15
29
|
getDocument: () => document_d_exports.Document | null;
|
|
30
|
+
/** Canonical snapshot, or null in the default session. */
|
|
31
|
+
getCanonicalDocument: () => document_d_exports.Document | null;
|
|
32
|
+
/** Set the explicit mode for an active canonical session. */
|
|
33
|
+
setCanonicalMode: (mode: CanonicalSessionMode) => boolean;
|
|
34
|
+
/** Resolve canonical revisions as one batch and journal its exact inverse. */
|
|
35
|
+
resolveCanonicalRevisions: (revisionIds: readonly number[], resolution: "accept" | "reject") => boolean;
|
|
36
|
+
updateCanonicalInputLifecycle: (action: "beginComposition" | "endComposition" | "breakUndoGroup") => boolean;
|
|
37
|
+
applyCanonicalStoryHistory: (options: CanonicalStoryHistoryOptions) => boolean;
|
|
38
|
+
applyCanonicalOperations: (ops: readonly DocumentOp[]) => boolean;
|
|
39
|
+
getCanonicalStorySelection: (story: OpStory) => {
|
|
40
|
+
anchor: number;
|
|
41
|
+
head: number;
|
|
42
|
+
} | null;
|
|
43
|
+
getCanonicalStoryProjection: (story: OpStory) => Node | null;
|
|
44
|
+
replaceCanonicalStoryText: (options: CanonicalStoryTextOptions) => boolean;
|
|
16
45
|
/** Focus the hidden editor */
|
|
17
46
|
focus: () => void;
|
|
18
47
|
/** Blur the hidden editor */
|
|
@@ -43,6 +72,12 @@ type HiddenEditorApi = {
|
|
|
43
72
|
type HiddenEditorApiDeps = {
|
|
44
73
|
getView: () => EditorView | null;
|
|
45
74
|
getDocumentContext: () => document_d_exports.Document | null;
|
|
75
|
+
getCanonicalDocument?: () => document_d_exports.Document | null;
|
|
76
|
+
setCanonicalMode?: HiddenEditorApi["setCanonicalMode"];
|
|
77
|
+
resolveCanonicalRevisions?: HiddenEditorApi["resolveCanonicalRevisions"];
|
|
78
|
+
executeCanonicalCommand?: (command: Command) => boolean | undefined;
|
|
79
|
+
getCanonicalHistory?: () => Pick<HiddenEditorApi, "undo" | "redo" | "canUndo" | "canRedo"> | null;
|
|
80
|
+
canonicalOperations?: Pick<HiddenEditorApi, "updateCanonicalInputLifecycle" | "applyCanonicalOperations" | "getCanonicalStoryProjection" | "replaceCanonicalStoryText" | "applyCanonicalStoryHistory" | "getCanonicalStorySelection">;
|
|
46
81
|
isDestroying: () => boolean;
|
|
47
82
|
ensureView: () => void;
|
|
48
83
|
isViewRequested: () => boolean;
|