@magic-marker/prosemirror-suggest-changes 0.3.3-wrap-unwrap.12 → 0.3.3-wrap-unwrap.14

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.
@@ -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: unknown): Promise<{
143
+ export declare function setupDocFromJSON(page: Page, docJSON: object): Promise<{
144
144
  initialState: EditorState;
145
- initialDoc: unknown;
145
+ initialDoc: object;
146
146
  }>;
147
147
  /**
148
148
  * Assert that document fully reverted to initial state.
@@ -2,6 +2,7 @@ import { Mark } from "prosemirror-model";
2
2
  import { canJoin, Transform } from "prosemirror-transform";
3
3
  import { ZWSP } from "../../constants.js";
4
4
  import { getSuggestionMarks } from "../../utils.js";
5
+ import { guardStructureMarkAttrs } from "../wrapUnwrap/types.js";
5
6
  export function isJoinMarkAttrs(attrs) {
6
7
  if (attrs["type"] !== "join") return false;
7
8
  if (attrs["data"] == null) return false;
@@ -94,7 +95,9 @@ export function maybeRevertJoinMark(tr, from, to, node, markType) {
94
95
  attrs: $endOfNode.nodeAfter.attrs,
95
96
  marks: $endOfNode.nodeAfter.marks.map((mark)=>mark.toJSON())
96
97
  };
98
+ const shouldSuppressJoinMark = hasStructureAddMark($endOfNode.nodeBefore) || hasStructureAddMark($endOfNode.nodeAfter);
97
99
  transform.join(mappedEndOfNode);
100
+ if (shouldSuppressJoinMark) return false;
98
101
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
99
102
  const joinStep = transform.steps[transform.steps.length - 1];
100
103
  const joinPos = joinStep.getMap().map(mappedEndOfNode);
@@ -111,6 +114,14 @@ export function maybeRevertJoinMark(tr, from, to, node, markType) {
111
114
  });
112
115
  return transform;
113
116
  }
117
+ function hasStructureAddMark(node) {
118
+ const { structure } = getSuggestionMarks(node.type.schema);
119
+ return node.marks.some((mark)=>{
120
+ if (mark.type !== structure) return false;
121
+ if (!guardStructureMarkAttrs(mark.attrs)) return false;
122
+ return mark.attrs.data.op.op === "add";
123
+ });
124
+ }
114
125
  /**
115
126
  * Find ZWSP nodes marked as insertions and deletions with the same mark id
116
127
  * Delete them from the given range
@@ -1,4 +1,5 @@
1
1
  import { getNodeId } from "./getNodeId.js";
2
+ import { DOC_NODE_ID } from "./constants.js";
2
3
  const TRACE_ENABLED = true;
3
4
  function trace(...args) {
4
5
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -17,8 +18,8 @@ export function buildMaterializedPaths(doc) {
17
18
  const rightSibling = children[index + 1];
18
19
  const rightSiblingId = rightSibling ? getNodeId(rightSibling) : null;
19
20
  const parent = {
20
- nodeId: "__doc__",
21
- nodeType: "__doc__",
21
+ nodeId: DOC_NODE_ID,
22
+ nodeType: DOC_NODE_ID,
22
23
  nodeAttrs: {},
23
24
  nodeMarks: [],
24
25
  childSiblingIds: [
@@ -29,6 +30,7 @@ export function buildMaterializedPaths(doc) {
29
30
  };
30
31
  paths.set(nodeId, {
31
32
  nodeType: node.type.name,
33
+ node,
32
34
  chain: [
33
35
  parent
34
36
  ]
@@ -70,6 +72,7 @@ export function buildMaterializedPaths(doc) {
70
72
  ];
71
73
  paths.set(nodeId, {
72
74
  nodeType: node.type.name,
75
+ node,
73
76
  chain
74
77
  });
75
78
  return true;
@@ -1,3 +1,3 @@
1
+ export declare const DOC_NODE_ID = "__doc__";
1
2
  export declare const STRUCTURE_CHANGES_ADD_MARKS = "structure-changes-add-mark-type";
2
3
  export declare const STRUCTURE_CHANGES_REVERT_MARKS = "structure-changes-revert-mark-type";
3
- export declare const LIST_NODES: string[];
@@ -1,7 +1,3 @@
1
+ export const DOC_NODE_ID = "__doc__";
1
2
  export const STRUCTURE_CHANGES_ADD_MARKS = "structure-changes-add-mark-type";
2
3
  export const STRUCTURE_CHANGES_REVERT_MARKS = "structure-changes-revert-mark-type";
3
- export const LIST_NODES = [
4
- "orderedList",
5
- "bulletList",
6
- "listItem"
7
- ];
@@ -118,5 +118,5 @@ export function findInsertionPos(node, pos, parent, child) {
118
118
  return leftSibling.pos + leftSibling.node.nodeSize;
119
119
  }
120
120
  // insert at end of node
121
- return pos != null ? pos + node.nodeSize - 1 : node.nodeSize - 1;
121
+ return pos != null ? pos + node.nodeSize - 1 : node.content.size;
122
122
  }
@@ -1,10 +1,17 @@
1
1
  import { type Schema, type Node } from "prosemirror-model";
2
2
  import { Plugin, PluginKey } from "prosemirror-state";
3
3
  import { type SuggestionId } from "../../generateId.js";
4
+ import { type StructuralContextPath } from "./types.js";
4
5
  import { Transform } from "prosemirror-transform";
5
6
  export declare const structureChangesKey: PluginKey<any>;
6
- export declare function structureChangesPlugin(generateId?: (schema: Schema, doc?: Node) => SuggestionId): Plugin<any>;
7
- export declare function suggestStructureChanges(docBefore: Node, docAfter: Node, generateId?: (schema: Schema, doc?: Node) => SuggestionId): {
7
+ export type SuggestStructureChangesReason = "split-derived-add";
8
+ export interface SuggestStructureChangesResult {
8
9
  handled: boolean;
9
10
  transform: Transform;
10
- };
11
+ reason?: SuggestStructureChangesReason;
12
+ }
13
+ export declare function structureChangesPlugin(generateId?: (schema: Schema, doc?: Node) => SuggestionId, opts?: {
14
+ experimental_trackStructures?: StructuralContextPath[];
15
+ }): Plugin<any>;
16
+ export declare function suggestStructureChanges(docBefore: Node, docAfter: Node, structuralContextPaths: StructuralContextPath[], generateId?: (schema: Schema, doc?: Node) => SuggestionId): SuggestStructureChangesResult;
17
+ export declare function getRequiredStructuralContextPaths(structuralContextPaths: StructuralContextPath[] | undefined): StructuralContextPath[];
@@ -3,7 +3,7 @@ import { getSuggestionMarks } from "../../utils.js";
3
3
  import { generateNextNumberId } from "../../generateId.js";
4
4
  import { getNodeId } from "./getNodeId.js";
5
5
  import { guardStructureMarkAttrs } from "./types.js";
6
- import { LIST_NODES, STRUCTURE_CHANGES_ADD_MARKS } from "./constants.js";
6
+ import { DOC_NODE_ID, STRUCTURE_CHANGES_ADD_MARKS } from "./constants.js";
7
7
  import { Transform } from "prosemirror-transform";
8
8
  import { isSuggestChangesEnabled, suggestChangesKey } from "../../plugin.js";
9
9
  import { buildMaterializedPaths } from "./buildMaterializedPaths.js";
@@ -15,12 +15,8 @@ function trace(...args) {
15
15
  console.log("[structureChanges]", ...args);
16
16
  }
17
17
  export const structureChangesKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes-structure-changes");
18
- // const listStructure = {
19
- // type: [LIST_NODE],
20
- // children: [{ type: [LIST_ITEM_NODE] }],
21
- // };
22
- // const structures = [listStructure];
23
- export function structureChangesPlugin(generateId) {
18
+ export function structureChangesPlugin(generateId, opts) {
19
+ const structuralContextPaths = getRequiredStructuralContextPaths(opts?.experimental_trackStructures);
24
20
  return new Plugin({
25
21
  key: structureChangesKey,
26
22
  appendTransaction (transactions, _oldState, newState) {
@@ -92,7 +88,7 @@ export function structureChangesPlugin(generateId) {
92
88
  trace("structureChangesPlugin", "appendTransaction", [
93
89
  ...transactions
94
90
  ]);
95
- const { transform } = suggestStructureChanges(oldDoc, newDoc, generateId);
91
+ const { transform } = suggestStructureChanges(oldDoc, newDoc, structuralContextPaths, generateId);
96
92
  const tr = newState.tr;
97
93
  transform.steps.forEach((step)=>{
98
94
  tr.step(step);
@@ -108,7 +104,7 @@ function isEnabled(tr, editorState) {
108
104
  const isEnabled = isSuggestChangesEnabled(editorState) && !tr.getMeta("history$") && !tr.getMeta("collab$") && !ySyncMeta.isUndoRedoOperation && !ySyncMeta.isChangeOrigin && !("skip" in (tr.getMeta(suggestChangesKey) ?? {}));
109
105
  return isEnabled;
110
106
  }
111
- export function suggestStructureChanges(docBefore, docAfter, generateId) {
107
+ export function suggestStructureChanges(docBefore, docAfter, structuralContextPaths, generateId) {
112
108
  const suggestionId = generateId ? generateId(docBefore.type.schema, docBefore) : generateNextNumberId(docBefore.type.schema, docBefore);
113
109
  const pathsBefore = buildMaterializedPaths(docBefore);
114
110
  const pathsAfter = buildMaterializedPaths(docAfter);
@@ -116,17 +112,31 @@ export function suggestStructureChanges(docBefore, docAfter, generateId) {
116
112
  pathsBefore: Object.fromEntries(pathsBefore.entries()),
117
113
  pathsAfter: Object.fromEntries(pathsAfter.entries())
118
114
  });
119
- const ops = getOps(pathsBefore, pathsAfter);
115
+ const { ops, reason } = getOps(pathsBefore, pathsAfter, structuralContextPaths);
120
116
  trace("suggestStructureChanges", "ops", {
121
- ops: Object.fromEntries(ops.entries())
117
+ ops: Object.fromEntries(ops.entries()),
118
+ reason
122
119
  });
123
120
  const transform = new Transform(docAfter);
121
+ if (reason) {
122
+ return {
123
+ handled: false,
124
+ transform,
125
+ reason
126
+ };
127
+ }
124
128
  addMarks(ops, transform, suggestionId);
125
129
  return {
126
130
  handled: ops.size > 0,
127
131
  transform
128
132
  };
129
133
  }
134
+ export function getRequiredStructuralContextPaths(structuralContextPaths) {
135
+ if (!structuralContextPaths?.length) {
136
+ throw new Error("experimental_trackStructures must be provided when structure tracking is enabled");
137
+ }
138
+ return structuralContextPaths;
139
+ }
130
140
  function addMarks(ops, tr, suggestionId) {
131
141
  const perfAddMarks = performance.now();
132
142
  const { structure } = getSuggestionMarks(tr.doc.type.schema);
@@ -172,20 +182,22 @@ function hasStructureAddMark(node) {
172
182
  return mark.attrs.data.op.op === "add";
173
183
  });
174
184
  }
175
- function getOps(beforePaths, afterPaths) {
185
+ function getOps(beforePaths, afterPaths, structuralContextPaths) {
176
186
  const ops = new Map();
187
+ const contextNodeTypes = getStructuralContextNodeTypes(structuralContextPaths);
177
188
  // first take care of nodes that exist in both
178
189
  for (const [id, beforePath] of beforePaths){
179
- if (LIST_NODES.includes(beforePath.nodeType)) continue;
190
+ if (contextNodeTypes.has(beforePath.nodeType)) continue;
180
191
  const afterPath = afterPaths.get(id);
181
192
  // node was removed - do nothing
182
193
  if (afterPath == null) continue;
194
+ if (contextNodeTypes.has(afterPath.nodeType)) continue;
183
195
  const sameChain = sameParentChain(beforePath.chain, afterPath.chain);
184
196
  // node did not move anywhere - do nothing
185
197
  if (sameChain) continue;
186
- const hasList = beforePath.chain.some((parent)=>LIST_NODES.includes(parent.nodeType)) || afterPath.chain.some((parent)=>LIST_NODES.includes(parent.nodeType));
187
- // node is outside lists
188
- if (!hasList) continue;
198
+ const hasStructuralContext = chainHasStructuralContext(beforePath.chain, structuralContextPaths) || chainHasStructuralContext(afterPath.chain, structuralContextPaths);
199
+ // node is outside configured structural contexts
200
+ if (!hasStructuralContext) continue;
189
201
  const op = {
190
202
  op: "move",
191
203
  from: beforePath.chain,
@@ -196,17 +208,87 @@ function getOps(beforePaths, afterPaths) {
196
208
  // now take care of nodes that exist only in afterPaths
197
209
  // (we don't care about nodes that exist only in beforePaths - they were deleted)
198
210
  for (const [id, afterPath] of afterPaths){
199
- if (LIST_NODES.includes(afterPath.nodeType)) continue;
211
+ if (contextNodeTypes.has(afterPath.nodeType)) continue;
200
212
  // ignore nodes that also exist in beforePaths - they are already handled
201
213
  if (beforePaths.has(id)) continue;
202
- const hasList = afterPath.chain.some((parent)=>LIST_NODES.includes(parent.nodeType));
203
- // node is outside lists
204
- if (!hasList) continue;
214
+ const hasStructuralContext = chainHasStructuralContext(afterPath.chain, structuralContextPaths);
215
+ // node is outside configured structural contexts
216
+ if (!hasStructuralContext) continue;
217
+ // detect block split - if detected, bail out, and let the main plugin handle it
218
+ if (isSplitDerivedAdd(id, beforePaths, afterPaths, contextNodeTypes)) {
219
+ return {
220
+ ops: new Map(),
221
+ reason: "split-derived-add"
222
+ };
223
+ }
205
224
  // node was added
206
225
  const op = {
207
226
  op: "add"
208
227
  };
209
228
  ops.set(id, op);
210
229
  }
211
- return ops;
230
+ return {
231
+ ops
232
+ };
233
+ }
234
+ function getStructuralContextNodeTypes(structuralContextPaths) {
235
+ return new Set(structuralContextPaths.flat());
236
+ }
237
+ function chainHasStructuralContext(chain, structuralContextPaths) {
238
+ const topDownChain = [
239
+ ...chain
240
+ ].reverse().filter((parent)=>parent.nodeType !== DOC_NODE_ID).map((parent)=>parent.nodeType);
241
+ return structuralContextPaths.some((path)=>containsContiguousPath(topDownChain, path));
242
+ }
243
+ // does a chain like: __doc__->nodeTypeA->nodeTypeB->nodeTypeC->nodeTypeD
244
+ // contain a structural context path (a "sub chain") like nodeTypeB->nodeTypeC ?
245
+ function containsContiguousPath(chainNodeTypes, structuralContextPath) {
246
+ if (structuralContextPath.length > chainNodeTypes.length) return false;
247
+ for(let start = 0; start <= chainNodeTypes.length - structuralContextPath.length; start++){
248
+ const matches = structuralContextPath.every((nodeType, index)=>chainNodeTypes[start + index] === nodeType);
249
+ if (matches) return true;
250
+ }
251
+ return false;
252
+ }
253
+ // detect block split - try to look at the node above, concatenate two text contents,
254
+ // and see if it combines into a single node in the old document
255
+ function isSplitDerivedAdd(newNodeId, beforePaths, afterPaths, contextNodeTypes) {
256
+ const newNode = afterPaths.get(newNodeId)?.node;
257
+ if (!newNode || newNode.textContent === "") return false;
258
+ if (matchesSplitDerivedPair(newNodeId, newNode.textContent, beforePaths, afterPaths)) {
259
+ return true;
260
+ }
261
+ const afterPath = afterPaths.get(newNodeId);
262
+ if (!afterPath) return false;
263
+ for (const parent of afterPath.chain){
264
+ if (parent.nodeType === DOC_NODE_ID) continue;
265
+ if (!contextNodeTypes.has(parent.nodeType)) continue;
266
+ const parentNode = afterPaths.get(parent.nodeId)?.node;
267
+ if (!parentNode || parentNode.textContent === "") continue;
268
+ if (matchesSplitDerivedPair(parent.nodeId, parentNode.textContent, beforePaths, afterPaths)) {
269
+ return true;
270
+ }
271
+ }
272
+ return false;
273
+ }
274
+ function matchesSplitDerivedPair(rightNodeId, rightText, beforePaths, afterPaths) {
275
+ const rightPath = afterPaths.get(rightNodeId);
276
+ const previousSiblingId = rightPath?.chain[0]?.childSiblingIds[0];
277
+ if (!previousSiblingId) return false;
278
+ const previousBefore = beforePaths.has(previousSiblingId) ? beforePaths.get(previousSiblingId)?.node : null;
279
+ const previousAfter = afterPaths.has(previousSiblingId) ? afterPaths.get(previousSiblingId)?.node : null;
280
+ if (!previousBefore || !previousAfter) return false;
281
+ if (hasStructureAddMarkInSubtree(previousBefore)) return false;
282
+ return previousBefore.textContent === previousAfter.textContent + rightText;
283
+ }
284
+ function hasStructureAddMarkInSubtree(node) {
285
+ if (hasStructureAddMark(node)) return true;
286
+ let found = false;
287
+ node.descendants((descendant)=>{
288
+ if (descendant.isText) return false;
289
+ if (!hasStructureAddMark(descendant)) return true;
290
+ found = true;
291
+ return false;
292
+ });
293
+ return found;
212
294
  }
@@ -1,5 +1,6 @@
1
- import { type Attrs } from "prosemirror-model";
1
+ import { type Attrs, type Node } from "prosemirror-model";
2
2
  import { type SuggestionId } from "../../generateId.js";
3
+ import { DOC_NODE_ID } from "./constants.js";
3
4
  interface NodeParent {
4
5
  nodeId: string;
5
6
  nodeType: string;
@@ -14,9 +15,10 @@ export interface StructureMarkAttrs {
14
15
  op: Op;
15
16
  };
16
17
  }
18
+ export type StructuralContextPath = readonly [string, ...string[]];
17
19
  export interface DocParent extends Omit<NodeParent, "nodeId"> {
18
- nodeId: "__doc__";
19
- nodeType: "__doc__";
20
+ nodeId: typeof DOC_NODE_ID;
21
+ nodeType: typeof DOC_NODE_ID;
20
22
  }
21
23
  export type Parent = NodeParent | DocParent;
22
24
  export interface MoveOp {
@@ -31,6 +33,7 @@ export type Op = MoveOp | AddOp;
31
33
  export type MaterializedPaths = Map<string, {
32
34
  chain: Parent[];
33
35
  nodeType: string;
36
+ node: Node;
34
37
  }>;
35
38
  export declare function guardDocParent(parent: Parent | undefined): parent is DocParent;
36
39
  export declare function guardStructureMarkAttrs(attrs: Attrs): attrs is StructureMarkAttrs;
@@ -1,5 +1,6 @@
1
+ import { DOC_NODE_ID } from "./constants.js";
1
2
  export function guardDocParent(parent) {
2
- return parent != null && parent.nodeId === "__doc__" && parent.nodeType === "__doc__";
3
+ return parent != null && parent.nodeId === DOC_NODE_ID && parent.nodeType === DOC_NODE_ID;
3
4
  }
4
5
  export function guardStructureMarkAttrs(attrs) {
5
6
  if (!("data" in attrs)) return false;
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled, } from "./p
4
4
  export { withSuggestChanges, transformToSuggestionTransaction, } from "./withSuggestChanges.js";
5
5
  export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled, } from "./ensureSelectionPlugin.js";
6
6
  export { guardStructureMarkAttrs } from "./features/wrapUnwrap/types.js";
7
- export type { Op as StructureOp, StructureMarkAttrs, } from "./features/wrapUnwrap/types.js";
7
+ export type { Op as StructureOp, StructureMarkAttrs, StructuralContextPath, } from "./features/wrapUnwrap/types.js";
8
8
  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" | "orderedList" | "bulletList" | "listItem" | "doc" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "image" | "hard_break", "insertion" | "deletion" | "modification" | "structure" | "code" | "em" | "link" | "strong">;
2
+ export declare function createSchema(deletionMarksVisibility?: "hidden" | "visible"): Schema<"blockquote" | "text" | "doc" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "image" | "hard_break" | "orderedList" | "bulletList" | "listItem", "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" | "orderedList" | "bulletList" | "listItem" | "doc" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "image" | "hard_break", "insertion" | "deletion" | "modification" | "structure" | "code" | "em" | "link" | "strong" | "difficulty">;
3
+ export declare const schema: Schema<"blockquote" | "text" | "doc" | "paragraph" | "horizontal_rule" | "heading" | "code_block" | "image" | "hard_break" | "orderedList" | "bulletList" | "listItem", "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
  };
@@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
3
3
  import { type Transform } from "prosemirror-transform";
4
4
  import { type EditorView } from "prosemirror-view";
5
5
  import { type SuggestionId } from "./generateId.js";
6
+ import { type StructuralContextPath } from "./features/wrapUnwrap/types.js";
6
7
  /**
7
8
  * Given a standard transaction from ProseMirror, produce
8
9
  * a new transaction that tracks the changes from the original,
@@ -27,5 +28,6 @@ export declare function transformToSuggestionTransaction(originalTransaction: Tr
27
28
  */
28
29
  export declare function withSuggestChanges(dispatchTransaction?: EditorView["dispatch"], generateId?: (schema: Schema, doc?: Node) => SuggestionId, opts?: {
29
30
  experimental_trackStructureChanges?: boolean;
31
+ experimental_trackStructures?: StructuralContextPath[];
30
32
  experimental_ensureUniqueNodeIds?: (transactions: Transaction[], oldDoc: Node, newDoc: Node) => Transform;
31
33
  }): EditorView["dispatch"];
@@ -10,7 +10,7 @@ import { isSuggestChangesEnabled, suggestChangesKey } from "./plugin.js";
10
10
  import { generateNextNumberId } from "./generateId.js";
11
11
  import { getSuggestionMarks } from "./utils.js";
12
12
  import { prependDeletionsWithZWSP } from "./prependDeletionsWithZWSP.js";
13
- import { suggestStructureChanges } from "./features/wrapUnwrap/structureChangesPlugin.js";
13
+ import { getRequiredStructuralContextPaths, suggestStructureChanges } from "./features/wrapUnwrap/structureChangesPlugin.js";
14
14
  const TRACE_ENABLED = true;
15
15
  function trace(...args) {
16
16
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -121,13 +121,15 @@ function getStepHandler(step) {
121
121
  if (isEnabled) {
122
122
  let structureChangesResult = null;
123
123
  const docBefore = transaction.docs[0];
124
- if (transaction.docChanged && docBefore && opts?.experimental_trackStructureChanges && typeof opts.experimental_ensureUniqueNodeIds === "function") {
124
+ const structuralContextPaths = opts?.experimental_trackStructureChanges ? getRequiredStructuralContextPaths(opts.experimental_trackStructures) : null;
125
+ const ensureUniqueNodeIds = opts?.experimental_ensureUniqueNodeIds;
126
+ if (transaction.docChanged && docBefore && structuralContextPaths && typeof ensureUniqueNodeIds === "function") {
125
127
  trace("trying to track structure changes first...");
126
128
  // after a transaction, some nodes may not yet have unique ids (they were just added, and the unique id plugin has not yet run)
127
129
  // this hook allows to "post-process" the transaction and add the missing ids
128
130
  // basically it allows to run the core logic of the unique ids plugin earlier
129
131
  const perfUid = performance.now();
130
- const uniqueNodeIdsTransform = opts.experimental_ensureUniqueNodeIds([
132
+ const uniqueNodeIdsTransform = ensureUniqueNodeIds([
131
133
  transaction
132
134
  ], docBefore, transaction.doc);
133
135
  trace("perf", "structure", "ensureUniqueNodsIds took", Number((performance.now() - perfUid).toFixed(2)), "ms");
@@ -137,7 +139,7 @@ function getStepHandler(step) {
137
139
  // if handled, then ignore the main plugin
138
140
  // otherwise use the main plugin
139
141
  const perfStructure = performance.now();
140
- structureChangesResult = suggestStructureChanges(docBefore, docAfter, generateId);
142
+ structureChangesResult = suggestStructureChanges(docBefore, docAfter, structuralContextPaths, generateId);
141
143
  trace("perf", "structure", "suggestStructureChanges took", Number((performance.now() - perfStructure).toFixed(2)), "ms");
142
144
  trace("structure changes transform completed", structureChangesResult.transform);
143
145
  if (structureChangesResult.handled) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-marker/prosemirror-suggest-changes",
3
- "version": "0.3.3-wrap-unwrap.12",
3
+ "version": "0.3.3-wrap-unwrap.14",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "dist/index.js",