@stll/folio-core 0.11.0 → 0.12.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 +602 -50
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +67 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +237 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +34 -0
- package/dist/compat/eigenpal.js +1 -1
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +1 -1
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +32 -11
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +16 -1
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +35 -3
- package/dist/document-operations.js +161 -3
- package/dist/docx/blockContentParser.js +2 -2
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/conformance.js +8 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/ensureParaIds.js +1 -1
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +63 -58
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +1 -1
- package/dist/docx/runParser.js +4 -2
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +1 -1
- package/dist/docx/selectiveSave.js +1 -1
- package/dist/docx/serializer/commentSerializer.js +1 -2
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +7 -1
- package/dist/docx/serializer/runSerializer.js +4 -2
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +17 -13
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +11 -1
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.js +18 -7
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +43 -28
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +21 -5
- package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
- package/dist/layout-engine/measure/measureBlocks.js +6 -3
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
- package/dist/layout-engine/measure/measureHelpers.js +3 -3
- package/dist/layout-engine/measure/measureParagraph.js +69 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +26 -13
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +56 -12
- package/dist/layout-painter/renderParagraph.js +22 -6
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +5 -2
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +154 -18
- package/dist/prosemirror/commands/comments.d.ts +3 -3
- package/dist/prosemirror/commands/comments.js +248 -12
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/index.js +1 -1
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +347 -98
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
- package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +1 -1
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +5 -1
- package/dist/prosemirror/schema/nodes.d.ts +64 -7
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/server.d.ts +6 -2
- package/dist/server.js +5 -1
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.js +22 -4
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { CONTENT_CONTROL_WIDGET_EVENT_NAME, dispatchDatePick, dispatchDropdownPick, handleContentControlWidgetClick } from "../prosemirror/plugins/contentControlWidgets.js";
|
|
2
|
+
import { Subscribable } from "../managers/Subscribable.js";
|
|
3
|
+
//#region src/controller/contentControlWidgetController.ts
|
|
4
|
+
const CLOSED_SNAPSHOT = { status: "closed" };
|
|
5
|
+
const PICKER_GAP_PX = 4;
|
|
6
|
+
const isListItem = (value) => {
|
|
7
|
+
if (typeof value !== "object" || value === null) return false;
|
|
8
|
+
if (!("displayText" in value) || !("value" in value)) return false;
|
|
9
|
+
return typeof value.displayText === "string" && typeof value.value === "string";
|
|
10
|
+
};
|
|
11
|
+
const parseContentControlListItems = (raw) => {
|
|
12
|
+
if (!raw) return [];
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(raw);
|
|
16
|
+
} catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(parsed)) return [];
|
|
20
|
+
return parsed.filter(isListItem);
|
|
21
|
+
};
|
|
22
|
+
const pickerPosition = (event) => {
|
|
23
|
+
const rect = event.anchor.getBoundingClientRect();
|
|
24
|
+
return {
|
|
25
|
+
x: rect.left,
|
|
26
|
+
y: rect.bottom + PICKER_GAP_PX
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const isContentControlWidgetEvent = (value) => {
|
|
30
|
+
if (typeof value !== "object" || value === null || !("kind" in value)) return false;
|
|
31
|
+
if (!("tag" in value) || typeof value.tag !== "string" || !("pmPos" in value) || typeof value.pmPos !== "number" || !("anchor" in value) || typeof value.anchor !== "object" || value.anchor === null || !("getBoundingClientRect" in value.anchor) || typeof value.anchor.getBoundingClientRect !== "function") return false;
|
|
32
|
+
if (value.kind === "dropdownOpen") return "sdtType" in value && (value.sdtType === "dropdown" || value.sdtType === "comboBox") && "listItemsJson" in value && (value.listItemsJson === void 0 || typeof value.listItemsJson === "string");
|
|
33
|
+
if (value.kind === "datePick") return "currentValue" in value && (value.currentValue === void 0 || typeof value.currentValue === "string");
|
|
34
|
+
return value.kind === "refused" && "sdtType" in value && "error" in value;
|
|
35
|
+
};
|
|
36
|
+
var ContentControlWidgetController = class extends Subscribable {
|
|
37
|
+
options;
|
|
38
|
+
view = null;
|
|
39
|
+
viewDom = null;
|
|
40
|
+
eventRoot = null;
|
|
41
|
+
ownerWindow = null;
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
super(CLOSED_SNAPSHOT);
|
|
44
|
+
this.options = options;
|
|
45
|
+
}
|
|
46
|
+
bind(view, eventRoot = null) {
|
|
47
|
+
if (this.view === view && this.eventRoot === eventRoot) return;
|
|
48
|
+
this.unbindView();
|
|
49
|
+
this.view = view;
|
|
50
|
+
this.viewDom = view?.dom ?? null;
|
|
51
|
+
this.eventRoot = eventRoot;
|
|
52
|
+
this.viewDom?.addEventListener(CONTENT_CONTROL_WIDGET_EVENT_NAME, this.onDomEvent);
|
|
53
|
+
this.eventRoot?.addEventListener("click", this.onPaintedClick);
|
|
54
|
+
}
|
|
55
|
+
handleWidgetEvent(event) {
|
|
56
|
+
if (event.kind === "refused") {
|
|
57
|
+
this.close();
|
|
58
|
+
this.options.onRefused?.(event);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (event.kind === "dropdownOpen") {
|
|
62
|
+
this.open({
|
|
63
|
+
status: "dropdown",
|
|
64
|
+
items: parseContentControlListItems(event.listItemsJson),
|
|
65
|
+
pmPos: event.pmPos,
|
|
66
|
+
position: pickerPosition(event)
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.open({
|
|
71
|
+
status: "date",
|
|
72
|
+
currentValue: event.currentValue ?? null,
|
|
73
|
+
pmPos: event.pmPos,
|
|
74
|
+
position: pickerPosition(event)
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
pickDropdown(value) {
|
|
78
|
+
const snapshot = this.getSnapshot();
|
|
79
|
+
if (snapshot.status !== "dropdown" || !this.view) return false;
|
|
80
|
+
const changed = dispatchDropdownPick(this.view, snapshot.pmPos, value);
|
|
81
|
+
this.close();
|
|
82
|
+
return changed;
|
|
83
|
+
}
|
|
84
|
+
pickDate(value) {
|
|
85
|
+
const snapshot = this.getSnapshot();
|
|
86
|
+
if (snapshot.status !== "date" || !this.view || value.length === 0) return false;
|
|
87
|
+
const changed = dispatchDatePick(this.view, snapshot.pmPos, value);
|
|
88
|
+
this.close();
|
|
89
|
+
return changed;
|
|
90
|
+
}
|
|
91
|
+
close() {
|
|
92
|
+
this.removeDismissListeners();
|
|
93
|
+
if (this.getSnapshot().status !== "closed") this.setSnapshot(CLOSED_SNAPSHOT);
|
|
94
|
+
}
|
|
95
|
+
destroy() {
|
|
96
|
+
this.unbindView();
|
|
97
|
+
this.view = null;
|
|
98
|
+
}
|
|
99
|
+
onDomEvent = (event) => {
|
|
100
|
+
if (!("detail" in event)) return;
|
|
101
|
+
const detail = event.detail;
|
|
102
|
+
if (isContentControlWidgetEvent(detail)) this.handleWidgetEvent(detail);
|
|
103
|
+
};
|
|
104
|
+
onPaintedClick = (event) => {
|
|
105
|
+
if (!this.view || !isPaintedBodyClick(event.target)) return;
|
|
106
|
+
handleContentControlWidgetClick({
|
|
107
|
+
view: this.view,
|
|
108
|
+
event,
|
|
109
|
+
onEvent: (widgetEvent) => this.handleWidgetEvent(widgetEvent)
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
onKeyDown = (event) => {
|
|
113
|
+
if (event.key === "Escape") this.close();
|
|
114
|
+
};
|
|
115
|
+
onScroll = () => this.close();
|
|
116
|
+
open(snapshot) {
|
|
117
|
+
this.removeDismissListeners();
|
|
118
|
+
this.ownerWindow = this.viewDom?.ownerDocument.defaultView ?? null;
|
|
119
|
+
this.ownerWindow?.addEventListener("keydown", this.onKeyDown);
|
|
120
|
+
this.ownerWindow?.addEventListener("scroll", this.onScroll, true);
|
|
121
|
+
this.setSnapshot(snapshot);
|
|
122
|
+
}
|
|
123
|
+
removeDismissListeners() {
|
|
124
|
+
this.ownerWindow?.removeEventListener("keydown", this.onKeyDown);
|
|
125
|
+
this.ownerWindow?.removeEventListener("scroll", this.onScroll, true);
|
|
126
|
+
this.ownerWindow = null;
|
|
127
|
+
}
|
|
128
|
+
unbindView() {
|
|
129
|
+
this.close();
|
|
130
|
+
this.viewDom?.removeEventListener(CONTENT_CONTROL_WIDGET_EVENT_NAME, this.onDomEvent);
|
|
131
|
+
this.eventRoot?.removeEventListener("click", this.onPaintedClick);
|
|
132
|
+
this.viewDom = null;
|
|
133
|
+
this.eventRoot = null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const isPaintedBodyClick = (target) => {
|
|
137
|
+
if (typeof target !== "object" || target === null || !("closest" in target) || typeof target.closest !== "function") return false;
|
|
138
|
+
return target.closest(".layout-page-content") !== null;
|
|
139
|
+
};
|
|
140
|
+
//#endregion
|
|
141
|
+
export { ContentControlWidgetController, parseContentControlListItems };
|
|
@@ -11,6 +11,8 @@ function documentFontsAreLoaded() {
|
|
|
11
11
|
const INITIAL_LAYOUT_FONT_TIMEOUT_MS = 2e3;
|
|
12
12
|
const DEFAULT_LAYOUT_FONT_FAMILY = "Calibri";
|
|
13
13
|
const OFFICE_FONT_FAMILY_MAP = {
|
|
14
|
+
Aptos: "Lato",
|
|
15
|
+
"Aptos Display": "Lato",
|
|
14
16
|
Arial: "Arimo",
|
|
15
17
|
Calibri: "Carlito",
|
|
16
18
|
Cambria: "Caladea",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { clearHeaderFooterVerbatimXml } from "../docx/headerFooterVerbatim.js";
|
|
2
1
|
import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
|
|
3
2
|
import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
|
|
4
3
|
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
@@ -7,6 +6,7 @@ import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdA
|
|
|
7
6
|
import { createStarterKit } from "../prosemirror/extensions/StarterKit.js";
|
|
8
7
|
import { schema } from "../prosemirror/schema/index.js";
|
|
9
8
|
import { headerFooterToProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
9
|
+
import { clearHeaderFooterVerbatimXml } from "../docx/headerFooterVerbatim.js";
|
|
10
10
|
import { EditorState } from "prosemirror-state";
|
|
11
11
|
import { EditorView } from "prosemirror-view";
|
|
12
12
|
//#region src/controller/headerFooterEditorManager.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fromProseDoc } from "../prosemirror/conversion/fromProseDoc.js";
|
|
2
|
-
import { NodeSelection, Selection, TextSelection } from "prosemirror-state";
|
|
3
|
-
import { redo, undo } from "prosemirror-history";
|
|
4
2
|
import { CellSelection } from "prosemirror-tables";
|
|
3
|
+
import { redo, undo } from "prosemirror-history";
|
|
4
|
+
import { NodeSelection, Selection, TextSelection } from "prosemirror-state";
|
|
5
5
|
//#region src/controller/hiddenEditorApi.ts
|
|
6
6
|
/**
|
|
7
7
|
* Hidden-editor imperative API
|
|
@@ -72,11 +72,22 @@ type HiddenEditorManagerDeps = {
|
|
|
72
72
|
onTransaction: (transaction: Transaction, newState: EditorState) => void;
|
|
73
73
|
onSelectionChange: (state: EditorState) => void;
|
|
74
74
|
onKeyDown: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
75
|
+
onCopy?: () => void;
|
|
76
|
+
onCut?: () => void;
|
|
77
|
+
onPaste?: () => void;
|
|
75
78
|
onReadOnlyEditAttempt: () => void;
|
|
76
79
|
onEditorViewReady: (view: EditorView) => void;
|
|
77
80
|
onEditorViewDestroy: () => void;
|
|
78
81
|
onRemoteSelectionsChange: (selections: HiddenProseMirrorRemoteSelection[]) => void;
|
|
79
82
|
};
|
|
83
|
+
type PreventableDomEvent = {
|
|
84
|
+
preventDefault: () => void;
|
|
85
|
+
};
|
|
86
|
+
declare const createHiddenEditorClipboardHandlers: (deps: Pick<HiddenEditorManagerDeps, "getReadOnly" | "onCopy" | "onCut" | "onPaste" | "onReadOnlyEditAttempt">) => {
|
|
87
|
+
copy: () => boolean;
|
|
88
|
+
cut: (_view: unknown, event: PreventableDomEvent) => boolean;
|
|
89
|
+
paste: (_view: unknown, event: PreventableDomEvent) => boolean;
|
|
90
|
+
};
|
|
80
91
|
type HiddenEditorManager = {
|
|
81
92
|
/** Request the view (sets the requested flag, then attempts creation). */ensureView: () => void; /** Re-attempt a previously-requested-but-deferred creation (no-op otherwise). */
|
|
82
93
|
retryViewCreation: () => void;
|
|
@@ -90,4 +101,4 @@ type HiddenEditorManager = {
|
|
|
90
101
|
};
|
|
91
102
|
declare const createHiddenEditorManager: (deps: HiddenEditorManagerDeps) => HiddenEditorManager;
|
|
92
103
|
//#endregion
|
|
93
|
-
export { CollaborationModules, CreateHiddenEditorStateOptions, HiddenEditorManager, HiddenEditorManagerDeps, HiddenProseMirrorCollaboration, HiddenProseMirrorRemoteSelection, collectRemoteSelections, createHiddenEditorManager, createHiddenEditorState };
|
|
104
|
+
export { CollaborationModules, CreateHiddenEditorStateOptions, HiddenEditorManager, HiddenEditorManagerDeps, HiddenProseMirrorCollaboration, HiddenProseMirrorRemoteSelection, collectRemoteSelections, createHiddenEditorClipboardHandlers, createHiddenEditorManager, createHiddenEditorState };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
2
2
|
import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
3
|
-
import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
3
|
+
import { ensureParaIdsInDoc, ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
4
4
|
import { schema } from "../prosemirror/schema/index.js";
|
|
5
5
|
import { createEmptyDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
6
6
|
import { createHiddenEditorApi } from "./hiddenEditorApi.js";
|
|
@@ -23,6 +23,7 @@ import { EditorView } from "prosemirror-view";
|
|
|
23
23
|
* *when* to act) and drives this manager through the methods below.
|
|
24
24
|
*/
|
|
25
25
|
const normalizeSeedState = (state) => ensureBaseDirectionInState(ensureParaIdsInState(state));
|
|
26
|
+
const normalizeLocalSeedState = (state) => ensureBaseDirectionInState(state);
|
|
26
27
|
const isObjectRecord = (value) => typeof value === "object" && value !== null;
|
|
27
28
|
const isYSyncState = (value, yjs) => {
|
|
28
29
|
if (!isObjectRecord(value)) return false;
|
|
@@ -92,8 +93,9 @@ function createHiddenEditorState(options) {
|
|
|
92
93
|
if (document) {
|
|
93
94
|
const startedAt = performance.now();
|
|
94
95
|
localDoc = styles === void 0 || styles === null ? toProseDoc(document) : toProseDoc(document, { styles });
|
|
96
|
+
localDoc = ensureParaIdsInDoc(localDoc);
|
|
95
97
|
recordHiddenEditorPhase(reason, "to-prose-doc", performance.now() - startedAt);
|
|
96
|
-
}
|
|
98
|
+
} else localDoc = ensureParaIdsInDoc(localDoc);
|
|
97
99
|
const styleResolverPlugin = createDocumentStylesPlugin(styles ?? document?.package.styles);
|
|
98
100
|
const plugins = [
|
|
99
101
|
...externalPlugins,
|
|
@@ -103,7 +105,7 @@ function createHiddenEditorState(options) {
|
|
|
103
105
|
if (collaboration) {
|
|
104
106
|
if (!collaborationModules) panic("Collaboration modules must be loaded before creating collaborative editor state.");
|
|
105
107
|
if (collaboration.shouldSeed && collaboration.yXmlFragment.length === 0) {
|
|
106
|
-
const seedState =
|
|
108
|
+
const seedState = normalizeLocalSeedState(EditorState.create({
|
|
107
109
|
doc: localDoc,
|
|
108
110
|
schema: activeSchema,
|
|
109
111
|
plugins
|
|
@@ -131,7 +133,7 @@ function createHiddenEditorState(options) {
|
|
|
131
133
|
return state;
|
|
132
134
|
}
|
|
133
135
|
const startedAt = performance.now();
|
|
134
|
-
const state =
|
|
136
|
+
const state = normalizeLocalSeedState(EditorState.create({
|
|
135
137
|
doc: localDoc,
|
|
136
138
|
schema: activeSchema,
|
|
137
139
|
plugins
|
|
@@ -149,6 +151,30 @@ function syncHiddenEditorAccessibility(view, readOnly) {
|
|
|
149
151
|
if (!dom.hasAttribute("tabindex")) dom.tabIndex = 0;
|
|
150
152
|
dom.setAttribute("aria-readonly", readOnly ? "true" : "false");
|
|
151
153
|
}
|
|
154
|
+
const createHiddenEditorClipboardHandlers = (deps) => ({
|
|
155
|
+
copy: () => {
|
|
156
|
+
deps.onCopy?.();
|
|
157
|
+
return false;
|
|
158
|
+
},
|
|
159
|
+
cut: (_view, event) => {
|
|
160
|
+
if (!deps.getReadOnly()) {
|
|
161
|
+
deps.onCut?.();
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
deps.onReadOnlyEditAttempt();
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
return true;
|
|
167
|
+
},
|
|
168
|
+
paste: (_view, event) => {
|
|
169
|
+
if (!deps.getReadOnly()) {
|
|
170
|
+
deps.onPaste?.();
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
deps.onReadOnlyEditAttempt();
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
152
178
|
const createHiddenEditorManager = (deps) => {
|
|
153
179
|
let view = null;
|
|
154
180
|
let isDestroying = false;
|
|
@@ -207,18 +233,13 @@ const createHiddenEditorManager = (deps) => {
|
|
|
207
233
|
handleDOMEvents: {
|
|
208
234
|
focus: () => false,
|
|
209
235
|
blur: () => false,
|
|
236
|
+
...createHiddenEditorClipboardHandlers(deps),
|
|
210
237
|
beforeinput: (_view, event) => {
|
|
211
238
|
if (!deps.getReadOnly()) return false;
|
|
212
239
|
deps.onReadOnlyEditAttempt();
|
|
213
240
|
event.preventDefault();
|
|
214
241
|
return true;
|
|
215
242
|
},
|
|
216
|
-
paste: (_view, event) => {
|
|
217
|
-
if (!deps.getReadOnly()) return false;
|
|
218
|
-
deps.onReadOnlyEditAttempt();
|
|
219
|
-
event.preventDefault();
|
|
220
|
-
return true;
|
|
221
|
-
},
|
|
222
243
|
drop: (_view, event) => {
|
|
223
244
|
if (!deps.getReadOnly()) return false;
|
|
224
245
|
deps.onReadOnlyEditAttempt();
|
|
@@ -305,4 +326,4 @@ const createHiddenEditorManager = (deps) => {
|
|
|
305
326
|
};
|
|
306
327
|
};
|
|
307
328
|
//#endregion
|
|
308
|
-
export { collectRemoteSelections, createHiddenEditorManager, createHiddenEditorState };
|
|
329
|
+
export { collectRemoteSelections, createHiddenEditorClipboardHandlers, createHiddenEditorManager, createHiddenEditorState };
|
|
@@ -31,6 +31,8 @@ type LayoutPipelineDeps<THfPMs> = {
|
|
|
31
31
|
};
|
|
32
32
|
margins: PageMargins;
|
|
33
33
|
pageGap: number;
|
|
34
|
+
showMarginGuides: boolean;
|
|
35
|
+
marginGuideColor: string | undefined;
|
|
34
36
|
syncCoordinator: LayoutSelectionGate;
|
|
35
37
|
headerContent: document_d_exports.HeaderFooter | null | undefined;
|
|
36
38
|
footerContent: document_d_exports.HeaderFooter | null | undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "../layout-engine/types.js";
|
|
2
|
+
import { resolveEffectiveParagraphSpacingTree } from "../layout-engine/paragraphSpacing.js";
|
|
2
3
|
import { renderPages } from "../layout-painter/renderPage.js";
|
|
3
4
|
import { templatePreviewValuesKey } from "../prosemirror/plugins/templatePreviewValues.js";
|
|
5
|
+
import { resolveDocumentGridLinePitch } from "../docx/documentGrid.js";
|
|
4
6
|
import { getMargins, getPageSize, twipsToPixels } from "../paged-layout/sectionGeometry.js";
|
|
5
7
|
import { getColumns } from "../layout-bridge/sectionColumns.js";
|
|
6
8
|
import { toFlowBlocks } from "../layout-bridge/convert/toFlowBlocks.js";
|
|
@@ -14,6 +16,7 @@ import { installCanvasMeasureProvider } from "../layout-engine/measure/measureCo
|
|
|
14
16
|
import { buildFootnoteContentMap, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, remapNoteMarkerText } from "../layout-bridge/convert/footnoteLayout.js";
|
|
15
17
|
import { applyTemplatePreviewToBlocks } from "../layout-bridge/convert/templatePreviewFlow.js";
|
|
16
18
|
import { layoutDocument } from "../layout-engine/index.js";
|
|
19
|
+
import { resolveJustificationCompatibility } from "../layout-engine/justificationCompatibility.js";
|
|
17
20
|
import { measureBlocks, measureSingleBlockWithoutFloatingZones } from "../layout-engine/measure/measureBlocks.js";
|
|
18
21
|
import { tryBuildIncrementalMeasures } from "../paged-layout/incrementalMeasure.js";
|
|
19
22
|
import { computePerBlockMeasureInputs } from "../paged-layout/sectionBlockWidths.js";
|
|
@@ -56,7 +59,7 @@ function bodyBlocksClearSectionHeaderFooter(blocks, { authoredMargins, sectionHe
|
|
|
56
59
|
return changed ? nextBlocks : blocks;
|
|
57
60
|
}
|
|
58
61
|
function runLayoutPipeline(deps, state, options = {}) {
|
|
59
|
-
const { contentWidth, columns, pageSize, margins, pageGap, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, documentFontsAreLoaded, buildFootnoteRenderItems, describeInvalidHighlightMarks, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES } = deps;
|
|
62
|
+
const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, documentFontsAreLoaded, buildFootnoteRenderItems, describeInvalidHighlightMarks, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES } = deps;
|
|
60
63
|
let outcome = {};
|
|
61
64
|
let pendingArtifacts;
|
|
62
65
|
let pendingTemplatePreview;
|
|
@@ -75,12 +78,16 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
75
78
|
if (defaultTabStop !== void 0) flowOpts.defaultTabStopTwips = defaultTabStop;
|
|
76
79
|
if (document?.package.settings?.lineBreakRules) flowOpts.lineBreakRules = document.package.settings.lineBreakRules;
|
|
77
80
|
const documentSettings = document?.package.settings;
|
|
81
|
+
const justificationCompatibility = resolveJustificationCompatibility(documentSettings?.compatibilityMode);
|
|
82
|
+
if (justificationCompatibility) flowOpts.justificationCompatibility = justificationCompatibility;
|
|
78
83
|
if (documentSettings?.autoHyphenation === true) flowOpts.automaticHyphenation = {
|
|
79
84
|
enabled: true,
|
|
80
85
|
...documentSettings.doNotHyphenateCaps !== void 0 ? { doNotHyphenateCaps: documentSettings.doNotHyphenateCaps } : {},
|
|
81
86
|
...documentSettings.consecutiveHyphenLimit !== void 0 ? { consecutiveLineLimit: documentSettings.consecutiveHyphenLimit } : {},
|
|
82
87
|
...documentSettings.hyphenationZoneTwips !== void 0 ? { hyphenationZoneTwips: documentSettings.hyphenationZoneTwips } : {}
|
|
83
88
|
};
|
|
89
|
+
const finalSectionDocumentGridLinePitchTwips = resolveDocumentGridLinePitch(document?.package.document.sections?.at(-1)?.properties.docGrid);
|
|
90
|
+
if (finalSectionDocumentGridLinePitchTwips !== void 0) flowOpts.finalSectionDocumentGridLinePitchTwips = finalSectionDocumentGridLinePitchTwips;
|
|
84
91
|
let newBlocks = toFlowBlocks(state.doc, flowOpts);
|
|
85
92
|
const previewState = templatePreviewValuesKey.getState(state);
|
|
86
93
|
const previewEntries = previewState?.entries ?? EMPTY_TEMPLATE_PREVIEW_ENTRIES;
|
|
@@ -127,6 +134,7 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
127
134
|
measureBlocks: hfMeasureBlocks,
|
|
128
135
|
...defaultTabStop !== void 0 ? { defaultTabStopTwips: defaultTabStop } : {},
|
|
129
136
|
...flowOpts.lineBreakRules ? { lineBreakRules: flowOpts.lineBreakRules } : {},
|
|
137
|
+
...flowOpts.justificationCompatibility ? { justificationCompatibility: flowOpts.justificationCompatibility } : {},
|
|
130
138
|
...flowOpts.automaticHyphenation ? { automaticHyphenation: flowOpts.automaticHyphenation } : {}
|
|
131
139
|
};
|
|
132
140
|
};
|
|
@@ -209,6 +217,8 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
209
217
|
marginRight: blockMeasureInputs.marginRights,
|
|
210
218
|
marginBottom: blockMeasureInputs.marginBottoms
|
|
211
219
|
});
|
|
220
|
+
newBlocks = resolveEffectiveParagraphSpacingTree(newBlocks);
|
|
221
|
+
outcome.blocks = newBlocks;
|
|
212
222
|
pendingArtifacts = {
|
|
213
223
|
blocks: newBlocks,
|
|
214
224
|
blockWidths,
|
|
@@ -256,6 +266,7 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
256
266
|
if (_theme !== void 0) footnoteOptions.theme = _theme;
|
|
257
267
|
if (defaultTabStop !== void 0) footnoteOptions.defaultTabStopTwips = defaultTabStop;
|
|
258
268
|
if (flowOpts.lineBreakRules) footnoteOptions.lineBreakRules = flowOpts.lineBreakRules;
|
|
269
|
+
if (flowOpts.justificationCompatibility) footnoteOptions.justificationCompatibility = flowOpts.justificationCompatibility;
|
|
259
270
|
if (flowOpts.automaticHyphenation) footnoteOptions.automaticHyphenation = flowOpts.automaticHyphenation;
|
|
260
271
|
return footnoteOptions;
|
|
261
272
|
})());
|
|
@@ -372,6 +383,10 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
372
383
|
blockLookup,
|
|
373
384
|
titlePg: hasTitlePg
|
|
374
385
|
};
|
|
386
|
+
if (showMarginGuides) {
|
|
387
|
+
renderOpts.showMarginGuides = true;
|
|
388
|
+
if (marginGuideColor !== void 0) renderOpts.marginGuideColor = marginGuideColor;
|
|
389
|
+
}
|
|
375
390
|
if (headerContentForRender) renderOpts.headerContent = headerContentForRender;
|
|
376
391
|
if (footerContentForRender) renderOpts.footerContent = footerContentForRender;
|
|
377
392
|
if (firstPageHeaderForRender) renderOpts.firstPageHeaderContent = firstPageHeaderForRender;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { Plugin } from "prosemirror-state";
|
|
3
|
+
import { EditorView } from "prosemirror-view";
|
|
4
|
+
|
|
5
|
+
//#region src/controller/noteEditorManager.d.ts
|
|
6
|
+
type NoteStoryKind = "footnote" | "endnote";
|
|
7
|
+
type NoteStoryKey = {
|
|
8
|
+
kind: NoteStoryKind;
|
|
9
|
+
noteId: number;
|
|
10
|
+
};
|
|
11
|
+
type NoteEditorTransaction = NoteStoryKey & {
|
|
12
|
+
docChanged: boolean;
|
|
13
|
+
selectionChanged: boolean;
|
|
14
|
+
view: EditorView;
|
|
15
|
+
};
|
|
16
|
+
type NoteEditorManagerDeps = {
|
|
17
|
+
getDocument: () => document_d_exports.Document | null;
|
|
18
|
+
getHost: () => HTMLElement | null;
|
|
19
|
+
getPlugins?: (() => Plugin[]) | undefined;
|
|
20
|
+
getStyles: () => document_d_exports.StyleDefinitions | null | undefined;
|
|
21
|
+
getTheme: () => document_d_exports.Theme | null | undefined;
|
|
22
|
+
onTransaction?: ((transaction: NoteEditorTransaction) => void) | undefined;
|
|
23
|
+
};
|
|
24
|
+
type NoteEditorManager = {
|
|
25
|
+
activate: (story: NoteStoryKey | null) => EditorView | null;
|
|
26
|
+
destroy: () => void;
|
|
27
|
+
getActive: () => NoteStoryKey | null;
|
|
28
|
+
getView: (story: NoteStoryKey) => EditorView | null;
|
|
29
|
+
listStories: () => NoteStoryKey[];
|
|
30
|
+
snapshotDocument: (document: document_d_exports.Document) => document_d_exports.Document;
|
|
31
|
+
sync: () => void;
|
|
32
|
+
};
|
|
33
|
+
declare const enumerateDocumentNoteStories: (document: document_d_exports.Document | null) => NoteStoryKey[];
|
|
34
|
+
declare const createNoteEditorManager: (deps: NoteEditorManagerDeps) => NoteEditorManager;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { NoteEditorManager, NoteEditorManagerDeps, NoteEditorTransaction, NoteStoryKey, NoteStoryKind, createNoteEditorManager, enumerateDocumentNoteStories };
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
|
|
2
|
+
import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
|
|
3
|
+
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
4
|
+
import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
5
|
+
import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
6
|
+
import { createStarterKit } from "../prosemirror/extensions/StarterKit.js";
|
|
7
|
+
import { schema } from "../prosemirror/schema/index.js";
|
|
8
|
+
import { footnoteToProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
9
|
+
import { isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
10
|
+
import { EditorState } from "prosemirror-state";
|
|
11
|
+
import { EditorView } from "prosemirror-view";
|
|
12
|
+
//#region src/controller/noteEditorManager.ts
|
|
13
|
+
/** Framework-neutral lifecycle owner for editable footnote and endnote stories. */
|
|
14
|
+
const EMPTY_PLUGINS = [];
|
|
15
|
+
const storyMapKey = ({ kind, noteId }) => `${kind}:${String(noteId)}`;
|
|
16
|
+
const isNormalFootnote = (note) => !isSeparatorFootnote(note);
|
|
17
|
+
const isNormalEndnote = (note) => !isSeparatorEndnote(note);
|
|
18
|
+
const enumerateDocumentNoteStories = (document) => [...(document?.package.footnotes ?? []).filter(isNormalFootnote).map(({ id }) => ({
|
|
19
|
+
kind: "footnote",
|
|
20
|
+
noteId: id
|
|
21
|
+
})), ...(document?.package.endnotes ?? []).filter(isNormalEndnote).map(({ id }) => ({
|
|
22
|
+
kind: "endnote",
|
|
23
|
+
noteId: id
|
|
24
|
+
}))];
|
|
25
|
+
const resolveNote = (document, story) => {
|
|
26
|
+
return (story.kind === "footnote" ? document?.package.footnotes : document?.package.endnotes)?.find(({ id }) => id === story.noteId) ?? null;
|
|
27
|
+
};
|
|
28
|
+
const noteToProseDocument = (note, styles, theme) => {
|
|
29
|
+
const options = {};
|
|
30
|
+
if (styles) options.styles = styles;
|
|
31
|
+
if (theme !== void 0) options.theme = theme;
|
|
32
|
+
return footnoteToProseDoc(note.content, options);
|
|
33
|
+
};
|
|
34
|
+
const buildInitialState = (document, styles, manager, externalPlugins) => {
|
|
35
|
+
return ensureBaseDirectionInState(ensureParaIdsInState(EditorState.create({
|
|
36
|
+
doc: document,
|
|
37
|
+
schema,
|
|
38
|
+
plugins: [
|
|
39
|
+
...manager.getPlugins(),
|
|
40
|
+
...externalPlugins,
|
|
41
|
+
createDocumentStylesPlugin(styles)
|
|
42
|
+
]
|
|
43
|
+
})));
|
|
44
|
+
};
|
|
45
|
+
const createNoteEditorManager = (deps) => {
|
|
46
|
+
const mounted = /* @__PURE__ */ new Map();
|
|
47
|
+
let active = null;
|
|
48
|
+
const updateVisibility = () => {
|
|
49
|
+
const activeKey = active ? storyMapKey(active) : null;
|
|
50
|
+
for (const [key, story] of mounted) story.mountNode.hidden = key !== activeKey;
|
|
51
|
+
};
|
|
52
|
+
const destroyMounted = (story) => {
|
|
53
|
+
story.view.destroy();
|
|
54
|
+
story.manager.destroy();
|
|
55
|
+
story.mountNode.remove();
|
|
56
|
+
};
|
|
57
|
+
const destroy = () => {
|
|
58
|
+
for (const story of mounted.values()) destroyMounted(story);
|
|
59
|
+
mounted.clear();
|
|
60
|
+
active = null;
|
|
61
|
+
};
|
|
62
|
+
const sync = () => {
|
|
63
|
+
const host = deps.getHost();
|
|
64
|
+
if (!host) return;
|
|
65
|
+
const document = deps.getDocument();
|
|
66
|
+
const styles = deps.getStyles();
|
|
67
|
+
const theme = deps.getTheme();
|
|
68
|
+
const externalPlugins = deps.getPlugins?.() ?? EMPTY_PLUGINS;
|
|
69
|
+
const wanted = new Map(enumerateDocumentNoteStories(document).map((story) => [storyMapKey(story), story]));
|
|
70
|
+
const activeKey = active ? storyMapKey(active) : null;
|
|
71
|
+
for (const [key, story] of mounted) {
|
|
72
|
+
if (wanted.has(key)) continue;
|
|
73
|
+
destroyMounted(story);
|
|
74
|
+
mounted.delete(key);
|
|
75
|
+
}
|
|
76
|
+
for (const [key, storyKey] of wanted) {
|
|
77
|
+
if (!mounted.has(key) && key !== activeKey) continue;
|
|
78
|
+
const note = resolveNote(document, storyKey);
|
|
79
|
+
if (!note) continue;
|
|
80
|
+
const existing = mounted.get(key);
|
|
81
|
+
if (existing) {
|
|
82
|
+
if (existing.mountNode.parentElement !== host) host.append(existing.mountNode);
|
|
83
|
+
const nextProseDocument = noteToProseDocument(note, styles, theme);
|
|
84
|
+
const contextIsCurrent = existing.appliedStyles === styles && existing.appliedTheme === theme && existing.appliedPlugins === externalPlugins;
|
|
85
|
+
if ((existing.appliedNote === note && existing.appliedContent === note.content || existing.appliedProseDocument.eq(nextProseDocument)) && contextIsCurrent) {
|
|
86
|
+
existing.appliedNote = note;
|
|
87
|
+
existing.appliedContent = note.content;
|
|
88
|
+
existing.appliedProseDocument = nextProseDocument;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
existing.view.updateState(buildInitialState(nextProseDocument, styles, existing.manager, externalPlugins));
|
|
92
|
+
existing.appliedNote = note;
|
|
93
|
+
existing.appliedContent = note.content;
|
|
94
|
+
existing.appliedStyles = styles;
|
|
95
|
+
existing.appliedTheme = theme;
|
|
96
|
+
existing.appliedPlugins = externalPlugins;
|
|
97
|
+
existing.appliedProseDocument = nextProseDocument;
|
|
98
|
+
existing.dirty = false;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const manager = new ExtensionManager(createStarterKit());
|
|
102
|
+
manager.buildSchema();
|
|
103
|
+
manager.initializeRuntime();
|
|
104
|
+
const mountNode = host.ownerDocument.createElement("div");
|
|
105
|
+
mountNode.dataset["noteKind"] = storyKey.kind;
|
|
106
|
+
mountNode.dataset["noteId"] = String(storyKey.noteId);
|
|
107
|
+
host.append(mountNode);
|
|
108
|
+
const proseDocument = noteToProseDocument(note, styles, theme);
|
|
109
|
+
const view = new EditorView(mountNode, {
|
|
110
|
+
state: buildInitialState(proseDocument, styles, manager, externalPlugins),
|
|
111
|
+
dispatchTransaction(transaction) {
|
|
112
|
+
view.updateState(view.state.apply(transaction));
|
|
113
|
+
const mountedStory = mounted.get(key);
|
|
114
|
+
if (mountedStory && transaction.docChanged) mountedStory.dirty = true;
|
|
115
|
+
deps.onTransaction?.({
|
|
116
|
+
...storyKey,
|
|
117
|
+
docChanged: transaction.docChanged,
|
|
118
|
+
selectionChanged: transaction.selectionSet,
|
|
119
|
+
view
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
mounted.set(key, {
|
|
124
|
+
appliedContent: note.content,
|
|
125
|
+
appliedNote: note,
|
|
126
|
+
appliedPlugins: externalPlugins,
|
|
127
|
+
appliedProseDocument: proseDocument,
|
|
128
|
+
appliedStyles: styles,
|
|
129
|
+
appliedTheme: theme,
|
|
130
|
+
dirty: false,
|
|
131
|
+
manager,
|
|
132
|
+
mountNode,
|
|
133
|
+
note: storyKey,
|
|
134
|
+
view
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (active && !wanted.has(storyMapKey(active))) active = null;
|
|
138
|
+
updateVisibility();
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
activate: (story) => {
|
|
142
|
+
active = story;
|
|
143
|
+
sync();
|
|
144
|
+
if (!story) return null;
|
|
145
|
+
const view = mounted.get(storyMapKey(story))?.view ?? null;
|
|
146
|
+
if (view) return view;
|
|
147
|
+
active = null;
|
|
148
|
+
updateVisibility();
|
|
149
|
+
return null;
|
|
150
|
+
},
|
|
151
|
+
destroy,
|
|
152
|
+
getActive: () => active,
|
|
153
|
+
getView: (story) => mounted.get(storyMapKey(story))?.view ?? null,
|
|
154
|
+
listStories: () => enumerateDocumentNoteStories(deps.getDocument()),
|
|
155
|
+
snapshotDocument: (document) => {
|
|
156
|
+
let footnotes = document.package.footnotes;
|
|
157
|
+
let endnotes = document.package.endnotes;
|
|
158
|
+
let footnotesChanged = false;
|
|
159
|
+
let endnotesChanged = false;
|
|
160
|
+
for (const story of mounted.values()) {
|
|
161
|
+
if (!story.dirty) continue;
|
|
162
|
+
if (story.note.kind === "footnote") {
|
|
163
|
+
const index = footnotes?.findIndex(({ id }) => id === story.note.noteId) ?? -1;
|
|
164
|
+
const current = index === -1 ? null : footnotes?.at(index);
|
|
165
|
+
if (!current || !footnotes) continue;
|
|
166
|
+
if (!footnotesChanged) {
|
|
167
|
+
footnotes = [...footnotes];
|
|
168
|
+
footnotesChanged = true;
|
|
169
|
+
}
|
|
170
|
+
const updated = {
|
|
171
|
+
...current,
|
|
172
|
+
content: proseDocToBlocks(story.view.state.doc)
|
|
173
|
+
};
|
|
174
|
+
footnotes[index] = updated;
|
|
175
|
+
story.appliedNote = updated;
|
|
176
|
+
story.appliedContent = updated.content;
|
|
177
|
+
story.appliedProseDocument = noteToProseDocument(updated, story.appliedStyles, story.appliedTheme);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const index = endnotes?.findIndex(({ id }) => id === story.note.noteId) ?? -1;
|
|
181
|
+
const current = index === -1 ? null : endnotes?.at(index);
|
|
182
|
+
if (!current || !endnotes) continue;
|
|
183
|
+
if (!endnotesChanged) {
|
|
184
|
+
endnotes = [...endnotes];
|
|
185
|
+
endnotesChanged = true;
|
|
186
|
+
}
|
|
187
|
+
const updated = {
|
|
188
|
+
...current,
|
|
189
|
+
content: proseDocToBlocks(story.view.state.doc)
|
|
190
|
+
};
|
|
191
|
+
endnotes[index] = updated;
|
|
192
|
+
story.appliedNote = updated;
|
|
193
|
+
story.appliedContent = updated.content;
|
|
194
|
+
story.appliedProseDocument = noteToProseDocument(updated, story.appliedStyles, story.appliedTheme);
|
|
195
|
+
}
|
|
196
|
+
if (!footnotesChanged && !endnotesChanged) return document;
|
|
197
|
+
return {
|
|
198
|
+
...document,
|
|
199
|
+
package: {
|
|
200
|
+
...document.package,
|
|
201
|
+
...footnotesChanged && footnotes ? { footnotes } : {},
|
|
202
|
+
...endnotesChanged && endnotes ? { endnotes } : {}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
sync
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
//#endregion
|
|
210
|
+
export { createNoteEditorManager, enumerateDocumentNoteStories };
|