What does a read-only editor even mean?

That makes sense!

I’m still feeling my way through the documentation, but conceptually, how would the comments update their range when another user makes an edit?

The collab demo from the website repository uses comments, so you should probably check it out: collab demo code.
In terms of updating the ranges, this is the important bit: code

I would be interested in an update on this topic in regards to the latest ProseMirror version. A lot of things have changed and filtering actions does not seem to be a viable option anymore, as we are letting the browser handle more events in the contenteditable than in older versions of ProseMirror.

What I’m currently trying to achieve is the following read-only mode:

  • The user should be able to select and copy content
  • The user should not be able to edit the document content in any way
  • The user should be able to select content and add annotations to the selected range
  • The editor should show changes from other collaborators who can edit the document in real-time
  • The user should not see any menus (this one is easy as menus are completely decoupled now)
  • Ideally the user should not see a caret

My current experimental approach looks like this:

  • Set the ProseMirror wrapper to contentEditable = false (needed to hide the caret)
  • Remove plugins that are used for editing (e.g. keymaps, input rules, history, etc.)
  • Ignore all actions of type transform, which do not have a collabState field set (ignore everything but real-time updates originating from other users). This is needed as there are still events that are not prevented by setting contentEditable to false:
    • Editing events like paste and for example custom click handlers that generate transforms

What do you think about this approach? Are there better ways to achieve the kind of read-only mode that I want?
The only thing that is currently not exposed by ProseMirror is the option to set contentEditable to false. This can be achieved by using editorView.docView.dom.contentEditable = "false". If this is the right approach it would be nice to expose a contentEditable prop.

One thing missing here is the ability to add annotations to the active selection as the selection isn’t tracked by ProseMirror as soon as we’re not using contenteditable.

Another question is if an “official” read-only mode provided by ProseMirror would be better to achieve these things. In that case I would expect ProseMirror to prevent all editing that is created through browser events while still accepting transforms through the API.

Filtering out transform actions is indeed the recommended approach to making an editor read-only. But since 0.15 you’ll still see edits flash and then disappear when you make them, which is non-optimal. Yet turning off contentEditable breaks selection, as you noted.

We can go in two directions, depending on whether we want to have a caret in readOnly mode. Either change the way events are handled to suppress DOM editing when readOnly is enabled, or disable contenteditable entirely, and handle the correspondence between the selection state and the DOM selection differently in that case.

I’m leaning towards the second. Does anyone feel having a cursor when the content isn’t editable is a useful feature?

During my testing I noticed that edits do not disappear immediately in 0.15. For example writing text like “hello” inside a paragraph, stays in the editor until I change the selection. I tested this in Chrome and Firefox on Linux.

I’m also leaning towards the second approach. For the use cases that I have in mind, I do not want to show a cursor in read-only mode, as it makes the user think that the document can be edited.

If you decide to ignore an action, you have to call updateState with the old state to ensure consistency. That’s awkward, so I’m working on removing this requirement right now. (This was already a problem for some types of changes in 0.14.)

Ah ok, I did not know that! Indeed, it fixes the problem, but great to hear that you’re working on removing this requirement.

See this patch.

1 Like

I am also for the second approach, and at my current hack I also set contenteditable to false for hiding caret at what is supposed to be non-editable nodes. It would be great to have a prosemirror provided way on how to deal with non-editable nodes.

Second approach would be preferred. I’m currently having my page render two divs of content, one that has an editor and one that has a copy of the content. When a user hits “edit”, I switch which div is visible. (I don’t currently have the requirement of annotations or collab).

I’ve pushed some changes that add an editable prop that you can set to false to disable editing. Testing would be appreciated.

Thanks for the fast implementation! I’m currently testing it and noticed the following issue:

Setting the editable prop to true or false does not work and it needs to be set to a function accepting the editor state. I think both options make sense, but I’m not sure if the current behavior is by design. Either we need to update the docs or we need to add the possibility of using a boolean in addition to being able to use a function.

Ah, right, the fact that it expects a function is intentional (so that you can easily make it depend on some piece of state).

We are also in the situation that we still need changes come through and usually only block 95% of editing actions, so we still need a caret. I understand that you are now going for the option without a caret, but the filtering (with flicker) will continue to work, right?

Right, the filtering continues to work.

How do you decide on which actions to block? Are these scoped to certain node types for different users? In that case one approach could be to let the node decide at render time whether to set its contenteditable attribute to false. You could save the information of which nodes to block inside the schema.cached object and access it inside the toDOM method of the node. That way you won’t have any caret inside the nodes that the user should not be able to edit and there won’t be any flickering.

@marijn any chance we will get the new read-only functionality in a new release before Christmas? I would love to put this in production as soon as possible :slight_smile:

I am going to try to do a release tomorrow, but Real Life might interfere, so don’t hold your breath.

1 Like

I didn’t answer as we are currently discussing this and we may change it. But in general, flickering doesn’t sound so bad. Peopel should know that they aren’t supposed to be able to type anything, so I’m not concerned about them getting a less-than-perfect UX if they don’t follow the rules.

Release 0.16.0 is out.

2 Likes

Awesome!! Thanks for the new release :slight_smile: