# Documents docs

> Install @texuli/document, @texuli/pages and @texuli/editor, edit a document, export it. In development.

Language: en
Canonical: https://texuli.com/guides/solutions/documents/
Markdown: https://texuli.com/guides/solutions/documents.md (this page). Every texuli.com page has a Markdown twin at its URL plus `.md` (the home page is https://texuli.com/index.md), and any page URL answers `Accept: text/markdown` with its twin instead of HTML.
Last Updated: 2026-11-13

---

Guides

Install @texuli/document, @texuli/pages and @texuli/editor, edit a document, export it. In development.

[Back to overview](https://texuli.com/solutions/documents/)

## 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.
- `PageEditor` and `PageReader` for people; `document.run` and `document.export` for 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/tokens
```

Import 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.
