import type { Transaction } from '@tiptap/pm/state' import type { NodeWithPos } from '@tiptap/react' export function updateNodesAttr( tr: Transaction, targets: readonly NodeWithPos[], attrName: A, next: V | ((prev: V | undefined) => V | undefined) ): boolean { if (!targets.length) return false let changed = false for (const { pos } of targets) { const currentNode = tr.doc.nodeAt(pos) if (!currentNode) continue const prevValue = (currentNode.attrs as Record)[attrName] as V | undefined const resolvedNext = typeof next === 'function' ? (next as (p: V | undefined) => V | undefined)(prevValue) : next if (prevValue === resolvedNext) continue const nextAttrs: Record = { ...currentNode.attrs } if (resolvedNext === undefined) { delete nextAttrs[attrName] } else { nextAttrs[attrName] = resolvedNext } tr.setNodeMarkup(pos, undefined, nextAttrs) changed = true } return changed }