RangeError: Position X out of range

Hello! This is my first time posting and I’m pretty new to ProseMirror. Thank you for this wonderful library.

I am trying to create a plugin to integrate a pre-existing image picker into ProseMirror. The existing picker is an entirely separate application, accessed by calling window.open when the user clicks the plugin’s menu item, then broadcasting the user’s selection via window.postMessage. I have a listener registered to receive the data from that message, and up to that point everything seems to work fine.

The run method of the menu item adds the message event listener, stores state.tr.selection in a variable called currentSelection (scoped to the plugin module) and opens the popup.

The message listener receives image data, closes the popup, creates a new image node, and creates a transaction. The transaction is created from the current state (at the time the message is received) which is accessed using the method found here. The pm-specific stuff looks like this:

let imageData = e.data.imageData,
    imageAtts = {src: imageData.id, title: "a title", alt:"an alt"},
    newImageNode = schema.nodes.image.create(imageAtts),
    state = getState(),
    tr = state.tr

currentSelection.replaceWith(tr, newImageNode)
view.dispatch(tr)

view is the top-level EditorView instance, which is passed to the plugin module after it is instantiated with a function that just goes:

const mountEditorViewToImagePlugin = (viewInstance) => {
  view = viewInstance
}

I have tried many variations on the above theme and so far nothing is working. I’ve run into a lot of different errors but the most common one is that the above code throws a RangeError: Position X out of range. It’s a little baffling because as far as I can tell, ProseMirror is pulling the ranges from the selection. But the selection was generated by ProseMirrror. So I would expect that to be within range.

This seems like it should be a very simple thing (inserting a node into a document) so I spent a solid couple days trying to figure it out myself before posting here - digging through source code, comparing to examples, searching for similar questions here, etc … but I seem to keep running into the same walls. Any help would be greatly appreciated. Thanks!

Hmmm I always do my transforms on a tr directly instead of through a selection like you have. It’s possible that currentSelection is for an outdated state.

What about something like:

tr.replaceWith(currentSelection.from, currentSelection.to, newImageNode);

Alternatively I’d recommend playing around in the console and trying to get your image to be inserted manually just so you can get to a working case.

Just curious, is this an open source image picker or custom?

Thanks for answering, it’s working! It is a custom image picker. We are building an internal editor with an eye to open-sourcing parts of it that make sense, though.

I had already tried your suggested code and gotten the same result. But playing around in the console was very illuminating. Thank you so much for that suggestion - I’m not really used to that being as easy as it is in PM. In the console I was actually able to get it to work with just :

let newImageNode = view.state.schema.nodes.image.create(imageProps)

view.dispatch(view.state.tr.replaceSelectionWith(newImageNode))

Dang, wow.

When I tried that code in my plugin though, nothing happened at all. So really quickly then I noticed I was using a different schema instance to create the new node. I changed schema.node.image.create({...}) to view.state.schema.nodes.image.create({...}) and bam it worked.

It’s kind of a long and convoluted story as to why there was a second schema instance in the first place. I thought it was necessary to create a custom node, but clearly it is not. It was also part of a broader, confused attempt at encapsulating the plugin. I think I’m not fully grasping the best way to structure these things yet. There are some paradigms at play here that I’m not accustomed to, but wow do they ever seem powerful.

Thank you for the help!

ADDENDUM: I just want to add that it’s amazing how much code I am now able to delete

Schemas are currently not ‘pluggable’, in that they need a certain coherence, and allowing plugins to add random nodes to them (which I tried at one point) doesn’t work very well because it’s not clear how these nodes should relate to the other nodes. As such, schemas need to be created ‘manually’ for a given setup, though you can of course have your modules export helper functions to make things easier.

1 Like