@stll/folio-core 0.10.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/collaborationModules.d.ts +7 -0
- package/dist/controller/collaborationModules.js +15 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.d.ts +41 -0
- package/dist/controller/headerFooterEditorManager.js +182 -0
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +13 -2
- 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/managers/FindReplaceManager.d.ts +8 -0
- package/dist/managers/FindReplaceManager.js +18 -0
- package/dist/managers/TableSelectionManager.d.ts +6 -0
- package/dist/managers/TableSelectionManager.js +14 -0
- 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/findReplaceSelection.d.ts +6 -1
- package/dist/prosemirror/findReplaceSelection.js +57 -13
- 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/render-dom/BodySelectionOverlay.d.ts +31 -0
- package/dist/render-dom/BodySelectionOverlay.js +76 -0
- package/dist/render-dom/HeaderFooterSelectionOverlay.d.ts +28 -0
- package/dist/render-dom/HeaderFooterSelectionOverlay.js +121 -0
- package/dist/render-dom/RemoteSelectionOverlay.d.ts +30 -0
- package/dist/render-dom/RemoteSelectionOverlay.js +67 -0
- package/dist/render-dom/RenderedDomContext.d.ts +40 -0
- package/dist/render-dom/RenderedDomContext.js +126 -0
- package/dist/render-dom/resolveSidebarItemPositions.d.ts +33 -0
- package/dist/render-dom/resolveSidebarItemPositions.js +45 -0
- package/dist/server.d.ts +6 -2
- package/dist/server.js +5 -1
- package/dist/utils/findReplace.d.ts +32 -0
- package/dist/utils/findReplace.js +118 -0
- 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/headerFooter.js +24 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { expectRunPropertyChangeMarkAttrs } from "../attrs/index.js";
|
|
2
|
+
import { markStructuralChange } from "../extensions/features/ParagraphChangeTrackerExtension.js";
|
|
1
3
|
import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, sectionRejectProperties, tableCellRejectAttrPatch, tableRejectAttrPatch, tableRowRejectAttrPatch } from "./propertyChangeScope.js";
|
|
4
|
+
import { textFormattingToMarks } from "../conversion/toProseDoc.js";
|
|
5
|
+
import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
|
|
6
|
+
import { hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge } from "./tableCellMergeResolution.js";
|
|
7
|
+
import { TableMap, removeRow } from "prosemirror-tables";
|
|
2
8
|
//#region src/prosemirror/commands/comments.ts
|
|
3
9
|
/**
|
|
4
10
|
* Add a comment mark to the current selection.
|
|
@@ -36,18 +42,18 @@ function removeCommentMark(commentId) {
|
|
|
36
42
|
* Resolve a tracked change: accept or reject.
|
|
37
43
|
* - Accept: keep insertions (remove mark), delete deletions (remove text)
|
|
38
44
|
* - Reject: keep deletions (remove mark), delete insertions (remove text)
|
|
45
|
+
* - Run formatting: accept the current formatting, or restore the previous formatting
|
|
39
46
|
*
|
|
40
47
|
* Pass `revisionId` to scope the operation to one specific
|
|
41
48
|
* revision — otherwise overlapping marks for other revisions get
|
|
42
49
|
* processed too, silently consuming pending work. Without an id
|
|
43
|
-
* the operation matches every
|
|
50
|
+
* the operation matches every revision mark or property-change entry in the
|
|
44
51
|
* range (the bulk accept-all/reject-all path).
|
|
45
52
|
*/
|
|
46
53
|
function resolveChange(from, to, mode, revisionIds) {
|
|
47
54
|
return (state, dispatch) => {
|
|
48
55
|
const insertionType = state.schema.marks["insertion"];
|
|
49
56
|
const deletionType = state.schema.marks["deletion"];
|
|
50
|
-
if (!insertionType && !deletionType) return false;
|
|
51
57
|
const keepType = mode === "accept" ? insertionType : deletionType;
|
|
52
58
|
const removeType = mode === "accept" ? deletionType : insertionType;
|
|
53
59
|
const revisionSet = revisionIds === void 0 ? null : new Set(revisionIds);
|
|
@@ -56,6 +62,8 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
56
62
|
const tr = state.tr;
|
|
57
63
|
const deleteRanges = [];
|
|
58
64
|
const pPrMarkOps = [];
|
|
65
|
+
const tableRowStructuralOps = [];
|
|
66
|
+
const tableCellStructuralOps = [];
|
|
59
67
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
60
68
|
if (node.type.name === "paragraph") {
|
|
61
69
|
const op = collectPPrMarkOp(node, pos, from, to, mode, revisionSet);
|
|
@@ -95,6 +103,11 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
95
103
|
if (nextAttrs) tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
96
104
|
return true;
|
|
97
105
|
}
|
|
106
|
+
if (node.type.name === "tableRow" && rangeCoversNode(from, to, pos, node)) {
|
|
107
|
+
const op = collectTableRowStructuralOp(node, pos, mode, revisionSet);
|
|
108
|
+
if (op) tableRowStructuralOps.push(op);
|
|
109
|
+
}
|
|
110
|
+
if ((node.type.name === "tableCell" || node.type.name === "tableHeader") && rangeCoversNode(from, to, pos, node)) tableCellStructuralOps.push(...collectTableCellStructuralOps(node, pos, mode, revisionSet));
|
|
98
111
|
const tableChangeAttrName = TABLE_PROPERTY_CHANGE_ATTR_BY_NODE[node.type.name];
|
|
99
112
|
if (tableChangeAttrName !== void 0) {
|
|
100
113
|
if (rangeCoversNode(from, to, pos, node)) {
|
|
@@ -107,6 +120,16 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
107
120
|
const nodeEnd = pos + node.nodeSize;
|
|
108
121
|
const rangeFrom = Math.max(from, pos);
|
|
109
122
|
const rangeTo = Math.min(to, nodeEnd);
|
|
123
|
+
const runPropertyChangeMark = node.marks.find((mark) => mark.type.name === "runPropertyChange");
|
|
124
|
+
if (runPropertyChangeMark) resolveRunPropertyChange({
|
|
125
|
+
tr,
|
|
126
|
+
node,
|
|
127
|
+
from: rangeFrom,
|
|
128
|
+
to: rangeTo,
|
|
129
|
+
mark: runPropertyChangeMark,
|
|
130
|
+
mode,
|
|
131
|
+
revisionSet
|
|
132
|
+
});
|
|
110
133
|
if (removeType && node.marks.some((m) => m.type === removeType && matchesRevision(m))) deleteRanges.push({
|
|
111
134
|
from: rangeFrom,
|
|
112
135
|
to: rangeTo
|
|
@@ -131,11 +154,189 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
131
154
|
tr.setNodeAttribute(mappedPos, "pPrMark", null);
|
|
132
155
|
} catch {}
|
|
133
156
|
}
|
|
157
|
+
tableRowStructuralOps.sort((left, right) => right.rowPos - left.rowPos);
|
|
158
|
+
let resolvedTableRowStructure = false;
|
|
159
|
+
for (const op of tableRowStructuralOps) {
|
|
160
|
+
const mappedPos = tr.mapping.map(op.rowPos);
|
|
161
|
+
const row = tr.doc.nodeAt(mappedPos);
|
|
162
|
+
if (!row || row.type.name !== "tableRow") continue;
|
|
163
|
+
if (op.action === "clear") {
|
|
164
|
+
tr.setNodeAttribute(mappedPos, op.attrName, null);
|
|
165
|
+
resolvedTableRowStructure = true;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
deleteTableRowAt(tr, mappedPos);
|
|
169
|
+
resolvedTableRowStructure = true;
|
|
170
|
+
}
|
|
171
|
+
if (resolvedTableRowStructure) markStructuralChange(tr);
|
|
172
|
+
tableCellStructuralOps.sort((left, right) => right.cellPos - left.cellPos);
|
|
173
|
+
let resolvedTableCellStructure = false;
|
|
174
|
+
let failedTableCellMergeResolution = false;
|
|
175
|
+
for (const op of tableCellStructuralOps) {
|
|
176
|
+
const mappedPos = tr.mapping.map(op.cellPos);
|
|
177
|
+
const cell = tr.doc.nodeAt(mappedPos);
|
|
178
|
+
if (!cell || cell.type.name !== "tableCell" && cell.type.name !== "tableHeader") continue;
|
|
179
|
+
if (op.type === "merge") {
|
|
180
|
+
const resolved = op.source === "collapsed" ? resolveCollapsedTableCellMerge(tr, mappedPos, op.mode, op.revisionSet) : resolveVisibleTableCellMerge(tr, mappedPos, op.mode);
|
|
181
|
+
if (!resolved) {
|
|
182
|
+
failedTableCellMergeResolution = true;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
resolvedTableCellStructure ||= resolved;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (op.action === "clear") {
|
|
189
|
+
tr.setNodeAttribute(mappedPos, "cellMarker", null);
|
|
190
|
+
resolvedTableCellStructure = true;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
deleteTableCellAt(tr, mappedPos);
|
|
194
|
+
resolvedTableCellStructure = true;
|
|
195
|
+
}
|
|
196
|
+
if (failedTableCellMergeResolution) return false;
|
|
197
|
+
if (resolvedTableCellStructure) markStructuralChange(tr);
|
|
134
198
|
if (tr.steps.length > 0) dispatch(tr);
|
|
135
199
|
}
|
|
136
200
|
return true;
|
|
137
201
|
};
|
|
138
202
|
}
|
|
203
|
+
const RUN_FORMATTING_MARK_NAMES = /* @__PURE__ */ new Set([
|
|
204
|
+
"bold",
|
|
205
|
+
"italic",
|
|
206
|
+
"underline",
|
|
207
|
+
"strike",
|
|
208
|
+
"textColor",
|
|
209
|
+
"highlight",
|
|
210
|
+
"runShading",
|
|
211
|
+
"fontSize",
|
|
212
|
+
"fontFamily",
|
|
213
|
+
"language",
|
|
214
|
+
"superscript",
|
|
215
|
+
"subscript",
|
|
216
|
+
"allCaps",
|
|
217
|
+
"smallCaps",
|
|
218
|
+
"characterSpacing",
|
|
219
|
+
"emboss",
|
|
220
|
+
"imprint",
|
|
221
|
+
"hidden",
|
|
222
|
+
"textShadow",
|
|
223
|
+
"emphasisMark",
|
|
224
|
+
"textOutline",
|
|
225
|
+
"rtl",
|
|
226
|
+
"textEffect",
|
|
227
|
+
"runFormattingOverride",
|
|
228
|
+
"characterStyle"
|
|
229
|
+
]);
|
|
230
|
+
const resolveRunPropertyChange = ({ tr, node, from, to, mark, mode, revisionSet }) => {
|
|
231
|
+
const { changes } = expectRunPropertyChangeMarkAttrs(mark);
|
|
232
|
+
const matches = changes.filter((change) => revisionSet === null || revisionSet.has(change.info.id));
|
|
233
|
+
if (matches.length === 0) return;
|
|
234
|
+
tr.removeMark(from, to, mark);
|
|
235
|
+
const remaining = changes.filter((change) => revisionSet !== null && !revisionSet.has(change.info.id));
|
|
236
|
+
if (remaining.length > 0) tr.addMark(from, to, mark.type.create({ changes: remaining }));
|
|
237
|
+
if (mode === "accept") return;
|
|
238
|
+
const previousFormatting = matches.at(0)?.previousFormatting;
|
|
239
|
+
for (const currentMark of node.marks) if (RUN_FORMATTING_MARK_NAMES.has(currentMark.type.name)) tr.removeMark(from, to, currentMark.type);
|
|
240
|
+
for (const previousMark of textFormattingToMarks(previousFormatting)) tr.addMark(from, to, previousMark);
|
|
241
|
+
if (previousFormatting?.styleId) {
|
|
242
|
+
const characterStyle = node.type.schema.marks["characterStyle"];
|
|
243
|
+
if (characterStyle) tr.addMark(from, to, characterStyle.create({
|
|
244
|
+
styleId: previousFormatting.styleId,
|
|
245
|
+
_styleRPr: null
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
function collectTableRowStructuralOp(node, rowPos, mode, revisionSet) {
|
|
250
|
+
for (const attrName of ["trIns", "trDel"]) {
|
|
251
|
+
const marker = node.attrs[attrName];
|
|
252
|
+
if (!isTableRowRevisionAttr(marker)) continue;
|
|
253
|
+
if (revisionSet !== null && !revisionSet.has(marker.revisionId)) continue;
|
|
254
|
+
return {
|
|
255
|
+
rowPos,
|
|
256
|
+
attrName,
|
|
257
|
+
action: attrName === "trIns" === (mode === "accept") ? "clear" : "remove"
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
function isTableRowRevisionAttr(value) {
|
|
263
|
+
return typeof value === "object" && value !== null && "revisionId" in value && typeof value.revisionId === "number";
|
|
264
|
+
}
|
|
265
|
+
function collectTableCellStructuralOps(node, cellPos, mode, revisionSet) {
|
|
266
|
+
const operations = [];
|
|
267
|
+
const marker = node.attrs["cellMarker"];
|
|
268
|
+
if (isTableCellRevisionAttr(marker) && (revisionSet === null || revisionSet.has(marker.info.revisionId))) if (marker.kind === "merge") operations.push({
|
|
269
|
+
type: "merge",
|
|
270
|
+
cellPos,
|
|
271
|
+
source: "visible",
|
|
272
|
+
mode,
|
|
273
|
+
revisionSet
|
|
274
|
+
});
|
|
275
|
+
else {
|
|
276
|
+
const keepsCell = marker.kind === "ins" === (mode === "accept");
|
|
277
|
+
operations.push({
|
|
278
|
+
type: "membership",
|
|
279
|
+
cellPos,
|
|
280
|
+
action: keepsCell ? "clear" : "remove"
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (hasMatchingCollapsedTableCellMerge(node, revisionSet)) operations.push({
|
|
284
|
+
type: "merge",
|
|
285
|
+
cellPos,
|
|
286
|
+
source: "collapsed",
|
|
287
|
+
mode,
|
|
288
|
+
revisionSet
|
|
289
|
+
});
|
|
290
|
+
return operations;
|
|
291
|
+
}
|
|
292
|
+
function isTableCellRevisionAttr(value) {
|
|
293
|
+
if (typeof value !== "object" || value === null || !("kind" in value) || !("info" in value)) return false;
|
|
294
|
+
if (value.kind !== "ins" && value.kind !== "del" && value.kind !== "merge") return false;
|
|
295
|
+
const info = value.info;
|
|
296
|
+
return typeof info === "object" && info !== null && "revisionId" in info && typeof info.revisionId === "number";
|
|
297
|
+
}
|
|
298
|
+
function deleteTableCellAt(tr, cellPos) {
|
|
299
|
+
const cell = tr.doc.nodeAt(cellPos);
|
|
300
|
+
if (!cell || cell.type.name !== "tableCell" && cell.type.name !== "tableHeader") return;
|
|
301
|
+
const resolved = tr.doc.resolve(cellPos);
|
|
302
|
+
const row = resolved.parent;
|
|
303
|
+
if (row.type.spec["tableRole"] !== "row") return;
|
|
304
|
+
if (row.childCount > 1) {
|
|
305
|
+
tr.delete(cellPos, cellPos + cell.nodeSize);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
deleteTableRowAt(tr, resolved.start() - 1);
|
|
309
|
+
}
|
|
310
|
+
function deleteTableRowAt(tr, rowPos) {
|
|
311
|
+
const resolved = tr.doc.resolve(rowPos);
|
|
312
|
+
const table = resolved.parent;
|
|
313
|
+
if (table.type.spec["tableRole"] !== "table") return;
|
|
314
|
+
const rowIndex = resolved.index();
|
|
315
|
+
if (table.childCount > 1) {
|
|
316
|
+
const map = TableMap.get(table);
|
|
317
|
+
removeRow(tr, {
|
|
318
|
+
map,
|
|
319
|
+
table,
|
|
320
|
+
tableStart: resolved.start(),
|
|
321
|
+
left: 0,
|
|
322
|
+
top: rowIndex,
|
|
323
|
+
right: map.width,
|
|
324
|
+
bottom: rowIndex + 1
|
|
325
|
+
}, rowIndex);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const tablePosition = resolved.start() - 1;
|
|
329
|
+
const tableEnd = tablePosition + table.nodeSize;
|
|
330
|
+
const outerResolved = tr.doc.resolve(tablePosition);
|
|
331
|
+
const parent = outerResolved.parent;
|
|
332
|
+
const tableIndex = outerResolved.index();
|
|
333
|
+
if (parent.canReplace(tableIndex, tableIndex + 1)) {
|
|
334
|
+
tr.delete(tablePosition, tableEnd);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const emptyParagraph = tr.doc.type.schema.nodes["paragraph"]?.createAndFill();
|
|
338
|
+
if (emptyParagraph && parent.canReplaceWith(tableIndex, tableIndex + 1, emptyParagraph.type, emptyParagraph.marks)) tr.replaceWith(tablePosition, tableEnd, emptyParagraph);
|
|
339
|
+
}
|
|
139
340
|
function collectPPrMarkOp(node, pos, from, to, mode, revisionSet) {
|
|
140
341
|
if (!rangeCoversParagraphBoundary(from, to, pos, node)) return null;
|
|
141
342
|
const pPrMark = node.attrs["pPrMark"];
|
|
@@ -279,30 +480,65 @@ function rejectAllChanges() {
|
|
|
279
480
|
return (state, dispatch) => rejectChange(0, state.doc.content.size)(state, dispatch);
|
|
280
481
|
}
|
|
281
482
|
/**
|
|
282
|
-
* Find the document range covered by all
|
|
483
|
+
* Find the document range covered by all inline revision marks
|
|
283
484
|
* carrying any of the given AI-edit `revisionIds`. Returns null when
|
|
284
485
|
* none of those marks are present (already accepted/rejected, or
|
|
285
486
|
* never existed). A replace operation typically passes two ids (one
|
|
286
487
|
* for its deletion side, one for its insertion side); inserts and
|
|
287
|
-
* standalone deletions pass a single id.
|
|
488
|
+
* standalone deletions and formatting changes pass a single id.
|
|
288
489
|
*/
|
|
289
490
|
function findAIEditRevisionRange(state, revisionIds) {
|
|
290
491
|
const insertionType = state.schema.marks["insertion"];
|
|
291
492
|
const deletionType = state.schema.marks["deletion"];
|
|
292
|
-
|
|
493
|
+
const runPropertyChangeType = state.schema.marks["runPropertyChange"];
|
|
293
494
|
const idSet = new Set(typeof revisionIds === "number" ? [revisionIds] : revisionIds);
|
|
294
495
|
const range = {
|
|
295
496
|
from: null,
|
|
296
497
|
to: null
|
|
297
498
|
};
|
|
298
499
|
state.doc.descendants((node, pos) => {
|
|
500
|
+
if (node.type.name === "tableRow") for (const attrName of ["trIns", "trDel"]) {
|
|
501
|
+
const marker = node.attrs[attrName];
|
|
502
|
+
if (isTableRowRevisionAttr(marker) && idSet.has(marker.revisionId)) {
|
|
503
|
+
const start = pos;
|
|
504
|
+
const end = pos + node.nodeSize;
|
|
505
|
+
if (range.from === null || start < range.from) range.from = start;
|
|
506
|
+
if (range.to === null || end > range.to) range.to = end;
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
511
|
+
const marker = node.attrs["cellMarker"];
|
|
512
|
+
const hasDirectRevision = isTableCellRevisionAttr(marker) && idSet.has(marker.info.revisionId);
|
|
513
|
+
const continuationCells = node.attrs["_docxVMergeContinuationCells"];
|
|
514
|
+
const hasCollapsedRevision = Array.isArray(continuationCells) && continuationCells.some((cell) => {
|
|
515
|
+
const change = getTableCellMergeChange(cell);
|
|
516
|
+
return change !== null && idSet.has(change.info.id);
|
|
517
|
+
});
|
|
518
|
+
if (hasDirectRevision || hasCollapsedRevision) {
|
|
519
|
+
const start = pos;
|
|
520
|
+
const end = pos + node.nodeSize;
|
|
521
|
+
if (range.from === null || start < range.from) range.from = start;
|
|
522
|
+
if (range.to === null || end > range.to) range.to = end;
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
299
526
|
if (!node.isInline) return;
|
|
300
|
-
for (const mark of node.marks)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
527
|
+
for (const mark of node.marks) {
|
|
528
|
+
if (mark.type === runPropertyChangeType && expectRunPropertyChangeMarkAttrs(mark).changes.some((change) => idSet.has(change.info.id))) {
|
|
529
|
+
const start = pos;
|
|
530
|
+
const end = pos + node.nodeSize;
|
|
531
|
+
if (range.from === null || start < range.from) range.from = start;
|
|
532
|
+
if (range.to === null || end > range.to) range.to = end;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
if ((mark.type === insertionType || mark.type === deletionType) && typeof mark.attrs["revisionId"] === "number" && idSet.has(mark.attrs["revisionId"])) {
|
|
536
|
+
const start = pos;
|
|
537
|
+
const end = pos + node.nodeSize;
|
|
538
|
+
if (range.from === null || start < range.from) range.from = start;
|
|
539
|
+
if (range.to === null || end > range.to) range.to = end;
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
306
542
|
}
|
|
307
543
|
});
|
|
308
544
|
if (range.from === null || range.to === null) return null;
|
|
@@ -313,7 +549,7 @@ function findAIEditRevisionRange(state, revisionIds) {
|
|
|
313
549
|
}
|
|
314
550
|
/**
|
|
315
551
|
* Accept the tracked-change marks belonging to an AI-edit operation.
|
|
316
|
-
* Pass a single revisionId for inserts
|
|
552
|
+
* Pass a single revisionId for inserts, standalone deletions, or formatting changes; or the
|
|
317
553
|
* full id list for a replace (one id per side). Returns false when
|
|
318
554
|
* none of the ids match anything in the doc.
|
|
319
555
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { formatDate } from "../../docx/fieldParser.js";
|
|
2
1
|
import { expectBlockSdtAttrs } from "../attrs/index.js";
|
|
3
2
|
import { ContentControlBoundError, ContentControlLockedError, ContentControlTypeError } from "../../content-controls/errors.js";
|
|
3
|
+
import { formatDate } from "../../docx/fieldParser.js";
|
|
4
4
|
import { SUGGESTION_BYPASS_META } from "../plugins/suggestionMode.js";
|
|
5
5
|
//#region src/prosemirror/commands/contentControls.ts
|
|
6
6
|
function nodeMatchesFilter(node, pos, filter) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { acceptAIEditRevision, acceptAllChanges, acceptChange, addCommentMark, findAIEditRevisionRange, findNextChange, findPreviousChange, rejectAIEditRevision, rejectAllChanges, rejectChange, removeCommentMark } from "./comments.js";
|
|
2
1
|
import { getParagraphAlignment, getParagraphBidi, getStyleId } from "../extensions/core/ParagraphExtension.js";
|
|
3
2
|
import { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive } from "../extensions/marks/markUtils.js";
|
|
4
3
|
import { getTableContext, isInTable as isInTableCell } from "../extensions/nodes/TableExtension.js";
|
|
5
4
|
import { getListInfo, isInList } from "../extensions/features/ListExtension.js";
|
|
6
5
|
import { CLIPBOARD_READ_ERROR_EVENT, buildPlainTextSlice, pasteWithoutFormatting } from "./pastePlainText.js";
|
|
7
6
|
import { getHyperlinkAttrs, getSelectedText, isHyperlinkActive } from "../extensions/marks/HyperlinkExtension.js";
|
|
7
|
+
import { acceptAIEditRevision, acceptAllChanges, acceptChange, addCommentMark, findAIEditRevisionRange, findNextChange, findPreviousChange, rejectAIEditRevision, rejectAllChanges, rejectChange, removeCommentMark } from "./comments.js";
|
|
8
8
|
import { addColumnLeft, addColumnRight, addRowAbove, addRowBelow, applyTableStyle, autoFitContents, deleteColumn, deleteRow, deleteTable, distributeColumns, insertTable, mergeCells, removeTableBorders, selectColumn, selectRow, selectTable, setAllTableBorders, setCellBorder, setCellFillColor, setCellMargins, setCellTextDirection, setCellVerticalAlign, setInsideTableBorders, setOutsideTableBorders, setRowHeight, setTableBorderColor, setTableBorderPreset, setTableBorderWidth, setTableBorders, setTableProperties, splitCell, toggleHeaderRow, toggleNoWrap } from "./table.js";
|
|
9
9
|
import { clearFontFamily, clearFontSize, clearHighlight, clearTextColor, insertHyperlink, removeHyperlink, setFontFamily, setFontSize, setHighlight, setHyperlink, setTextColor, setUnderlineStyle, toggleBold, toggleItalic, toggleStrike, toggleSubscript, toggleSuperscript, toggleUnderline } from "./formatting.js";
|
|
10
10
|
import { addTabStop, alignCenter, alignJustify, alignLeft, alignRight, applyStyle, clearStyle, decreaseIndent, decreaseListLevel, doubleSpacing, generateTOC, increaseIndent, increaseListLevel, oneAndHalfSpacing, removeList, removeTabStop, setAlignment, setIndentFirstLine, setIndentLeft, setIndentRight, setLineSpacing, setLtr, setRtl, setSpaceAfter, setSpaceBefore, singleSpacing, toggleBidi, toggleBulletList, toggleNumberedList } from "./paragraph.js";
|
|
@@ -18,9 +18,9 @@ type AttrPatch = Record<string, unknown>;
|
|
|
18
18
|
* `runInWithNext` (`w:specVanish` lives in that rPr)
|
|
19
19
|
* - load-time style/numbering bookkeeping the command layer cannot recompute
|
|
20
20
|
* without a style resolver: `numPrFromStyle`, the `list*` rendering attrs,
|
|
21
|
-
* `spacingFromDocDefaults`
|
|
21
|
+
* `spacingFromDocDefaults`, `spacingFromImplicitDefaultStyle`
|
|
22
22
|
*/
|
|
23
|
-
declare const PPR_CHANGE_SCOPED_ATTR_KEYS: readonly ["styleId", "numPr", "alignment", "spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "spacingExplicit", "indentLeft", "indentRight", "indentFirstLine", "hangingIndent", "borders", "shading", "tabs", "pageBreakBefore", "keepNext", "keepLines", "widowControl", "contextualSpacing", "outlineLevel", "direction", "_autospacingBase"];
|
|
23
|
+
declare const PPR_CHANGE_SCOPED_ATTR_KEYS: readonly ["styleId", "numPr", "alignment", "spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "snapToGrid", "spacingExplicit", "indentLeft", "indentRight", "indentFirstLine", "hangingIndent", "borders", "shading", "tabs", "pageBreakBefore", "keepNext", "keepLines", "widowControl", "contextualSpacing", "outlineLevel", "direction", "_autospacingBase"];
|
|
24
24
|
/**
|
|
25
25
|
* Build the attr patch that rejecting one pPrChange applies to a paragraph:
|
|
26
26
|
* every in-scope key set to the stored previous value, or reset to `null`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { setAutospacingBaseValue } from "../autospacingBase.js";
|
|
2
1
|
import { directionFromBidi } from "../paragraphDirection.js";
|
|
2
|
+
import { setAutospacingBaseValue } from "../autospacingBase.js";
|
|
3
3
|
//#region src/prosemirror/commands/propertyChangeScope.ts
|
|
4
4
|
/**
|
|
5
5
|
* Paragraph attrs governed by a `w:pPrChange` — the attrs
|
|
@@ -17,7 +17,7 @@ import { directionFromBidi } from "../paragraphDirection.js";
|
|
|
17
17
|
* `runInWithNext` (`w:specVanish` lives in that rPr)
|
|
18
18
|
* - load-time style/numbering bookkeeping the command layer cannot recompute
|
|
19
19
|
* without a style resolver: `numPrFromStyle`, the `list*` rendering attrs,
|
|
20
|
-
* `spacingFromDocDefaults`
|
|
20
|
+
* `spacingFromDocDefaults`, `spacingFromImplicitDefaultStyle`
|
|
21
21
|
*/
|
|
22
22
|
const PPR_CHANGE_SCOPED_ATTR_KEYS = [
|
|
23
23
|
"styleId",
|
|
@@ -27,6 +27,7 @@ const PPR_CHANGE_SCOPED_ATTR_KEYS = [
|
|
|
27
27
|
"spaceAfter",
|
|
28
28
|
"lineSpacing",
|
|
29
29
|
"lineSpacingRule",
|
|
30
|
+
"snapToGrid",
|
|
30
31
|
"spacingExplicit",
|
|
31
32
|
"indentLeft",
|
|
32
33
|
"indentRight",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Transaction } from "prosemirror-state";
|
|
2
|
+
import { Node } from "prosemirror-model";
|
|
3
|
+
|
|
4
|
+
//#region src/prosemirror/commands/tableCellMergeResolution.d.ts
|
|
5
|
+
declare const hasMatchingCollapsedTableCellMerge: (node: Node, revisionSet: Set<number> | null) => boolean;
|
|
6
|
+
declare const resolveVisibleTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject") => boolean;
|
|
7
|
+
declare const resolveCollapsedTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject", revisionSet: Set<number> | null) => boolean;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
|
|
2
|
+
import { standaloneTableCellToProseMirror } from "../conversion/toProseDoc.js";
|
|
3
|
+
import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
|
|
4
|
+
import { Result } from "better-result";
|
|
5
|
+
import { TableMap } from "prosemirror-tables";
|
|
6
|
+
//#region src/prosemirror/commands/tableCellMergeResolution.ts
|
|
7
|
+
const hasMatchingCollapsedTableCellMerge = (node, revisionSet) => {
|
|
8
|
+
const continuationCells = node.attrs["_docxVMergeContinuationCells"];
|
|
9
|
+
if (!Array.isArray(continuationCells)) return false;
|
|
10
|
+
return continuationCells.some((cell) => {
|
|
11
|
+
const change = getTableCellMergeChange(cell);
|
|
12
|
+
return change !== null && (revisionSet === null || revisionSet.has(change.info.id));
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const isTableCellMergeRevisionAttr = (value) => {
|
|
16
|
+
if (typeof value !== "object" || value === null || !("kind" in value) || value.kind !== "merge" || !("info" in value)) return false;
|
|
17
|
+
const info = value.info;
|
|
18
|
+
return typeof info === "object" && info !== null && "revisionId" in info && typeof info.revisionId === "number";
|
|
19
|
+
};
|
|
20
|
+
const tableCellContext = (doc, cellPos) => {
|
|
21
|
+
const resolved = doc.resolve(cellPos);
|
|
22
|
+
for (let depth = resolved.depth; depth > 0; depth -= 1) {
|
|
23
|
+
const node = resolved.node(depth);
|
|
24
|
+
if (node.type.spec["tableRole"] !== "table") continue;
|
|
25
|
+
const tablePos = resolved.before(depth);
|
|
26
|
+
return {
|
|
27
|
+
table: node,
|
|
28
|
+
tableStart: tablePos + 1,
|
|
29
|
+
relativeCellPos: cellPos - tablePos - 1
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const resolveVisibleTableCellMerge = (tr, cellPos, mode) => {
|
|
35
|
+
const cell = tr.doc.nodeAt(cellPos);
|
|
36
|
+
const marker = cell?.attrs["cellMarker"];
|
|
37
|
+
if (!cell || !isTableCellMergeRevisionAttr(marker)) return false;
|
|
38
|
+
if (mode === "accept") {
|
|
39
|
+
tr.setNodeAttribute(cellPos, "cellMarker", null);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if ((marker.verticalMergeOriginal ?? "rest") === "continue") return mergeTableCellWithCellAbove(tr, cellPos);
|
|
43
|
+
const originalFormatting = cell.attrs["_originalFormatting"];
|
|
44
|
+
let nextOriginalFormatting = originalFormatting;
|
|
45
|
+
if (typeof originalFormatting === "object" && originalFormatting !== null) {
|
|
46
|
+
nextOriginalFormatting = { ...originalFormatting };
|
|
47
|
+
delete nextOriginalFormatting.vMerge;
|
|
48
|
+
}
|
|
49
|
+
tr.setNodeMarkup(cellPos, void 0, {
|
|
50
|
+
...cell.attrs,
|
|
51
|
+
cellMarker: null,
|
|
52
|
+
_originalFormatting: nextOriginalFormatting
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
const mergeTableCellWithCellAbove = (tr, cellPos) => {
|
|
57
|
+
const context = tableCellContext(tr.doc, cellPos);
|
|
58
|
+
const cell = tr.doc.nodeAt(cellPos);
|
|
59
|
+
if (!context || !cell) return false;
|
|
60
|
+
const map = TableMap.get(context.table);
|
|
61
|
+
const rectangle = map.findCell(context.relativeCellPos);
|
|
62
|
+
if (rectangle.top === 0) return false;
|
|
63
|
+
const aboveRelativePos = map.map[(rectangle.top - 1) * map.width + rectangle.left];
|
|
64
|
+
if (aboveRelativePos === void 0 || aboveRelativePos === context.relativeCellPos) return false;
|
|
65
|
+
const aboveRectangle = map.findCell(aboveRelativePos);
|
|
66
|
+
if (aboveRectangle.left !== rectangle.left || aboveRectangle.right !== rectangle.right || aboveRectangle.bottom !== rectangle.top) return false;
|
|
67
|
+
const abovePos = context.tableStart + aboveRelativePos;
|
|
68
|
+
const aboveCell = tr.doc.nodeAt(abovePos);
|
|
69
|
+
const aboveRowspan = aboveCell?.attrs["rowspan"];
|
|
70
|
+
const cellRowspan = cell.attrs["rowspan"];
|
|
71
|
+
if (!aboveCell || typeof aboveRowspan !== "number" || typeof cellRowspan !== "number" || aboveRowspan < 1 || cellRowspan < 1) return false;
|
|
72
|
+
const continuationCells = tableCellContinuationCells(aboveCell, aboveRowspan);
|
|
73
|
+
continuationCells.push(tableCellContinuationFromNode(cell));
|
|
74
|
+
const nestedContinuations = cell.attrs["_docxVMergeContinuationCells"];
|
|
75
|
+
if (Array.isArray(nestedContinuations)) continuationCells.push(...nestedContinuations);
|
|
76
|
+
tr.delete(cellPos, cellPos + cell.nodeSize);
|
|
77
|
+
tr.setNodeMarkup(abovePos, void 0, {
|
|
78
|
+
...aboveCell.attrs,
|
|
79
|
+
rowspan: aboveRowspan + cellRowspan,
|
|
80
|
+
_docxVMergeContinuationCells: continuationCells
|
|
81
|
+
});
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
const tableCellContinuationCells = (cell, rowspan) => {
|
|
85
|
+
const stored = cell.attrs["_docxVMergeContinuationCells"];
|
|
86
|
+
const cells = Array.isArray(stored) ? [...stored] : [];
|
|
87
|
+
while (cells.length < rowspan - 1) cells.push(emptyVerticalMergeContinuation());
|
|
88
|
+
return cells;
|
|
89
|
+
};
|
|
90
|
+
const tableCellContinuationFromNode = (cell) => {
|
|
91
|
+
const continuation = standaloneTableCellFromProseMirror(cell);
|
|
92
|
+
const formatting = {
|
|
93
|
+
...continuation.formatting,
|
|
94
|
+
vMerge: "continue"
|
|
95
|
+
};
|
|
96
|
+
const colspan = cell.attrs["colspan"];
|
|
97
|
+
if (typeof colspan === "number" && colspan > 1) formatting.gridSpan = colspan;
|
|
98
|
+
continuation.formatting = formatting;
|
|
99
|
+
delete continuation.structuralChange;
|
|
100
|
+
return continuation;
|
|
101
|
+
};
|
|
102
|
+
const emptyVerticalMergeContinuation = () => ({
|
|
103
|
+
type: "tableCell",
|
|
104
|
+
formatting: { vMerge: "continue" },
|
|
105
|
+
content: [{
|
|
106
|
+
type: "paragraph",
|
|
107
|
+
content: []
|
|
108
|
+
}]
|
|
109
|
+
});
|
|
110
|
+
const resolveCollapsedTableCellMerge = (tr, cellPos, mode, revisionSet) => {
|
|
111
|
+
const cell = tr.doc.nodeAt(cellPos);
|
|
112
|
+
const stored = cell?.attrs["_docxVMergeContinuationCells"];
|
|
113
|
+
if (!cell || !Array.isArray(stored)) return false;
|
|
114
|
+
const matchingIndices = [];
|
|
115
|
+
const nextCells = stored.map((continuationCell, index) => {
|
|
116
|
+
const change = getTableCellMergeChange(continuationCell);
|
|
117
|
+
if (!change || revisionSet !== null && !revisionSet.has(change.info.id)) return continuationCell;
|
|
118
|
+
matchingIndices.push(index);
|
|
119
|
+
const nextCell = { ...continuationCell };
|
|
120
|
+
delete nextCell.structuralChange;
|
|
121
|
+
return nextCell;
|
|
122
|
+
});
|
|
123
|
+
if (matchingIndices.length === 0) return false;
|
|
124
|
+
if (mode === "accept") {
|
|
125
|
+
tr.setNodeAttribute(cellPos, "_docxVMergeContinuationCells", nextCells);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
const splitIndices = matchingIndices.filter((index) => {
|
|
129
|
+
const change = getTableCellMergeChange(stored[index]);
|
|
130
|
+
return change?.verticalMerge === "continue" && (change.verticalMergeOriginal ?? "rest") === "rest";
|
|
131
|
+
});
|
|
132
|
+
if (splitIndices.length === 0) {
|
|
133
|
+
tr.setNodeAttribute(cellPos, "_docxVMergeContinuationCells", nextCells);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
const firstSplitIndex = splitIndices.at(0);
|
|
137
|
+
if (firstSplitIndex === void 0 || splitIndices.length !== stored.length - firstSplitIndex || splitIndices.some((index, offset) => index !== firstSplitIndex + offset)) return false;
|
|
138
|
+
const context = tableCellContext(tr.doc, cellPos);
|
|
139
|
+
if (!context) return false;
|
|
140
|
+
const map = TableMap.get(context.table);
|
|
141
|
+
const rectangle = map.findCell(context.relativeCellPos);
|
|
142
|
+
const rowspan = cell.attrs["rowspan"];
|
|
143
|
+
if (typeof rowspan !== "number" || rowspan !== stored.length + 1 || rectangle.bottom - rectangle.top !== rowspan) return false;
|
|
144
|
+
const restorations = [];
|
|
145
|
+
for (const index of splitIndices) {
|
|
146
|
+
const source = nextCells[index];
|
|
147
|
+
if (!source) return false;
|
|
148
|
+
const restoredCell = createRestoredTableCell(cell, source);
|
|
149
|
+
if (!restoredCell) return false;
|
|
150
|
+
restorations.push({
|
|
151
|
+
index,
|
|
152
|
+
cell: restoredCell
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const mapFrom = tr.mapping.maps.length;
|
|
156
|
+
for (const restoration of restorations) {
|
|
157
|
+
const row = rectangle.top + restoration.index + 1;
|
|
158
|
+
const insertionPos = context.tableStart + map.positionAt(row, rectangle.left, context.table);
|
|
159
|
+
tr.insert(tr.mapping.slice(mapFrom).map(insertionPos, 1), restoration.cell);
|
|
160
|
+
}
|
|
161
|
+
const remainingCells = nextCells.slice(0, firstSplitIndex);
|
|
162
|
+
tr.setNodeMarkup(cellPos, void 0, {
|
|
163
|
+
...cell.attrs,
|
|
164
|
+
rowspan: firstSplitIndex + 1,
|
|
165
|
+
_docxVMergeContinuationCells: remainingCells.length > 0 ? remainingCells : null
|
|
166
|
+
});
|
|
167
|
+
return true;
|
|
168
|
+
};
|
|
169
|
+
const createRestoredTableCell = (origin, source) => {
|
|
170
|
+
const formatting = source.formatting ? { ...source.formatting } : void 0;
|
|
171
|
+
if (formatting) delete formatting.vMerge;
|
|
172
|
+
const restored = standaloneTableCellToProseMirror({
|
|
173
|
+
type: "tableCell",
|
|
174
|
+
...formatting ? { formatting } : {},
|
|
175
|
+
...source.propertyChanges ? { propertyChanges: source.propertyChanges } : {},
|
|
176
|
+
content: source.content
|
|
177
|
+
}, origin.type.name === "tableHeader" ? "tableHeader" : "tableCell");
|
|
178
|
+
return Result.try(() => origin.type.schema.nodeFromJSON(restored.toJSON())).unwrapOr(null);
|
|
179
|
+
};
|
|
180
|
+
//#endregion
|
|
181
|
+
export { hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge };
|
|
@@ -10,6 +10,7 @@ declare function fromProseDoc(pmDoc: Node, baseDocument?: document_d_exports.Doc
|
|
|
10
10
|
* Convert ProseMirror marks to TextFormatting
|
|
11
11
|
*/
|
|
12
12
|
declare function marksToTextFormatting(marks: readonly Mark[]): document_d_exports.TextFormatting;
|
|
13
|
+
declare const standaloneTableCellFromProseMirror: (node: Node) => document_d_exports.TableCell;
|
|
13
14
|
/**
|
|
14
15
|
* Update a Document with content from a ProseMirror document
|
|
15
16
|
* Preserves all non-content parts of the original document
|
|
@@ -24,4 +25,4 @@ declare function updateDocumentContent(originalDocument: document_d_exports.Docu
|
|
|
24
25
|
*/
|
|
25
26
|
declare function proseDocToBlocks(pmDoc: Node): document_d_exports.BlockContent[];
|
|
26
27
|
//#endregion
|
|
27
|
-
export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, updateDocumentContent };
|
|
28
|
+
export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, updateDocumentContent };
|