内容不该从”选 PPT / Doc / Sheet”开始。你维护一份 Document,Plain 用模板把它渲染成合适的表达 ——
换模板 0 重新生成。这是 Plain 与”单一格式”办公软件的根本区别。
顶层:Document
DocMeta
defaultMode 是唯一区分”三件套”的地方,而且它只是渲染倾向。 任何 Document 都能切另一个 mode ——
present 与 report 共用同一份 blocks,只是包裹层的 CSS 不同(present 按 pageBreak 切屏)。Block 信封(所有块共有)
每个 block 都带这些字段:16 种通用 Block
复用的原子结构
三条不变量
Ids are identity
block 的
id 是它的身份。编辑(patch)靠 id 定位,不靠数组下标。生成时发确定性 id,
编辑时永不重新生成已有 block 的 id(会让引用/版本对不上)。Pure data
Document 是纯数据,永不携带可执行代码。
chart 的配置是纯 JSON(无函数);
prose 的 markdown 与文本会被 sanitize。AI 只输出这份 content JSON,不写 HTML —— 模板负责渲染。Additive & forward-compatible
格式增量演进。生成时只用本页列出的字段;不要发明属性名(未知键会被丢弃,typo 导致样式丢失)。
给 agent 的约定
Plain 通过 MCP(
plain mcp → generate_artifact / edit_artifact)和 CLI(plain generate --as deck|doc|sheet)
暴露生成与编辑。agent 不直接拼 Document JSON —— 给意图,Plain 生成合规的 Document;
要改就发自然语言指令或 block.id 定位的 JSON Patch。- 数值对比 →
chart或metrics(比散落的文本框强) - 结构化网格 →
table - 步骤 / 时间线 →
sequence或cards[layout=steps] - 需要并排 →
group[layout=row]包多块 - 编辑已有产物 → 保留原 block 的
id,只改字段
- 不发明本页未列的属性名(未知键静默丢弃)
- 不在 content 里写 HTML / 内联样式 / 可执行代码(交给模板)
- 不为已有 block 重新生成 id
- 不假设某个
type支持未列出的字段
与渲染的关系
一份 Document → 模板(token + 每种 block 的 renderer)→ 自包含 HTML。- report mode:blocks 垂直流,可滚动(doc / dashboard 默认)
- present mode:按
pageBreak切屏,每屏是一页(deck 默认)