Wordgard Release 0.1

GeneralMaximus1 pts0 comments

Wordgard Release 0.1

Marijn Haverbeke's blog<br>(license)

Wordgard Release 0.1

Thursday, July 2, 2026 typescript architecture prosemirror wordgard<br>I am happy to announce that my latest project, which I've been talking<br>about for years, is now out with a first release.

The project is called Wordgard. It is a new<br>iteration of a ProseMirror-style rich text<br>editor system, integrating the things I've learned since stabilizing<br>ProseMirror, nine years ago. The architecture also takes a lot of<br>inspiration from the version 6 redesign of the<br>CodeMirror text editor.

Wordgard is (once again) a JavaScript library that uses the browser<br>DOM to display its editor interface. It is licensed under an MIT<br>license. The code is available on my Forgejo<br>server.

It's a little concerning how I keep implementing new editors over and<br>over (by my count this is the 6th non-trivial one). But doing this<br>somehow hasn't lost its charm yet. It still feels like the designs get<br>better every iteration. I don't expect I'll find editor-architecture<br>nirvana before I retire, but I'm sure I'm getting closer to it.

Motivation

I'm still proud of ProseMirror, and ProseMirror isn't going<br>anywhere—it will continue to be maintained. But there are parts of its<br>design that make me wince every time I have to work with them, because<br>at this point I know that I should have done them differently.

Instead of trying to change ProseMirror to incorporate these new<br>insights, I have chosen to create a completely new system with a new<br>name. A ProseMirror 2.0 with an incompatible interface would amount to<br>the same but make it ambiguous what people mean when referring to<br>ProseMirror. Trying to graft stuff on in a backwards-compatible way as<br>an 1.x version would produce a compromised win32-style mess. I'm not<br>all that fond of the ProseMirror pun anymore either (it's CodeMirror<br>but for prose, get it?) So: green field full rewrite! You'll find a<br>lot of ideas from ProseMirror in Wordgard, but the programming<br>interface is built from scratch, without concern for compatibility.

Let's look at the parts of ProseMirror that I think I improved on.

Stop Doing Steps

“Make sure you compensate for the document shift caused by the first<br>step when adding a second.” “To figure out what range of the document<br>was replaced, you have to iterate through the sequence of steps in<br>both directions, mapping positions in the new document forward and<br>positions in the old document backward.” “Yes, I'll have one<br>replace-around-step please.” — statements dreamed up by the utterly<br>deranged.

ProseMirror change representation was designed by a person who was<br>very much occupied with the problem of preserving semantic meaning for<br>changes even if the changes were transformed, but who also didn't have<br>a lot of experience with change formats. Steps break down changes into<br>atomic parts that each do a single clear thing. A given editor update<br>might involve any number of them, each defined to act on the document<br>produced by the one before it. They serve their purpose, but they are<br>seriously awkward to work with.

Wordgard uses a much simpler but arguably more powerful system based<br>on my experience with CodeMirror's change representation, which<br>derives from the old “delta” format from ShareJS. In CodeMirror, a<br>change is a sequence of sections, each of which either preserves a<br>part of the old document, or replaces it with a piece of new content.<br>So in a document of length 10, inserting an L at 4 is represented<br>[keep 4] [replace 0 with "L"] [keep 6], and deleting the first two<br>characters would be [replace 2 with ""] [keep 8].

Wordgard extends this with modification sections, which preserve the<br>structure of a section, but add or remove marks to it (which are<br>things like emphasis, link style, or image alt text). Making the word<br>from 3 to 6 bold would be represented as [keep 3] [update 3 +bold] [keep 4].

Of course, unlike CodeMirror's plain text, rich text content isn't<br>just a flat string. Because Wordgard uses a token-counting indexing<br>system for document positions (the same system ProseMirror uses) the<br>change format can address the document as a flat sequence of tokens<br>(node open and close tokens, and leaf tokens), into which it splices<br>new sequences of tokens.

These types of changes can easily be combined, so that a single<br>transaction always has a single change associated with it, which is<br>easy to inspect and reason about. They also support a limited form of<br>operational transformation, making it possible to merge a bunch of<br>changes that are all described in terms of the start document. That<br>gives us an ergonomic way of describing transactions with multiple<br>changes and makes it possible to implement collaborative editing and<br>an undo history that supports undoing some changes but not others.

But the document is not really a flat sequence of tokens. Those<br>tokens only make sense if they combine to form a well-formed tree. If<br>you delete a node closing token, for example, the tokens aren't<br>balanced anymore,...

prosemirror document wordgard change changes tokens

Related Articles