@stll/folio-core 0.53.0 → 0.54.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 +390 -166
- package/dist/ai-edits/batch-claims.d.ts +21 -6
- package/dist/ai-edits/batch-claims.js +47 -16
- package/dist/ai-edits/headless.d.ts +20 -0
- package/dist/ai-edits/headless.js +140 -11
- package/dist/ai-edits/pending-suggestions.js +1 -1
- package/dist/ai-edits/read.d.ts +5 -3
- package/dist/ai-edits/read.js +97 -47
- package/dist/ai-edits/revisionStretches.d.ts +17 -0
- package/dist/ai-edits/revisionStretches.js +80 -0
- package/dist/ai-edits/snapshot.js +3 -2
- package/dist/ai-edits/table-cell-mutations.d.ts +3 -1
- package/dist/ai-edits/table-cell-mutations.js +2 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +6 -1
- package/dist/ai-edits/table-row-column-mutations.js +27 -10
- package/dist/compare/content-alignment.d.ts +5 -1
- package/dist/compare/content-alignment.js +39 -32
- package/dist/compare/inline-provenance.js +1 -1
- package/dist/controller/layoutPipeline.js +3 -2
- package/dist/docx/archiveInflation.d.ts +127 -0
- package/dist/docx/archiveInflation.js +181 -0
- package/dist/docx/metadataPrivacy.js +24 -1
- package/dist/docx/noteReferenceMark.d.ts +15 -0
- package/dist/docx/noteReferenceMark.js +48 -0
- package/dist/docx/paragraphParser.js +5 -3
- package/dist/docx/paragraphPropertySource.d.ts +1 -1
- package/dist/docx/paragraphPropertySource.js +3 -1
- package/dist/docx/parser.js +2 -2
- package/dist/docx/rezip.js +4 -2
- package/dist/docx/selectiveXmlPatch.d.ts +3 -1
- package/dist/docx/selectiveXmlPatch.js +81 -3
- package/dist/docx/server/boundedArchive.d.ts +8 -13
- package/dist/docx/server/boundedArchive.js +70 -59
- package/dist/docx/server/extractDocxText.d.ts +8 -2
- package/dist/docx/server/extractDocxText.js +2 -2
- package/dist/docx/server/validateDocxConformance.d.ts +1 -1
- package/dist/docx/server/validateDocxConformance.js +1 -0
- package/dist/docx/unzip.d.ts +19 -2
- package/dist/docx/unzip.js +135 -24
- package/dist/fonts/embeddedFonts.js +1 -1
- package/dist/i18n/messages/catalogs.gen.d.ts +34 -34
- package/dist/i18n/messages/catalogs.gen.js +34 -34
- package/dist/i18n/messages/messages.gen.d.ts +2 -2
- package/dist/internal/wholeStoryRevisionResolution.js +95 -25
- package/dist/layout-engine/measure/measureParagraph.js +2 -2
- package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
- package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
- package/dist/markdown/escape.d.ts +2 -2
- package/dist/markdown/escape.js +4 -5
- package/dist/markdown/renderBlock.js +91 -25
- package/dist/markdown/renderRuns.d.ts +10 -1
- package/dist/markdown/renderRuns.js +320 -59
- package/dist/prosemirror/clearRunColor.d.ts +6 -0
- package/dist/prosemirror/clearRunColor.js +46 -0
- package/dist/prosemirror/commands/comments.js +56 -21
- package/dist/prosemirror/commands/formatPainter.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
- package/dist/prosemirror/commands/resolveAllTableChanges.js +46 -41
- package/dist/prosemirror/commands/resolveParagraphProperties.js +20 -6
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
- package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
- package/dist/prosemirror/containerFinalParagraph.js +7 -6
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +12 -590
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
- package/dist/prosemirror/conversion/toProseDoc.js +31 -150
- package/dist/prosemirror/documentSchema.d.ts +8 -0
- package/dist/prosemirror/documentSchema.js +18 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +104 -63
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -1
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +10 -9
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -4
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +14 -8
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
- package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -6
- package/dist/prosemirror/extensions/marks/markUtils.js +17 -8
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +3 -1
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +12 -1
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +104 -210
- package/dist/prosemirror/extensions/types.d.ts +7 -0
- package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
- package/dist/prosemirror/hyperlinkRemoval.js +129 -0
- package/dist/prosemirror/index.d.ts +2 -1
- package/dist/prosemirror/index.js +2 -1
- package/dist/prosemirror/listNumbering.d.ts +3 -21
- package/dist/prosemirror/listNumbering.js +10 -25
- package/dist/prosemirror/listRendering.d.ts +27 -0
- package/dist/prosemirror/listRendering.js +27 -0
- package/dist/prosemirror/markupViewNotes.d.ts +8 -0
- package/dist/prosemirror/markupViewNotes.js +49 -0
- package/dist/prosemirror/markupViewProjection.d.ts +3 -1
- package/dist/prosemirror/markupViewProjection.js +7 -2
- package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
- package/dist/prosemirror/noteReferenceReview.js +332 -0
- package/dist/prosemirror/paragraphIndentation.js +5 -2
- package/dist/prosemirror/paragraphMarkJoin.d.ts +6 -4
- package/dist/prosemirror/paragraphMarkJoin.js +21 -19
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
- package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
- package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
- package/dist/prosemirror/plugins/documentStyleState.js +32 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +2 -21
- package/dist/prosemirror/plugins/documentStyles.js +66 -29
- package/dist/prosemirror/plugins/index.d.ts +2 -1
- package/dist/prosemirror/plugins/index.js +2 -1
- package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
- package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
- package/dist/prosemirror/plugins/suggestionMode.d.ts +12 -2
- package/dist/prosemirror/plugins/suggestionMode.js +391 -28
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +22 -2
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
- package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
- package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
- package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
- package/dist/prosemirror/runFormattingFromMarks.js +567 -0
- package/dist/prosemirror/runFormattingReconciliation.js +10 -9
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
- package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
- package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
- package/dist/prosemirror/tableCellPaste.d.ts +74 -0
- package/dist/prosemirror/tableCellPaste.js +525 -0
- package/dist/prosemirror/tableGridMutation.d.ts +53 -14
- package/dist/prosemirror/tableGridMutation.js +166 -18
- package/dist/prosemirror/tableRunIn.d.ts +51 -0
- package/dist/prosemirror/tableRunIn.js +165 -0
- package/dist/prosemirror/textInput.js +11 -2
- package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
- package/dist/prosemirror/trackedRevisionPath.js +32 -0
- package/dist/server.d.ts +2 -2
- package/package.json +5 -5
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
1
|
+
import { expectParagraphAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
|
|
2
|
+
import { paragraphPropertiesSnapshot } from "../commands/propertyChangeScope.js";
|
|
3
|
+
import { paragraphEndsItsContainer } from "../containerFinalParagraph.js";
|
|
2
4
|
import { encloseWholeControls } from "../contentControlRevisions.js";
|
|
3
5
|
import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
|
|
6
|
+
import { JOINED_RUNS_RESTYLED_META } from "../extensions/features/JoinedRunStyleExtension.js";
|
|
4
7
|
import { expandNoteReferenceDeletionRange } from "../extensions/marks/noteReferenceDeletion.js";
|
|
8
|
+
import { carryParagraphProperties, paragraphLeftAfter, recordReplacedParagraphProperties } from "../paragraphPropertyCarry.js";
|
|
9
|
+
import { rebaseParagraphRuns } from "../rebaseParagraphRunFormatting.js";
|
|
10
|
+
import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
|
|
11
|
+
import { paragraphRunStyleContext } from "../runStyleFormatting.js";
|
|
12
|
+
import { cellPasteRange, pasteTableCells } from "../tableCellPaste.js";
|
|
5
13
|
import { handleEditorBeforeInput } from "../textInput.js";
|
|
6
14
|
import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
|
|
15
|
+
import { getDocumentNumbering } from "./documentNumbering.js";
|
|
16
|
+
import { getDocumentStyleResolver } from "./documentStyleState.js";
|
|
7
17
|
import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
18
|
+
import { Slice } from "prosemirror-model";
|
|
19
|
+
import { Mapping, ReplaceStep } from "prosemirror-transform";
|
|
20
|
+
import { AllSelection, Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
|
|
10
21
|
import { isHistoryTransaction } from "prosemirror-history";
|
|
11
22
|
import { undoInputRule } from "prosemirror-inputrules";
|
|
12
23
|
//#region src/prosemirror/plugins/suggestionMode.ts
|
|
@@ -48,6 +59,27 @@ function makeRevisionInfo(state) {
|
|
|
48
59
|
date: attrs.date
|
|
49
60
|
};
|
|
50
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* A removed paragraph mark leaves the paragraph after it once accepted. An
|
|
64
|
+
* edit that reads as the paragraph before it (Backspace or Delete at the
|
|
65
|
+
* break, a deletion running from inside one paragraph into another) gives the
|
|
66
|
+
* paragraph left that paragraph's properties now, under the mark's revision:
|
|
67
|
+
* accepting reads as the edit made directly, rejecting restores its own.
|
|
68
|
+
*/
|
|
69
|
+
function carryIntoParagraphLeft(state, tr, source, survivorPos, info) {
|
|
70
|
+
carryParagraphProperties({
|
|
71
|
+
tr,
|
|
72
|
+
position: survivorPos,
|
|
73
|
+
source,
|
|
74
|
+
styleResolver: getDocumentStyleResolver(state),
|
|
75
|
+
numbering: getDocumentNumbering(state),
|
|
76
|
+
revision: {
|
|
77
|
+
id: info.id,
|
|
78
|
+
author: info.author ?? "",
|
|
79
|
+
date: info.date ?? ""
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
51
83
|
function makeParagraphMarkInfo(pluginState) {
|
|
52
84
|
const attrs = makeMarkAttrs(pluginState);
|
|
53
85
|
return {
|
|
@@ -82,7 +114,7 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
|
|
|
82
114
|
* Retract the current author's own inserted text.
|
|
83
115
|
* Processes in reverse order to maintain position validity.
|
|
84
116
|
*/
|
|
85
|
-
function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState) {
|
|
117
|
+
function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState, joinState) {
|
|
86
118
|
const ranges = [];
|
|
87
119
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
88
120
|
if (!canCarryTrackedRunMark(node)) return;
|
|
@@ -98,19 +130,38 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
98
130
|
});
|
|
99
131
|
});
|
|
100
132
|
const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
133
|
+
let removedMark = false;
|
|
101
134
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
102
135
|
if (node.type.name === "tableRow" && pos >= from && pos + node.nodeSize <= to && node.attrs["trIns"] == null && node.attrs["trDel"] == null) tr.setNodeAttribute(pos, "trDel", delAttrs);
|
|
103
136
|
if (node.type.name !== "paragraph") return;
|
|
104
137
|
const boundary = pos + node.nodeSize;
|
|
105
|
-
if (boundary > from && boundary <= to && node.attrs["pPrMark"] == null)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
if (boundary > from && boundary <= to && node.attrs["pPrMark"] == null) {
|
|
139
|
+
tr.setNodeAttribute(pos, "pPrMark", {
|
|
140
|
+
kind: "del",
|
|
141
|
+
info: {
|
|
142
|
+
id: delAttrs.revisionId,
|
|
143
|
+
author: delAttrs.author,
|
|
144
|
+
date: delAttrs.date
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
removedMark = true;
|
|
148
|
+
}
|
|
113
149
|
});
|
|
150
|
+
const $first = doc.resolve(from);
|
|
151
|
+
const $last = doc.resolve(to);
|
|
152
|
+
if (joinState && removedMark && $first.parent.type.name === "paragraph" && $last.parent.type.name === "paragraph" && $first.parent !== $last.parent && from > $first.start()) {
|
|
153
|
+
const lastPos = doc === tr.doc ? $last.before() : tr.mapping.map($last.before());
|
|
154
|
+
const lastMark = tr.doc.nodeAt(lastPos)?.attrs["pPrMark"];
|
|
155
|
+
const survivorPos = lastMark?.kind === "del" || lastMark?.kind === "moveFrom" ? paragraphLeftAfter({
|
|
156
|
+
doc: tr.doc,
|
|
157
|
+
paragraphPos: lastPos
|
|
158
|
+
}) ?? lastPos : lastPos;
|
|
159
|
+
carryIntoParagraphLeft(joinState, tr, $first.parent, survivorPos, {
|
|
160
|
+
id: delAttrs.revisionId,
|
|
161
|
+
author: delAttrs.author,
|
|
162
|
+
date: delAttrs.date
|
|
163
|
+
});
|
|
164
|
+
}
|
|
114
165
|
if (ranges.length === 0) return;
|
|
115
166
|
encloseWholeControls({
|
|
116
167
|
tr,
|
|
@@ -124,6 +175,23 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
124
175
|
else tr.addMark(range.from, range.to, deletionType.create(delAttrs));
|
|
125
176
|
}
|
|
126
177
|
}
|
|
178
|
+
/** Preserve clipboard revisions inside the revision that inserted their content. */
|
|
179
|
+
function enclosePastedRunRevisions({ tr, from, to, revision: insertion }) {
|
|
180
|
+
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
181
|
+
if (!canCarryTrackedRunMark(node)) return;
|
|
182
|
+
const revision = node.marks.find(({ type }) => type.name === "insertion" || type.name === "deletion");
|
|
183
|
+
if (!revision) return;
|
|
184
|
+
const previous = expectTrackedChangeMarkAttrs(revision);
|
|
185
|
+
tr.addMark(Math.max(from, pos), Math.min(to, pos + node.nodeSize), revision.type.create({
|
|
186
|
+
...revision.attrs,
|
|
187
|
+
_docxRevisionAncestors: [{
|
|
188
|
+
type: "insertion",
|
|
189
|
+
...insertion,
|
|
190
|
+
outerWrapperCount: 0
|
|
191
|
+
}, ...previous._docxRevisionAncestors ?? []]
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
127
195
|
/**
|
|
128
196
|
* Track every inserted run, paragraph break, and table row in `[from, to)`.
|
|
129
197
|
* Content already carrying a revision keeps its attribution.
|
|
@@ -137,10 +205,13 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
|
|
|
137
205
|
if (start >= end) return;
|
|
138
206
|
tr.addMark(start, end, insertionType.create(attrs));
|
|
139
207
|
});
|
|
208
|
+
const $end = doc.resolve(to);
|
|
209
|
+
const endsBetweenBlocks = !$end.parent.isTextblock && $end.nodeBefore !== null && $end.nodeBefore.type.name !== "paragraph";
|
|
140
210
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
141
211
|
if (node.type.name === "paragraph") {
|
|
142
212
|
const boundary = pos + node.nodeSize;
|
|
143
|
-
|
|
213
|
+
const keepsOwnMark = endsBetweenBlocks && boundary - 1 === from;
|
|
214
|
+
if (boundary > from && boundary < to && !keepsOwnMark && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
|
|
144
215
|
kind: "ins",
|
|
145
216
|
info: {
|
|
146
217
|
id: attrs.revisionId,
|
|
@@ -162,32 +233,221 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
|
|
|
162
233
|
* With track changes on, pasting over a non-empty text selection marks the
|
|
163
234
|
* replaced text as a tracked deletion and the pasted slice as a tracked
|
|
164
235
|
* insertion (so they read as one replacement), matching typing over a
|
|
165
|
-
* selection.
|
|
236
|
+
* selection. Select-all (an `AllSelection`) counts as a text selection over the
|
|
237
|
+
* whole document. Returns false for a collapsed cursor or another selection so the
|
|
166
238
|
* default paste + the `appendTransaction` catch-all marks a plain insertion
|
|
167
239
|
* (eigenpal/docx-editor#784).
|
|
168
240
|
*/
|
|
169
241
|
function handleSuggestionPaste(view, slice, pluginState) {
|
|
170
242
|
const { selection } = view.state;
|
|
171
|
-
|
|
243
|
+
const selectsAll = selection instanceof AllSelection;
|
|
244
|
+
if (!(selection instanceof TextSelection || selectsAll) || selection.empty) return false;
|
|
172
245
|
const insertionType = view.state.schema.marks["insertion"];
|
|
173
246
|
const deletionType = view.state.schema.marks["deletion"];
|
|
174
247
|
if (!insertionType || !deletionType) return false;
|
|
175
|
-
|
|
248
|
+
if (selectsAll && (slice.openStart > 0 || slice.openEnd > 0)) slice = new Slice(view.state.tr.replaceSelection(slice).doc.content, 0, 0);
|
|
249
|
+
const { from, to } = selectsAll ? {
|
|
250
|
+
from: Selection.atStart(view.state.doc).from,
|
|
251
|
+
to: Selection.atEnd(view.state.doc).to
|
|
252
|
+
} : selection;
|
|
176
253
|
const tr = view.state.tr;
|
|
177
254
|
tr.setMeta(SUGGESTION_META, true);
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
const
|
|
255
|
+
const { doc } = view.state;
|
|
256
|
+
const $from = doc.resolve(from);
|
|
257
|
+
const $to = doc.resolve(to);
|
|
258
|
+
const closedBlocks = slice.openStart === 0 && slice.content.firstChild?.isBlock === true;
|
|
259
|
+
const beforeStruck = closedBlocks && $from.parent.isTextblock && $from.parentOffset === 0;
|
|
260
|
+
const wholeBlocks = beforeStruck && $to.parent.isTextblock && $to.parentOffset === $to.parent.content.size;
|
|
261
|
+
const endsContainer = wholeBlocks && paragraphEndsItsContainer(doc.resolve($to.before()), $to.parent.type.name);
|
|
262
|
+
const rotates = endsContainer && slice.content.firstChild?.type === $to.parent.type && slice.content.lastChild?.type === $to.parent.type;
|
|
263
|
+
const tableFirst = endsContainer && !rotates;
|
|
264
|
+
if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to, insertionType, deletionType, pluginState);
|
|
265
|
+
const carryAcross = () => {
|
|
266
|
+
if (selectsAll || $from.parent.type.name !== "paragraph" || $from.parent === $to.parent || from <= $from.start()) return;
|
|
267
|
+
const firstPos = tr.mapping.map($from.before());
|
|
268
|
+
const mark = tr.doc.nodeAt(firstPos)?.attrs["pPrMark"];
|
|
269
|
+
const survivorPos = paragraphLeftAfter({
|
|
270
|
+
doc: tr.doc,
|
|
271
|
+
paragraphPos: firstPos
|
|
272
|
+
});
|
|
273
|
+
if (mark?.kind === "del" && survivorPos !== null) carryIntoParagraphLeft(view.state, tr, $from.parent, survivorPos, mark.info);
|
|
274
|
+
};
|
|
275
|
+
const inlineSlice = slice.content.firstChild?.isInline === true;
|
|
276
|
+
if (inlineSlice) carryAcross();
|
|
277
|
+
let at = tr.mapping.map(to);
|
|
278
|
+
if (rotates) at = tr.mapping.map($to.after());
|
|
279
|
+
else if (tableFirst) at = $from.before();
|
|
280
|
+
else if (beforeStruck) at = tr.mapping.map(from, -1);
|
|
281
|
+
const firstStep = tr.steps.length;
|
|
282
|
+
tr.replaceRange(at, at, slice);
|
|
283
|
+
const placed = tr.steps.slice(firstStep).flatMap((step, offset) => {
|
|
284
|
+
const later = new Mapping(tr.mapping.maps.slice(firstStep + offset + 1));
|
|
285
|
+
const ranges = [];
|
|
286
|
+
step.getMap().forEach((_oldFrom, _oldTo, newFrom, newTo) => {
|
|
287
|
+
ranges.push({
|
|
288
|
+
from: later.map(newFrom, -1),
|
|
289
|
+
to: later.map(newTo, 1)
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
return ranges;
|
|
293
|
+
});
|
|
294
|
+
let insertFrom = placed.length === 0 ? at : Math.min(...placed.map((range) => range.from));
|
|
295
|
+
let insertTo = placed.length === 0 ? at : Math.max(...placed.map((range) => range.to));
|
|
296
|
+
if (tableFirst) {
|
|
297
|
+
const struck = tr.steps.length;
|
|
298
|
+
markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to), insertionType, deletionType, pluginState);
|
|
299
|
+
const since = tr.mapping.slice(struck);
|
|
300
|
+
insertFrom = since.map(insertFrom, -1);
|
|
301
|
+
insertTo = since.map(insertTo, -1);
|
|
302
|
+
}
|
|
303
|
+
const insertAttrs = findAdjacentRevision(doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
304
|
+
enclosePastedRunRevisions({
|
|
305
|
+
tr,
|
|
306
|
+
from: insertFrom,
|
|
307
|
+
to: insertTo,
|
|
308
|
+
revision: insertAttrs
|
|
309
|
+
});
|
|
185
310
|
markRangeAsInserted(tr, tr.doc, insertFrom, insertTo, insertionType, deletionType, insertAttrs);
|
|
311
|
+
if (!inlineSlice) carryAcross();
|
|
312
|
+
if (!closedBlocks && $to.parent.type.name === "paragraph") {
|
|
313
|
+
const $end = tr.doc.resolve(tr.mapping.map(to));
|
|
314
|
+
if ($end.parent.type.name === "paragraph" && $end.before() !== tr.mapping.map($from.before())) recordReplacedParagraphProperties({
|
|
315
|
+
tr,
|
|
316
|
+
position: $end.before(),
|
|
317
|
+
replaced: $to.parent,
|
|
318
|
+
revision: {
|
|
319
|
+
id: insertAttrs.revisionId,
|
|
320
|
+
author: insertAttrs.author,
|
|
321
|
+
date: insertAttrs.date
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
const firstPasted = slice.content.firstChild;
|
|
326
|
+
if (selectsAll && !closedBlocks && firstPasted?.type.name === "paragraph") {
|
|
327
|
+
const $first = tr.doc.resolve(insertFrom);
|
|
328
|
+
if ($first.parent.type.name === "paragraph") carryParagraphProperties({
|
|
329
|
+
tr,
|
|
330
|
+
position: $first.before(),
|
|
331
|
+
source: firstPasted,
|
|
332
|
+
revision: {
|
|
333
|
+
id: insertAttrs.revisionId,
|
|
334
|
+
author: insertAttrs.author,
|
|
335
|
+
date: insertAttrs.date
|
|
336
|
+
},
|
|
337
|
+
styleResolver: getDocumentStyleResolver(view.state),
|
|
338
|
+
numbering: getDocumentNumbering(view.state)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (rotates) {
|
|
342
|
+
const rotation = tr.steps.length;
|
|
343
|
+
const joined = rotateIntoFinalParagraph(view.state, tr, tr.mapping.map($to.before()), insertAttrs);
|
|
344
|
+
insertFrom = Math.min(tr.mapping.slice(rotation).map(insertFrom, -1), joined);
|
|
345
|
+
insertTo = tr.mapping.slice(rotation).map(insertTo);
|
|
346
|
+
}
|
|
186
347
|
tr.setSelection(TextSelection.near(tr.doc.resolve(insertTo)));
|
|
187
348
|
view.dispatch(tr.scrollIntoView());
|
|
188
349
|
return true;
|
|
189
350
|
}
|
|
190
351
|
/**
|
|
352
|
+
* With track changes on, a paste into a table's cells (a cell selection, or a
|
|
353
|
+
* block of copied cells) replaces each cell's content the way a paste over a
|
|
354
|
+
* text selection does: the old content struck through, the pasted content
|
|
355
|
+
* after it as an insertion. When the pasted content is whole paragraphs, the
|
|
356
|
+
* last one ends the cell, so its paragraph mark is tracked as added too:
|
|
357
|
+
* rejecting then drops it and accepting joins the struck paragraphs into it.
|
|
358
|
+
*/
|
|
359
|
+
function handleSuggestionTableCellPaste(view, slice, pluginState) {
|
|
360
|
+
const insertionType = view.state.schema.marks["insertion"];
|
|
361
|
+
const deletionType = view.state.schema.marks["deletion"];
|
|
362
|
+
if (!insertionType || !deletionType) return false;
|
|
363
|
+
const revision = makeMarkAttrs(pluginState);
|
|
364
|
+
return pasteTableCells(view.state, slice, (tr) => view.dispatch(tr.setMeta(SUGGESTION_META, true)), {
|
|
365
|
+
revision,
|
|
366
|
+
replaceCellContent: (tr, cellPos, content) => {
|
|
367
|
+
const cell = tr.doc.nodeAt(cellPos);
|
|
368
|
+
if (!cell) return;
|
|
369
|
+
const { from, to } = cellPasteRange(cell, cellPos, content);
|
|
370
|
+
const mapFrom = tr.mapping.maps.length;
|
|
371
|
+
markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
|
|
372
|
+
const insertFrom = tr.mapping.slice(mapFrom).map(to);
|
|
373
|
+
const sizeBefore = tr.doc.content.size;
|
|
374
|
+
tr.replaceRange(insertFrom, insertFrom, content);
|
|
375
|
+
const insertTo = insertFrom + (tr.doc.content.size - sizeBefore);
|
|
376
|
+
enclosePastedRunRevisions({
|
|
377
|
+
tr,
|
|
378
|
+
from: insertFrom,
|
|
379
|
+
to: insertTo,
|
|
380
|
+
revision
|
|
381
|
+
});
|
|
382
|
+
markRangeAsInserted(tr, tr.doc, insertFrom, insertTo, insertionType, deletionType, revision);
|
|
383
|
+
tr.doc.nodesBetween(insertFrom, insertTo, (node, pos) => {
|
|
384
|
+
if (node.type.name === "paragraph" && pos >= insertFrom && pos + node.nodeSize <= insertTo && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
|
|
385
|
+
kind: "ins",
|
|
386
|
+
info: {
|
|
387
|
+
id: revision.revisionId,
|
|
388
|
+
author: revision.author,
|
|
389
|
+
date: revision.date
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Pasted paragraphs replaced a container's last paragraph. They were placed
|
|
398
|
+
* after it, and the paragraph was struck but keeps its mark, as a container's
|
|
399
|
+
* last paragraph must. Join the first pasted paragraph onto it, so the struck
|
|
400
|
+
* text and the first paste share one paragraph whose break is the inserted one,
|
|
401
|
+
* and record the replaced paragraph's formatting on the paragraph that now ends
|
|
402
|
+
* the container: accepting leaves only the paste, and rejecting closes every
|
|
403
|
+
* pasted break back into one paragraph that returns to its old formatting.
|
|
404
|
+
* Returns the position of the joined paragraph.
|
|
405
|
+
*/
|
|
406
|
+
function rotateIntoFinalParagraph(state, tr, replacedPos, attrs) {
|
|
407
|
+
const replaced = tr.doc.nodeAt(replacedPos);
|
|
408
|
+
const first = replaced ? tr.doc.nodeAt(replacedPos + replaced.nodeSize) : null;
|
|
409
|
+
if (!replaced || !first) return replacedPos;
|
|
410
|
+
const replacedAttrs = expectParagraphAttrs(replaced);
|
|
411
|
+
const previousFormatting = {
|
|
412
|
+
...paragraphPropertiesSnapshot(replaced),
|
|
413
|
+
numPr: replacedAttrs.numPr ?? null
|
|
414
|
+
};
|
|
415
|
+
const { _sectionProperties: sectionProperties } = replaced.attrs;
|
|
416
|
+
tr.join(replacedPos + replaced.nodeSize);
|
|
417
|
+
tr.setNodeMarkup(replacedPos, void 0, first.attrs);
|
|
418
|
+
const styleResolver = getDocumentStyleResolver(state);
|
|
419
|
+
if (styleResolver && replaced.content.size > 0) rebaseParagraphRuns({
|
|
420
|
+
previousContext: paragraphRunStyleContext(replaced, styleResolver),
|
|
421
|
+
paragraphPosition: replacedPos,
|
|
422
|
+
range: {
|
|
423
|
+
from: 0,
|
|
424
|
+
to: replaced.content.size
|
|
425
|
+
},
|
|
426
|
+
styleResolver,
|
|
427
|
+
tr
|
|
428
|
+
});
|
|
429
|
+
const $joined = tr.doc.resolve(replacedPos);
|
|
430
|
+
const container = $joined.parent;
|
|
431
|
+
let finalPos = replacedPos;
|
|
432
|
+
for (let index = $joined.index(); index < container.childCount - 1; index += 1) finalPos += container.child(index).nodeSize;
|
|
433
|
+
const final = tr.doc.nodeAt(finalPos);
|
|
434
|
+
if (final) tr.setNodeMarkup(finalPos, void 0, {
|
|
435
|
+
...final.attrs,
|
|
436
|
+
pPrMark: null,
|
|
437
|
+
...sectionProperties == null ? {} : { _sectionProperties: sectionProperties },
|
|
438
|
+
_propertyChanges: [{
|
|
439
|
+
type: "paragraphPropertyChange",
|
|
440
|
+
info: {
|
|
441
|
+
id: attrs.revisionId,
|
|
442
|
+
author: attrs.author,
|
|
443
|
+
date: attrs.date
|
|
444
|
+
},
|
|
445
|
+
previousFormatting
|
|
446
|
+
}]
|
|
447
|
+
});
|
|
448
|
+
return replacedPos;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
191
451
|
* Insert text as a tracked insertion, optionally marking replaced selection as deletion.
|
|
192
452
|
*/
|
|
193
453
|
function applySuggestionInsert(view, from, to, text, pluginState) {
|
|
@@ -196,20 +456,65 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
|
|
|
196
456
|
const tr = view.state.tr;
|
|
197
457
|
tr.setMeta(SUGGESTION_META, true);
|
|
198
458
|
tr.setMeta(SUGGESTED_TEXT_INPUT_META, text);
|
|
459
|
+
const selectsAll = view.state.selection instanceof AllSelection;
|
|
460
|
+
if (selectsAll) {
|
|
461
|
+
from = Math.max(from, Selection.atStart(view.state.doc).from);
|
|
462
|
+
to = Math.min(to, Selection.atEnd(view.state.doc).to);
|
|
463
|
+
}
|
|
464
|
+
const replacedFormatting = selectsAll ? (view.state.doc.resolve(from).nodeAfter?.marks ?? []).filter(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name)) : null;
|
|
199
465
|
const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
200
466
|
if (from !== to) {
|
|
201
467
|
const deletionType = view.state.schema.marks["deletion"];
|
|
202
|
-
if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState);
|
|
468
|
+
if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState, view.state);
|
|
203
469
|
}
|
|
204
470
|
const insertAt = tr.mapping.map(to);
|
|
205
471
|
tr.insertText(text, insertAt, insertAt);
|
|
206
472
|
const deletionType = view.state.schema.marks["deletion"];
|
|
207
473
|
if (deletionType) tr.removeMark(insertAt, insertAt + text.length, deletionType);
|
|
208
474
|
tr.addMark(insertAt, insertAt + text.length, insertionType.create(insertAttrs));
|
|
475
|
+
if (replacedFormatting) {
|
|
476
|
+
for (const mark of tr.doc.resolve(insertAt).nodeAfter?.marks ?? []) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr.removeMark(insertAt, insertAt + text.length, mark.type);
|
|
477
|
+
for (const mark of replacedFormatting) tr.addMark(insertAt, insertAt + text.length, mark);
|
|
478
|
+
}
|
|
479
|
+
separateSplitStretch(tr, insertAt, insertAt + text.length, insertAttrs.revisionId);
|
|
480
|
+
if (view.state.selection instanceof AllSelection) tr.setSelection(TextSelection.create(tr.doc, insertAt + text.length));
|
|
209
481
|
view.dispatch(tr.scrollIntoView());
|
|
210
482
|
return true;
|
|
211
483
|
}
|
|
212
484
|
/**
|
|
485
|
+
* Text typed inside another revision leaves that revision in two stretches.
|
|
486
|
+
* A saved package writes each stretch as a wrapper of its own with its own
|
|
487
|
+
* `w:id`, so the stretch after the typed text gets a fresh id now: the editor
|
|
488
|
+
* lists, resolves and saves the same changes a reopened package does.
|
|
489
|
+
*/
|
|
490
|
+
function separateSplitStretch(tr, from, to, typedId) {
|
|
491
|
+
const $from = tr.doc.resolve(from);
|
|
492
|
+
const $to = tr.doc.resolve(to);
|
|
493
|
+
const before = $from.nodeBefore;
|
|
494
|
+
if (!before || $to.parent !== $from.parent) return;
|
|
495
|
+
const parent = $to.parent;
|
|
496
|
+
const start = $to.start();
|
|
497
|
+
for (const mark of before.marks) {
|
|
498
|
+
if (mark.type.name !== "insertion" && mark.type.name !== "deletion") continue;
|
|
499
|
+
const revisionId = mark.attrs["revisionId"];
|
|
500
|
+
if (revisionId === typedId || typeof revisionId !== "number") continue;
|
|
501
|
+
let end = to;
|
|
502
|
+
let offset = to - start;
|
|
503
|
+
while (offset < parent.content.size) {
|
|
504
|
+
const child = parent.childAfter(offset).node;
|
|
505
|
+
if (!child?.marks.some((candidate) => candidate.eq(mark))) break;
|
|
506
|
+
offset += child.nodeSize;
|
|
507
|
+
end = start + offset;
|
|
508
|
+
}
|
|
509
|
+
if (end === to) continue;
|
|
510
|
+
tr.removeMark(to, end, mark);
|
|
511
|
+
tr.addMark(to, end, mark.type.create({
|
|
512
|
+
...mark.attrs,
|
|
513
|
+
revisionId: mintRevisionId()
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
213
518
|
* Remove `from`..`to` of `tr.doc` as a suggestion: text the current author
|
|
214
519
|
* inserted is retracted, anything else is marked deleted. `false` when the
|
|
215
520
|
* editor is not suggesting.
|
|
@@ -248,6 +553,13 @@ function handleSuggestionEnter(view, pluginState) {
|
|
|
248
553
|
info: makeParagraphMarkInfo(pluginState)
|
|
249
554
|
};
|
|
250
555
|
tr.setNodeAttribute(sourcePos, "pPrMark", markInfo);
|
|
556
|
+
const $caret = tr.selection.$from;
|
|
557
|
+
if ($caret.parent.type.name === "paragraph" && $caret.before() !== sourcePos) recordReplacedParagraphProperties({
|
|
558
|
+
tr,
|
|
559
|
+
position: $caret.before(),
|
|
560
|
+
replaced: $from.parent,
|
|
561
|
+
revision: markInfo.info
|
|
562
|
+
});
|
|
251
563
|
}
|
|
252
564
|
}
|
|
253
565
|
view.dispatch(tr.scrollIntoView());
|
|
@@ -301,9 +613,22 @@ function applyPPrDel(view, targetParagraphPos, pluginState) {
|
|
|
301
613
|
const tr = view.state.tr;
|
|
302
614
|
tr.setMeta(SUGGESTION_META, true);
|
|
303
615
|
const joinPos = targetParagraphPos + targetNode.nodeSize;
|
|
616
|
+
const joined = view.state.doc.nodeAt(joinPos);
|
|
304
617
|
try {
|
|
305
618
|
tr.join(joinPos);
|
|
306
619
|
tr.setNodeAttribute(targetParagraphPos, "pPrMark", null);
|
|
620
|
+
const styleResolver = getDocumentStyleResolver(view.state);
|
|
621
|
+
if (styleResolver && joined?.type === targetNode.type && joined.content.size > 0) rebaseParagraphRuns({
|
|
622
|
+
previousContext: paragraphRunStyleContext(joined, styleResolver),
|
|
623
|
+
paragraphPosition: targetParagraphPos,
|
|
624
|
+
range: {
|
|
625
|
+
from: targetNode.content.size,
|
|
626
|
+
to: targetNode.content.size + joined.content.size
|
|
627
|
+
},
|
|
628
|
+
styleResolver,
|
|
629
|
+
tr
|
|
630
|
+
});
|
|
631
|
+
tr.setMeta(JOINED_RUNS_RESTYLED_META, true);
|
|
307
632
|
view.dispatch(tr.scrollIntoView());
|
|
308
633
|
} catch {
|
|
309
634
|
return true;
|
|
@@ -318,6 +643,10 @@ function applyPPrDel(view, targetParagraphPos, pluginState) {
|
|
|
318
643
|
info: makeParagraphMarkInfo(pluginState)
|
|
319
644
|
};
|
|
320
645
|
tr.setNodeAttribute(targetParagraphPos, "pPrMark", markInfo);
|
|
646
|
+
carryIntoParagraphLeft(view.state, tr, targetNode, paragraphLeftAfter({
|
|
647
|
+
doc: tr.doc,
|
|
648
|
+
paragraphPos: targetParagraphPos
|
|
649
|
+
}) ?? targetParagraphPos + targetNode.nodeSize, markInfo.info);
|
|
321
650
|
view.dispatch(tr.scrollIntoView());
|
|
322
651
|
return true;
|
|
323
652
|
}
|
|
@@ -345,7 +674,7 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
345
674
|
from: rangeFrom.pos,
|
|
346
675
|
to: rangeTo.pos
|
|
347
676
|
}).sort((a, b) => b.from - a.from);
|
|
348
|
-
for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, insertionType, deletionType, pluginState);
|
|
677
|
+
for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, insertionType, deletionType, pluginState, state);
|
|
349
678
|
const cursorPos = tr.mapping.map($to.pos);
|
|
350
679
|
tr.setSelection(TextSelection.near(tr.doc.resolve(cursorPos)));
|
|
351
680
|
dispatch(tr.scrollIntoView());
|
|
@@ -414,6 +743,33 @@ function markComposedAsInsertion(view, from, pluginState, replaced) {
|
|
|
414
743
|
view.dispatch(tr);
|
|
415
744
|
}
|
|
416
745
|
/**
|
|
746
|
+
* Paragraphs inserted into one (a paste at the caret) split it: its break ends
|
|
747
|
+
* the last part, which reads as the last inserted paragraph. Rejecting the
|
|
748
|
+
* inserted breaks leaves that part, so it records what the paragraph read as.
|
|
749
|
+
*/
|
|
750
|
+
function recordSplitParagraph(tr, before, step, insertedFrom, insertedTo, attrs) {
|
|
751
|
+
if (!(step instanceof ReplaceStep) || !before) return;
|
|
752
|
+
const $at = before.resolve(step.from);
|
|
753
|
+
if ($at.parent.type.name !== "paragraph" || step.to > $at.after() || before.textBetween(step.from, step.to) !== "") return;
|
|
754
|
+
const split = $at.parent;
|
|
755
|
+
const $first = tr.doc.resolve(insertedFrom);
|
|
756
|
+
const $last = tr.doc.resolve(insertedTo);
|
|
757
|
+
let lastPos = null;
|
|
758
|
+
if ($last.parent.type.name === "paragraph") lastPos = $last.before();
|
|
759
|
+
else if ($last.nodeBefore?.type.name === "paragraph") lastPos = insertedTo - $last.nodeBefore.nodeSize;
|
|
760
|
+
if ($first.parent.type.name !== "paragraph" || lastPos === null || lastPos === $first.before()) return;
|
|
761
|
+
recordReplacedParagraphProperties({
|
|
762
|
+
tr,
|
|
763
|
+
position: lastPos,
|
|
764
|
+
replaced: split,
|
|
765
|
+
revision: {
|
|
766
|
+
id: attrs.revisionId,
|
|
767
|
+
author: attrs.author,
|
|
768
|
+
date: attrs.date
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
417
773
|
* Create the suggestion mode plugin.
|
|
418
774
|
* When active, text edits become tracked changes.
|
|
419
775
|
*/
|
|
@@ -490,7 +846,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
490
846
|
handleKeyDown(view, event) {
|
|
491
847
|
const pluginState = suggestionModeKey.getState(view.state);
|
|
492
848
|
if (!pluginState?.active) return false;
|
|
493
|
-
if (event.key === "Enter") {
|
|
849
|
+
if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
494
850
|
const { $from, empty } = view.state.selection;
|
|
495
851
|
if (empty && $from.parent.type.name === "paragraph" && expectParagraphAttrs($from.parent).numPr?.kind === "reference") return false;
|
|
496
852
|
return handleSuggestionEnter(view, pluginState);
|
|
@@ -515,7 +871,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
515
871
|
handlePaste(view, _event, slice) {
|
|
516
872
|
const pluginState = suggestionModeKey.getState(view.state);
|
|
517
873
|
if (!pluginState?.active) return false;
|
|
518
|
-
return handleSuggestionPaste(view, slice, pluginState);
|
|
874
|
+
return handleSuggestionTableCellPaste(view, slice, pluginState) || handleSuggestionPaste(view, slice, pluginState);
|
|
519
875
|
}
|
|
520
876
|
},
|
|
521
877
|
appendTransaction(transactions, _oldState, newState) {
|
|
@@ -538,7 +894,14 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
538
894
|
const newFrom = following.map(stepFrom, -1);
|
|
539
895
|
const newTo = following.map(stepTo, 1);
|
|
540
896
|
if (newTo > newFrom) {
|
|
897
|
+
if (userTr.getMeta("paste")) enclosePastedRunRevisions({
|
|
898
|
+
tr,
|
|
899
|
+
from: newFrom,
|
|
900
|
+
to: newTo,
|
|
901
|
+
revision: markAttrs
|
|
902
|
+
});
|
|
541
903
|
if (deletionType) markRangeAsInserted(tr, newState.doc, newFrom, newTo, insertionType, deletionType, markAttrs);
|
|
904
|
+
recordSplitParagraph(tr, userTr.docs[stepIndex], step, newFrom, newTo, markAttrs);
|
|
542
905
|
}
|
|
543
906
|
});
|
|
544
907
|
}
|
|
@@ -573,4 +936,4 @@ function isSuggestionModeActive(state) {
|
|
|
573
936
|
return suggestionModeKey.getState(state)?.active ?? false;
|
|
574
937
|
}
|
|
575
938
|
//#endregion
|
|
576
|
-
export { SUGGESTED_TEXT_INPUT_META, SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, deleteSelectionAsSuggestion, handleSuggestionEnter, handleSuggestionPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestRangeDeletion, suggestionModeKey, toggleSuggestionMode };
|
|
939
|
+
export { SUGGESTED_TEXT_INPUT_META, SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, deleteSelectionAsSuggestion, handleSuggestionEnter, handleSuggestionPaste, handleSuggestionTableCellPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestRangeDeletion, suggestionModeKey, toggleSuggestionMode };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { RunStyleResolver } from "./runStyleFormatting.js";
|
|
1
|
+
import { ParagraphRunStyleContext, RunStyleResolver } from "./runStyleFormatting.js";
|
|
2
2
|
import { Node } from "prosemirror-model";
|
|
3
3
|
import { Transaction } from "prosemirror-state";
|
|
4
|
+
import { Transform } from "prosemirror-transform";
|
|
4
5
|
//#region src/prosemirror/rebaseParagraphRunFormatting.d.ts
|
|
5
6
|
type RebaseParagraphRunFormattingOptions = {
|
|
6
7
|
nextAttrs: Record<string, unknown>;
|
|
@@ -9,10 +10,29 @@ type RebaseParagraphRunFormattingOptions = {
|
|
|
9
10
|
styleResolver: RunStyleResolver;
|
|
10
11
|
tr: Transaction;
|
|
11
12
|
};
|
|
13
|
+
type RebaseParagraphRunsOptions<T extends Transform> = {
|
|
14
|
+
/** The style context the runs' marks were resolved in. */
|
|
15
|
+
previousContext: ParagraphRunStyleContext;
|
|
16
|
+
paragraphPosition: number;
|
|
17
|
+
/** Content offsets inside the paragraph to rebase; all of it when omitted. */
|
|
18
|
+
range?: {
|
|
19
|
+
from: number;
|
|
20
|
+
to: number;
|
|
21
|
+
};
|
|
22
|
+
shouldRebase?: (node: Node) => boolean;
|
|
23
|
+
styleResolver: RunStyleResolver;
|
|
24
|
+
tr: T;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Re-resolve the inherited marks of runs resolved in `previousContext` in the
|
|
28
|
+
* paragraph's current style context: direct formatting stays direct, and what
|
|
29
|
+
* the old context lent the runs goes with it.
|
|
30
|
+
*/
|
|
31
|
+
declare const rebaseParagraphRuns: <T extends Transform>({ previousContext, paragraphPosition, range, shouldRebase, styleResolver, tr }: RebaseParagraphRunsOptions<T>) => T;
|
|
12
32
|
/**
|
|
13
33
|
* Change paragraph attrs and re-resolve inherited run marks in the new style
|
|
14
34
|
* context without turning the old rendered style into direct run formatting.
|
|
15
35
|
*/
|
|
16
36
|
declare const setParagraphAttrsWithRebasedRunFormatting: ({ nextAttrs, paragraphPosition, shouldRebase, styleResolver, tr }: RebaseParagraphRunFormattingOptions) => Transaction;
|
|
17
37
|
//#endregion
|
|
18
|
-
export { setParagraphAttrsWithRebasedRunFormatting };
|
|
38
|
+
export { rebaseParagraphRuns, setParagraphAttrsWithRebasedRunFormatting };
|