tdoc

A document model you own.

Open, edit, paginate and write .docx β€” in a browser and in a Worker. The page breaks you see while editing are the page breaks in the exported PDF, because the same layout decided both.

Open the editor Render a PDF, no browser

The one thing it will not do is lose your contract

Fidelity is the default and understanding is opt-in. Every node keeps a range into the source, so a node nobody touched serializes as the bytes it arrived as β€” and an element tdoc has never heard of still round-trips. Nine real Word contracts assert it on every run: read the file, write it back, compare byte for byte.

29/29documents round-trip byte-identical
465tests, most of them against real contracts
100%of pages printed where the screen put them

Why it exists

Four requirements, and one of them could not be met on top of an existing editor at any price. They are in requirements.md, and each one names the thing that was tried first.

R1 β€” the screen is the PDF
One layout engine decides page assignment, once. The PDF renderer never repaginates; it draws pages that were already sliced. So a line can wrap differently inside a page, and a page cannot move under a signature block.
R2 β€” one CRDT, written by browsers and by agents
A document's shared state has to be readable and writable by every writer. That is not something you can rent: the editor this replaced kept an obfuscated operations log, sharded across rooms, with no room-joining API and a licence forbidding reconstruction. Owning the model is the whole point.
R3 β€” round-trip to Word without content loss
Layout may shift. Content may not β€” including everything tdoc does not model, which is the harder half.
R4 β€” tracked changes through the same API a person uses
An agent's suggestion reads as the agent's. Authorship is a per-operation attribute, so a suggestion made over HTTP is reviewable rather than attributed to whoever's browser happened to apply it.

The layers

tdoc/editor   ProseMirror over the model            browser
tdoc/crdt     Yjs over the model                    anywhere
tdoc/pdf      pages -> a PDF file                    anywhere
tdoc/layout   pagination: blocks -> pages           browser + headless
tdoc/model    typed document model                  anywhere
tdoc/opc      parts, content types, relationships   anywhere
tdoc/xml      lossless XML tree                     anywhere

β€œAnywhere” means a Cloudflare Worker. No Node built-ins, no subprocess, no DOM below the layout layer β€” zip inflate and deflate go through DecompressionStream. That is load-bearing rather than tidy: the PDF on the next page is rendered by a Worker, with no browser and no office suite anywhere in it.

What it does not do