What Documents is
Documents is a complete edit-read-export system: @texuli/document holds the framework-free block format, validator, operations with undo, Markdown, pagination, owned DOCX and PDF writers and CRDT sync; @texuli/pages is the Svelte 5 editor, reader, paged view, export and collaboration on top of it; @texuli/editor is the rich-text surface inside text blocks. All three packages are published as 0.18.0 and are **in development** (pre-1.0). Pin the exact version or use ^0.17.0.
Editor and Pages are subsolutions of Documents, not separate Solutions.
What you get
- A validated document as data (
format: "texuli-document",formatVersion: 1), not HTML. - Typed operations with exact inverses and an op log, so undo, persistence and sync share one history.
- Markdown in and out, plus owned DOCX and PDF writers.
PageEditorandPageReaderfor people;document.runanddocument.exportfor agents.
There is no hosted editor and no document storage. The host app keeps the data. Paged-view editing is planned; the paged view is read-only today.
Install
@texuli/document has no peer dependencies. @texuli/pages and @texuli/editor need Svelte 5 (^5.57.2).
npm install @texuli/pages @texuli/document @texuli/tokensImport the token stylesheet once. The editor and reader import their own CSS.
import '@texuli/tokens/base.css';Create and edit a document
Every entry point is total: it never throws on hostile input.
import { createDocument, paragraphBlock } from '@texuli/document/model';
import { applyOperations } from '@texuli/document/ops';
import { documentToMarkdown } from '@texuli/document/markdown';
const doc = createDocument({
meta: { title: 'Release notes' },
blocks: [paragraphBlock('p1', 'Shipped the new editor.')],
});
const edit = applyOperations(doc, [
{ op: 'insert-text', id: 'p1', offset: 0, text: 'Today: ' },
]);
if (edit.ok) {
console.log(documentToMarkdown(edit.document));
}Put it on the page
Keep the document in $state.raw. Validate untrusted JSON first.
<script lang="ts">
import { loadUntrustedDocument } from '@texuli/document/schema';
import { PageEditor } from '@texuli/pages/editor';
import { PageReader } from '@texuli/pages/reader';
import '@texuli/tokens/base.css';
let { json }: { json: string } = $props();
const loaded = loadUntrustedDocument(json);
let doc = $state.raw(loaded.ok ? loaded.document : null);
</script>
{#if doc}
<PageEditor document={doc} onChange={(next) => (doc = next)} />
<PageReader document={doc} />
{/if}Document format
The canonical document is data: format, formatVersion, meta, optional page, blocks and annotations. Text blocks are the @texuli/core editor vocabulary (paragraph, heading 1–4, code, list, quote, divider, media) with strong, em, strike, code and link marks. Document blocks add callout, toggle, columns, nested pages, wrapped @texuli/blocks blocks and embeds (component, deck, sheet or canvas).
loadUntrustedDocument from @texuli/document/schema bounds work and validates at the untrusted tier.
import { loadUntrustedDocument } from '@texuli/document/schema';
const checked = loadUntrustedDocument(jsonText);Operations
applyOperations in @texuli/document/ops takes typed operations by persisted id with exact inverses. The op log is the single edit path: undo, redo, persistence and CRDT sync all replay it.
Editor model
@texuli/editor owns the text surface. RichTextEditor takes a core editor document; Markdown in and out is @texuli/editor/markdown. Pages maps those text rows onto @texuli/document blocks.
<script lang="ts">
import { RichTextEditor } from '@texuli/editor/editor';
import { documentToMarkdown, markdownToDocument } from '@texuli/editor/markdown';
const { doc } = markdownToDocument('# Plan\n\n- [ ] Draft the **brief**');
</script>
<RichTextEditor value={doc} label="Notes" onChange={(next) => {}} />Editor and reader
PageEditor from @texuli/pages/editor edits the document: slash menu, block selection and drag with keyboard equivalents, nested pages, columns, and embeds painted by @texuli/player at the untrusted tier. PageReader from @texuli/pages/reader is the read-only view with an outline.
Every change is a @texuli/document operation. Keep the document in $state.raw.
Paged view and export
PagedView from @texuli/pages/paged lays the document on real pages (A4, Letter and others) through @texuli/document/paged. It is read-only. exportDocument from @texuli/pages/export writes Markdown, DOCX or PDF as a Blob.
import { downloadBlob, exportDocument } from '@texuli/pages/export';
const { blob, filename, diagnostics } = await exportDocument(doc, 'pdf');
downloadBlob(blob, filename);Collaboration
CollabEditor from @texuli/pages/collab sits on @texuli/document/collab. The host supplies a SyncAdapter. There is no hosted collab service. Comments, suggestions and versions are in development with the rest of the package.
Keyboard
The slash menu is an APG combobox. Block handles use Space or Enter to pick up, arrows to move, Space or Enter to drop and Escape to cancel; Alt+Up and Alt+Down move at once. Column splitters step with arrow keys. Every move is announced politely.
Text rows hand the caret between blocks at their edges. Enter splits; Backspace at the start joins.
Motion and links
Inserts, removes and reorders use the shared motion runtime; prefers-reduced-motion and motion={false} are instant. Links pass safeHref and images safeImageSrc at the untrusted tier. There is no HTML sink.
Status
Documents is **in development**. @texuli/document, @texuli/pages and @texuli/editor are on npm as 0.18.0. Under 0.x a new minor may break. Native rendering is planned, not built.
Not built
- Paged-view editing (the paged view is read-only)
- Painted sheet and canvas embeds (they are typed references)
- A hosted editor, storage or collaboration service (the host supplies transport)
- Custom-format copy and drop in the rich-text editor, and a block menu of handles beyond the sortable handle
Untrusted documents
A document from an agent, a file or a share link is untrusted data. Use loadUntrustedDocument from @texuli/document/schema before any deep work. Default URL policy is the untrusted tier. There is no HTML sink.
MCP operations
Agents call document.run to create, validate, inspect, patch, convert Markdown and emit a blueprint node. PDF and DOCX go through document.export (Node only). Both operations live in the Texuli tools registry. Inputs are bounded; diagnostics come back instead of throws.
import { loadUntrustedDocument } from '@texuli/document/schema';
const checked = loadUntrustedDocument(jsonText);createDocumentPlayerHost from @texuli/pages/host lets the player paint a validated document blueprint with the reader. Do not invent a hosted generation service.

