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

# Block kinds

> The canonical block taxonomy and how viewers MUST handle unknown kinds.

A canvas is a list of typed `blocks`. Each block has a stable `id`, a `kind` from the closed set below, and a set of kind‑specific fields. A VIEWER MUST render every kind it claims to support and MUST render a fallback for kinds it does not (see [Fallback](#fallback)).

The source of truth for the field set on every block is the `Block` interface in [apps/hub/src/types.ts](https://github.com/quickowl/knify/blob/main/apps/hub/src/types.ts).

## Common fields

Every block carries:

* `id` (string, **required**) — stable across edits; survives patches; URLs anchor on it.
* `kind` (string, **required**) — one of the kinds below, or a vendor extension shaped `vendor.kind` (see [conformance.mdx#extension-namespaces](./conformance.mdx#extension-namespaces)).
* `title` (string, optional)
* `metadata` (object, optional) — extension data; viewers MUST ignore unknown keys.

## Kinds

| Kind             | Purpose                                   | Notable fields                                                                                                            |
| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `heading`        | Section heading                           | `text`, `level` (1–6)                                                                                                     |
| `markdown`       | Prose / mixed content                     | `markdown`                                                                                                                |
| `image`          | Image asset                               | `assetId` OR `url`, `alt`, `caption`                                                                                      |
| `terminal`       | A command and its output                  | `command`, `output`, `exitCode`, `language`                                                                               |
| `diff`           | Unified diff                              | `diff` (string of the diff body), `language`                                                                              |
| `chart`          | Data visualization                        | `chart: ChartSpec` (type, axes, series)                                                                                   |
| `checklist`      | Items that can be checked off             | `items: ChecklistItem[]`                                                                                                  |
| `decision`       | A prompt for a human to pick from options | `prompt`, `options`                                                                                                       |
| `link`           | External or internal URL card             | `url`, `title`, `caption`, `thumbnailUrl`, `thumbnailAssetId`                                                             |
| `metadata`       | A small grid of key/value rows            | `metadata` map                                                                                                            |
| `video`          | Embedded video                            | `assetId` OR `url`, `platform`, `duration`, `thumbnailUrl`, `thumbnailAssetId`, `posterAlt`                               |
| `html`           | Free‑form rendered HTML                   | `html` (inline body, sandboxed) OR `screenshotAssetId` (rendered fallback image); optional `sandbox`, `height`, `caption` |
| `collection`     | A list of mixed items                     | `items: ChecklistItem[]` (used as item rows), `pageSize`                                                                  |
| `form`           | A small input form                        | `fields: FormField[]`                                                                                                     |
| `orderable-list` | A list with drag‑to‑reorder               | `orderableItems: OrderableItem[]`, optional `itemEditor: FormSchema`, optional `total`                                    |
| `split`          | Weighted split into named slices          | `slices: SplitSlice[]`                                                                                                    |
| `rule-builder`   | A typed rule builder                      | `ruleSchema: RuleSchema`, `clauses: RuleClause[]`                                                                         |

The full per‑kind field set (e.g. `ChartSpec.series[].data[]`, `FormField.type`) is normative and lives in [apps/hub/src/types.ts](https://github.com/quickowl/knify/blob/main/apps/hub/src/types.ts). A future MINOR bump MAY add new kinds; viewers MUST forward‑compatibly fall back to a labeled placeholder.

<h2 id="fallback">
  Fallback
</h2>

When a VIEWER encounters a `block.kind` it does not support, it MUST:

* Render a visually distinct placeholder that includes the `kind` value and the block `id`.
* Preserve the block in the document model so that round‑trips do not drop it.
* Not block the rest of the canvas from rendering.

The reference web viewer renders such blocks as a `<UnknownBlock>` card with the kind label visible.

## Edit ops and block kinds

`POST /v1/canvases/:id/edits` (see [lifecycle.mdx#canvas-edits](./lifecycle.mdx#canvas-edits)) carries ops that target specific block kinds. The HUB MUST validate kind/op compatibility via `opRequiresBlockKind`. A consumer that authors edits MUST consult [apps/hub/src/validation.ts](https://github.com/quickowl/knify/blob/main/apps/hub/src/validation.ts) for the current op→kind mapping; a future MINOR bump SHOULD lift that mapping into this document.

<h2 id="html">
  HTML blocks
</h2>

The `html` kind lets an agent emit a free‑form rendered view — a small report, a chart, an annotated screenshot, or any HTML fragment a reviewer should see verbatim. Two carriers are supported, and a block MAY include either or both:

* `html` — an inline HTML body, capped at 256 KiB. VIEWERS MUST render it inside a sandboxed iframe using `srcdoc`. The default `sandbox` is `strict` (no allowances). `sandbox: "relaxed"` MAY grant `allow-scripts`; a VIEWER MUST NOT grant `allow-same-origin` or `allow-top-navigation` for either mode.
* `screenshotAssetId` — an image asset (typically a PNG/JPEG of the rendered HTML). The HUB MUST validate that the asset exists in the bound canvas's workspace and SHOULD decorate the block with a public `screenshotUrl` when an asset CDN is configured. VIEWERS MUST display this image as a fallback when `html` is absent or when execution is blocked by viewer policy.

Other fields:

* `title`, `caption` — same conventions as `image`.
* `height` — initial iframe height in CSS pixels, 1–1600; VIEWERS MAY clamp.

A block with neither `html` nor `screenshotAssetId` is invalid and the HUB MUST reject it with HTTP 400.

Agents SHOULD prefer `html` when reviewers benefit from interactive content (sortable tables, hover details, sparklines) and SHOULD include `screenshotAssetId` whenever a frozen visual record is also useful — for example, post‑run reports archived after the live session ends.

## Assets

Blocks that reference assets do so by `assetId`. The HUB MUST validate that any viewer session with `asset.read` can only fetch assets referenced by its bound canvas (see [auth.mdx#viewer-links](./auth.mdx#viewer-links)).

A canvas MAY embed asset bodies inline by base64 only inside an exported `CanvasBundle` (`bundle.assets[].bodyBase64`). Top‑level canvases delivered through `GET /v1/canvases/:id` MUST reference assets by id, never by inline body.
