@magic-marker/prosemirror-suggest-changes 0.4.1-wrap-unwrap.1 → 0.4.1-wrap-unwrap.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/__tests__/playwrightPage.d.ts +6 -3
- package/dist/{ensureSelectionPlugin.js → features/ensureValidSelection/ensureSelectionPlugin.js} +44 -77
- package/dist/features/ensureValidSelection/ensureSelectionPlugin.test.d.ts +1 -0
- package/dist/features/ensureValidSelection/ensureSelectionPlugin.test.js +112 -0
- package/dist/features/ensureValidSelection/selectionPosition.d.ts +3 -0
- package/dist/features/ensureValidSelection/selectionPosition.js +50 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/testing/e2eTestSchema.d.ts +1 -1
- package/dist/testing/testBuilders.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{ensureSelectionPlugin.d.ts → features/ensureValidSelection/ensureSelectionPlugin.d.ts} +0 -0
|
@@ -50,9 +50,7 @@ export declare class EditorPage {
|
|
|
50
50
|
* editor.view.state is a different object (ProseMirror creates a new
|
|
51
51
|
* immutable state on every transaction).
|
|
52
52
|
*/
|
|
53
|
-
insertText(text: string
|
|
54
|
-
waitForSelectionChange?: boolean;
|
|
55
|
-
}): Promise<void>;
|
|
53
|
+
insertText(text: string): Promise<void>;
|
|
56
54
|
/**
|
|
57
55
|
* Press a key multiple times, waiting for editor state update after each press.
|
|
58
56
|
*/
|
|
@@ -60,4 +58,9 @@ export declare class EditorPage {
|
|
|
60
58
|
waitForSelectionChange?: boolean;
|
|
61
59
|
}): Promise<void>;
|
|
62
60
|
setNextNodeId(nextNodeId: number): Promise<void>;
|
|
61
|
+
enableTrackChanges(): Promise<void>;
|
|
62
|
+
disableTrackChanges(): Promise<void>;
|
|
63
|
+
focusEditor(): Promise<void>;
|
|
64
|
+
setCursorToStart(): Promise<void>;
|
|
65
|
+
setCursorToEnd(): Promise<void>;
|
|
63
66
|
}
|
package/dist/{ensureSelectionPlugin.js → features/ensureValidSelection/ensureSelectionPlugin.js}
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
2
|
-
import {
|
|
3
|
-
import { ZWSP } from "./constants.js";
|
|
2
|
+
import { getInvalidSelectionPositionReason } from "./selectionPosition.js";
|
|
4
3
|
const TRACE_ENABLED = false;
|
|
5
4
|
function trace(...args) {
|
|
6
5
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -47,8 +46,9 @@ export function ensureSelection() {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
},
|
|
50
|
-
appendTransaction (
|
|
49
|
+
appendTransaction (transactions, oldState, newState) {
|
|
51
50
|
const pluginState = ensureSelectionKey.getState(newState);
|
|
51
|
+
const isSelectionOnly = transactions.every((tr)=>!tr.docChanged);
|
|
52
52
|
if (!(newState.selection instanceof TextSelection)) {
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
@@ -58,12 +58,12 @@ export function ensureSelection() {
|
|
|
58
58
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
59
59
|
if (TRACE_ENABLED) console.groupCollapsed("[ensureSelectionPlugin]", "appendTransaction");
|
|
60
60
|
trace("appendTransaction", "search for new valid $anchor...");
|
|
61
|
-
let $newAnchor = getNewValidPos(
|
|
61
|
+
let $newAnchor = getNewValidPos(oldState.selection.$anchor, newState.selection.$anchor, isSelectionOnly, pluginState);
|
|
62
62
|
trace("appendTransaction", "new valid $anchor", $newAnchor?.pos, {
|
|
63
63
|
$newAnchor
|
|
64
64
|
});
|
|
65
65
|
trace("appendTransaction", "search for new valid $head...");
|
|
66
|
-
let $newHead = newState.selection.empty ? $newAnchor : getNewValidPos(
|
|
66
|
+
let $newHead = newState.selection.empty ? $newAnchor : getNewValidPos(oldState.selection.$head, newState.selection.$head, isSelectionOnly, pluginState);
|
|
67
67
|
trace("appendTransaction", "new valid $head", $newHead?.pos, {
|
|
68
68
|
$newHead
|
|
69
69
|
});
|
|
@@ -95,77 +95,9 @@ export function isEnsureSelectionEnabled() {
|
|
|
95
95
|
return true;
|
|
96
96
|
}
|
|
97
97
|
function isPosValid($pos) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: not in inlineContent node", {
|
|
102
|
-
$pos
|
|
103
|
-
});
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
const { deletion, insertion } = getSuggestionMarks($pos.doc.type.schema);
|
|
107
|
-
const deletionBefore = deletion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
108
|
-
const deletionAfter = deletion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
109
|
-
const isAnchorBefore = deletionBefore && deletionBefore.attrs["type"] === "anchor";
|
|
110
|
-
const isAnchorAfter = deletionAfter && deletionAfter.attrs["type"] === "anchor";
|
|
111
|
-
if (isAnchorBefore && deletionAfter && !isAnchorAfter) {
|
|
112
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between deletion anchor and non-anchor deletion", {
|
|
113
|
-
$pos
|
|
114
|
-
});
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (deletionBefore && deletionAfter && !isAnchorBefore && !isAnchorAfter) {
|
|
118
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between two non-anchor deletions", {
|
|
119
|
-
$pos
|
|
120
|
-
});
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
if ($pos.nodeBefore == null && deletionAfter && !isAnchorAfter) {
|
|
124
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between node boundary and non-anchor deletion", {
|
|
125
|
-
$pos
|
|
126
|
-
});
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
if (deletionBefore && $pos.nodeAfter == null && !isAnchorBefore) {
|
|
130
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and node boundary", {
|
|
131
|
-
$pos
|
|
132
|
-
});
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
if (deletionBefore && !isAnchorBefore && $pos.nodeAfter == null) {
|
|
136
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and node boundary", {
|
|
137
|
-
$pos
|
|
138
|
-
});
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
if (deletionBefore && !isAnchorBefore) {
|
|
142
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and anything", {
|
|
143
|
-
$pos
|
|
144
|
-
});
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
const insertionBefore = insertion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
148
|
-
const insertionAfter = insertion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
149
|
-
const ZWSP_REGEXP = new RegExp(ZWSP, "g");
|
|
150
|
-
const isZWSPBefore = $pos.nodeBefore && $pos.nodeBefore.isText && $pos.nodeBefore.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
151
|
-
const isZWSPAfter = $pos.nodeAfter && $pos.nodeAfter.isText && $pos.nodeAfter.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
152
|
-
if (insertionBefore && insertionAfter && isZWSPBefore && isZWSPAfter) {
|
|
153
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between two ZWSP insertions", {
|
|
154
|
-
$pos
|
|
155
|
-
});
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
if (insertionBefore && isZWSPBefore && $pos.nodeAfter == null && // a position like this:
|
|
159
|
-
// <p><insertion>ZWSP</insertion>|</p>
|
|
160
|
-
// because it means this paragraph was just created and it's empty
|
|
161
|
-
$pos.parent.textContent.replace(ZWSP_REGEXP, "") !== "") {
|
|
162
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between ZWSP insertion and right node boundary", {
|
|
163
|
-
$pos
|
|
164
|
-
});
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
if (insertionAfter && isZWSPAfter && $pos.nodeBefore == null) {
|
|
168
|
-
trace("isPosValid", $pos.pos, "pos invalid", "reason: between ZWSP insertion and left node boundary", {
|
|
98
|
+
const invalidReason = getInvalidSelectionPositionReason($pos);
|
|
99
|
+
if (invalidReason) {
|
|
100
|
+
trace("isPosValid", $pos.pos, "pos invalid", `reason: ${invalidReason}`, {
|
|
169
101
|
$pos
|
|
170
102
|
});
|
|
171
103
|
return false;
|
|
@@ -241,7 +173,38 @@ function findPreviousValidPos($initialPos) {
|
|
|
241
173
|
}
|
|
242
174
|
return isPosValid($pos) ? $pos : null;
|
|
243
175
|
}
|
|
244
|
-
function
|
|
176
|
+
function findNearestValidPosInSameParent($initialPos) {
|
|
177
|
+
if (!$initialPos.parent.inlineContent) return null;
|
|
178
|
+
const start = $initialPos.start();
|
|
179
|
+
const end = $initialPos.end();
|
|
180
|
+
const maxDistance = Math.max($initialPos.pos - start, end - $initialPos.pos);
|
|
181
|
+
for(let distance = 0; distance <= maxDistance; distance++){
|
|
182
|
+
const nextPos = $initialPos.pos + distance;
|
|
183
|
+
if (nextPos <= end) {
|
|
184
|
+
const $nextPos = $initialPos.doc.resolve(nextPos);
|
|
185
|
+
if (isPosValid($nextPos)) return $nextPos;
|
|
186
|
+
}
|
|
187
|
+
const prevPos = $initialPos.pos - distance;
|
|
188
|
+
if (distance > 0 && prevPos >= start) {
|
|
189
|
+
const $prevPos = $initialPos.doc.resolve(prevPos);
|
|
190
|
+
if (isPosValid($prevPos)) return $prevPos;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
function getNewValidPosInSelectionDestinationParent($oldPos, $newPos) {
|
|
196
|
+
if (isPosValid($newPos)) return null;
|
|
197
|
+
if ($oldPos.parent === $newPos.parent) return null;
|
|
198
|
+
if (!$newPos.parent.inlineContent) return null;
|
|
199
|
+
const $sameParentPos = findNearestValidPosInSameParent($newPos);
|
|
200
|
+
trace("getNewValidPosInSelectionDestinationParent", "$sameParentPos", $sameParentPos?.pos, {
|
|
201
|
+
$oldPos,
|
|
202
|
+
$newPos,
|
|
203
|
+
$sameParentPos
|
|
204
|
+
});
|
|
205
|
+
return $sameParentPos;
|
|
206
|
+
}
|
|
207
|
+
function getNewValidPosByDirection($pos, dir) {
|
|
245
208
|
if (isPosValid($pos)) return $pos;
|
|
246
209
|
trace("getNewValidPos for", $pos.pos, {
|
|
247
210
|
$pos,
|
|
@@ -298,6 +261,10 @@ function getNewValidPos($pos, dir) {
|
|
|
298
261
|
const prevDist = Math.abs($pos.pos - $prevValidPos.pos);
|
|
299
262
|
return nextDist <= prevDist ? $nextValidPos : $prevValidPos;
|
|
300
263
|
}
|
|
264
|
+
function getNewValidPos($oldPos, $newPos, isSelectionOnly, pluginState) {
|
|
265
|
+
const $sameParentPos = isSelectionOnly ? getNewValidPosInSelectionDestinationParent($oldPos, $newPos) : null;
|
|
266
|
+
return $sameParentPos ?? getNewValidPosByDirection($newPos, getDirection($oldPos, $newPos, pluginState));
|
|
267
|
+
}
|
|
301
268
|
function getDirection($oldPos, $newPos, pluginState) {
|
|
302
269
|
if (pluginState?.handleKeyDown.backspace) return "left";
|
|
303
270
|
if ($newPos.pos > $oldPos.pos) return "right";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
|
|
2
|
+
import { assert, describe, it } from "vitest";
|
|
3
|
+
import { ZWSP } from "../../constants.js";
|
|
4
|
+
import { testBuilders } from "../../testing/testBuilders.js";
|
|
5
|
+
import { ensureSelection } from "./ensureSelectionPlugin.js";
|
|
6
|
+
function createState(doc, selection) {
|
|
7
|
+
return EditorState.create({
|
|
8
|
+
doc,
|
|
9
|
+
selection,
|
|
10
|
+
plugins: [
|
|
11
|
+
ensureSelection()
|
|
12
|
+
]
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function applyTextSelection(state, anchor, head = anchor) {
|
|
16
|
+
const selection = TextSelection.create(state.doc, anchor, head);
|
|
17
|
+
const transaction = state.tr.setSelection(selection);
|
|
18
|
+
return state.apply(transaction);
|
|
19
|
+
}
|
|
20
|
+
function assertTextSelection(state, anchor, head = anchor) {
|
|
21
|
+
assert(state.selection instanceof TextSelection);
|
|
22
|
+
assert.equal(state.selection.anchor, anchor);
|
|
23
|
+
assert.equal(state.selection.head, head);
|
|
24
|
+
}
|
|
25
|
+
function getTag(doc, tag) {
|
|
26
|
+
const pos = doc.tag[tag];
|
|
27
|
+
assert(pos !== undefined, `Expected tag "${tag}" to exist`);
|
|
28
|
+
return pos;
|
|
29
|
+
}
|
|
30
|
+
describe("ensureSelection", ()=>{
|
|
31
|
+
it("leaves valid text selections unchanged", ()=>{
|
|
32
|
+
const doc = testBuilders.doc(testBuilders.paragraph("one<from>"), testBuilders.paragraph("two<to>"));
|
|
33
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "from")));
|
|
34
|
+
const nextState = applyTextSelection(state, getTag(doc, "to"));
|
|
35
|
+
assertTextSelection(nextState, getTag(doc, "to"));
|
|
36
|
+
});
|
|
37
|
+
it("ignores non-text selections", ()=>{
|
|
38
|
+
const doc = testBuilders.doc(testBuilders.image({
|
|
39
|
+
src: "https://example.com/image.png"
|
|
40
|
+
}), testBuilders.paragraph("after<cursor>"));
|
|
41
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "cursor")));
|
|
42
|
+
const nextState = state.apply(state.tr.setSelection(NodeSelection.create(doc, 0)));
|
|
43
|
+
assert(nextState.selection instanceof NodeSelection);
|
|
44
|
+
assert.equal(nextState.selection.anchor, 0);
|
|
45
|
+
});
|
|
46
|
+
it("prefers a valid position in the destination textblock for selection-only movement", ()=>{
|
|
47
|
+
const doc = testBuilders.doc(testBuilders.paragraph("Item 2<old>"), testBuilders.paragraph(testBuilders.insertion({
|
|
48
|
+
id: 1
|
|
49
|
+
}, "<invalid>" + ZWSP + "<valid>")), testBuilders.paragraph(testBuilders.insertion({
|
|
50
|
+
id: 1
|
|
51
|
+
}, ZWSP), "Item 3"));
|
|
52
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
53
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
54
|
+
assertTextSelection(nextState, getTag(doc, "valid"));
|
|
55
|
+
});
|
|
56
|
+
it("uses direction fallback when the transaction changes the document", ()=>{
|
|
57
|
+
const doc = testBuilders.doc(testBuilders.paragraph("<markStart>A<expected><markEnd>"), testBuilders.paragraph(testBuilders.insertion({
|
|
58
|
+
id: 1
|
|
59
|
+
}, "<invalid>" + ZWSP + "<sameParent>")), testBuilders.paragraph(testBuilders.insertion({
|
|
60
|
+
id: 1
|
|
61
|
+
}, ZWSP), "<old>Item 3"));
|
|
62
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
63
|
+
const transaction = state.tr;
|
|
64
|
+
transaction.addMark(getTag(doc, "markStart"), getTag(doc, "markEnd"), testBuilders.schema.marks.strong.create());
|
|
65
|
+
transaction.setSelection(TextSelection.create(transaction.doc, getTag(doc, "invalid")));
|
|
66
|
+
const nextState = state.apply(transaction);
|
|
67
|
+
assertTextSelection(nextState, getTag(doc, "expected"));
|
|
68
|
+
});
|
|
69
|
+
it("uses direction fallback for invalid positions in the same textblock", ()=>{
|
|
70
|
+
const doc = testBuilders.doc(testBuilders.paragraph("A", testBuilders.deletion({
|
|
71
|
+
id: 1
|
|
72
|
+
}, "<expected>B<invalid>"), "C<old>"));
|
|
73
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
74
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
75
|
+
assertTextSelection(nextState, getTag(doc, "expected"));
|
|
76
|
+
});
|
|
77
|
+
it("keeps deletion anchor and non-anchor deletion positions continuous", ()=>{
|
|
78
|
+
const doc = testBuilders.doc(testBuilders.paragraph(testBuilders.deletion({
|
|
79
|
+
id: 1,
|
|
80
|
+
type: "anchor"
|
|
81
|
+
}, "<expected>" + ZWSP + "<invalid>"), testBuilders.deletion({
|
|
82
|
+
id: 1
|
|
83
|
+
}, "deleted"), "visible<old>"));
|
|
84
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
85
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
86
|
+
assertTextSelection(nextState, getTag(doc, "expected"));
|
|
87
|
+
});
|
|
88
|
+
it("moves hidden-deletion-style boundary positions to visible content", ()=>{
|
|
89
|
+
const doc = testBuilders.doc(testBuilders.paragraph("before<old>"), testBuilders.paragraph(testBuilders.deletion({
|
|
90
|
+
id: 1
|
|
91
|
+
}, "<invalid>deleted"), "v<expected>isible"));
|
|
92
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
93
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
94
|
+
assertTextSelection(nextState, getTag(doc, "expected"));
|
|
95
|
+
});
|
|
96
|
+
it("keeps paired split marker positions attached to textblock boundaries", ()=>{
|
|
97
|
+
const doc = testBuilders.doc(testBuilders.paragraph("before<old>"), testBuilders.paragraph("content<expected>", testBuilders.insertion({
|
|
98
|
+
id: 1
|
|
99
|
+
}, ZWSP + "<invalid>")));
|
|
100
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
101
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
102
|
+
assertTextSelection(nextState, getTag(doc, "expected"));
|
|
103
|
+
});
|
|
104
|
+
it("moves positions between adjacent insertion ZWSPs to a valid neighbor", ()=>{
|
|
105
|
+
const doc = testBuilders.doc(testBuilders.paragraph("before<old>", testBuilders.insertion({
|
|
106
|
+
id: 1
|
|
107
|
+
}, "<expected>" + ZWSP + "<invalid>" + ZWSP), "<valid>after"));
|
|
108
|
+
const state = createState(doc, TextSelection.create(doc, getTag(doc, "old")));
|
|
109
|
+
const nextState = applyTextSelection(state, getTag(doc, "invalid"));
|
|
110
|
+
assertTextSelection(nextState, getTag(doc, "valid"));
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ResolvedPos } from "prosemirror-model";
|
|
2
|
+
export type InvalidSelectionPositionReason = "not-in-inline-content" | "between-deletion-anchor-and-non-anchor-deletion" | "between-two-non-anchor-deletions" | "between-node-boundary-and-non-anchor-deletion" | "between-non-anchor-deletion-and-node-boundary" | "between-non-anchor-deletion-and-anything" | "between-two-zwsp-insertions" | "between-zwsp-insertion-and-right-node-boundary" | "between-zwsp-insertion-and-left-node-boundary";
|
|
3
|
+
export declare function getInvalidSelectionPositionReason($pos: ResolvedPos): InvalidSelectionPositionReason | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ZWSP } from "../../constants.js";
|
|
2
|
+
import { getSuggestionMarks } from "../../utils.js";
|
|
3
|
+
export function getInvalidSelectionPositionReason($pos) {
|
|
4
|
+
// Text selection is only valid in nodes that allow inline content.
|
|
5
|
+
// https://github.com/ProseMirror/prosemirror-state/blob/1.4.4/src/selection.ts#L219
|
|
6
|
+
if (!$pos.parent.inlineContent) {
|
|
7
|
+
return "not-in-inline-content";
|
|
8
|
+
}
|
|
9
|
+
const { deletion, insertion } = getSuggestionMarks($pos.doc.type.schema);
|
|
10
|
+
const deletionBefore = deletion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
11
|
+
const deletionAfter = deletion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
12
|
+
const isAnchorBefore = deletionBefore && deletionBefore.attrs["type"] === "anchor";
|
|
13
|
+
const isAnchorAfter = deletionAfter && deletionAfter.attrs["type"] === "anchor";
|
|
14
|
+
if (isAnchorBefore && deletionAfter && !isAnchorAfter) {
|
|
15
|
+
return "between-deletion-anchor-and-non-anchor-deletion";
|
|
16
|
+
}
|
|
17
|
+
if (deletionBefore && deletionAfter && !isAnchorBefore && !isAnchorAfter) {
|
|
18
|
+
return "between-two-non-anchor-deletions";
|
|
19
|
+
}
|
|
20
|
+
if ($pos.nodeBefore == null && deletionAfter && !isAnchorAfter) {
|
|
21
|
+
return "between-node-boundary-and-non-anchor-deletion";
|
|
22
|
+
}
|
|
23
|
+
if (deletionBefore && $pos.nodeAfter == null && !isAnchorBefore) {
|
|
24
|
+
return "between-non-anchor-deletion-and-node-boundary";
|
|
25
|
+
}
|
|
26
|
+
if (deletionBefore && !isAnchorBefore && $pos.nodeAfter == null) {
|
|
27
|
+
return "between-non-anchor-deletion-and-node-boundary";
|
|
28
|
+
}
|
|
29
|
+
if (deletionBefore && !isAnchorBefore) {
|
|
30
|
+
return "between-non-anchor-deletion-and-anything";
|
|
31
|
+
}
|
|
32
|
+
const insertionBefore = insertion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
33
|
+
const insertionAfter = insertion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
34
|
+
const ZWSP_REGEXP = new RegExp(ZWSP, "g");
|
|
35
|
+
const isZWSPBefore = $pos.nodeBefore && $pos.nodeBefore.isText && $pos.nodeBefore.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
36
|
+
const isZWSPAfter = $pos.nodeAfter && $pos.nodeAfter.isText && $pos.nodeAfter.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
37
|
+
if (insertionBefore && insertionAfter && isZWSPBefore && isZWSPAfter) {
|
|
38
|
+
return "between-two-zwsp-insertions";
|
|
39
|
+
}
|
|
40
|
+
if (insertionBefore && isZWSPBefore && $pos.nodeAfter == null && // A position like this:
|
|
41
|
+
// <p><insertion>ZWSP</insertion>|</p>
|
|
42
|
+
// because it means this paragraph was just created and it's empty.
|
|
43
|
+
$pos.parent.textContent.replace(ZWSP_REGEXP, "") !== "") {
|
|
44
|
+
return "between-zwsp-insertion-and-right-node-boundary";
|
|
45
|
+
}
|
|
46
|
+
if (insertionAfter && isZWSPAfter && $pos.nodeBefore == null) {
|
|
47
|
+
return "between-zwsp-insertion-and-left-node-boundary";
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { addSuggestionMarks, insertion, deletion, modification, hiddenDeletion,
|
|
|
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, suggestStructureChanges as experimental_suggestStructureChanges, } from "./withSuggestChanges.js";
|
|
5
|
-
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled, } from "./ensureSelectionPlugin.js";
|
|
5
|
+
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled, } from "./features/ensureValidSelection/ensureSelectionPlugin.js";
|
|
6
6
|
export { guardStructureMarkAttrs } from "./features/wrapUnwrap/types.js";
|
|
7
7
|
export type { Op as StructureOp, StructureMarkAttrs, StructuralContextPath, } from "./features/wrapUnwrap/types.js";
|
|
8
8
|
export { wrappingInputRule as experimental_wrappingInputRule } from "./wrappingInputRule.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,6 @@ export { addSuggestionMarks, insertion, deletion, modification, hiddenDeletion,
|
|
|
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, suggestStructureChanges as experimental_suggestStructureChanges } from "./withSuggestChanges.js";
|
|
5
|
-
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled } from "./ensureSelectionPlugin.js";
|
|
5
|
+
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled } from "./features/ensureValidSelection/ensureSelectionPlugin.js";
|
|
6
6
|
export { guardStructureMarkAttrs } from "./features/wrapUnwrap/types.js";
|
|
7
7
|
export { wrappingInputRule as experimental_wrappingInputRule } from "./wrappingInputRule.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Schema } from "prosemirror-model";
|
|
2
|
-
export declare function createSchema(deletionMarksVisibility?: "hidden" | "visible"): Schema<"blockquote" | "text" | "
|
|
2
|
+
export declare function createSchema(deletionMarksVisibility?: "hidden" | "visible"): Schema<"blockquote" | "text" | "image" | "doc" | "orderedList" | "bulletList" | "listItem" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "hard_break" | "hardBreak", "insertion" | "deletion" | "modification" | "structure" | "code" | "em" | "link" | "strong">;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema, type Node } from "prosemirror-model";
|
|
2
2
|
import { type MarkBuilder, type NodeBuilder } from "prosemirror-test-builder";
|
|
3
|
-
export declare const schema: Schema<"blockquote" | "text" | "
|
|
3
|
+
export declare const schema: Schema<"blockquote" | "text" | "image" | "doc" | "orderedList" | "bulletList" | "listItem" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "hard_break", "insertion" | "deletion" | "modification" | "structure" | "code" | "em" | "link" | "strong" | "difficulty">;
|
|
4
4
|
export declare const testBuilders: { [NodeTypeName in keyof (typeof schema)["nodes"]]: NodeBuilder; } & { [MarkTypeName in keyof (typeof schema)["marks"]]: MarkBuilder; } & {
|
|
5
5
|
schema: typeof schema;
|
|
6
6
|
};
|
package/package.json
CHANGED
|
File without changes
|