@magic-marker/prosemirror-suggest-changes 0.2.1-block-join.0 → 0.2.1-block-join.2
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/ensureSelectionPlugin.d.ts +13 -0
- package/dist/ensureSelectionPlugin.js +247 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/plugin.js +2 -14
- package/dist/prependDeletionsWithZWSP.d.ts +4 -0
- package/dist/prependDeletionsWithZWSP.js +54 -0
- package/dist/schema.d.ts +4 -1
- package/dist/schema.js +23 -2
- package/dist/withSuggestChanges.d.ts +3 -1
- package/dist/withSuggestChanges.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
+
interface PluginState {
|
|
3
|
+
handleKeyDown: {
|
|
4
|
+
backspace: boolean;
|
|
5
|
+
delete: boolean;
|
|
6
|
+
arrowLeft: boolean;
|
|
7
|
+
arrowRight: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare const ensureSelectionKey: PluginKey<PluginState>;
|
|
11
|
+
export declare function ensureSelection(): Plugin<PluginState>;
|
|
12
|
+
export declare function isEnsureSelectionEnabled(): boolean;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
2
|
+
import { getSuggestionMarks } from "./utils.js";
|
|
3
|
+
export const ensureSelectionKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes-ensure-selection");
|
|
4
|
+
export function ensureSelection() {
|
|
5
|
+
return new Plugin({
|
|
6
|
+
key: ensureSelectionKey,
|
|
7
|
+
state: {
|
|
8
|
+
apply (tr, state) {
|
|
9
|
+
const newPluginState = tr.getMeta(ensureSelectionKey);
|
|
10
|
+
if (newPluginState) {
|
|
11
|
+
return newPluginState;
|
|
12
|
+
}
|
|
13
|
+
return state;
|
|
14
|
+
},
|
|
15
|
+
init () {
|
|
16
|
+
return {
|
|
17
|
+
handleKeyDown: {
|
|
18
|
+
backspace: false,
|
|
19
|
+
delete: false,
|
|
20
|
+
arrowLeft: false,
|
|
21
|
+
arrowRight: false
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
props: {
|
|
27
|
+
handleKeyDown (view, event) {
|
|
28
|
+
const state = this.getState(view.state);
|
|
29
|
+
if (!state) return;
|
|
30
|
+
const newState = {
|
|
31
|
+
...state
|
|
32
|
+
};
|
|
33
|
+
newState.handleKeyDown.backspace = event.key === "Backspace";
|
|
34
|
+
newState.handleKeyDown.delete = event.key === "Delete";
|
|
35
|
+
newState.handleKeyDown.arrowLeft = event.key === "ArrowLeft";
|
|
36
|
+
newState.handleKeyDown.arrowRight = event.key === "ArrowRight";
|
|
37
|
+
if (newState.handleKeyDown.backspace !== state.handleKeyDown.backspace || newState.handleKeyDown.delete !== state.handleKeyDown.delete || newState.handleKeyDown.arrowLeft !== state.handleKeyDown.arrowLeft || newState.handleKeyDown.arrowRight !== state.handleKeyDown.arrowRight) {
|
|
38
|
+
console.log("handleKeyDown newState =", newState);
|
|
39
|
+
view.dispatch(view.state.tr.setMeta(ensureSelectionKey, newState));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
appendTransaction (_transactions, oldState, newState) {
|
|
44
|
+
const state = newState;
|
|
45
|
+
if (!(state.selection instanceof TextSelection)) return null;
|
|
46
|
+
if (!(oldState.selection instanceof TextSelection)) return null;
|
|
47
|
+
const { $cursor } = state.selection;
|
|
48
|
+
if ($cursor == null) return null;
|
|
49
|
+
const $oldCursor = oldState.selection.$cursor;
|
|
50
|
+
let dir;
|
|
51
|
+
if ($oldCursor != null) {
|
|
52
|
+
dir = $cursor.pos > $oldCursor.pos ? 1 : -1;
|
|
53
|
+
} else {
|
|
54
|
+
const { $from, $to } = oldState.selection;
|
|
55
|
+
const distToFrom = $cursor.pos - $from.pos;
|
|
56
|
+
const distToTo = $to.pos - $cursor.pos;
|
|
57
|
+
// if cursor ended up closer to the right side of the selection (to),
|
|
58
|
+
// consider direction as 1 - "to the right"
|
|
59
|
+
dir = distToTo <= distToFrom ? 1 : -1;
|
|
60
|
+
}
|
|
61
|
+
const pluginState = ensureSelectionKey.getState(newState);
|
|
62
|
+
if (pluginState?.handleKeyDown.backspace || pluginState?.handleKeyDown.arrowLeft) {
|
|
63
|
+
dir = -1;
|
|
64
|
+
} else if (pluginState?.handleKeyDown.delete || pluginState?.handleKeyDown.arrowRight) {
|
|
65
|
+
dir = 1;
|
|
66
|
+
}
|
|
67
|
+
if (!isValidPos($cursor, dir)) {
|
|
68
|
+
console.groupCollapsed("$cursor is not valid, find new $cursor", $cursor.pos, {
|
|
69
|
+
dir,
|
|
70
|
+
oldSelection: oldState.selection,
|
|
71
|
+
$cursor
|
|
72
|
+
});
|
|
73
|
+
let $pos = $cursor;
|
|
74
|
+
if (dir > 0) {
|
|
75
|
+
$pos = findNextPos($pos, dir);
|
|
76
|
+
if (!isValidPos($pos, dir)) {
|
|
77
|
+
console.warn("failed to find next valid $cursor, trying prev");
|
|
78
|
+
$pos = findPrevPos($pos, dir);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
$pos = findPrevPos($pos, dir);
|
|
82
|
+
if (!isValidPos($pos, dir)) {
|
|
83
|
+
console.warn("failed to find prev valid $cursor, trying next");
|
|
84
|
+
$pos = findNextPos($pos, dir);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!isValidPos($pos, dir)) {
|
|
88
|
+
console.warn("failed to find valid $cursor after all attempts", $pos.pos, "keeping the original $cursor", $cursor.pos, {
|
|
89
|
+
$cursor,
|
|
90
|
+
$pos
|
|
91
|
+
});
|
|
92
|
+
console.groupEnd();
|
|
93
|
+
console.log("final $cursor (unchanged)", $cursor);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
console.info("found new valid $cursor", $pos.pos, {
|
|
97
|
+
$pos
|
|
98
|
+
});
|
|
99
|
+
console.groupEnd();
|
|
100
|
+
console.log("final $cursor", $pos.pos, {
|
|
101
|
+
$pos
|
|
102
|
+
});
|
|
103
|
+
return newState.tr.setSelection(TextSelection.create(newState.doc, $pos.pos, $pos.pos));
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export function isEnsureSelectionEnabled() {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
function findNextPos($initialPos, dir) {
|
|
113
|
+
console.groupCollapsed("finding next valid pos from $initialPos =", $initialPos);
|
|
114
|
+
let $pos = $initialPos;
|
|
115
|
+
while(!isValidPos($pos, dir) && ($pos.nodeAfter != null || $pos.depth > 0)){
|
|
116
|
+
if ($pos.nodeAfter != null) {
|
|
117
|
+
if ($pos.nodeAfter.isInline) {
|
|
118
|
+
// nodeAfter is inline - move in by one
|
|
119
|
+
$pos = $pos.doc.resolve($pos.pos + 1);
|
|
120
|
+
console.log("nodeAfter is inline, move to end of it", $pos);
|
|
121
|
+
} else {
|
|
122
|
+
// nodeAfter is not inline - find first inline descendant in nodeAfter
|
|
123
|
+
let localStartPos = null;
|
|
124
|
+
$pos.nodeAfter.descendants((child, pos)=>{
|
|
125
|
+
if (!child.isInline) return true;
|
|
126
|
+
if (localStartPos !== null) return false;
|
|
127
|
+
localStartPos = pos;
|
|
128
|
+
return false;
|
|
129
|
+
});
|
|
130
|
+
if (localStartPos !== null) {
|
|
131
|
+
// we have a local position of a first inline descendant - convert it to global position
|
|
132
|
+
// +1 to "enter" the node, and add local pos
|
|
133
|
+
$pos = $pos.doc.resolve($pos.pos + 1 + localStartPos);
|
|
134
|
+
console.log("found first inline descendant, move to start of it", $pos);
|
|
135
|
+
} else {
|
|
136
|
+
// unable to find first inline descendant of nodeAfter - just skip nodeAfter
|
|
137
|
+
$pos = $pos.doc.resolve($pos.pos + $pos.nodeAfter.nodeSize);
|
|
138
|
+
console.log("unable to find first inline descendant, move to end of nodeAfter", $pos);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else if ($pos.depth > 0) {
|
|
142
|
+
// nodeAfter is null - go up
|
|
143
|
+
$pos = $pos.doc.resolve($pos.after());
|
|
144
|
+
console.log("nodeAfter is null, go up", $pos);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (isValidPos($pos, dir)) {
|
|
148
|
+
console.log("found next valid $pos", $pos);
|
|
149
|
+
} else {
|
|
150
|
+
console.warn("failed to find next valid $pos", $pos, "keep initial pos", $initialPos);
|
|
151
|
+
}
|
|
152
|
+
console.groupEnd();
|
|
153
|
+
return isValidPos($pos, dir) ? $pos : $initialPos;
|
|
154
|
+
}
|
|
155
|
+
function findPrevPos($initialPos, dir) {
|
|
156
|
+
console.groupCollapsed("finding prev valid pos from $initialPos =", $initialPos);
|
|
157
|
+
let $pos = $initialPos;
|
|
158
|
+
while(!isValidPos($pos, dir) && ($pos.nodeBefore != null || $pos.depth > 0)){
|
|
159
|
+
if ($pos.nodeBefore != null) {
|
|
160
|
+
if ($pos.nodeBefore.isInline) {
|
|
161
|
+
// nodeBefore is inline - move in by one
|
|
162
|
+
$pos = $pos.doc.resolve($pos.pos - 1);
|
|
163
|
+
console.log("nodeBefore is inline, move to start of it", $pos);
|
|
164
|
+
} else {
|
|
165
|
+
// nodeBefore is not inline - find last inline descendant in nodeBefore
|
|
166
|
+
let localEndPos = null;
|
|
167
|
+
$pos.nodeBefore.descendants((child, pos)=>{
|
|
168
|
+
if (!child.isInline) return true;
|
|
169
|
+
localEndPos = pos + child.nodeSize;
|
|
170
|
+
return false;
|
|
171
|
+
});
|
|
172
|
+
if (localEndPos !== null) {
|
|
173
|
+
// we have a local position of a last inline descendant - convert it to global position
|
|
174
|
+
// move pos to start of node before, add 1 to "enter" nodeBefore, then add local pos
|
|
175
|
+
$pos = $pos.doc.resolve($pos.pos - $pos.nodeBefore.nodeSize + 1 + localEndPos);
|
|
176
|
+
console.log("found last inline descendant, move to end of it", $pos);
|
|
177
|
+
} else {
|
|
178
|
+
// unable to find last inline descendant of nodeBefore - just skip nodeBefore
|
|
179
|
+
$pos = $pos.doc.resolve($pos.pos - $pos.nodeBefore.nodeSize);
|
|
180
|
+
console.log("unable to find last inline descendant, move to start of nodeBefore", $pos);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else if ($pos.depth > 0) {
|
|
184
|
+
// nodeBefore is null - go up
|
|
185
|
+
$pos = $pos.doc.resolve($pos.before());
|
|
186
|
+
console.log("nodeBefore is null, go up", $pos);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (isValidPos($pos, dir)) {
|
|
190
|
+
console.log("found prev valid $pos", $pos);
|
|
191
|
+
} else {
|
|
192
|
+
console.warn("failed to find prev valid $pos", $pos, "keep initial pos", $initialPos);
|
|
193
|
+
}
|
|
194
|
+
console.groupEnd();
|
|
195
|
+
return isValidPos($pos, dir) ? $pos : $initialPos;
|
|
196
|
+
}
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
198
|
+
function isValidPos($pos, _dir) {
|
|
199
|
+
// text selection is only valid in nodes that allow inline content
|
|
200
|
+
// https://github.com/ProseMirror/prosemirror-state/blob/1.4.4/src/selection.ts#L219
|
|
201
|
+
if (!$pos.parent.inlineContent) {
|
|
202
|
+
console.warn("cursor invalid", "not in inlineContent node", $pos);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const { deletion } = getSuggestionMarks($pos.doc.type.schema);
|
|
206
|
+
// moving right - zwsp is on the right - skip forward
|
|
207
|
+
// if (dir > 0 && $pos.nodeAfter?.text?.startsWith(ZWSP)) {
|
|
208
|
+
// console.warn(
|
|
209
|
+
// "cursor invalid",
|
|
210
|
+
// "moving right - zwsp is on the right - skip forward",
|
|
211
|
+
// $pos,
|
|
212
|
+
// );
|
|
213
|
+
// return false;
|
|
214
|
+
// }
|
|
215
|
+
// // moving left - zwsp is on the left - skip backward
|
|
216
|
+
// if (dir < 0 && $pos.nodeBefore?.text?.startsWith(ZWSP)) {
|
|
217
|
+
// console.warn(
|
|
218
|
+
// "cursor invalid",
|
|
219
|
+
// "moving left - zwsp is on the left - skip backward",
|
|
220
|
+
// $pos,
|
|
221
|
+
// );
|
|
222
|
+
// return false;
|
|
223
|
+
// }
|
|
224
|
+
const deletionBefore = deletion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
225
|
+
const deletionAfter = deletion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
226
|
+
// between two deletions
|
|
227
|
+
if (deletionBefore && deletionAfter) {
|
|
228
|
+
console.warn("cursor invalid", "between two deletions", $pos);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
// between a deletion and a node boundary
|
|
232
|
+
if (deletionBefore && $pos.nodeAfter == null) {
|
|
233
|
+
console.warn("cursor invalid", "between a deletion and a node boundary", $pos);
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
// between a node boundary and a deletion, if deletion is not anchor
|
|
237
|
+
if ($pos.nodeBefore == null && deletionAfter && deletionAfter.attrs["type"] !== "anchor") {
|
|
238
|
+
console.warn("cursor invalid", "between a node boundary and a deletion", $pos);
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
// deletion is on the left, non-deletion is on the right
|
|
242
|
+
if (deletionBefore && $pos.nodeAfter != null && !deletionAfter) {
|
|
243
|
+
console.warn("cursor invalid", "deletion is on the left, non-deletion is on the right", $pos);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { addSuggestionMarks, insertion, deletion, modification, } from "./schema.js";
|
|
1
|
+
export { addSuggestionMarks, insertion, deletion, modification, hiddenDeletion, } from "./schema.js";
|
|
2
2
|
export { selectSuggestion, revertSuggestion, revertSuggestions, applySuggestion, applySuggestions, enableSuggestChanges, disableSuggestChanges, toggleSuggestChanges, } from "./commands.js";
|
|
3
3
|
export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled, } from "./plugin.js";
|
|
4
4
|
export { withSuggestChanges, transformToSuggestionTransaction, } from "./withSuggestChanges.js";
|
|
5
|
+
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled, } from "./ensureSelectionPlugin.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { addSuggestionMarks, insertion, deletion, modification } from "./schema.js";
|
|
1
|
+
export { addSuggestionMarks, insertion, deletion, modification, hiddenDeletion } from "./schema.js";
|
|
2
2
|
export { selectSuggestion, revertSuggestion, revertSuggestions, applySuggestion, applySuggestions, enableSuggestChanges, disableSuggestChanges, toggleSuggestChanges } from "./commands.js";
|
|
3
3
|
export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled } from "./plugin.js";
|
|
4
4
|
export { withSuggestChanges, transformToSuggestionTransaction } from "./withSuggestChanges.js";
|
|
5
|
+
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled } from "./ensureSelectionPlugin.js";
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin, PluginKey
|
|
1
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
2
|
import { getSuggestionDecorations } from "./decorations.js";
|
|
3
3
|
export const suggestChangesKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes");
|
|
4
4
|
export function suggestChanges() {
|
|
@@ -17,19 +17,7 @@ export function suggestChanges() {
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
props: {
|
|
20
|
-
decorations: getSuggestionDecorations
|
|
21
|
-
// Add a custom keydown handler that skips over any zero-width
|
|
22
|
-
// spaces that we've inserted so that users aren't aware of them
|
|
23
|
-
handleKeyDown (view, event) {
|
|
24
|
-
if (event.key === "ArrowRight" && view.state.selection instanceof TextSelection && view.state.selection.empty && view.state.selection.$cursor?.nodeAfter?.text?.startsWith("\u200B")) {
|
|
25
|
-
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, view.state.selection.$cursor.pos + 1)));
|
|
26
|
-
}
|
|
27
|
-
if (event.key === "ArrowLeft" && view.state.selection instanceof TextSelection && view.state.selection.empty && view.state.selection.$cursor?.nodeBefore?.text?.endsWith("\u200B")) {
|
|
28
|
-
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, view.state.selection.$cursor.pos - 1)));
|
|
29
|
-
}
|
|
30
|
-
// Never block any other handlers from running after
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
20
|
+
decorations: getSuggestionDecorations
|
|
33
21
|
}
|
|
34
22
|
});
|
|
35
23
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getSuggestionMarks } from "./utils.js";
|
|
2
|
+
import { Transform } from "prosemirror-transform";
|
|
3
|
+
import { ZWSP } from "./constants.js";
|
|
4
|
+
export function prependDeletionsWithZWSP(transaction, opts) {
|
|
5
|
+
console.groupCollapsed("prepend deletions with zwsp");
|
|
6
|
+
const { deletion } = getSuggestionMarks(transaction.doc.type.schema);
|
|
7
|
+
let transform = new Transform(transaction.doc);
|
|
8
|
+
transform.doc.descendants((node, pos)=>{
|
|
9
|
+
const mark = node.marks.find((mark)=>mark.type === deletion && mark.attrs["type"] === "anchor");
|
|
10
|
+
if (!node.isText || mark == null) return true;
|
|
11
|
+
const mappedPos = transform.mapping.map(pos);
|
|
12
|
+
transform.delete(mappedPos, mappedPos + node.nodeSize);
|
|
13
|
+
console.log("found zwsp, deleted", {
|
|
14
|
+
from: mappedPos,
|
|
15
|
+
to: mappedPos + node.nodeSize
|
|
16
|
+
});
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
transform.steps.forEach((step)=>{
|
|
20
|
+
transaction.step(step);
|
|
21
|
+
});
|
|
22
|
+
console.log(`added ${String(transform.steps.length)} remove zwsp steps to tr`);
|
|
23
|
+
if (opts?.experimental_deletions !== "hidden") {
|
|
24
|
+
console.log("deletions are visible, skipping prepend zwsp");
|
|
25
|
+
console.groupEnd();
|
|
26
|
+
return transaction;
|
|
27
|
+
}
|
|
28
|
+
transform = new Transform(transaction.doc);
|
|
29
|
+
transform.doc.descendants((node, pos)=>{
|
|
30
|
+
if (!node.inlineContent) return true;
|
|
31
|
+
const mark = node.firstChild?.marks.find((mark)=>mark.type === deletion);
|
|
32
|
+
if (mark == null) return true;
|
|
33
|
+
if (mark.attrs["type"] === "anchor") return true;
|
|
34
|
+
const zwspNode = transform.doc.type.schema.text(ZWSP, [
|
|
35
|
+
deletion.create({
|
|
36
|
+
type: "anchor",
|
|
37
|
+
id: mark.attrs["id"]
|
|
38
|
+
})
|
|
39
|
+
]);
|
|
40
|
+
const mappedPos = transform.mapping.map(pos);
|
|
41
|
+
transform.insert(mappedPos + 1, zwspNode);
|
|
42
|
+
console.log("found first-child-deletion inline content node, inserted zwsp at", {
|
|
43
|
+
pos,
|
|
44
|
+
mappedPos
|
|
45
|
+
});
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
transform.steps.forEach((step)=>{
|
|
49
|
+
transaction.step(step);
|
|
50
|
+
});
|
|
51
|
+
console.log(`added ${String(transform.steps.length)} add zwsp steps to tr`);
|
|
52
|
+
console.groupEnd();
|
|
53
|
+
return transaction;
|
|
54
|
+
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type MarkSpec } from "prosemirror-model";
|
|
2
2
|
export declare const deletion: MarkSpec;
|
|
3
|
+
export declare const hiddenDeletion: MarkSpec;
|
|
3
4
|
export declare const insertion: MarkSpec;
|
|
4
5
|
export declare const modification: MarkSpec;
|
|
5
6
|
/**
|
|
6
7
|
* Add the deletion, insertion, and modification marks to
|
|
7
8
|
* the provided MarkSpec map.
|
|
8
9
|
*/
|
|
9
|
-
export declare function addSuggestionMarks<Marks extends string>(marks: Record<Marks, MarkSpec
|
|
10
|
+
export declare function addSuggestionMarks<Marks extends string>(marks: Record<Marks, MarkSpec>, opts?: {
|
|
11
|
+
experimental_deletions?: "hidden" | "visible";
|
|
12
|
+
}): Record<Marks | "deletion" | "insertion" | "modification", MarkSpec>;
|
package/dist/schema.js
CHANGED
|
@@ -43,6 +43,27 @@ export const deletion = {
|
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
};
|
|
46
|
+
export const hiddenDeletion = {
|
|
47
|
+
...deletion,
|
|
48
|
+
toDOM (mark, inline) {
|
|
49
|
+
return [
|
|
50
|
+
"del",
|
|
51
|
+
{
|
|
52
|
+
"data-id": JSON.stringify(mark.attrs["id"]),
|
|
53
|
+
"data-inline": String(inline),
|
|
54
|
+
...!inline && {
|
|
55
|
+
style: "display: block"
|
|
56
|
+
},
|
|
57
|
+
...inline && mark.attrs["type"] !== "anchor" && {
|
|
58
|
+
style: "font-size: 0px;"
|
|
59
|
+
},
|
|
60
|
+
"data-type": JSON.stringify(mark.attrs["type"]),
|
|
61
|
+
"data-data": JSON.stringify(mark.attrs["data"])
|
|
62
|
+
},
|
|
63
|
+
0
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
};
|
|
46
67
|
export const insertion = {
|
|
47
68
|
inclusive: false,
|
|
48
69
|
excludes: "deletion modification insertion",
|
|
@@ -140,10 +161,10 @@ export const modification = {
|
|
|
140
161
|
/**
|
|
141
162
|
* Add the deletion, insertion, and modification marks to
|
|
142
163
|
* the provided MarkSpec map.
|
|
143
|
-
*/ export function addSuggestionMarks(marks) {
|
|
164
|
+
*/ export function addSuggestionMarks(marks, opts) {
|
|
144
165
|
return {
|
|
145
166
|
...marks,
|
|
146
|
-
deletion,
|
|
167
|
+
deletion: opts?.experimental_deletions === "hidden" ? hiddenDeletion : deletion,
|
|
147
168
|
insertion,
|
|
148
169
|
modification
|
|
149
170
|
};
|
|
@@ -24,4 +24,6 @@ export declare function transformToSuggestionTransaction(originalTransaction: Tr
|
|
|
24
24
|
* applying them, e.g. by marking a range with the deletion mark rather
|
|
25
25
|
* than removing it from the document.
|
|
26
26
|
*/
|
|
27
|
-
export declare function withSuggestChanges(dispatchTransaction?: EditorView["dispatch"], generateId?: (schema: Schema, doc?: Node) => SuggestionId
|
|
27
|
+
export declare function withSuggestChanges(dispatchTransaction?: EditorView["dispatch"], generateId?: (schema: Schema, doc?: Node) => SuggestionId, opts?: {
|
|
28
|
+
experimental_deletions?: "hidden" | "visible";
|
|
29
|
+
}): EditorView["dispatch"];
|
|
@@ -9,6 +9,7 @@ import { suggestReplaceStep } from "./replaceStep.js";
|
|
|
9
9
|
import { isSuggestChangesEnabled, suggestChangesKey } from "./plugin.js";
|
|
10
10
|
import { generateNextNumberId } from "./generateId.js";
|
|
11
11
|
import { getSuggestionMarks } from "./utils.js";
|
|
12
|
+
import { prependDeletionsWithZWSP } from "./prependDeletionsWithZWSP.js";
|
|
12
13
|
function getStepHandler(step) {
|
|
13
14
|
if (step instanceof ReplaceStep) {
|
|
14
15
|
return suggestReplaceStep;
|
|
@@ -102,13 +103,16 @@ function getStepHandler(step) {
|
|
|
102
103
|
* These modified transactions will suggest changes instead of directly
|
|
103
104
|
* applying them, e.g. by marking a range with the deletion mark rather
|
|
104
105
|
* than removing it from the document.
|
|
105
|
-
*/ export function withSuggestChanges(dispatchTransaction, generateId) {
|
|
106
|
+
*/ export function withSuggestChanges(dispatchTransaction, generateId, opts) {
|
|
106
107
|
const dispatch = dispatchTransaction ?? function(tr) {
|
|
107
108
|
this.updateState(this.state.apply(tr));
|
|
108
109
|
};
|
|
109
110
|
return function dispatchTransaction(tr) {
|
|
110
111
|
const ySyncMeta = tr.getMeta("y-sync$") ?? {};
|
|
111
112
|
const transaction = isSuggestChangesEnabled(this.state) && !tr.getMeta("history$") && !tr.getMeta("collab$") && !ySyncMeta.isUndoRedoOperation && !ySyncMeta.isChangeOrigin && !("skip" in (tr.getMeta(suggestChangesKey) ?? {})) ? transformToSuggestionTransaction(tr, this.state, generateId) : tr;
|
|
113
|
+
if (transaction.docChanged) {
|
|
114
|
+
prependDeletionsWithZWSP(transaction, opts);
|
|
115
|
+
}
|
|
112
116
|
dispatch.call(this, transaction);
|
|
113
117
|
};
|
|
114
118
|
}
|