Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
98b858b
feat(document-api): bootstrap package with type model and read API core
harbournick Feb 17, 2026
a48aeed
feat(document-api): add mutating/comment/list/track-changes API modules
harbournick Feb 17, 2026
5fa2513
feat(super-editor): add read-path document-api adapters and resolver …
harbournick Feb 17, 2026
5ae2e83
feat(super-editor): add mutation document-api adapters and command pl…
harbournick Feb 17, 2026
7da5112
fix(super-editor): correct document-api tracked IDs, list dry-run beh…
harbournick Feb 17, 2026
c5a4860
fix(super-editor): make list commands can()-safe and prevent sdBlockI…
harbournick Feb 17, 2026
4a4f8d4
fix(search): honor caseSensitive for plain-string queries
harbournick Feb 17, 2026
bd6e1ad
fix(super-editor): honor forceTrackChanges in dispatch and reject amb…
harbournick Feb 17, 2026
a152f78
fix(super-editor): enforce tracked-mode user preconditions in dry-run…
harbournick Feb 17, 2026
d779e52
refactor(document-api-adapters): unify adapter capability errors and …
harbournick Feb 17, 2026
bf4fb0c
feat(document-api): add capability model and super-editor capability …
harbournick Feb 17, 2026
a9519d9
feat(document-api): add contract schemas and derive operation member …
harbournick Feb 17, 2026
75078a7
feat(document-api): add contract generation/check scripts and parity …
harbournick Feb 17, 2026
4697841
chore(document-api): document generated vs manual file ownership boun…
harbournick Feb 17, 2026
da87f46
chore(super-editor): add doc-api helper utilities and coordsAtPos reg…
harbournick Feb 17, 2026
4f0acef
refactor(comment): harden comment target identity and anchor resolution
harbournick Feb 17, 2026
1f5077c
test(document-api): align conformance vectors with adapter behavior u…
harbournick Feb 17, 2026
a7eb1b6
fix(super-editor): harden document-api adapters with offset, capabili…
harbournick Feb 17, 2026
d79b05f
chore(document-api): automate contract output sync/check via hooks an…
harbournick Feb 18, 2026
ea7cc47
fix(super-editor): preserve direct mutations and remove leaked docume…
harbournick Feb 18, 2026
e559581
fix(document-api): pipe generated JSON through Prettier to fix sync/f…
harbournick Feb 18, 2026
4304a3a
fix(document-api): align list mutator catalog flags with implemented …
harbournick Feb 18, 2026
dc1c158
feat(document-api): add invoke dynamic dispatch with typed operation …
harbournick Feb 18, 2026
331d599
Merge branch 'main' into nick/sd-1937-contract-first-document-api-alpha
harbournick Feb 18, 2026
ed3024e
fix(document-api): enforce skipTrackChanges for direct-mode bold form…
harbournick Feb 18, 2026
e962a89
chore: consolidate operation definitions and enforce typed invoke dis…
harbournick Feb 18, 2026
ed004ea
Merge branch 'main' into nick/sd-1937-contract-first-document-api-alpha
harbournick Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci-document-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI Document API

permissions:
contents: read

on:
pull_request:
paths:
- 'packages/document-api/**'
- 'apps/docs/document-api/**'
- 'package.json'
- '.github/workflows/ci-document-api.yml'
workflow_dispatch:

concurrency:
group: ci-document-api-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check contract parity and generated outputs
run: pnpm run docapi:check
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ tests/visual/ Visual regression tests (Playwright + R2 baselines)
| Style resolution | `layout-engine/style-engine/` |
| Main entry point (Vue) | `superdoc/src/SuperDoc.vue` |
| Visual regression tests | `tests/visual/` (see its CLAUDE.md) |
| Document API contract | `packages/document-api/src/contract/operation-definitions.ts` |
| Adding a doc-api operation | See `packages/document-api/README.md` § "Adding a new operation" |

## Style Resolution Boundary

Expand All @@ -82,6 +84,18 @@ tests/visual/ Visual regression tests (Playwright + R2 baselines)
- **Editing commands/behavior**: Modify `super-editor/src/extensions/`
- **State bridging**: Modify `PresentationEditor.ts`

## Document API Contract

The `packages/document-api/` package uses a contract-first pattern with a single source of truth.

- **`operation-definitions.ts`** — canonical object defining every operation's key, metadata, member path, reference doc path, and group. All downstream maps are projected from this file automatically.
- **`operation-registry.ts`** — type-level registry mapping each operation to its `input`, `options`, and `output` types.
- **`invoke.ts`** — `TypedDispatchTable` validates dispatch wiring against the registry at compile time.

Adding a new operation touches 4 files: `operation-definitions.ts`, `operation-registry.ts`, `invoke.ts` (dispatch table), and the implementation. See `packages/document-api/README.md` for the full guide.

