> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asakiri.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rich-text documents

> The Tiptap document format and the custom nodes that embed content and media.

<Info>
  Canonical format, version 1. The document file carries the same `format` and
  `formatVersion` envelope as every other file in the course, alongside the Tiptap `doc`
  node. Ignore those two keys when handing the document to a ProseMirror renderer.
</Info>

A `tiptap` part is a standard [Tiptap](https://tiptap.dev) / ProseMirror JSON document: a
root `doc` node with a `content` array of nodes. Renderers can use Tiptap's own
`generateHTML`, or walk the JSON directly.

```json theme={null}
{
  "type": "doc",
  "content": [
    { "type": "heading", "attrs": { "level": 1 }, "content": [{ "type": "text", "text": "Meet 猫" }] },
    { "type": "paragraph", "content": [{ "type": "text", "text": "猫 means cat." }] }
  ]
}
```

## Standard nodes

Documents use Tiptap StarterKit plus a few extensions. Expect these node and mark types:

* **StarterKit**: `doc`, `paragraph`, `text`, `heading`, `bulletList`, `orderedList`,
  `listItem`, `blockquote`, `codeBlock`, `horizontalRule`, `hardBreak`, and the marks
  `bold`, `italic`, `strike`, `code`.
* **Extensions**: `highlight` (mark), `textStyle` (mark), `table` / `tableRow` /
  `tableHeader` / `tableCell`, and `youtube`.

## Custom nodes

Three custom nodes connect a document to reusable content and media. A learner or creator
app must handle these; a generic Tiptap renderer will not.

### contentRecord

Inline atom node that embeds a reusable record via a [binding](/data-structure/bindings).
Resolve the binding and render according to `presentation`.

```json theme={null}
{
  "type": "contentRecord",
  "attrs": {
    "binding": { "kind": "record", "recordId": "record_cat" },
    "presentation": "vocabulary-card",
    "label": null
  }
}
```

| Attr | Meaning |
| - | - |
| `binding` | Any [`Binding`](/data-structure/bindings) (`record`, `field`, `item`, `asset`, or `literal`). |
| `presentation` | A named view hint, for example `"vocabulary-card"`. |
| `label` | Optional display label. |

### audio and video

Block atom nodes for media. They can carry a resolved `src` or reference an asset by
`assetId` (resolve `assetId` to the asset's file when present).

```json theme={null}
{ "type": "audio", "attrs": { "assetId": "asset_cat_ja_audio", "label": "Listen", "src": null } }
```

```json theme={null}
{ "type": "video", "attrs": { "assetId": "asset_intro_clip", "label": null, "src": null } }
```

### image

The image node is Tiptap's `image` extended with an `assetId` attribute, so an image can
point at a project asset instead of an external URL. Prefer `assetId` when present and
resolve it to the asset's file.

## Rendering checklist

1. Render standard nodes and marks with Tiptap or your own walker.
2. For `contentRecord`, resolve `attrs.binding` with the
   [binding resolver](/build/resolving-and-rendering) and render by `presentation`.
3. For `audio`, `video`, and `image`, resolve `assetId` to the asset file and render the player.
4. Handle a `missing` resolution gracefully (see
   [Resolving and rendering](/build/resolving-and-rendering)); a broken reference should
   not crash the page.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.