@magic-marker/prosemirror-suggest-changes 0.4.0 → 0.4.1-wrap-unwrap.1
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__/playwrightHelpers.d.ts +2 -2
- package/dist/__tests__/playwrightPage.d.ts +50 -2
- package/dist/commands.js +222 -43
- package/dist/ensureSelectionPlugin.js +3 -3
- package/dist/features/joinOnDelete/__tests__/joinOnDeleteInLists.playwright.test.d.ts +1 -0
- package/dist/features/joinOnDelete/__tests__/joinOnDeleteInListsTipTapStyle.playwright.test.d.ts +1 -0
- package/dist/features/joinOnDelete/__tests__/listWithJoinsAndStructureMarks.playwright.test.d.ts +1 -0
- package/dist/features/joinOnDelete/index.d.ts +4 -19
- package/dist/features/joinOnDelete/index.js +166 -53
- package/dist/features/joinOnDelete/normalizeJoinNodesMetadata.d.ts +6 -0
- package/dist/features/joinOnDelete/normalizeJoinNodesMetadata.js +24 -0
- package/dist/features/joinOnDelete/types.d.ts +36 -0
- package/dist/features/joinOnDelete/types.js +23 -0
- package/dist/features/transactionShaping/detectSpecialTransactionShape.d.ts +3 -0
- package/dist/features/transactionShaping/detectSpecialTransactionShape.js +4 -0
- package/dist/features/transactionShaping/index.d.ts +3 -0
- package/dist/features/transactionShaping/index.js +11 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/detectTipTapParagraphIntoListJoin.d.ts +3 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/detectTipTapParagraphIntoListJoin.js +48 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/detectTipTapParagraphIntoListJoin.test.d.ts +1 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/detectTipTapParagraphIntoListJoin.test.js +188 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/handleTipTapParagraphIntoListJoin.d.ts +3 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/handleTipTapParagraphIntoListJoin.js +69 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/index.d.ts +2 -0
- package/dist/features/transactionShaping/tipTapParagraphIntoListJoin/index.js +2 -0
- package/dist/features/transactionShaping/types.d.ts +20 -0
- package/dist/features/transactionShaping/types.js +1 -0
- package/dist/features/wrapUnwrap/__tests__/blockquoteStructure.playwright.test.d.ts +1 -0
- package/dist/features/wrapUnwrap/__tests__/buildMaterializedPaths.test.d.ts +1 -0
- package/dist/features/wrapUnwrap/__tests__/listStructure.playwright.test.d.ts +1 -0
- package/dist/features/wrapUnwrap/__tests__/listStructureTextEdits.playwright.test.d.ts +1 -0
- package/dist/features/wrapUnwrap/__tests__/splitDetection.test.d.ts +1 -0
- package/dist/features/wrapUnwrap/addIdAttr.d.ts +2 -0
- package/dist/features/wrapUnwrap/addIdAttr.js +60 -0
- package/dist/features/wrapUnwrap/apply/applyStructureSuggestions.d.ts +10 -0
- package/dist/features/wrapUnwrap/apply/applyStructureSuggestions.js +41 -0
- package/dist/features/wrapUnwrap/apply/index.d.ts +5 -0
- package/dist/features/wrapUnwrap/apply/index.js +21 -0
- package/dist/features/wrapUnwrap/areEquivalentStructureMarks.d.ts +2 -0
- package/dist/features/wrapUnwrap/areEquivalentStructureMarks.js +17 -0
- package/dist/features/wrapUnwrap/buildMaterializedPaths.d.ts +3 -0
- package/dist/features/wrapUnwrap/buildMaterializedPaths.js +82 -0
- package/dist/features/wrapUnwrap/constants.d.ts +3 -0
- package/dist/features/wrapUnwrap/constants.js +3 -0
- package/dist/features/wrapUnwrap/generateUniqueNodeId.d.ts +1 -0
- package/dist/features/wrapUnwrap/generateUniqueNodeId.js +6 -0
- package/dist/features/wrapUnwrap/getNodeId.d.ts +2 -0
- package/dist/features/wrapUnwrap/getNodeId.js +5 -0
- package/dist/features/wrapUnwrap/revert/deleteNodeUpwards.d.ts +3 -0
- package/dist/features/wrapUnwrap/revert/deleteNodeUpwards.js +37 -0
- package/dist/features/wrapUnwrap/revert/index.d.ts +5 -0
- package/dist/features/wrapUnwrap/revert/index.js +19 -0
- package/dist/features/wrapUnwrap/revert/revertAddOp.d.ts +4 -0
- package/dist/features/wrapUnwrap/revert/revertAddOp.js +4 -0
- package/dist/features/wrapUnwrap/revert/revertMoveOp.d.ts +16 -0
- package/dist/features/wrapUnwrap/revert/revertMoveOp.js +122 -0
- package/dist/features/wrapUnwrap/revert/revertStructureSuggestions.d.ts +10 -0
- package/dist/features/wrapUnwrap/revert/revertStructureSuggestions.js +236 -0
- package/dist/features/wrapUnwrap/sameParentChain.d.ts +2 -0
- package/dist/features/wrapUnwrap/sameParentChain.js +4 -0
- package/dist/features/wrapUnwrap/structureChangesPlugin.d.ts +17 -0
- package/dist/features/wrapUnwrap/structureChangesPlugin.js +299 -0
- package/dist/features/wrapUnwrap/types.d.ts +54 -0
- package/dist/features/wrapUnwrap/types.js +23 -0
- package/dist/features/wrapUnwrap/uniqueNodeIdsPlugin.d.ts +17 -0
- package/dist/features/wrapUnwrap/uniqueNodeIdsPlugin.js +106 -0
- package/dist/generateId.js +31 -4
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/listInputRules.d.ts +2 -0
- package/dist/listInputRules.js +14 -0
- package/dist/rebaseStep.d.ts +9 -0
- package/dist/rebaseStep.js +11 -0
- package/dist/replaceStep.d.ts +1 -1
- package/dist/replaceStep.js +1 -0
- package/dist/schema.d.ts +2 -1
- package/dist/schema.js +37 -1
- package/dist/testing/e2eTestSchema.d.ts +2 -0
- package/dist/testing/testBuilders.d.ts +1 -2
- package/dist/transformToSuggestionTransaction.d.ts +22 -0
- package/dist/transformToSuggestionTransaction.js +101 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +6 -2
- package/dist/withSuggestChanges.d.ts +11 -14
- package/dist/withSuggestChanges.js +64 -94
- package/dist/wrappingInputRule.d.ts +4 -0
- package/dist/wrappingInputRule.js +28 -0
- package/package.json +1 -1
- package/src/features/joinOnDelete/README.md +0 -8
|
@@ -140,9 +140,9 @@ export declare function assertReverted(finalState: EditorState, initialState: Ed
|
|
|
140
140
|
* This helper replaces the editor document with the provided JSON,
|
|
141
141
|
* clears transactions, and returns initial state.
|
|
142
142
|
*/
|
|
143
|
-
export declare function setupDocFromJSON(page: Page, docJSON:
|
|
143
|
+
export declare function setupDocFromJSON(page: Page, docJSON: object): Promise<{
|
|
144
144
|
initialState: EditorState;
|
|
145
|
-
initialDoc:
|
|
145
|
+
initialDoc: object;
|
|
146
146
|
}>;
|
|
147
147
|
/**
|
|
148
148
|
* Assert that document fully reverted to initial state.
|
|
@@ -1,15 +1,63 @@
|
|
|
1
|
+
import { type Node } from "prosemirror-model";
|
|
1
2
|
import { type Locator, type Page } from "@playwright/test";
|
|
2
3
|
export declare class EditorPage {
|
|
3
4
|
readonly page: Page;
|
|
5
|
+
readonly deletionMarksVisibility: "hidden" | "visible";
|
|
4
6
|
private readonly selectors;
|
|
5
|
-
constructor(page: Page);
|
|
7
|
+
constructor(page: Page, deletionMarksVisibility?: "hidden" | "visible");
|
|
6
8
|
get editor(): Locator;
|
|
7
|
-
getParagraphText(index: number): Promise<string>;
|
|
9
|
+
getParagraphText(index: number, childIndexes?: number[]): Promise<string>;
|
|
10
|
+
getParagraphAt(index: number): Locator;
|
|
8
11
|
getParagraphCount(): Promise<number>;
|
|
12
|
+
getListItems(): Locator;
|
|
13
|
+
getParagraphs(): Locator;
|
|
14
|
+
getListItemCount(): Promise<number>;
|
|
9
15
|
getProseMirrorMarkCount(name: string): Promise<number>;
|
|
16
|
+
getProseMirrorMarksJSON(): Promise<unknown[]>;
|
|
10
17
|
getProseMirrorSelection(): Promise<{
|
|
11
18
|
anchor: number;
|
|
12
19
|
head: number;
|
|
13
20
|
}>;
|
|
14
21
|
getDOMTextContentOfChildAtIndex(index: number): Promise<string>;
|
|
22
|
+
getDocJSON(): Promise<object>;
|
|
23
|
+
getCurrentDoc(): Promise<Node>;
|
|
24
|
+
getCurrentAndExpectedDoc(expectedDocJSON: object): Promise<{
|
|
25
|
+
currentDoc: Node;
|
|
26
|
+
expectedDoc: Node;
|
|
27
|
+
}>;
|
|
28
|
+
revertSuggestion(suggestionId: number): Promise<void>;
|
|
29
|
+
revertAll(): Promise<void>;
|
|
30
|
+
applyAll(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Perform an action and wait for ProseMirror state to update.
|
|
33
|
+
* Stores the current state reference before pressing, then polls until
|
|
34
|
+
* editor.view.state is a different object (ProseMirror creates a new
|
|
35
|
+
* immutable state on every transaction).
|
|
36
|
+
*/
|
|
37
|
+
private doActionAndWaitForState;
|
|
38
|
+
/**
|
|
39
|
+
* Press a key and wait for ProseMirror state to update.
|
|
40
|
+
* Stores the current state reference before pressing, then polls until
|
|
41
|
+
* editor.view.state is a different object (ProseMirror creates a new
|
|
42
|
+
* immutable state on every transaction).
|
|
43
|
+
*/
|
|
44
|
+
pressKey(key: string, opts?: {
|
|
45
|
+
waitForSelectionChange?: boolean;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Insert text and wait for ProseMirror state to update.
|
|
49
|
+
* Stores the current state reference before pressing, then polls until
|
|
50
|
+
* editor.view.state is a different object (ProseMirror creates a new
|
|
51
|
+
* immutable state on every transaction).
|
|
52
|
+
*/
|
|
53
|
+
insertText(text: string, opts?: {
|
|
54
|
+
waitForSelectionChange?: boolean;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Press a key multiple times, waiting for editor state update after each press.
|
|
58
|
+
*/
|
|
59
|
+
pressKeyMultiple(key: string, count: number, opts?: {
|
|
60
|
+
waitForSelectionChange?: boolean;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
setNextNodeId(nextNodeId: number): Promise<void>;
|
|
15
63
|
}
|
package/dist/commands.js
CHANGED
|
@@ -5,6 +5,9 @@ import { suggestChangesKey } from "./plugin.js";
|
|
|
5
5
|
import { getSuggestionMarks } from "./utils.js";
|
|
6
6
|
import { ZWSP } from "./constants.js";
|
|
7
7
|
import { maybeRevertJoinMark } from "./features/joinOnDelete/index.js";
|
|
8
|
+
import { isJoinMark } from "./features/joinOnDelete/types.js";
|
|
9
|
+
import { revertAllStructureSuggestions, revertStructureSuggestion } from "./features/wrapUnwrap/revert/index.js";
|
|
10
|
+
import { applyAllStructureSuggestions, applyStructureSuggestion } from "./features/wrapUnwrap/apply/index.js";
|
|
8
11
|
/**
|
|
9
12
|
* Given a node and a transform, add a set of steps to the
|
|
10
13
|
* transform that applies all marks of type markTypeToApply
|
|
@@ -29,6 +32,7 @@ import { maybeRevertJoinMark } from "./features/joinOnDelete/index.js";
|
|
|
29
32
|
tr.removeNodeMark(0, markTypeToApply);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
35
|
+
const restoredStructureSuggestionIds = new Set();
|
|
32
36
|
node.descendants((child, pos)=>{
|
|
33
37
|
if (from !== undefined && pos < from) {
|
|
34
38
|
return true;
|
|
@@ -60,8 +64,12 @@ import { maybeRevertJoinMark } from "./features/joinOnDelete/index.js";
|
|
|
60
64
|
const insertionTo = insertionFrom + child.nodeSize;
|
|
61
65
|
if (child.isInline) {
|
|
62
66
|
tr.removeMark(insertionFrom, insertionTo, markTypeToApply);
|
|
63
|
-
const
|
|
64
|
-
|
|
67
|
+
const joinRevertResult = maybeRevertJoinMark(tr, insertionFrom, insertionTo, child, markTypeToApply);
|
|
68
|
+
// reverting a join mark may produce new structure marks that were serialized in the join metadata
|
|
69
|
+
if (joinRevertResult) {
|
|
70
|
+
joinRevertResult.restoredStructureSuggestionIds.forEach((id)=>restoredStructureSuggestionIds.add(id));
|
|
71
|
+
}
|
|
72
|
+
if (!joinRevertResult && child.text === ZWSP) {
|
|
65
73
|
tr.delete(insertionFrom, insertionTo);
|
|
66
74
|
}
|
|
67
75
|
} else {
|
|
@@ -69,6 +77,50 @@ import { maybeRevertJoinMark } from "./features/joinOnDelete/index.js";
|
|
|
69
77
|
}
|
|
70
78
|
return true;
|
|
71
79
|
});
|
|
80
|
+
return {
|
|
81
|
+
restoredStructureSuggestionIds
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Collect suggestion IDs of join marks in the node
|
|
86
|
+
*
|
|
87
|
+
* @param node
|
|
88
|
+
* @param deletion
|
|
89
|
+
* @returns an array of suggestion IDs
|
|
90
|
+
*/ function findJoinSuggestionIds(node, deletion) {
|
|
91
|
+
const joinSuggestionIds = [];
|
|
92
|
+
node.descendants((child)=>{
|
|
93
|
+
const mark = deletion.isInSet(child.marks);
|
|
94
|
+
if (!mark || !isJoinMark(mark)) return true;
|
|
95
|
+
if (!child.isText || child.text !== ZWSP) return true;
|
|
96
|
+
joinSuggestionIds.push(mark.attrs.id);
|
|
97
|
+
return true;
|
|
98
|
+
});
|
|
99
|
+
return joinSuggestionIds.reverse();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Revert suggestions revealed by reverting a join mark
|
|
103
|
+
* Prioritize reverting revealed suggestions with the same id as the join mark
|
|
104
|
+
* (that means they are linked to the join mark and has to be reverted together as one)
|
|
105
|
+
* Revert other revealed suggestions as well so the user doesn't have to revert multiple times at the same place
|
|
106
|
+
*
|
|
107
|
+
* @param tr
|
|
108
|
+
* @param suggestionId
|
|
109
|
+
* @param restoredStructureSuggestionIds
|
|
110
|
+
*/ function revertRestoredStructureSuggestions(tr, suggestionId, restoredStructureSuggestionIds) {
|
|
111
|
+
if (restoredStructureSuggestionIds.has(suggestionId)) {
|
|
112
|
+
const restoredStructureTransform = revertStructureSuggestion(tr.doc, suggestionId);
|
|
113
|
+
restoredStructureTransform.steps.forEach((step)=>{
|
|
114
|
+
tr.step(step);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
restoredStructureSuggestionIds.forEach((restoredSuggestionId)=>{
|
|
118
|
+
if (restoredSuggestionId === suggestionId) return;
|
|
119
|
+
const restoredStructureTransform = revertStructureSuggestion(tr.doc, restoredSuggestionId);
|
|
120
|
+
restoredStructureTransform.steps.forEach((step)=>{
|
|
121
|
+
tr.step(step);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
72
124
|
}
|
|
73
125
|
function revertModifications(node, pos, tr) {
|
|
74
126
|
const { modification } = getSuggestionMarks(node.type.schema);
|
|
@@ -134,20 +186,42 @@ function applyModificationsToTransform(node, tr, dir, suggestionId, from, to) {
|
|
|
134
186
|
}
|
|
135
187
|
export function applySuggestionsToNode(node) {
|
|
136
188
|
const { deletion, insertion } = getSuggestionMarks(node.type.schema);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
189
|
+
// first, create a structure transform that applies all structure changes on the given node
|
|
190
|
+
const structureTransform = applyAllStructureSuggestions(node);
|
|
191
|
+
// then start a clear transform from the document where the structure changes are applied
|
|
192
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
193
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, insertion, deletion);
|
|
194
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, 1);
|
|
195
|
+
// replay suggestion transform on top of the structure transform
|
|
196
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
197
|
+
structureTransform.step(step);
|
|
198
|
+
});
|
|
199
|
+
const secondStructureTransform = applyAllStructureSuggestions(structureTransform.doc);
|
|
200
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
201
|
+
structureTransform.step(step);
|
|
202
|
+
});
|
|
203
|
+
return structureTransform.doc;
|
|
141
204
|
}
|
|
142
205
|
export function applySuggestionsToRange(doc, from, to) {
|
|
206
|
+
const { deletion, insertion } = getSuggestionMarks(doc.type.schema);
|
|
143
207
|
// blockRange can only return null if a predicate is provided
|
|
144
208
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
145
209
|
const nodeRange = doc.resolve(from).blockRange(doc.resolve(to));
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
210
|
+
// create a structure transform that applies all structure changes on the given node range
|
|
211
|
+
const structureTransform = applyAllStructureSuggestions(doc, nodeRange.start, nodeRange.end);
|
|
212
|
+
// then start a clear transform from the document where the structure changes are applied
|
|
213
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
214
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, insertion, deletion, undefined, nodeRange.start, nodeRange.end);
|
|
215
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, 1, undefined, nodeRange.start, nodeRange.end);
|
|
216
|
+
// replay suggestion transform on top of the structure transform
|
|
217
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
218
|
+
structureTransform.step(step);
|
|
219
|
+
});
|
|
220
|
+
const secondStructureTransform = applyAllStructureSuggestions(structureTransform.doc, structureTransform.mapping.map(from), structureTransform.mapping.map(to));
|
|
221
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
222
|
+
structureTransform.step(step);
|
|
223
|
+
});
|
|
224
|
+
return structureTransform.doc.slice(structureTransform.mapping.map(from), structureTransform.mapping.map(to));
|
|
151
225
|
}
|
|
152
226
|
/**
|
|
153
227
|
* Command that applies all tracked changes in a document.
|
|
@@ -157,13 +231,30 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
157
231
|
* contents left in the doc.
|
|
158
232
|
*/ export function applySuggestions(state, dispatch) {
|
|
159
233
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
234
|
+
// create a structure transform that applies all structure changes on the given document
|
|
235
|
+
const structureTransform = applyAllStructureSuggestions(state.doc);
|
|
236
|
+
// then start a clear transform from the document where the structure changes are applied
|
|
237
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
238
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, insertion, deletion);
|
|
239
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, 1);
|
|
240
|
+
// replay suggestion transform on top of the structure transform
|
|
241
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
242
|
+
structureTransform.step(step);
|
|
243
|
+
});
|
|
244
|
+
const secondStructureTransform = applyAllStructureSuggestions(structureTransform.doc);
|
|
245
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
246
|
+
structureTransform.step(step);
|
|
247
|
+
});
|
|
248
|
+
// apply the structure transform to the transaction
|
|
249
|
+
const transaction = state.tr;
|
|
250
|
+
structureTransform.steps.forEach((step)=>{
|
|
251
|
+
transaction.step(step);
|
|
252
|
+
});
|
|
253
|
+
if (!transaction.steps.length) return false;
|
|
254
|
+
transaction.setMeta(suggestChangesKey, {
|
|
164
255
|
skip: true
|
|
165
256
|
});
|
|
166
|
-
dispatch?.(
|
|
257
|
+
dispatch?.(transaction);
|
|
167
258
|
return true;
|
|
168
259
|
}
|
|
169
260
|
/**
|
|
@@ -175,13 +266,30 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
175
266
|
*/ export function applySuggestionsInRange(from, to) {
|
|
176
267
|
return (state, dispatch)=>{
|
|
177
268
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
269
|
+
// create a structure transform that applies all structure changes on the given node range
|
|
270
|
+
const structureTransform = applyAllStructureSuggestions(state.doc, from, to);
|
|
271
|
+
// then start a clear transform from the document where the structure changes are applied
|
|
272
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
273
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, insertion, deletion, undefined, from, to);
|
|
274
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, 1, undefined, from, to);
|
|
275
|
+
// replay suggestion transform on top of the structure transform
|
|
276
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
277
|
+
structureTransform.step(step);
|
|
278
|
+
});
|
|
279
|
+
const secondStructureTransform = applyAllStructureSuggestions(structureTransform.doc, from === undefined ? undefined : structureTransform.mapping.map(from), to === undefined ? undefined : structureTransform.mapping.map(to));
|
|
280
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
281
|
+
structureTransform.step(step);
|
|
282
|
+
});
|
|
283
|
+
// apply the structure transform to the transaction
|
|
284
|
+
const transaction = state.tr;
|
|
285
|
+
structureTransform.steps.forEach((step)=>{
|
|
286
|
+
transaction.step(step);
|
|
287
|
+
});
|
|
288
|
+
if (!transaction.steps.length) return false;
|
|
289
|
+
transaction.setMeta(suggestChangesKey, {
|
|
182
290
|
skip: true
|
|
183
291
|
});
|
|
184
|
-
dispatch?.(
|
|
292
|
+
dispatch?.(transaction);
|
|
185
293
|
return true;
|
|
186
294
|
};
|
|
187
295
|
}
|
|
@@ -194,14 +302,26 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
194
302
|
*/ export function applySuggestion(suggestionId, from, to) {
|
|
195
303
|
return (state, dispatch)=>{
|
|
196
304
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
305
|
+
// create a structure transform that applies the given structure change on the given node
|
|
306
|
+
const structureTransform = applyStructureSuggestion(state.doc, suggestionId);
|
|
307
|
+
// then start a clear transform from the document where the structure changes are applied
|
|
308
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
309
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, insertion, deletion, suggestionId, from, to);
|
|
310
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, 1, undefined, from, to);
|
|
311
|
+
// replay suggestion transform on top of the structure transform
|
|
312
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
313
|
+
structureTransform.step(step);
|
|
314
|
+
});
|
|
315
|
+
// apply the structure transform to the transaction
|
|
316
|
+
const transaction = state.tr;
|
|
317
|
+
structureTransform.steps.forEach((step)=>{
|
|
318
|
+
transaction.step(step);
|
|
319
|
+
});
|
|
320
|
+
if (!transaction.steps.length) return false;
|
|
321
|
+
transaction.setMeta(suggestChangesKey, {
|
|
202
322
|
skip: true
|
|
203
323
|
});
|
|
204
|
-
dispatch?.(
|
|
324
|
+
dispatch?.(transaction);
|
|
205
325
|
return true;
|
|
206
326
|
};
|
|
207
327
|
}
|
|
@@ -213,13 +333,40 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
213
333
|
* Modifications tracked in modification marks will be reverted.
|
|
214
334
|
*/ export function revertSuggestions(state, dispatch) {
|
|
215
335
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
336
|
+
// create a structure transform that reverts all structure changes on the given document
|
|
337
|
+
const structureTransform = revertAllStructureSuggestions(state.doc);
|
|
338
|
+
// revert all join marks first as well as any structure marks they contain serialized
|
|
339
|
+
const joinSuggestionIds = findJoinSuggestionIds(structureTransform.doc, deletion);
|
|
340
|
+
joinSuggestionIds.forEach((suggestionId)=>{
|
|
341
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
342
|
+
const { restoredStructureSuggestionIds } = applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, deletion, insertion, suggestionId);
|
|
343
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
344
|
+
structureTransform.step(step);
|
|
345
|
+
});
|
|
346
|
+
revertRestoredStructureSuggestions(structureTransform, suggestionId, restoredStructureSuggestionIds);
|
|
347
|
+
});
|
|
348
|
+
// then start a clear transform from the document where the structure changes and join marks are reverted
|
|
349
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
350
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, deletion, insertion);
|
|
351
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, -1);
|
|
352
|
+
// replay suggestion transform on top of the structure transform
|
|
353
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
354
|
+
structureTransform.step(step);
|
|
355
|
+
});
|
|
356
|
+
const secondStructureTransform = revertAllStructureSuggestions(structureTransform.doc);
|
|
357
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
358
|
+
structureTransform.step(step);
|
|
359
|
+
});
|
|
360
|
+
// apply the structure transform to the transaction
|
|
361
|
+
const transaction = state.tr;
|
|
362
|
+
structureTransform.steps.forEach((step)=>{
|
|
363
|
+
transaction.step(step);
|
|
364
|
+
});
|
|
365
|
+
if (!transaction.steps.length) return false;
|
|
366
|
+
transaction.setMeta(suggestChangesKey, {
|
|
220
367
|
skip: true
|
|
221
368
|
});
|
|
222
|
-
dispatch?.(
|
|
369
|
+
dispatch?.(transaction);
|
|
223
370
|
return true;
|
|
224
371
|
}
|
|
225
372
|
/**
|
|
@@ -231,13 +378,30 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
231
378
|
*/ export function revertSuggestionsInRange(from, to) {
|
|
232
379
|
return (state, dispatch)=>{
|
|
233
380
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
381
|
+
// create a structure transform that reverts all structure changes on the given node range
|
|
382
|
+
const structureTransform = revertAllStructureSuggestions(state.doc, from, to);
|
|
383
|
+
// then start a clear transform from the document where the structure changes are reverted
|
|
384
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
385
|
+
applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, deletion, insertion, undefined, from, to);
|
|
386
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, -1, undefined, from, to);
|
|
387
|
+
// replay suggestion transform on top of the structure transform
|
|
388
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
389
|
+
structureTransform.step(step);
|
|
390
|
+
});
|
|
391
|
+
const secondStructureTransform = revertAllStructureSuggestions(structureTransform.doc, from === undefined ? undefined : structureTransform.mapping.map(from), to === undefined ? undefined : structureTransform.mapping.map(to));
|
|
392
|
+
secondStructureTransform.steps.forEach((step)=>{
|
|
393
|
+
structureTransform.step(step);
|
|
394
|
+
});
|
|
395
|
+
// apply the structure transform to the transaction
|
|
396
|
+
const transaction = state.tr;
|
|
397
|
+
structureTransform.steps.forEach((step)=>{
|
|
398
|
+
transaction.step(step);
|
|
399
|
+
});
|
|
400
|
+
if (!transaction.steps.length) return false;
|
|
401
|
+
transaction.setMeta(suggestChangesKey, {
|
|
238
402
|
skip: true
|
|
239
403
|
});
|
|
240
|
-
dispatch?.(
|
|
404
|
+
dispatch?.(transaction);
|
|
241
405
|
return true;
|
|
242
406
|
};
|
|
243
407
|
}
|
|
@@ -250,14 +414,29 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
250
414
|
*/ export function revertSuggestion(suggestionId, from, to) {
|
|
251
415
|
return (state, dispatch)=>{
|
|
252
416
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
417
|
+
// create a structure transform that reverts the given structure change on the given node
|
|
418
|
+
const structureTransform = revertStructureSuggestion(state.doc, suggestionId);
|
|
419
|
+
// then start a clear transform from the document where the structure changes are reverted
|
|
420
|
+
const suggestionsTransform = new Transform(structureTransform.doc);
|
|
421
|
+
const { restoredStructureSuggestionIds } = applySuggestionsToTransform(suggestionsTransform.doc, suggestionsTransform, deletion, insertion, suggestionId, from, to);
|
|
422
|
+
applyModificationsToTransform(suggestionsTransform.doc, suggestionsTransform, -1, undefined, from, to);
|
|
423
|
+
// replay suggestion transform on top of the structure transform
|
|
424
|
+
suggestionsTransform.steps.forEach((step)=>{
|
|
425
|
+
structureTransform.step(step);
|
|
426
|
+
});
|
|
427
|
+
// in case there are structure marks revealed after join mark revert,
|
|
428
|
+
// revert them as well
|
|
429
|
+
revertRestoredStructureSuggestions(structureTransform, suggestionId, restoredStructureSuggestionIds);
|
|
430
|
+
// apply the structure transform to the transaction
|
|
431
|
+
const transaction = state.tr;
|
|
432
|
+
structureTransform.steps.forEach((step)=>{
|
|
433
|
+
transaction.step(step);
|
|
434
|
+
});
|
|
435
|
+
if (!transaction.steps.length) return false;
|
|
436
|
+
transaction.setMeta(suggestChangesKey, {
|
|
257
437
|
skip: true
|
|
258
438
|
});
|
|
259
|
-
|
|
260
|
-
dispatch?.(tr);
|
|
439
|
+
dispatch?.(transaction);
|
|
261
440
|
return true;
|
|
262
441
|
};
|
|
263
442
|
}
|
|
@@ -63,7 +63,7 @@ export function ensureSelection() {
|
|
|
63
63
|
$newAnchor
|
|
64
64
|
});
|
|
65
65
|
trace("appendTransaction", "search for new valid $head...");
|
|
66
|
-
let $newHead = getNewValidPos(newState.selection.$head, getDirection(oldState.selection.$head, newState.selection.$head, pluginState));
|
|
66
|
+
let $newHead = newState.selection.empty ? $newAnchor : getNewValidPos(newState.selection.$head, getDirection(oldState.selection.$head, newState.selection.$head, pluginState));
|
|
67
67
|
trace("appendTransaction", "new valid $head", $newHead?.pos, {
|
|
68
68
|
$newHead
|
|
69
69
|
});
|
|
@@ -147,8 +147,8 @@ function isPosValid($pos) {
|
|
|
147
147
|
const insertionBefore = insertion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
148
148
|
const insertionAfter = insertion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
149
149
|
const ZWSP_REGEXP = new RegExp(ZWSP, "g");
|
|
150
|
-
const isZWSPBefore = $pos.nodeBefore && $pos.nodeBefore.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
151
|
-
const isZWSPAfter = $pos.nodeAfter && $pos.nodeAfter.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
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
152
|
if (insertionBefore && insertionAfter && isZWSPBefore && isZWSPAfter) {
|
|
153
153
|
trace("isPosValid", $pos.pos, "pos invalid", "reason: between two ZWSP insertions", {
|
|
154
154
|
$pos
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/features/joinOnDelete/__tests__/joinOnDeleteInListsTipTapStyle.playwright.test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/features/joinOnDelete/__tests__/listWithJoinsAndStructureMarks.playwright.test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
import { Mark, type Node, type
|
|
1
|
+
import { Mark, type Node, type MarkType, type ResolvedPos } from "prosemirror-model";
|
|
2
2
|
import { Transform } from "prosemirror-transform";
|
|
3
3
|
import { type Transaction } from "prosemirror-state";
|
|
4
4
|
import { type SuggestionId } from "../../generateId.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
leftNode: {
|
|
9
|
-
type: string;
|
|
10
|
-
attrs: object;
|
|
11
|
-
marks: object[];
|
|
12
|
-
};
|
|
13
|
-
rightNode: {
|
|
14
|
-
type: string;
|
|
15
|
-
attrs: object;
|
|
16
|
-
marks: object[];
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export declare function isJoinMarkAttrs(attrs: Attrs): attrs is JoinMarkAttrs;
|
|
21
|
-
export declare function maybeRevertJoinMark(tr: Transform, from: number, to: number, node: Node, markType: MarkType): boolean;
|
|
5
|
+
export declare function maybeRevertJoinMark(tr: Transform, from: number, to: number, node: Node, markType: MarkType): false | {
|
|
6
|
+
restoredStructureSuggestionIds: Set<SuggestionId>;
|
|
7
|
+
};
|
|
22
8
|
/**
|
|
23
9
|
* Remove ZWSP text nodes marked as deletions (except for type=join) from the given range
|
|
24
10
|
*/
|
|
@@ -34,4 +20,3 @@ export declare function joinNodesAndMarkJoinPoints(trackedTransaction: Transacti
|
|
|
34
20
|
*/
|
|
35
21
|
export declare function collapseZWSPNodes(trackedTransaction: Transaction, from: number, to: number): Transform;
|
|
36
22
|
export declare function findJoinMark(pos: ResolvedPos): Mark | null;
|
|
37
|
-
export {};
|