@magic-marker/prosemirror-suggest-changes 0.2.1-wrap-unwrap.2 → 0.3.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/__tests__/playwrightBaseTest.d.ts +6 -0
- package/dist/__tests__/playwrightPage.d.ts +15 -0
- package/dist/commands.js +1 -10
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/decorations.js +4 -3
- package/dist/ensureSelectionPlugin.d.ts +13 -0
- package/dist/ensureSelectionPlugin.js +307 -0
- package/dist/generateId.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/plugin.js +2 -14
- package/dist/prependDeletionsWithZWSP.d.ts +2 -0
- package/dist/prependDeletionsWithZWSP.js +45 -0
- package/dist/replaceAroundStep.js +1 -6
- package/dist/replaceStep.js +3 -7
- package/dist/schema.d.ts +4 -2
- package/dist/schema.js +32 -39
- package/dist/testing/testBuilders.d.ts +2 -1
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js +2 -6
- package/dist/withSuggestChanges.js +4 -0
- package/package.json +1 -1
- package/dist/contentBetween.d.ts +0 -2
- package/dist/contentBetween.js +0 -33
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapAllNodes.data.d.ts +0 -38
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapOneNode.data.d.ts +0 -45
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapOneNode.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapSingleNode.data.d.ts +0 -38
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapSingleNode.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteUnwrapSingleNode.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapAllNodes.data.d.ts +0 -38
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapAllNodes.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapAllNodes.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapSingleNode.data.d.ts +0 -38
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapSingleNode.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/blockquoteWrapSingleNode.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftLast.data.d.ts +0 -54
- package/dist/features/wrapUnwrap/__tests__/listItemLiftLast.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftLast.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMiddle.data.d.ts +0 -48
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMiddle.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMiddle.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMultipleToTheTop.data.d.ts +0 -74
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMultipleToTheTop.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftMultipleToTheTop.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftNestedOnce.data.d.ts +0 -71
- package/dist/features/wrapUnwrap/__tests__/listItemLiftNestedOnce.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftNestedOnce.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftTop.data.d.ts +0 -54
- package/dist/features/wrapUnwrap/__tests__/listItemLiftTop.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemLiftTop.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemSinkMultiple.data.d.ts +0 -221
- package/dist/features/wrapUnwrap/__tests__/listItemSinkMultiple.page.d.ts +0 -16
- package/dist/features/wrapUnwrap/__tests__/listItemSinkMultiple.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemSinkMultiple.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemSinkOneOnce.data.d.ts +0 -51
- package/dist/features/wrapUnwrap/__tests__/listItemSinkOneOnce.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemSinkOneOnce.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemsLiftMixedLevels.data.d.ts +0 -74
- package/dist/features/wrapUnwrap/__tests__/listItemsLiftMixedLevels.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/listItemsLiftMixedLevels.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/nestedListItemLiftToOuter.data.d.ts +0 -71
- package/dist/features/wrapUnwrap/__tests__/nestedListItemLiftToOuter.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/nestedListItemLiftToOuter.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/nestedListItemsLiftMultipleLevels.data.d.ts +0 -114
- package/dist/features/wrapUnwrap/__tests__/nestedListItemsLiftMultipleLevels.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/nestedListItemsLiftMultipleLevels.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/testUtils.d.ts +0 -5
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesToTheTop.data.d.ts +0 -44
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesToTheTop.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesToTheTop.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesUp.data.d.ts +0 -38
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesUp.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftMultipleNodesUp.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeFromMiddle.data.d.ts +0 -46
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeFromMiddle.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeFromMiddle.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeToTheTop.data.d.ts +0 -57
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeToTheTop.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeToTheTop.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeUp.data.d.ts +0 -45
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeUp.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteLiftOneNodeUp.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrap.data.d.ts +0 -44
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrap.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrap.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrapMultiple.data.d.ts +0 -44
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrapMultiple.playwright.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/__tests__/tripleBlockquoteWrapMultiple.test.d.ts +0 -1
- package/dist/features/wrapUnwrap/handleStructureStep.d.ts +0 -4
- package/dist/features/wrapUnwrap/handleStructureStep.js +0 -630
- package/dist/features/wrapUnwrap/revertStructureSuggestion.d.ts +0 -44
- package/dist/features/wrapUnwrap/revertStructureSuggestion.js +0 -368
- package/dist/rebaseStep.d.ts +0 -9
- package/dist/rebaseStep.js +0 -11
- package/src/features/wrapUnwrap/README.md +0 -198
- /package/dist/features/{wrapUnwrap/__tests__/blockquoteUnwrapAllNodes.playwright.test.d.ts → hiddenDeletions/__tests__/hiddenDeletionsBehavior.playwright.test.d.ts} +0 -0
- /package/dist/features/{wrapUnwrap/__tests__/blockquoteUnwrapAllNodes.test.d.ts → joinBlocks/__tests__/joinBlockBackspace.playwright.test.d.ts} +0 -0
- /package/dist/features/{wrapUnwrap/__tests__/blockquoteUnwrapOneNode.playwright.test.d.ts → joinBlocks/__tests__/joinBlockDelete.playwright.test.d.ts} +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { expect } from "@playwright/test";
|
|
2
|
+
export interface TestOptions {
|
|
3
|
+
deletionMarksVisibility: "hidden" | "visible";
|
|
4
|
+
}
|
|
5
|
+
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & TestOptions, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
6
|
+
export { expect };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Locator, type Page } from "@playwright/test";
|
|
2
|
+
export declare class EditorPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
private readonly selectors;
|
|
5
|
+
constructor(page: Page);
|
|
6
|
+
get editor(): Locator;
|
|
7
|
+
getParagraphText(index: number): Promise<string>;
|
|
8
|
+
getParagraphCount(): Promise<number>;
|
|
9
|
+
getProseMirrorMarkCount(name: string): Promise<number>;
|
|
10
|
+
getProseMirrorSelection(): Promise<{
|
|
11
|
+
anchor: number;
|
|
12
|
+
head: number;
|
|
13
|
+
}>;
|
|
14
|
+
getDOMTextContentOfChildAtIndex(index: number): Promise<string>;
|
|
15
|
+
}
|
package/dist/commands.js
CHANGED
|
@@ -5,7 +5,6 @@ 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 { applyStructureSuggestion, isStructureSuggestion, revertAllStructureSuggestions, revertStructureSuggestion } from "./features/wrapUnwrap/revertStructureSuggestion.js";
|
|
9
8
|
/**
|
|
10
9
|
* Given a node and a transform, add a set of steps to the
|
|
11
10
|
* transform that applies all marks of type markTypeToApply
|
|
@@ -194,9 +193,6 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
194
193
|
* contents left in the doc.
|
|
195
194
|
*/ export function applySuggestion(suggestionId, from, to) {
|
|
196
195
|
return (state, dispatch)=>{
|
|
197
|
-
if (isStructureSuggestion(suggestionId, state.tr)) {
|
|
198
|
-
return applyStructureSuggestion(suggestionId)(state, dispatch);
|
|
199
|
-
}
|
|
200
196
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
201
197
|
const tr = state.tr;
|
|
202
198
|
applySuggestionsToTransform(state.doc, tr, insertion, deletion, suggestionId, from, to);
|
|
@@ -218,9 +214,7 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
218
214
|
*/ export function revertSuggestions(state, dispatch) {
|
|
219
215
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
220
216
|
const tr = state.tr;
|
|
221
|
-
|
|
222
|
-
revertAllStructureSuggestions(doc, tr);
|
|
223
|
-
applySuggestionsToTransform(doc, tr, deletion, insertion);
|
|
217
|
+
applySuggestionsToTransform(state.doc, tr, deletion, insertion);
|
|
224
218
|
applyModificationsToTransform(tr.doc, tr, -1);
|
|
225
219
|
tr.setMeta(suggestChangesKey, {
|
|
226
220
|
skip: true
|
|
@@ -255,9 +249,6 @@ export function applySuggestionsToRange(doc, from, to) {
|
|
|
255
249
|
* Modifications tracked in modification marks will be reverted.
|
|
256
250
|
*/ export function revertSuggestion(suggestionId, from, to) {
|
|
257
251
|
return (state, dispatch)=>{
|
|
258
|
-
if (isStructureSuggestion(suggestionId, state.tr)) {
|
|
259
|
-
return revertStructureSuggestion(suggestionId)(state, dispatch);
|
|
260
|
-
}
|
|
261
252
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
262
253
|
const tr = state.tr;
|
|
263
254
|
applySuggestionsToTransform(state.doc, tr, deletion, insertion, suggestionId, from, to);
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/dist/decorations.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
2
2
|
import { getSuggestionMarks } from "./utils.js";
|
|
3
|
+
import { PILCROW } from "./constants.js";
|
|
3
4
|
function pilcrow() {
|
|
4
5
|
const span = document.createElement("span");
|
|
5
|
-
span.appendChild(document.createTextNode(
|
|
6
|
+
span.appendChild(document.createTextNode(PILCROW));
|
|
6
7
|
return span;
|
|
7
8
|
}
|
|
8
9
|
export function getSuggestionDecorations(state) {
|
|
@@ -43,7 +44,7 @@ export function getSuggestionDecorations(state) {
|
|
|
43
44
|
if (currentDeletionMark?.attrs["id"] !== lastDeletionMark?.attrs["id"] && currentInsertionMark?.attrs["id"] !== lastInsertionMark?.attrs["id"]) {
|
|
44
45
|
return true;
|
|
45
46
|
}
|
|
46
|
-
if (currentDeletionMark) {
|
|
47
|
+
if (currentDeletionMark && currentDeletionMark.attrs["id"] === lastDeletionMark?.attrs["id"]) {
|
|
47
48
|
changeDecorations.push(Decoration.widget(widgetPos, pilcrow, {
|
|
48
49
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
49
50
|
key: currentDeletionMark.attrs["id"],
|
|
@@ -55,7 +56,7 @@ export function getSuggestionDecorations(state) {
|
|
|
55
56
|
]
|
|
56
57
|
}));
|
|
57
58
|
}
|
|
58
|
-
if (currentInsertionMark) {
|
|
59
|
+
if (currentInsertionMark && currentInsertionMark.attrs["id"] === lastInsertionMark?.attrs["id"]) {
|
|
59
60
|
changeDecorations.push(Decoration.widget(widgetPos, pilcrow, {
|
|
60
61
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
61
62
|
key: currentInsertionMark.attrs["id"],
|
|
@@ -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,307 @@
|
|
|
1
|
+
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
2
|
+
import { getSuggestionMarks } from "./utils.js";
|
|
3
|
+
import { ZWSP } from "./constants.js";
|
|
4
|
+
// import { ZWSP } from "./constants.js";
|
|
5
|
+
const TRACE_ENABLED = true;
|
|
6
|
+
function trace(...args) {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
8
|
+
if (!TRACE_ENABLED) return;
|
|
9
|
+
console.log("[ensureSelectionPlugin]", ...args);
|
|
10
|
+
}
|
|
11
|
+
export const ensureSelectionKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes-ensure-selection");
|
|
12
|
+
export function ensureSelection() {
|
|
13
|
+
return new Plugin({
|
|
14
|
+
key: ensureSelectionKey,
|
|
15
|
+
state: {
|
|
16
|
+
apply (tr, state) {
|
|
17
|
+
const newPluginState = tr.getMeta(ensureSelectionKey);
|
|
18
|
+
if (newPluginState) {
|
|
19
|
+
return newPluginState;
|
|
20
|
+
}
|
|
21
|
+
return state;
|
|
22
|
+
},
|
|
23
|
+
init () {
|
|
24
|
+
return {
|
|
25
|
+
handleKeyDown: {
|
|
26
|
+
backspace: false,
|
|
27
|
+
delete: false,
|
|
28
|
+
arrowLeft: false,
|
|
29
|
+
arrowRight: false
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
props: {
|
|
35
|
+
handleKeyDown (view, event) {
|
|
36
|
+
const state = this.getState(view.state);
|
|
37
|
+
if (!state) return;
|
|
38
|
+
const newState = {
|
|
39
|
+
...state
|
|
40
|
+
};
|
|
41
|
+
newState.handleKeyDown.backspace = event.key === "Backspace";
|
|
42
|
+
newState.handleKeyDown.delete = event.key === "Delete";
|
|
43
|
+
newState.handleKeyDown.arrowLeft = event.key === "ArrowLeft";
|
|
44
|
+
newState.handleKeyDown.arrowRight = event.key === "ArrowRight";
|
|
45
|
+
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) {
|
|
46
|
+
trace("handleKeyDown newState =", newState);
|
|
47
|
+
view.dispatch(view.state.tr.setMeta(ensureSelectionKey, newState));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
appendTransaction (_transactions, oldState, newState) {
|
|
52
|
+
const pluginState = ensureSelectionKey.getState(newState);
|
|
53
|
+
if (!(newState.selection instanceof TextSelection)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (isPosValid(newState.selection.$anchor) && isPosValid(newState.selection.$head)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
60
|
+
if (TRACE_ENABLED) console.groupCollapsed("[ensureSelectionPlugin]", "appendTransaction");
|
|
61
|
+
trace("appendTransaction", "search for new valid $anchor...");
|
|
62
|
+
let $newAnchor = getNewValidPos(newState.selection.$anchor, getDirection(oldState.selection.$anchor, newState.selection.$anchor, pluginState));
|
|
63
|
+
trace("appendTransaction", "new valid $anchor", $newAnchor?.pos, {
|
|
64
|
+
$newAnchor
|
|
65
|
+
});
|
|
66
|
+
trace("appendTransaction", "search for new valid $head...");
|
|
67
|
+
let $newHead = getNewValidPos(newState.selection.$head, getDirection(oldState.selection.$head, newState.selection.$head, pluginState));
|
|
68
|
+
trace("appendTransaction", "new valid $head", $newHead?.pos, {
|
|
69
|
+
$newHead
|
|
70
|
+
});
|
|
71
|
+
$newAnchor = $newAnchor ?? newState.selection.$anchor;
|
|
72
|
+
$newHead = $newHead ?? newState.selection.$head;
|
|
73
|
+
const newSelection = new TextSelection($newAnchor, $newHead);
|
|
74
|
+
if (newSelection.anchor === newState.selection.anchor && newSelection.head === newState.selection.head) {
|
|
75
|
+
trace("appendTransaction", "new selection is the same as old selection, skipping", {
|
|
76
|
+
$newAnchor,
|
|
77
|
+
$newHead,
|
|
78
|
+
selection: newSelection
|
|
79
|
+
});
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
81
|
+
if (TRACE_ENABLED) console.groupEnd();
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
85
|
+
if (TRACE_ENABLED) console.groupEnd();
|
|
86
|
+
trace("appendTransaction", "setting new selection", $newAnchor.pos, $newHead.pos, {
|
|
87
|
+
$newAnchor,
|
|
88
|
+
$newHead,
|
|
89
|
+
selection: newSelection
|
|
90
|
+
});
|
|
91
|
+
return newState.tr.setSelection(newSelection);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export function isEnsureSelectionEnabled() {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function isPosValid($pos) {
|
|
99
|
+
// text selection is only valid in nodes that allow inline content
|
|
100
|
+
// https://github.com/ProseMirror/prosemirror-state/blob/1.4.4/src/selection.ts#L219
|
|
101
|
+
if (!$pos.parent.inlineContent) {
|
|
102
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: not in inlineContent node", {
|
|
103
|
+
$pos
|
|
104
|
+
});
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const { deletion, insertion } = getSuggestionMarks($pos.doc.type.schema);
|
|
108
|
+
const deletionBefore = deletion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
109
|
+
const deletionAfter = deletion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
110
|
+
const isAnchorBefore = deletionBefore && deletionBefore.attrs["type"] === "anchor";
|
|
111
|
+
const isAnchorAfter = deletionAfter && deletionAfter.attrs["type"] === "anchor";
|
|
112
|
+
if (isAnchorBefore && deletionAfter && !isAnchorAfter) {
|
|
113
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between deletion anchor and non-anchor deletion", {
|
|
114
|
+
$pos
|
|
115
|
+
});
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (deletionBefore && deletionAfter && !isAnchorBefore && !isAnchorAfter) {
|
|
119
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between two non-anchor deletions", {
|
|
120
|
+
$pos
|
|
121
|
+
});
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if ($pos.nodeBefore == null && deletionAfter && !isAnchorAfter) {
|
|
125
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between node boundary and non-anchor deletion", {
|
|
126
|
+
$pos
|
|
127
|
+
});
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (deletionBefore && $pos.nodeAfter == null && !isAnchorBefore) {
|
|
131
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and node boundary", {
|
|
132
|
+
$pos
|
|
133
|
+
});
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (deletionBefore && !isAnchorBefore && $pos.nodeAfter == null) {
|
|
137
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and node boundary", {
|
|
138
|
+
$pos
|
|
139
|
+
});
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (deletionBefore && !isAnchorBefore) {
|
|
143
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between non-anchor deletion and anything", {
|
|
144
|
+
$pos
|
|
145
|
+
});
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const insertionBefore = insertion.isInSet($pos.nodeBefore?.marks ?? []);
|
|
149
|
+
const insertionAfter = insertion.isInSet($pos.nodeAfter?.marks ?? []);
|
|
150
|
+
const ZWSP_REGEXP = new RegExp(ZWSP, "g");
|
|
151
|
+
const isZWSPBefore = $pos.nodeBefore && $pos.nodeBefore.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
152
|
+
const isZWSPAfter = $pos.nodeAfter && $pos.nodeAfter.textContent.replace(ZWSP_REGEXP, "") === "";
|
|
153
|
+
if (insertionBefore && insertionAfter && isZWSPBefore && isZWSPAfter) {
|
|
154
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between two ZWSP insertions", {
|
|
155
|
+
$pos
|
|
156
|
+
});
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (insertionBefore && isZWSPBefore && $pos.nodeAfter == null && // a position like this:
|
|
160
|
+
// <p><insertion>ZWSP</insertion>|</p>
|
|
161
|
+
// because it means this paragraph was just created and it's empty
|
|
162
|
+
$pos.parent.textContent.replace(ZWSP_REGEXP, "") !== "") {
|
|
163
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between ZWSP insertion and right node boundary", {
|
|
164
|
+
$pos
|
|
165
|
+
});
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (insertionAfter && isZWSPAfter && $pos.nodeBefore == null) {
|
|
169
|
+
trace("isPosValid", $pos.pos, "pos invalid", "reason: between ZWSP insertion and left node boundary", {
|
|
170
|
+
$pos
|
|
171
|
+
});
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
function findNextValidPos($initialPos) {
|
|
177
|
+
let $pos = $initialPos;
|
|
178
|
+
// to keep searching for the next valid pos we need non-null nodeAfter so we can go right or non-root depth so we can go up
|
|
179
|
+
while(!isPosValid($pos) && ($pos.nodeAfter != null || $pos.depth > 0)){
|
|
180
|
+
// first check if we can go into nodeAfter
|
|
181
|
+
if ($pos.nodeAfter != null) {
|
|
182
|
+
// if nodeAfter is inline, we can step into it and search for the valid pos in it
|
|
183
|
+
if ($pos.nodeAfter.isInline) {
|
|
184
|
+
// nodeAfter is inline - move in by one
|
|
185
|
+
$pos = $pos.doc.resolve($pos.pos + 1);
|
|
186
|
+
} else {
|
|
187
|
+
// nodeAfter is not inline - find starting position of the first inline descendant in nodeAfter
|
|
188
|
+
let localStartPos = null;
|
|
189
|
+
$pos.nodeAfter.descendants((child, pos)=>{
|
|
190
|
+
if (!child.isInline) return true;
|
|
191
|
+
if (localStartPos !== null) return false;
|
|
192
|
+
localStartPos = pos;
|
|
193
|
+
return false;
|
|
194
|
+
});
|
|
195
|
+
if (localStartPos !== null) {
|
|
196
|
+
// we have a local starting position of the first inline descendant - convert it to global position
|
|
197
|
+
// +1 to "enter" the node, and add local pos
|
|
198
|
+
$pos = $pos.doc.resolve($pos.pos + 1 + localStartPos);
|
|
199
|
+
} else {
|
|
200
|
+
// unable to find first inline descendant of nodeAfter - just skip nodeAfter altogether
|
|
201
|
+
$pos = $pos.doc.resolve($pos.pos + $pos.nodeAfter.nodeSize);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} else if ($pos.depth > 0) {
|
|
205
|
+
// nodeAfter is null - go up
|
|
206
|
+
$pos = $pos.doc.resolve($pos.after());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return isPosValid($pos) ? $pos : null;
|
|
210
|
+
}
|
|
211
|
+
function findPreviousValidPos($initialPos) {
|
|
212
|
+
let $pos = $initialPos;
|
|
213
|
+
// in order to be able to keep searching, we need either nodeBefore so we can go left, or non-root depth so we can go up
|
|
214
|
+
while(!isPosValid($pos) && ($pos.nodeBefore != null || $pos.depth > 0)){
|
|
215
|
+
// first check if we can go into nodeBefore
|
|
216
|
+
if ($pos.nodeBefore != null) {
|
|
217
|
+
// if nodeBefore is inline, we can step into it and search for the valid pos in it
|
|
218
|
+
if ($pos.nodeBefore.isInline) {
|
|
219
|
+
// nodeBefore is inline - move in by one
|
|
220
|
+
$pos = $pos.doc.resolve($pos.pos - 1);
|
|
221
|
+
} else {
|
|
222
|
+
// nodeBefore is not inline - find ending position of the last inline descendant in nodeBefore
|
|
223
|
+
let localEndPos = null;
|
|
224
|
+
$pos.nodeBefore.descendants((child, pos)=>{
|
|
225
|
+
if (!child.isInline) return true;
|
|
226
|
+
localEndPos = pos + child.nodeSize;
|
|
227
|
+
return false;
|
|
228
|
+
});
|
|
229
|
+
if (localEndPos !== null) {
|
|
230
|
+
// we have a local ending position of the last inline descendant - convert it to global position
|
|
231
|
+
// move pos to start of node before, add 1 to "enter" nodeBefore, then add local pos
|
|
232
|
+
$pos = $pos.doc.resolve($pos.pos - $pos.nodeBefore.nodeSize + 1 + localEndPos);
|
|
233
|
+
} else {
|
|
234
|
+
// unable to find last inline descendant of nodeBefore - just skip nodeBefore altogether
|
|
235
|
+
$pos = $pos.doc.resolve($pos.pos - $pos.nodeBefore.nodeSize);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} else if ($pos.depth > 0) {
|
|
239
|
+
// nodeBefore is null - go up
|
|
240
|
+
$pos = $pos.doc.resolve($pos.before());
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return isPosValid($pos) ? $pos : null;
|
|
244
|
+
}
|
|
245
|
+
function getNewValidPos($pos, dir) {
|
|
246
|
+
if (isPosValid($pos)) return $pos;
|
|
247
|
+
trace("getNewValidPos for", $pos.pos, {
|
|
248
|
+
$pos,
|
|
249
|
+
dir
|
|
250
|
+
});
|
|
251
|
+
if (dir === "right") {
|
|
252
|
+
const $nextValidPos = findNextValidPos($pos);
|
|
253
|
+
trace("getNewValidPos", "$nextValidPos", $nextValidPos?.pos, {
|
|
254
|
+
dir,
|
|
255
|
+
$pos,
|
|
256
|
+
$nextValidPos
|
|
257
|
+
});
|
|
258
|
+
if ($nextValidPos != null) return $nextValidPos;
|
|
259
|
+
const $prevValidPos = findPreviousValidPos($pos);
|
|
260
|
+
trace("getNewValidPos", "$prevValidPos", $prevValidPos?.pos, {
|
|
261
|
+
dir,
|
|
262
|
+
$pos,
|
|
263
|
+
$prevValidPos
|
|
264
|
+
});
|
|
265
|
+
if ($prevValidPos != null) return $prevValidPos;
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
if (dir === "left") {
|
|
269
|
+
const $prevValidPos = findPreviousValidPos($pos);
|
|
270
|
+
trace("getNewValidPos", "$prevValidPos", $prevValidPos?.pos, {
|
|
271
|
+
dir,
|
|
272
|
+
$pos,
|
|
273
|
+
$prevValidPos
|
|
274
|
+
});
|
|
275
|
+
if ($prevValidPos != null) return $prevValidPos;
|
|
276
|
+
const $nextValidPos = findNextValidPos($pos);
|
|
277
|
+
trace("getNewValidPos", "$nextValidPos", $nextValidPos?.pos, {
|
|
278
|
+
dir,
|
|
279
|
+
$pos,
|
|
280
|
+
$nextValidPos
|
|
281
|
+
});
|
|
282
|
+
if ($nextValidPos != null) return $nextValidPos;
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const $nextValidPos = findNextValidPos($pos);
|
|
286
|
+
const $prevValidPos = findPreviousValidPos($pos);
|
|
287
|
+
trace("getNewValidPos", "$nextValidPos", $nextValidPos?.pos, "$prevValidPos", $prevValidPos?.pos, {
|
|
288
|
+
dir,
|
|
289
|
+
$pos,
|
|
290
|
+
$nextValidPos,
|
|
291
|
+
$prevValidPos
|
|
292
|
+
});
|
|
293
|
+
if ($nextValidPos == null && $prevValidPos == null) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
if ($nextValidPos == null) return $prevValidPos;
|
|
297
|
+
if ($prevValidPos == null) return $nextValidPos;
|
|
298
|
+
const nextDist = Math.abs($pos.pos - $nextValidPos.pos);
|
|
299
|
+
const prevDist = Math.abs($pos.pos - $prevValidPos.pos);
|
|
300
|
+
return nextDist <= prevDist ? $nextValidPos : $prevValidPos;
|
|
301
|
+
}
|
|
302
|
+
function getDirection($oldPos, $newPos, pluginState) {
|
|
303
|
+
if (pluginState?.handleKeyDown.backspace) return "left";
|
|
304
|
+
if ($newPos.pos > $oldPos.pos) return "right";
|
|
305
|
+
if ($newPos.pos < $oldPos.pos) return "left";
|
|
306
|
+
return null;
|
|
307
|
+
}
|
package/dist/generateId.js
CHANGED
|
@@ -8,12 +8,12 @@ export function parseSuggestionId(id) {
|
|
|
8
8
|
return parsed;
|
|
9
9
|
}
|
|
10
10
|
export function generateNextNumberId(schema, doc) {
|
|
11
|
-
const { deletion, insertion, modification
|
|
11
|
+
const { deletion, insertion, modification } = getSuggestionMarks(schema);
|
|
12
12
|
// Find the highest change id in the document so far,
|
|
13
13
|
// and use that as the starting point for new changes
|
|
14
14
|
let suggestionId = 0;
|
|
15
15
|
doc?.descendants((node)=>{
|
|
16
|
-
const mark = node.marks.find((mark)=>mark.type === insertion || mark.type === deletion || mark.type === modification
|
|
16
|
+
const mark = node.marks.find((mark)=>mark.type === insertion || mark.type === deletion || mark.type === modification);
|
|
17
17
|
if (mark) {
|
|
18
18
|
suggestionId = Math.max(suggestionId, mark.attrs["id"]);
|
|
19
19
|
return false;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { addSuggestionMarks, insertion, deletion, modification,
|
|
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,
|
|
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,45 @@
|
|
|
1
|
+
import { getSuggestionMarks } from "./utils.js";
|
|
2
|
+
import { Transform } from "prosemirror-transform";
|
|
3
|
+
import { ZWSP } from "./constants.js";
|
|
4
|
+
const TRACE_ENABLED = true;
|
|
5
|
+
function trace(...args) {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
7
|
+
if (!TRACE_ENABLED) return;
|
|
8
|
+
console.log("[prependDeletionsWithZWSP]", ...args);
|
|
9
|
+
}
|
|
10
|
+
export function prependDeletionsWithZWSP(transaction) {
|
|
11
|
+
const { deletion } = getSuggestionMarks(transaction.doc.type.schema);
|
|
12
|
+
let transform = new Transform(transaction.doc);
|
|
13
|
+
transform.doc.descendants((node, pos)=>{
|
|
14
|
+
const mark = node.marks.find((mark)=>mark.type === deletion && mark.attrs["type"] === "anchor");
|
|
15
|
+
if (!node.isText || mark == null) return true;
|
|
16
|
+
const mappedPos = transform.mapping.map(pos);
|
|
17
|
+
transform.delete(mappedPos, mappedPos + node.nodeSize);
|
|
18
|
+
return true;
|
|
19
|
+
});
|
|
20
|
+
transform.steps.forEach((step)=>{
|
|
21
|
+
transaction.step(step);
|
|
22
|
+
});
|
|
23
|
+
if (transform.steps.length > 0) trace(`added ${String(transform.steps.length)} remove zwsp steps to tr`, transform);
|
|
24
|
+
transform = new Transform(transaction.doc);
|
|
25
|
+
transform.doc.descendants((node, pos)=>{
|
|
26
|
+
if (!node.inlineContent) return true;
|
|
27
|
+
const mark = node.firstChild?.marks.find((mark)=>mark.type === deletion);
|
|
28
|
+
if (mark == null) return true;
|
|
29
|
+
if (mark.attrs["type"] === "anchor") return true;
|
|
30
|
+
const zwspNode = transform.doc.type.schema.text(ZWSP, [
|
|
31
|
+
deletion.create({
|
|
32
|
+
type: "anchor",
|
|
33
|
+
id: mark.attrs["id"]
|
|
34
|
+
})
|
|
35
|
+
]);
|
|
36
|
+
const mappedPos = transform.mapping.map(pos);
|
|
37
|
+
transform.insert(mappedPos + 1, zwspNode);
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
transform.steps.forEach((step)=>{
|
|
41
|
+
transaction.step(step);
|
|
42
|
+
});
|
|
43
|
+
if (transform.steps.length > 0) trace(`added ${String(transform.steps.length)} add zwsp steps to tr`, transform);
|
|
44
|
+
return transaction;
|
|
45
|
+
}
|
|
@@ -6,7 +6,6 @@ import { suggestRemoveNodeMarkStep } from "./removeNodeMarkStep.js";
|
|
|
6
6
|
import { suggestReplaceStep } from "./replaceStep.js";
|
|
7
7
|
import { getSuggestionMarks } from "./utils.js";
|
|
8
8
|
import { applySuggestionsToRange } from "./commands.js";
|
|
9
|
-
import { handleStructureStep } from "./features/wrapUnwrap/handleStructureStep.js";
|
|
10
9
|
/**
|
|
11
10
|
* This detects and handles changes from `setNodeMarkup` so that these are tracked as a modification
|
|
12
11
|
* instead of a deletion + insertion
|
|
@@ -81,11 +80,7 @@ import { handleStructureStep } from "./features/wrapUnwrap/handleStructureStep.j
|
|
|
81
80
|
* equivalent replace step will be generated, and then processed via
|
|
82
81
|
* trackReplaceStep().
|
|
83
82
|
*/ export function suggestReplaceAroundStep(trackedTransaction, state, doc, step, prevSteps, suggestionId) {
|
|
84
|
-
|
|
85
|
-
if (handled) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
handled = suggestSetNodeMarkup(trackedTransaction, state, doc, step, prevSteps, suggestionId);
|
|
83
|
+
const handled = suggestSetNodeMarkup(trackedTransaction, state, doc, step, prevSteps, suggestionId);
|
|
89
84
|
if (handled) {
|
|
90
85
|
return true;
|
|
91
86
|
}
|
package/dist/replaceStep.js
CHANGED
|
@@ -4,7 +4,6 @@ import { rebasePos } from "./rebasePos.js";
|
|
|
4
4
|
import { getSuggestionMarks } from "./utils.js";
|
|
5
5
|
import { joinBlocks } from "./features/joinBlocks/index.js";
|
|
6
6
|
import { collapseZWSPNodes, findJoinMark, joinNodesAndMarkJoinPoints, removeZWSPDeletions } from "./features/joinOnDelete/index.js";
|
|
7
|
-
import { handleStructureStep } from "./features/wrapUnwrap/handleStructureStep.js";
|
|
8
7
|
/**
|
|
9
8
|
* Transform a replace step into its equivalent tracked steps.
|
|
10
9
|
*
|
|
@@ -35,10 +34,6 @@ import { handleStructureStep } from "./features/wrapUnwrap/handleStructureStep.j
|
|
|
35
34
|
* will be joined and given the same ids. Any no-longer-necessary
|
|
36
35
|
* zero-width spaces will be removed.
|
|
37
36
|
*/ export function suggestReplaceStep(trackedTransaction, state, doc, step, prevSteps, suggestionId) {
|
|
38
|
-
const handled = handleStructureStep(trackedTransaction, state, step, prevSteps, suggestionId);
|
|
39
|
-
if (handled) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
37
|
const { deletion, insertion } = getSuggestionMarks(state.schema);
|
|
43
38
|
// Check for insertion and deletion marks directly
|
|
44
39
|
// adjacent to this step's boundaries. If they exist,
|
|
@@ -205,15 +200,16 @@ import { handleStructureStep } from "./features/wrapUnwrap/handleStructureStep.j
|
|
|
205
200
|
trackedTransaction.setSelection(TextSelection.near(trackedTransaction.doc.resolve($stepFrom.pos - 1)));
|
|
206
201
|
}
|
|
207
202
|
// Handle insertions
|
|
208
|
-
// When didBlockJoin is true, only process insertions if the slice contains
|
|
203
|
+
// When didBlockJoin is true, or nodes were joined, only process insertions if the slice contains
|
|
209
204
|
// actual new content (closed slice) rather than just structural info for the join (open slice).
|
|
210
205
|
// Open slices have openStart > 0 or openEnd > 0 and represent block structure.
|
|
211
206
|
// Closed slices have openStart = 0 and openEnd = 0 and contain new user content.
|
|
212
207
|
// TODO: Done with AI, not 100% sure about the argument but it works. Kind of.
|
|
213
208
|
// The replaced content is not equivalent to what would happen without suggestions, just with insertions
|
|
214
209
|
// but it's workable. Only issues are with deleting between different depths ( for ex. between list and root level paragraph )
|
|
210
|
+
const didJoinNodes = didBlockJoin || joinNodesTransform.steps.length > 0;
|
|
215
211
|
const sliceHasNewContent = step.slice.openStart === 0 && step.slice.openEnd === 0;
|
|
216
|
-
const shouldProcessInsertion = step.slice.content.size && (!
|
|
212
|
+
const shouldProcessInsertion = step.slice.content.size && (!didJoinNodes || sliceHasNewContent);
|
|
217
213
|
if (shouldProcessInsertion) {
|
|
218
214
|
const $to = trackedTransaction.doc.resolve(stepTo);
|
|
219
215
|
// Don't allow inserting content within an existing deletion
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,10 +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
|
-
export declare const structure: MarkSpec;
|
|
6
6
|
/**
|
|
7
7
|
* Add the deletion, insertion, and modification marks to
|
|
8
8
|
* the provided MarkSpec map.
|
|
9
9
|
*/
|
|
10
|
-
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>;
|