Skip to content

Conversation

@Some1Somewhere
Copy link

@Some1Somewhere Some1Somewhere commented Nov 12, 2025

Fixes : #24

When a footnote reference is pasted, the UUID gets copied, creating duplicate data-id across references.
We add a paste handler plugin to go through the slice and ensure that pasted footnote references get a new UUID

Footnote reference handling improvements

  • Added a new ProseMirror plugin (footnotePasteHandler) to the FootnoteReference node that recursively traverses pasted content and assigns a new unique data-id to every footnoteReference, ensuring no duplicate IDs are introduced when pasting.
  • Implemented a recursive mapNode function to rebuild the node tree for pasted content, updating attributes as needed for each footnoteReference.

Code cleanup

  • Removed unused nodeInputRule import from @tiptap/core in package/src/footnotes/reference.ts.
  • Added explicit imports for Node, Fragment, and Slice from @tiptap/pm/model to support the new plugin logic.

When a footnote reference is pasted, the uuid gets copied, creating duplicate references.
We add a paste handler, to go through the slice, and ensure that pasted footnoteReferences get a new UUID
Copy link

@shailejajain shailejajain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some quick questions

Comment on lines +129 to +133
transformPasted(slice) {
const newTopLevelNodes: PMNode[] = [];
slice.content.forEach(topNode => {
newTopLevelNodes.push(mapNode(topNode));
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this not use map? feels like its just mapping each node in slice.content

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiptap fragment (which is what slice.content is) does not have a map function. It only has a forEach function to interate through the nodes in the fragment. That's why I used a forEach

*/
const mapNode = (node: PMNode): PMNode => {
// 1. If this is a footnoteReference, create a new one with a new ID.
if (node.type.name === this.name) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does this.name get set to footnoteReference?

Copy link
Author

@Some1Somewhere Some1Somewhere Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

// 2. If this node has children, we must recurse by mapping over its content.
if (node.content && node.content.size > 0) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if node.type.name === this.name is set, the node can't have children?

Copy link
Author

@Some1Somewhere Some1Somewhere Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

footnoteReference cannot have child nodes. It can only have text within it, according to the schema defined :
https://github.com/Some1Somewhere/tiptap-footnotes/blob/2984459a4eb67167ad65c17fd2647a018fd6c9be/package/src/footnotes/reference.ts#L25

Copy link

@shailejajain shailejajain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copying a footnote reference duplicates the data-id value

2 participants