I think that I found an approach that will work for my use case using a Plugin
My plugin state is essentially:
enum UIDPresence {
Present,
Removed,
}
type UIDPluginState = {
/** UID Value to isAlive */
uids: Map<String, UIDPresence>;
updates: ((tr: GTransaction) => GTransaction)[],
};
I have an apply
set up to create the transaction updates passed to appendTransactions
and I basically used prosemirror-utils
's findChildrenByType(oldState.doc, grammarSchema.nodes.atom)
and findChildrenByType(newState.doc, grammarSchema.nodes.atom)
to get lists of atom nodes in both before and after documents.
This allowed me to basically create transaction updates based on if the transactions introduce an existing UID into the doc, or insert a previously “Removed” UID into the doc.
for (const [addedUID, addedAt] of diffUIDs.added) {
if (addedUID === "unknown") {
const newUID = getUID()
value.updates.push(tr => tr.setNodeMarkup(addedAt.pos, undefined, { ...addedAt.attrs, uid: newUID, uidCopied: "unknown" }))
value.uids.set(newUID, UIDPresence.Present)
} else {
const addedUIDPresence = value.uids.get(addedUID)
if (addedUIDPresence === UIDPresence.Present) {
// already exists, so we need to ensure the new atom is a "copy"
const newUID = getUID()
value.updates.push(tr => tr.setNodeMarkup(addedAt.pos, undefined, { ...addedAt.attrs, uid: newUID, uidCopied: addedUID }))
value.uids.set(newUID, UIDPresence.Present)
} else if (addedUIDPresence === UIDPresence.Removed) {
// existed previously, but is now being reinserted
value.uids.set(addedUID, UIDPresence.Present)
} else {
value.uids.set(addedUID, UIDPresence.Present)
}
}
}
for (const [removedUID, _removedAt] of diffUIDs.toRemove) {
value.uids.set(removedUID, UIDPresence.Removed)
}
So far it looks like it’s working really well, and I think this approach gives me several ideas for how to approach other problems I’m aiming to solve!