@magic-marker/prosemirror-suggest-changes 0.3.3-wrap-unwrap.1 → 0.3.3-wrap-unwrap.3

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.
@@ -48,7 +48,6 @@ export function ensureSelection() {
48
48
  }
49
49
  },
50
50
  appendTransaction (_transactions, oldState, newState) {
51
- console.log("ensureSelectionPlugin.appendTransaction", newState.selection.toJSON());
52
51
  const pluginState = ensureSelectionKey.getState(newState);
53
52
  if (!(newState.selection instanceof TextSelection)) {
54
53
  return null;
@@ -0,0 +1 @@
1
+ export declare function generateUniqueNodeId(): string;
@@ -1,4 +1,4 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */ export function generateNodeId(_node, _pos, _parent, _index) {
1
+ export function generateUniqueNodeId() {
2
2
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
3
3
  return crypto.randomUUID();
4
4
  }
@@ -101,19 +101,19 @@ function applyStructureMarkGroup(tr, suggestionId) {
101
101
  });
102
102
  }
103
103
  function revertStructureMarkGroupInOrder(tr, suggestionId) {
104
- console.group("reverting structure mark group", suggestionId);
104
+ console.group("revertStructureMarkGroupInOrder", "reverting structure mark group", suggestionId);
105
105
  const suggestionIds = findOrderedSuggestionIdsToRevert(tr.doc, suggestionId, buildMaterializedPaths(tr.doc));
106
- console.log("suggestion groups to revert", suggestionIds);
106
+ console.log("revertStructureMarkGroupInOrder", "suggestion groups to revert", suggestionIds);
107
107
  for (const suggestionId of suggestionIds){
108
108
  revertStructureMarkGroup(tr, suggestionId);
109
109
  }
110
110
  console.groupEnd();
111
111
  }
112
112
  function revertStructureMarkGroup(tr, suggestionId) {
113
- console.group("reverting structure suggestion", suggestionId);
113
+ console.group("revertStructureMarkGroup", "reverting structure suggestion", suggestionId);
114
114
  let structureMark = findNextStructureMark(tr.doc, suggestionId);
115
115
  while(structureMark !== null){
116
- console.groupCollapsed("reverting structure mark", structureMark.mark.attrs["id"], "at pos", structureMark.pos, "at node", structureMark.node.toString(), {
116
+ console.groupCollapsed("revertStructureMarkGroup", "reverting structure mark", structureMark.mark.attrs["id"], "at pos", structureMark.pos, "at node", structureMark.node.toString(), {
117
117
  structureMark
118
118
  });
119
119
  revertStructureMark(tr, structureMark.mark, structureMark.pos);
@@ -232,7 +232,7 @@ function findOrderedSuggestionIdsToRevert(node, suggestionId, materializedPaths)
232
232
  if (attrs.data.op.op !== "move") return false;
233
233
  return !sameParentChain(attrs.data.op.to, parentChain.chain);
234
234
  });
235
- console.log("mismatch?", mismatch);
235
+ console.log("findOrderedSuggestionIdsToRevert", "mismatch?", mismatch);
236
236
  if (mismatch == null) {
237
237
  return Array.from(suggestionIds).reverse();
238
238
  }
@@ -18,7 +18,7 @@ export function structureChangesPlugin(generateId) {
18
18
  key: structureChangesKey,
19
19
  appendTransaction (transactions, _oldState, newState) {
20
20
  if (transactions.some((tr)=>!isEnabled(tr, newState))) {
21
- console.warn("tracking changes is disabled, skipping structure changes plugin", [
21
+ console.warn("structureChangesPlugin", "tracking changes is disabled, skipping structure changes plugin", [
22
22
  ...transactions
23
23
  ]);
24
24
  return;
@@ -26,7 +26,7 @@ export function structureChangesPlugin(generateId) {
26
26
  // do nothing if doc hasn't changed
27
27
  const docChanged = transactions.some((transaction)=>transaction.docChanged);
28
28
  if (!docChanged) {
29
- console.warn("doc not changed, skipping structure changes plugin", [
29
+ console.warn("structureChangesPlugin", "doc not changed, skipping", [
30
30
  ...transactions
31
31
  ]);
32
32
  return;
@@ -37,7 +37,7 @@ export function structureChangesPlugin(generateId) {
37
37
  const newDoc = lastTr?.doc;
38
38
  // do nothing if there isn't a pair of docs to compare
39
39
  if (!oldDoc || !newDoc) {
40
- console.warn("old or new doc is missing, skipping structure changes plugin", {
40
+ console.warn("structureChangesPlugin", "old or new doc is missing, skipping", {
41
41
  oldDoc,
42
42
  newDoc,
43
43
  transactions: [
@@ -51,7 +51,7 @@ export function structureChangesPlugin(generateId) {
51
51
  if (node.isText) return true;
52
52
  const nodeId = getNodeId(node);
53
53
  if (nodeId == null) {
54
- console.warn("node", node.type.name, "at pos", pos, "is missing a stable id", {
54
+ console.warn("structureChangesPlugin", "node", node.type.name, "at pos", pos, "is missing a unique id", {
55
55
  id: node.attrs["id"]
56
56
  });
57
57
  idsSettled = false;
@@ -63,7 +63,7 @@ export function structureChangesPlugin(generateId) {
63
63
  if (node.isText) return true;
64
64
  const nodeId = getNodeId(node);
65
65
  if (nodeId == null) {
66
- console.warn("node", node.type.name, "at pos", pos, "is missing a stable id", {
66
+ console.warn("structureChangesPlugin", "node", node.type.name, "at pos", pos, "is missing a unique id", {
67
67
  id: node.attrs["id"]
68
68
  });
69
69
  idsSettled = false;
@@ -71,9 +71,9 @@ export function structureChangesPlugin(generateId) {
71
71
  }
72
72
  return true;
73
73
  });
74
- // do nothing if some nodes are missing stable ids - the diff is not possible then
74
+ // do nothing if some nodes are missing unique ids - the diff is not possible then
75
75
  if (!idsSettled) {
76
- console.warn("ids not settled, skipping structure changes plugin", {
76
+ console.warn("structureChangesPlugin", "ids not settled, skipping", {
77
77
  oldDoc,
78
78
  newDoc,
79
79
  transactions: [
@@ -105,12 +105,12 @@ export function suggestStructureChanges(docBefore, docAfter, generateId) {
105
105
  const suggestionId = generateId ? generateId(docBefore.type.schema, docBefore) : generateNextNumberId(docBefore.type.schema, docBefore);
106
106
  const pathsBefore = buildMaterializedPaths(docBefore);
107
107
  const pathsAfter = buildMaterializedPaths(docAfter);
108
- console.log("materialized paths", {
108
+ console.log("suggestStructureChanges", "materialized paths", {
109
109
  pathsBefore: Object.fromEntries(pathsBefore.entries()),
110
110
  pathsAfter: Object.fromEntries(pathsAfter.entries())
111
111
  });
112
112
  const ops = getOps(pathsBefore, pathsAfter);
113
- console.log("ops", {
113
+ console.log("suggestStructureChanges", "ops", {
114
114
  ops: Object.fromEntries(ops.entries())
115
115
  });
116
116
  const transform = new Transform(docAfter);
@@ -1,5 +1,4 @@
1
1
  import { type Attrs, type Node } from "prosemirror-model";
2
- export type NodeIdGenerator = (node: Node, pos: number, parent: Node | null, index: number) => string;
3
2
  interface NonDocNodeWithChildren {
4
3
  node: Node;
5
4
  pos: number;
@@ -0,0 +1,17 @@
1
+ import { type Node } from "prosemirror-model";
2
+ import { Plugin, PluginKey, type Transaction } from "prosemirror-state";
3
+ import { Transform } from "prosemirror-transform";
4
+ export declare const uniqueNodeIdsPluginKey: PluginKey<{
5
+ completedInitialRun: boolean;
6
+ }>;
7
+ export declare const UNIQUE_NODE_IDS_PLUGIN_META = "unique-node-ids-plugin";
8
+ export declare function uniqueNodeIdsPlugin({ attributeName, generateID, }: {
9
+ attributeName: string;
10
+ generateID: () => string;
11
+ }): Plugin<{
12
+ completedInitialRun: boolean;
13
+ }>;
14
+ export declare function ensureUniqueNodeIds(_transactions: Transaction[], _oldDoc: Node, newDoc: Node, options: {
15
+ attributeName: string;
16
+ generateID: () => string;
17
+ }): Transform;
@@ -1,38 +1,41 @@
1
1
  import { Plugin, PluginKey } from "prosemirror-state";
2
2
  import { getNodeId } from "./getNodeId.js";
3
3
  import { Transform } from "prosemirror-transform";
4
- // stable ids plugin
4
+ // unique ids plugin
5
5
  // https://discuss.prosemirror.net/t/how-to-avoid-copying-attributes-to-new-paragraph/4568/2
6
6
  // (also checks and fix duplicates that inevitably appear)
7
- export const stableNodeIdsKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes-stable-node-ids");
8
- export const STABLE_NODE_IDS_PLUGIN_META = "stable-node-ids-plugin";
9
- export function stableNodeIds(generateNodeId) {
7
+ export const uniqueNodeIdsPluginKey = new PluginKey("@handlewithcare/prosemirror-suggest-changes-unique-node-ids");
8
+ export const UNIQUE_NODE_IDS_PLUGIN_META = "unique-node-ids-plugin";
9
+ export function uniqueNodeIdsPlugin({ attributeName, generateID }) {
10
10
  return new Plugin({
11
- key: stableNodeIdsKey,
12
- appendTransaction (transactions, _oldState, newState) {
13
- console.log("stableNodeIdsPlugin.appendTransaction");
14
- const pluginState = stableNodeIdsKey.getState(newState);
11
+ key: uniqueNodeIdsPluginKey,
12
+ appendTransaction (transactions, oldState, newState) {
13
+ console.log("uniqueNodeIdsPlugin.appendTransaction");
14
+ const pluginState = uniqueNodeIdsPluginKey.getState(newState);
15
15
  // do nothing if doc hasn't changed (but make sure it runs initially)
16
16
  const docChanged = transactions.some((transaction)=>transaction.docChanged);
17
17
  if (!docChanged && pluginState?.completedInitialRun) {
18
- console.warn("doc not changed, skipping stable node ids plugin", [
18
+ console.warn("uniqueNodeIdsPlugin", "doc not changed, skipping", [
19
19
  ...transactions
20
20
  ]);
21
21
  return;
22
22
  }
23
- console.groupCollapsed("stableNodeIdsPlugin", "appendTransaction");
24
- console.log("stableNodeIdsPlugin", "appendTransaction", [
23
+ console.groupCollapsed("uniqueNodeIdsPlugin", "appendTransaction");
24
+ console.log("uniqueNodeIdsPlugin", "appendTransaction", [
25
25
  ...transactions
26
26
  ]);
27
27
  const tr = newState.tr;
28
- const transform = ensureStableIds(tr.doc, generateNodeId);
28
+ const transform = ensureUniqueNodeIds(transactions, oldState.doc, newState.doc, {
29
+ attributeName,
30
+ generateID
31
+ });
29
32
  transform.steps.forEach((step)=>{
30
33
  tr.step(step);
31
34
  });
32
- console.log("tr steps", tr.steps);
35
+ console.log("ensureUniqueNodeIdsPlugin", "tr steps", tr.steps);
33
36
  console.groupEnd();
34
37
  if (!tr.steps.length) return;
35
- tr.setMeta(stableNodeIdsKey, STABLE_NODE_IDS_PLUGIN_META);
38
+ tr.setMeta(uniqueNodeIdsPluginKey, UNIQUE_NODE_IDS_PLUGIN_META);
36
39
  return tr;
37
40
  },
38
41
  state: {
@@ -42,8 +45,8 @@ export function stableNodeIds(generateNodeId) {
42
45
  };
43
46
  },
44
47
  apply (tr, value) {
45
- const meta = tr.getMeta(stableNodeIdsKey);
46
- if (meta === STABLE_NODE_IDS_PLUGIN_META && !value.completedInitialRun) {
48
+ const meta = tr.getMeta(uniqueNodeIdsPluginKey);
49
+ if (meta === UNIQUE_NODE_IDS_PLUGIN_META && !value.completedInitialRun) {
47
50
  return {
48
51
  completedInitialRun: true
49
52
  };
@@ -53,10 +56,10 @@ export function stableNodeIds(generateNodeId) {
53
56
  }
54
57
  });
55
58
  }
56
- export function ensureStableIds(doc, generateNodeId) {
57
- const tr = new Transform(doc);
59
+ export function ensureUniqueNodeIds(_transactions, _oldDoc, newDoc, options) {
60
+ const tr = new Transform(newDoc);
58
61
  const nodeIds = new Set();
59
- tr.doc.descendants((node, pos, parent, index)=>{
62
+ tr.doc.descendants((node, pos)=>{
60
63
  if (node.isText) return false;
61
64
  const nodeId = getNodeId(node);
62
65
  // nodeId is set and is not duplicated
@@ -66,13 +69,13 @@ export function ensureStableIds(doc, generateNodeId) {
66
69
  }
67
70
  // nodeId is set and it is duplicated
68
71
  if (nodeId != null && nodeIds.has(nodeId)) {
69
- const id = generateNodeId(node, pos, parent, index);
72
+ const id = options.generateID();
70
73
  nodeIds.add(id);
71
74
  tr.setNodeMarkup(pos, node.type, {
72
75
  ...node.attrs,
73
- id
76
+ [options.attributeName]: id
74
77
  }, node.marks);
75
- console.log("fixed duplicate id", id, "for node", node.type.name, "at pos", pos, {
78
+ console.log("ensureUniqueNodeIds", "fixed duplicate id", id, "for node", node.type.name, "at pos", pos, {
76
79
  was: nodeId,
77
80
  is: id
78
81
  });
@@ -80,13 +83,13 @@ export function ensureStableIds(doc, generateNodeId) {
80
83
  }
81
84
  // node id is not set
82
85
  if (nodeId == null) {
83
- const id = generateNodeId(node, pos, parent, index);
86
+ const id = options.generateID();
84
87
  nodeIds.add(id);
85
88
  tr.setNodeMarkup(pos, node.type, {
86
89
  ...node.attrs,
87
- id
90
+ [options.attributeName]: id
88
91
  }, node.marks);
89
- console.log("set stable id", id, "for node", node.type.name, "at pos", pos);
92
+ console.log("ensureUniqueNodeIds", "set unique id", id, "for node", node.type.name, "at pos", pos);
90
93
  return true;
91
94
  }
92
95
  return true;
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { selectSuggestion, revertSuggestion, revertSuggestions, applySuggestion,
3
3
  export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled, } from "./plugin.js";
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
- export { stableNodeIds as experimental_stableNodeIds, stableNodeIdsKey as experimental_stableNodeIdsKey, } from "./features/wrapUnwrap/stableNodeIdsPlugin.js";
6
+ export { uniqueNodeIdsPlugin as experimental_uniqueNodeIdsPlugin, uniqueNodeIdsPluginKey as experimental_uniqueNodeIdsPluginKey, } from "./features/wrapUnwrap/uniqueNodeIdsPlugin.js";
7
7
  export { structureChangesPlugin as experimental_structureChangesPlugin, structureChangesKey as experimental_structureChangesKey, } from "./features/wrapUnwrap/structureChangesPlugin.js";
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@ export { selectSuggestion, revertSuggestion, revertSuggestions, applySuggestion,
3
3
  export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled } from "./plugin.js";
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
- export { stableNodeIds as experimental_stableNodeIds, stableNodeIdsKey as experimental_stableNodeIdsKey } from "./features/wrapUnwrap/stableNodeIdsPlugin.js";
6
+ export { uniqueNodeIdsPlugin as experimental_uniqueNodeIdsPlugin, uniqueNodeIdsPluginKey as experimental_uniqueNodeIdsPluginKey } from "./features/wrapUnwrap/uniqueNodeIdsPlugin.js";
7
7
  export { structureChangesPlugin as experimental_structureChangesPlugin, structureChangesKey as experimental_structureChangesKey } from "./features/wrapUnwrap/structureChangesPlugin.js";
@@ -1,8 +1,8 @@
1
1
  import { type Schema, type Node } from "prosemirror-model";
2
2
  import { type EditorState, type Transaction } from "prosemirror-state";
3
+ import { type Transform } from "prosemirror-transform";
3
4
  import { type EditorView } from "prosemirror-view";
4
5
  import { type SuggestionId } from "./generateId.js";
5
- import { type NodeIdGenerator } from "./features/wrapUnwrap/types.js";
6
6
  /**
7
7
  * Given a standard transaction from ProseMirror, produce
8
8
  * a new transaction that tracks the changes from the original,
@@ -27,5 +27,5 @@ export declare function transformToSuggestionTransaction(originalTransaction: Tr
27
27
  */
28
28
  export declare function withSuggestChanges(dispatchTransaction?: EditorView["dispatch"], generateId?: (schema: Schema, doc?: Node) => SuggestionId, opts?: {
29
29
  experimental_trackStructureChanges?: boolean;
30
- experimental_generateNodeId?: NodeIdGenerator;
30
+ experimental_ensureUniqueNodeIds?: (transactions: Transaction[], oldDoc: Node, newDoc: Node) => Transform;
31
31
  }): EditorView["dispatch"];
@@ -11,7 +11,12 @@ import { generateNextNumberId } from "./generateId.js";
11
11
  import { getSuggestionMarks } from "./utils.js";
12
12
  import { prependDeletionsWithZWSP } from "./prependDeletionsWithZWSP.js";
13
13
  import { suggestStructureChanges } from "./features/wrapUnwrap/structureChangesPlugin.js";
14
- import { ensureStableIds } from "./features/wrapUnwrap/stableNodeIdsPlugin.js";
14
+ const TRACE_ENABLED = true;
15
+ function trace(...args) {
16
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
17
+ if (!TRACE_ENABLED) return;
18
+ console.log("[withSuggestChanges]", ...args);
19
+ }
15
20
  function getStepHandler(step) {
16
21
  if (step instanceof ReplaceStep) {
17
22
  return suggestReplaceStep;
@@ -115,41 +120,41 @@ function getStepHandler(step) {
115
120
  let transaction = tr;
116
121
  if (isEnabled) {
117
122
  let structureChangesTransform = null;
118
- if (opts?.experimental_trackStructureChanges && typeof opts.experimental_generateNodeId === "function") {
119
- const stableIdsTransform = ensureStableIds(tr.doc, opts.experimental_generateNodeId);
123
+ const docBefore = transaction.docs[0];
124
+ if (transaction.docChanged && docBefore && opts?.experimental_trackStructureChanges && typeof opts.experimental_ensureUniqueNodeIds === "function") {
125
+ trace("trying to track structure changes first...");
126
+ // 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
+ // this hook allows to "post-process" the transaction and add the missing ids
128
+ // basically it allows to run the core logic of the unique ids plugin earlier
129
+ const uniqueNodeIdsTransform = opts.experimental_ensureUniqueNodeIds([
130
+ transaction
131
+ ], docBefore, transaction.doc);
132
+ const docAfter = uniqueNodeIdsTransform.doc;
133
+ trace("unique node ids set", docAfter);
120
134
  // try running structure changes first
121
135
  // if handled, then ignore the main plugin
122
136
  // otherwise use the main plugin
123
- const docBefore = tr.docs[0];
124
- const docAfter = stableIdsTransform.doc;
125
- if (docBefore && tr.docChanged) {
126
- structureChangesTransform = suggestStructureChanges(docBefore, docAfter, generateId);
127
- if (structureChangesTransform.steps.length > 0) {
128
- console.log("withSuggestChanges", "structure changes, applying transform", {
129
- structureChangesTransform
130
- });
131
- stableIdsTransform.steps.forEach((step)=>{
132
- transaction.step(step);
133
- });
134
- structureChangesTransform.steps.forEach((step)=>{
135
- transaction.step(step);
136
- });
137
- }
137
+ structureChangesTransform = suggestStructureChanges(docBefore, docAfter, generateId);
138
+ trace("structure changes transform completed", structureChangesTransform);
139
+ if (structureChangesTransform.steps.length > 0) {
140
+ uniqueNodeIdsTransform.steps.forEach((step)=>{
141
+ transaction.step(step);
142
+ });
143
+ structureChangesTransform.steps.forEach((step)=>{
144
+ transaction.step(step);
145
+ });
146
+ trace("applied unique id transform and structure changes transform to the transaction", transaction);
138
147
  }
139
148
  }
140
- if (structureChangesTransform == null || structureChangesTransform.steps.length === 0) {
141
- console.log("withSuggestChanges", "no structure changes, using main plugin", {
142
- structureChangesTransform
143
- });
149
+ if (transaction.docChanged && (structureChangesTransform == null || structureChangesTransform.steps.length === 0)) {
150
+ trace("running the main suggestions plugin...");
144
151
  transaction = transformToSuggestionTransaction(tr, this.state, generateId);
152
+ trace("main suggestions plugin completed", transaction);
145
153
  }
146
154
  }
147
155
  if (transaction.docChanged) {
148
156
  prependDeletionsWithZWSP(transaction);
149
157
  }
150
- console.log("withSuggestChanges", "final transaction", {
151
- transaction
152
- });
153
158
  dispatch.call(this, transaction);
154
159
  };
155
160
  }
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.1",
3
+ "version": "0.3.3-wrap-unwrap.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "dist/index.js",
@@ -1,2 +0,0 @@
1
- import { type Node } from "prosemirror-model";
2
- export declare function generateNodeId(_node: Node, _pos: number, _parent: Node | null, _index: number): string;
@@ -1,12 +0,0 @@
1
- import { type Node } from "prosemirror-model";
2
- import { Plugin, PluginKey } from "prosemirror-state";
3
- import { Transform } from "prosemirror-transform";
4
- import { type NodeIdGenerator } from "./types.js";
5
- export declare const stableNodeIdsKey: PluginKey<{
6
- completedInitialRun: boolean;
7
- }>;
8
- export declare const STABLE_NODE_IDS_PLUGIN_META = "stable-node-ids-plugin";
9
- export declare function stableNodeIds(generateNodeId: NodeIdGenerator): Plugin<{
10
- completedInitialRun: boolean;
11
- }>;
12
- export declare function ensureStableIds(doc: Node, generateNodeId: NodeIdGenerator): Transform;