Closed ruipserra closed 3 months ago
Cool idea! I added it to our tracking board. Since it'll be of higher complexity we'll have to see when we can tackle this feature.
This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 7 days
bump
This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 7 days
@bdbch
Better error handling seems quite important. I've run into an issue where I had a lot of list items, and while modifying the List extension the document got into a bad state and all the data stopped being displayed and was unrecoverable 😱
Since I was using the Collab extension, there was literally no way to get it back because the error had propagated to the server and also overwrote the local IndexedDB copy too. I've been trying to figure out how to be able to roll back to an earlier version of the Collab stored document but there doesn't seem to be a way to do this right now.
@ruipserra
Did you manage to find a solution to handling invalid content in the interim?
Hi @ruipserra Thanks for the elaborate issue explanation, that I am currently facing too. So do we have any news update for the solution so far? Thanks in advance. cc: @bdbch
We are also looking for a solution on how to solve this problem, does anyone have an idea? So much content is lost due to this "error handling".
Maybe it helps someone, we have added this function before the json is loaded into tiptap:
function validateNodeType(jsonDoc, availableNodes) {
// Check if the current node is valid
if (!availableNodes.includes(jsonDoc.type)) {
return null;
}
// If the node has content, apply validation to each child
if (jsonDoc.content) {
// Filter the content array, removing invalid nodes
jsonDoc.content = jsonDoc.content
.map(child => validateNodeType(child, availableNodes))
.filter(child => child !== null);
}
return jsonDoc;
}
const nodes = [
'doc',
'paragraph',
'text',
'heading',
'bullet_list',
'ordered_list',
'list_item',
'image',
'hard_break',
'blockquote',
// Add any additional custom nodes here
]
validateNodeType(this.value.json, nodes)
This feels like a huge oversight with the system. I have an editor that implements an auto-save when content changes, and I've tried hacking in ways to prevent the save from kicking in when invalid content is passed in. If I don't catch it, it results in lost data because it'll then save out a blank document. It would be much easier and make the app more stable if there were an onError callback we could hook into.
Is there someone from the team that can provide a minimal guide on where to start or how to fix this? I'm willing to commit some time to resolve
I'm no expert but this seems difficult to implement based on my understanding of ProseMirror.
We are working on a fix for this with: https://github.com/ueberdosis/tiptap/pull/5178
Addressed with: https://github.com/ueberdosis/tiptap/pull/5178
@nperez0111 great! how to enable that feature? or is a try/catch enough?
Not yet released yet, and we are working on a big docs overhaul so I have written my documentation in a separate place.
But I'll copy it here for you, hasn't gone through review yet from others:
To track and respond to content errors, Tiptap supports checking that the content provided matches the schema derived from the registered extensions. To use this, set the enableContentCheck
option to true
, which activates checking the content and emitting contentError
events. These events can be listened to with the onContentError
callback. By default, this flag is set to false
to maintain compatibility with previous versions.
contentError
eventThe contentError
event is emitted when the initial content
provided during editor setup is incompatible with the schema.
disableCollaboration
function. Invoking this function reinitializes the editor without the collaboration extension, ensuring that any removed content is not synchronized with other users.This event can be handled either directly as an option through onContentError
like:
new Editor({
content: invalidContent,
onContentError() {
// your handler here
}
})
Or by attaching a listener to the contentError
event on the editor instance.
const editor = new Editor({ ...options })
editor.on('contentError', ()=> {
// your handler here
})
For more implementation examples, refer to the [events] section.
How you handle schema errors may be specific to your application but here are our suggestions:
Depending on your use case, the default behavior of stripping unknown content keeps your content in a known valid state for future editing.
Depending on your use case, you may want to set the enableContentCheck
flag and listen to contentError
events. When this event is received, you may want to do things like this example:
onContentError({ editor, error, disableCollaboration }) {
// This callback is only available on editor initialization.
if (disableCollaboration) {
// Removes the collaboration extension.
disableCollaboration()
}
// Since the content is invalid, we don't want to emit an update
// Preventing synchronization with other editors or to a server
const emitUpdate = false
// Disable the editor to prevent further user input
editor.setEditable(false, emitUpdate)
// Maybe show a notification to the user that they need to refresh the app
}
What problem are you facing?
As a developer building an application with TipTap at its core, I would like a way to handle invalid content errors caused by unknown nodes or mark types.
Rationale
Long lived production systems will eventually hit a point where the editor schema evolves in non-backwards-compatible ways. An application that stores documents in persistent storage will later be unable to load them, since the editor will error out and instead render an empty document. For end users, this is equivalent to full data loss.
Some possible solutions to this:
In my view, these strategies highlight a gap in TipTap's API. I believe TipTap itself can help solve this in a more robust way.
What’s the solution you would like to see?
At a high-level, I like the approach taken by remirror: https://remirror.io/docs/concepts/error-handling/
onError
callback that is called when loading the content failsAnother benefit to this approach is that the
onError
callback can be used to capture the error in an error monitoring system. Currently it's not obvious how to do so because TipTap is catching the error.That said, this is only one way, and maybe there are simpler alternatives that would be a better fit for TipTap.
What alternatives did you consider?
https://github.com/ueberdosis/tiptap/issues/2283#issuecomment-995526706 suggests storing and loading HTML instead:
In my opinion this is not feasible for a lot of apps. Processing the HTML in a backend would involve reimplementing a lot of ProseMirror's code and editor schema.
Anything to add? (optional)
Thanks for developing TipTap! It's been a very positive experience so far 💞
Are you sponsoring us?