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

# Artifact format (V32)

> Plain 产物的规范数据格式 —— 一份统一的 block 文档模型,让 agent 可靠生成与编辑。

Plain 的产物不是 PPT、Word 或 Excel 文件,而是一份 **living artifact**:同一份结构化的源,
能渲染成 deck(演示)、doc(长文报告)或 dashboard(数据面板)。这份源就是本页规范的 **Document**。

<Note>
  内容不该从"选 PPT / Doc / Sheet"开始。你维护一份 Document,Plain 用模板把它渲染成合适的表达 ——
  换模板 0 重新生成。这是 Plain 与"单一格式"办公软件的根本区别。
</Note>

## 顶层:Document

```json theme={null}
{
  "meta": { "title": "Q3 增长复盘", "defaultMode": "present", "density": "high" },
  "blocks": [ /* Block[] */ ]
}
```

| 字段       | 类型       | 说明                   |
| -------- | -------- | -------------------- |
| `meta`   | DocMeta  | 文档元信息(见下)            |
| `blocks` | Block\[] | 有序的语义块数组。顺序即阅读/演示顺序。 |

### DocMeta

| 字段                                   | 类型                        | 说明                                                                     |
| ------------------------------------ | ------------------------- | ---------------------------------------------------------------------- |
| `title`                              | string                    | 必填。文档标题。                                                               |
| `defaultMode`                        | `"report"` \| `"present"` | 默认渲染模式。`present`=分屏演示(deck),`report`=可滚动长文(doc/dashboard)。缺省 `report`。 |
| `density`                            | `"low"` \| `"high"`       | 内容密度提示给模板。缺省 `high`。                                                   |
| `author` `date` `description` `deck` | string                    | 可选。`deck` 是副标题 / 一句话引子。                                                |

<Info>
  **`defaultMode` 是唯一区分"三件套"的地方,而且它只是渲染倾向。** 任何 Document 都能切另一个 mode ——
  present 与 report 共用同一份 blocks,只是包裹层的 CSS 不同(present 按 `pageBreak` 切屏)。
</Info>

## Block 信封(所有块共有)

每个 block 都带这些字段:

| 字段          | 类型                                   | 说明                                                      |
| ----------- | ------------------------------------ | ------------------------------------------------------- |
| `id`        | string                               | **必填,稳定,唯一。** patch 编辑按 id 定位(不用数组下标,防插删漂移)。生成时用确定性 id。 |
| `type`      | string                               | 块类型(见下 16 种),决定其余字段形状。                                  |
| `pageBreak` | boolean?                             | present mode 下此块起新一屏(report mode 忽略)。                   |
| `span`      | `"full"` \| `"half"` \| `"third"`?   | 容器 / group 内列宽。                                         |
| `emphasis`  | `"hero"` \| `"normal"` \| `"quiet"`? | 强度提示给模板。                                                |

## 16 种通用 Block

| type        | 用途                               | 关键字段                                                               |
| ----------- | -------------------------------- | ------------------------------------------------------------------ |
| `cover`     | 封面 / 大标题                         | `display` (主标), `kicker`, `lead`, `byline[]`                       |
| `statement` | 一句核心论断 / 大数字                     | `text`, `bigNumber?`, `annotation?`                                |
| `prose`     | Markdown 正文(收编段落/列表/洞察)          | `body` (markdown), `tone?`                                         |
| `heading`   | 分节标题                             | `level` (1-4), `text`                                              |
| `quote`     | 引用                               | `text`, `attribution?`                                             |
| `callout`   | 高亮框                              | `tone` (info/ok/warn/danger/tip/note), `body`, `title?`            |
| `metrics`   | 量化指标组(收编 stats/kpi/data-block)   | `items: Mark[]`, `title?`                                          |
| `cards`     | 卡片组(收编 steps/features/diagnosis) | `layout` (grid/steps/numbered), `items: CardItem[]`                |
| `sequence`  | 序列(时间轴 / 流程)                     | `flow` (time/arrow), `items: SeqItem[]`                            |
| `compare`   | 左右对比                             | `left: Col`, `right: Col`                                          |
| `quadrant`  | 二维定位图                            | `xLabel`, `yLabel`, `quadrantLabels[4]`, `points[]`                |
| `table`     | 表格                               | `headers[]`, `rows[][]`                                            |
| `chart`     | 图表                               | `variant` (line/bar/area/bar-stack/pie/scatter), `x[]`, `series[]` |
| `media`     | 图文 / 引用 / 图表 + 文字分栏              | `text{title,...}`, `media{kind,src?}`, `side` (left/right)         |
| `closing`   | 结尾 / CTA                         | `display`, `cta{primary?,secondary?}`                              |
| `group`     | 递归容器(把多块排成 row / stack)          | `layout` (row/stack), `children: Block[]`                          |

### 复用的原子结构

```
Mark      = { value, label, hint?, delta?: "up"|"down"|"flat" }
CardItem  = { head, body, num?, icon?, when?, metric?, metricLabel? }
SeqItem   = { label, when?, hint? }
Col       = { label, bullets: string[] }
ChartSeries = { name, data: number[] }
```

## 三条不变量

<CardGroup cols={1}>
  <Card title="Ids are identity" icon="fingerprint">
    block 的 `id` 是它的身份。编辑(patch)靠 id 定位,不靠数组下标。生成时发确定性 id,
    编辑时**永不重新生成已有 block 的 id**(会让引用/版本对不上)。
  </Card>

  <Card title="Pure data" icon="shield-check">
    Document 是纯数据,永不携带可执行代码。`chart` 的配置是纯 JSON(无函数);
    `prose` 的 markdown 与文本会被 sanitize。AI 只输出这份 content JSON,**不写 HTML** —— 模板负责渲染。
  </Card>

  <Card title="Additive & forward-compatible" icon="layer-group">
    格式增量演进。生成时只用本页列出的字段;不要发明属性名(未知键会被丢弃,typo 导致样式丢失)。
  </Card>
</CardGroup>

## 给 agent 的约定

<Note>
  Plain 通过 **MCP**(`plain mcp` → `generate_artifact` / `edit_artifact`)和 **CLI**(`plain generate --as deck|doc|sheet`)
  暴露生成与编辑。agent 不直接拼 Document JSON —— 给意图,Plain 生成合规的 Document;
  要改就发自然语言指令或 block.id 定位的 JSON Patch。
</Note>

**Do**

* 数值对比 → `chart` 或 `metrics`(比散落的文本框强)
* 结构化网格 → `table`
* 步骤 / 时间线 → `sequence` 或 `cards[layout=steps]`
* 需要并排 → `group[layout=row]` 包多块
* 编辑已有产物 → 保留原 block 的 `id`,只改字段

**Don't**

* 不发明本页未列的属性名(未知键静默丢弃)
* 不在 content 里写 HTML / 内联样式 / 可执行代码(交给模板)
* 不为已有 block 重新生成 id
* 不假设某个 `type` 支持未列出的字段

## 与渲染的关系

一份 Document → 模板(token + 每种 block 的 renderer)→ 自包含 HTML。

* **report mode**:blocks 垂直流,可滚动(doc / dashboard 默认)
* **present mode**:按 `pageBreak` 切屏,每屏是一页(deck 默认)

同一份 Document 换任意模板、切任意 mode,都**不需要重新调用 AI**。这就是"一份源,多种活的 Artifact"。