Do NOT hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFERENCE_DOC_PATH_MAP`, or `REFERENCE_OPERATION_GROUPS` — they are derived from `OPERATION_DEFINITIONS`.

## JSDoc types

Many packages use `.js` files with JSDoc `@typedef` for type definitions (e.g., `packages/superdoc/src/core/types/index.js`). These typedefs ARE the published type declarations — `vite-plugin-dts` generates `.d.ts` files from them.
Expand Down
17 changes: 17 additions & 0 deletions apps/docs/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ When moving or renaming a page, always add a redirect in `docs.json`:
}
```

## Document API generation boundary

Document API docs have mixed manual/generated ownership. Treat these paths as authoritative:

- `apps/docs/document-api/reference/*`: generated, commit to git, do not hand-edit.
- `packages/document-api/generated/*`: generated, commit to git, do not hand-edit.
- `apps/docs/document-api/overview.mdx`: manual except for the block between:
- `/* DOC_API_GENERATED_API_SURFACE_START */`
- `/* DOC_API_GENERATED_API_SURFACE_END */`

To refresh generated content:

```bash
pnpm exec tsx packages/document-api/scripts/generate-contract-outputs.ts
pnpm exec tsx packages/document-api/scripts/check-contract-outputs.ts
```

## Brand voice

One personality, two registers. SuperDoc is the same person in every conversation — warm, clear, technically confident. It adjusts **what it emphasizes** based on who's listening. Developers hear about the how. Leaders hear about the why.
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
},
{
"group": "Document API",
"tag": "SOON",
"tag": "ALPHA",
"pages": ["document-api/overview"]
},
{
Expand Down
182 changes: 67 additions & 115 deletions apps/docs/document-api/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,122 +5,74 @@ description: A stable, engine-agnostic interface for programmatic document acces
keywords: "document api, programmatic access, query documents, document manipulation, headless docx"
---

The Document API is a new way to interact with documents programmatically. Query content, make changes, and build automations — all without touching editor internals.
Document API gives you a consistent way to read and edit documents without relying on editor internals.

<Info>
**Coming Soon** — The Document API is currently in development. This page previews what's coming.
Document API is in <strong>alpha</strong> and subject to breaking changes while the contract and adapters continue to evolve. The current API is not yet comprehensive, and more commands and namespaces are being added on an ongoing basis.
</Info>

## Why Document API?

Today, programmatic access requires using internal editor methods:

```javascript
// Current approach - uses internal APIs
editor.commands.insertContent(content);
editor.state.doc.descendants((node) => { ... });
```

This works, but:
- Internal APIs can change between versions
- Requires understanding ProseMirror internals
- Tightly coupled to the editor implementation

## What's coming

The Document API provides a stable, high-level interface:

```javascript
// Document API - stable public interface
const paragraphs = doc.query({ type: 'paragraph' });
const tables = doc.query({ type: 'table', contains: 'Revenue' });

doc.replace(paragraphs[0], { text: 'New content' });
```

<CardGroup cols={2}>
<Card title="Query DSL" icon="search">
Find content by type, attributes, or text. Filter tables, paragraphs, lists — anything in the document.
</Card>
<Card title="Stable Interface" icon="shield-check">
Public API that won't break between versions. Build with confidence.
</Card>
<Card title="Engine Agnostic" icon="repeat">
Works the same whether you're in the browser, Node.js, or headless mode.
</Card>
<Card title="Type Safe" icon="code">
Full TypeScript support with autocomplete and type checking.
</Card>
</CardGroup>

## Feature preview

### Querying content

Find any content in your document:

```javascript
// Find all paragraphs
const paragraphs = doc.query({ type: 'paragraph' });

// Find tables containing specific text
const tables = doc.query({
type: 'table',
contains: 'Q4 Revenue'
});

// Find content by attributes
const signatures = doc.query({
type: 'field-annotation',
attrs: { fieldType: 'signature' }
});
```

### Making changes

Modify documents with a clean API:

```javascript
// Replace content
doc.replace(address, { text: 'Updated text' });

// Insert at position
doc.insert(address, { type: 'paragraph', text: 'New paragraph' });

// Delete content
doc.delete(address);
```

### Working with tables

First-class table operations:

```javascript
// Add a row
doc.table(tableAddress).addRow({ after: 2 });

// Update a cell
doc.table(tableAddress).cell(1, 2).replace({ text: 'New value' });
```

## Timeline

<Steps>
<Step title="v0 — Read-only API">
Query DSL for finding and reading document content
</Step>
<Step title="v1 — Mutations">
Insert, replace, and delete operations
</Step>
<Step title="v2 — Advanced Operations">
Table operations, list manipulation, track changes integration
</Step>
</Steps>

## Stay updated

Join Discord to get notified when Document API launches:

<Card title="Join Discord" icon="discord" href="https://discord.com/invite/b9UuaZRyaB">
Get early access and share feedback
</Card>
## Why use Document API

- Build automations without editor-specific code.
- Work with predictable inputs and outputs defined per operation.
- Check capabilities up front and branch safely when features are unavailable.

## Reference

- Full operation reference: [/document-api/reference/index](/document-api/reference/index)
- Machine-readable files are available for automation use (contract schema, tool manifest, and agent compatibility artifacts).

{/* DOC_API_OPERATIONS_START */}
### Available operations

Use the tables below to see what operations are available and where each one is documented.

| Namespace | Operations | Reference |
| --- | --- | --- |
| Core | 8 | [Reference](/document-api/reference/core/index) |
| Capabilities | 1 | [Reference](/document-api/reference/capabilities/index) |
| Create | 1 | [Reference](/document-api/reference/create/index) |
| Format | 1 | [Reference](/document-api/reference/format/index) |
| Lists | 8 | [Reference](/document-api/reference/lists/index) |
| Comments | 11 | [Reference](/document-api/reference/comments/index) |
| Track Changes | 6 | [Reference](/document-api/reference/track-changes/index) |

| Editor method | Operation ID |
| --- | --- |
| `editor.doc.find(...)` | [`find`](/document-api/reference/find) |
| `editor.doc.getNode(...)` | [`getNode`](/document-api/reference/get-node) |
| `editor.doc.getNodeById(...)` | [`getNodeById`](/document-api/reference/get-node-by-id) |
| `editor.doc.getText(...)` | [`getText`](/document-api/reference/get-text) |
| `editor.doc.info(...)` | [`info`](/document-api/reference/info) |
| `editor.doc.insert(...)` | [`insert`](/document-api/reference/insert) |
| `editor.doc.replace(...)` | [`replace`](/document-api/reference/replace) |
| `editor.doc.delete(...)` | [`delete`](/document-api/reference/delete) |
| `editor.doc.format.bold(...)` | [`format.bold`](/document-api/reference/format/bold) |
| `editor.doc.create.paragraph(...)` | [`create.paragraph`](/document-api/reference/create/paragraph) |
| `editor.doc.lists.list(...)` | [`lists.list`](/document-api/reference/lists/list) |
| `editor.doc.lists.get(...)` | [`lists.get`](/document-api/reference/lists/get) |
| `editor.doc.lists.insert(...)` | [`lists.insert`](/document-api/reference/lists/insert) |
| `editor.doc.lists.setType(...)` | [`lists.setType`](/document-api/reference/lists/set-type) |
| `editor.doc.lists.indent(...)` | [`lists.indent`](/document-api/reference/lists/indent) |
| `editor.doc.lists.outdent(...)` | [`lists.outdent`](/document-api/reference/lists/outdent) |
| `editor.doc.lists.restart(...)` | [`lists.restart`](/document-api/reference/lists/restart) |
| `editor.doc.lists.exit(...)` | [`lists.exit`](/document-api/reference/lists/exit) |
| `editor.doc.comments.add(...)` | [`comments.add`](/document-api/reference/comments/add) |
| `editor.doc.comments.edit(...)` | [`comments.edit`](/document-api/reference/comments/edit) |
| `editor.doc.comments.reply(...)` | [`comments.reply`](/document-api/reference/comments/reply) |
| `editor.doc.comments.move(...)` | [`comments.move`](/document-api/reference/comments/move) |
| `editor.doc.comments.resolve(...)` | [`comments.resolve`](/document-api/reference/comments/resolve) |
| `editor.doc.comments.remove(...)` | [`comments.remove`](/document-api/reference/comments/remove) |
| `editor.doc.comments.setInternal(...)` | [`comments.setInternal`](/document-api/reference/comments/set-internal) |
| `editor.doc.comments.setActive(...)` | [`comments.setActive`](/document-api/reference/comments/set-active) |
| `editor.doc.comments.goTo(...)` | [`comments.goTo`](/document-api/reference/comments/go-to) |
| `editor.doc.comments.get(...)` | [`comments.get`](/document-api/reference/comments/get) |
| `editor.doc.comments.list(...)` | [`comments.list`](/document-api/reference/comments/list) |
| `editor.doc.trackChanges.list(...)` | [`trackChanges.list`](/document-api/reference/track-changes/list) |
| `editor.doc.trackChanges.get(...)` | [`trackChanges.get`](/document-api/reference/track-changes/get) |
| `editor.doc.trackChanges.accept(...)` | [`trackChanges.accept`](/document-api/reference/track-changes/accept) |
| `editor.doc.trackChanges.reject(...)` | [`trackChanges.reject`](/document-api/reference/track-changes/reject) |
| `editor.doc.trackChanges.acceptAll(...)` | [`trackChanges.acceptAll`](/document-api/reference/track-changes/accept-all) |
| `editor.doc.trackChanges.rejectAll(...)` | [`trackChanges.rejectAll`](/document-api/reference/track-changes/reject-all) |
| `editor.doc.capabilities()` | [`capabilities.get`](/document-api/reference/capabilities/get) |
{/* DOC_API_OPERATIONS_END */}
Loading
Loading