# showme-report 模板 · 数据字段表 > **你要读的是这一份,不是 `core.js`。** > > `core.js` 是渲染与交互的**实现**(约 40KB):控件放哪、怎么组句、什么时候发消息。 > 这些你都不需要懂,也不该改——四份骨架共用它一份。 > **这一份才是你要写的接口。** > > 实测:一个会话为了搞清"它认哪些块类型",把整个 `core.js` 读了一遍(≈5–6 千 token), > 而答案就在下面这张表里。 --- ## 一页 = 外壳 + 内核 + 数据 ``` templates/.html 外壳(**直接 write,别 cp** —— 见下) templates/core.js 内核:别读、别改 templates/README.md 本文件 <名字>.html 你写的页面 <名字>.data.js ← **你唯一要写的东西** ``` > ⚠️ **`templates/` 在新工作区里还不存在。** 宿主是在**第一次 `show_html`** 时才创建它的, > 所以第一步**不要 `cp`**(会报 no such file)—— **直接 `write` 下面这个外壳**: ```html —
``` `'review'` 换成 `'pick'` 或 `'report'` 就是另外两个形状。 你引用的 `core.js`、`presets/*.css` 会在**展示那一刻已经就位**(宿主先落地、后校验), 浏览器取它们时一定存在。 > 🔧 **想先把资产催出来**(比如你想读这份 README 或 `presets/index.json`): > **随便调一次 `show_html`**,哪怕路径不存在、扩展名不对—— > 落地排在**校验之前**,失败也会跑。之后 `ls .dsh/showme/` 就能看到 `templates/` 与 `presets/`。 然后 `write .dsh/showme/<名字>.data.js`,最后 `show_html({ path: '.dsh/showme/<名字>.html' })`。 **数据文件名必须和页面同名**:`foo.html` ↔ `foo.data.js`。 内核按页面自己的文件名去推导数据文件名;不同名会显示"没找到数据文件"。 | 骨架 | 形状 | 什么时候用 | |---|---|---| | `review.html` | 逐项表态 | 一排条目,每条一个判定 + 备注。带图不带图都行。**最常用** | | `pick.html` | 候选挑选 | 单选 / 多选。封面选哪张、这几张素材留哪几个 | | `report.html` | 图文汇报 | 有图 / 表 / 时间线、不需要逐项表态;每小节一个轻回执 | | `doc.html` | 纯文档 | 长说明 / 机制讲解;**没有**判定控件与回执,只有「复制全文」。 | --- ## 数据文件的样子 ```js window.SHOWME = { skin: 'soft', kicker: '栏目', title: '大标题', lead: '导语', meta: ['标签', '标签'], sections: [ /* … */ ], } ``` ### 共用字段 | 字段 | 类型 | 说明 | |---|---|---| | `skin` | string | `soft`(默认)/ `swiss` / `brutal` / `blueprint`。**换皮肤就改这一行** | | `shape` | string | 一般不用写——外壳已经声明了(`window.SHOWME_SHAPE`) | | `kicker` | string | 标题上方的小栏目名 | | `title` | string | 大标题(必填,也用作浏览器标签页标题) | | `lead` | string | 导语,一到两句 | | `meta` | string[] | 报头下方那一排小字 | | `verdicts` | string[] | 可选:覆盖该形状的默认判动词 | ### 每个可点名的东西都要有 `id` `id` 就是**用户点名用的手柄**(`hero-v3`、`cover-a`、`seg-02`): 短、唯一、可粘贴、无空格。它会显示在卡片上,并出现在回执的每一行里。 **同页内不许重名。** --- ## `review` 形状 —— 逐项表态 ```js window.SHOWME = { skin: 'swiss', kicker: '设计评审', title: '三个变体,挑一个', lead: '看看要不要改', meta: ['14 镜', '1920×1080'], sections: [ { heading: '候选', sub: '可选的副标题', intro: '这一段可选的引导语', two: true, // true = 两栏,默认自适应多栏 items: [ { id: 'hero-v3', title: '第 3 版', desc: '蓝底、大标题', image: 'stills/hero-v3.png', caption: '图 1 · 展开态' }, { id: 'hero-v4', title: '第 4 版', desc: '灰底' }, ], blocks: [ /* 可选,见下面的块类型表 */ ], }, ], } ``` | 字段 | 类型 | 说明 | |---|---|---| | `sections[].heading` | string | 小节标题 | | `sections[].sub` | string | 副标题(小字) | | `sections[].intro` | string | 小节引导语 | | `sections[].two` | boolean | 两栏(只影响带图卡片那组) | | `sections[].items[]` | object[] | **每条都带判定控件**:纯文字排成一行一条,带图才单独成卡 | | `items[].id` | string | 必填、唯一 | | `items[].title` | string | 条目标题 | | `items[].desc` | string | 一句话说明 | | `items[].image` | string | **相对页面**的图片路径(给了才成卡) | | `items[].caption` | string | 图注 | | `items[].options` | object[] | 条目内选项集:2–4 个互斥走法,见下 | | `items[].preferred` | string | 可选:标一个"我倾向"的 `value`(只是徽标,不预选) | 默认判动词:`同意` / `要改` / `不要`。 ### 条目内选项集(一条里有 A/B 两条走法) ```js { id: 'p1', title: 'hidden 渠道作品的详情页', desc: '两条路挑一条', preferred: 'A', options: [ { value: 'A', label: '详情 404', why: '§EN-1 的字面是「完全不可见」' }, { value: 'B', label: '200 但不给预览', why: '要给 DemoDetail 加 previewable 字段' }, ], } ``` - 回执写 `p1 A` / `p1 B`——**value 就是判定词**,所以它要短、无空格、同条目内唯一(≤12 个字母数字 `._-`)。 - `label` 是给人看的名字(≤40 字);`why` 是理由(≤300 字,**不许空行 / 列表 / 表格**,段落级内容搬进 `blocks`)。 - 一条 2–4 个选项:1 个是确认不是选择,5 个以上用户就不比较了(该拆页面)。 - 带 `options` 的条目**不再显示页级判定词**,但备注框还在;回执词表会把 value 并进去。 - `pick` 的 `candidates[]` 上没有这组字段——候选是"挑对象",不是"挑走法"。 --- ## `pick` 形状 —— 从候选里挑 ```js window.SHOWME = { skin: 'brutal', title: '封面用哪一张', mode: 'single', // 'single'(默认)或 'multi' question: '选一张当封面', // 显示在候选上方 heading: '候选', candidates: [ { id: 'cover-a', title: 'A', image: 'stills/a.png' }, { id: 'cover-b', title: 'B', image: 'stills/b.png' }, ], } ``` `single` 模式下选新的会自动取消旧的。默认判动词:`选这个`。 --- ## `report` 形状 —— 图文汇报 ```js window.SHOWME = { title: '架构说明', sections: [ { heading: '数据流', id: 'flow', // 可选:回执用的 id(默认由 heading 生成) sectionReceipt: true, // 默认 true;false 则这一节不带回执 intro: '可选的引导语', blocks: [ /* 见下表 */ ], }, ], } ``` ### 文本字段能写什么(只有这四种行内写法) `title` / `desc` / `lead` / `text` / `callout.text` / 表格单元格 / `kv` 的值 / chips / 图注——这些字段里**只支持四种行内写法**,其余一律按原文显示: | 写法 | 渲染成 | 不生效的情况 | |---|---|---| | `**粗**` | `` | 没成对、里面还有 `*`、`**` 内外是空白 | | `` `代码` `` | `` | 没成对、跨行 | | `[文字](https://…)` | 新标签页打开的链接 | scheme 不是 `http(s)` / `#` / `./` `../`(`javascript:` 之类**原样显示**) | | 换行 | 换行 | — | **结构不要写成 markdown**:表格、列表、引用、标题、围栏代码这些**已经有块类型**了(`table` / `timeline` / `chips` / `callout` / `sections[].heading` / `code`)。 数据里写了 md 结构,`show_html` 会**直接失败**并告诉你该用哪个块——因为同一种结构有两种写法,页面风格就会长出两套。 确实要原样显示一段 markdown(比如你在写"怎么用 markdown"的页面):给那个 `text` 块加 `plain: true`,这一页就跳过结构检查。 ### 块类型全表(`core.js` 认识的就是这 10 种) | `type` | 字段 | 例子 | |---|---|---| | `text` | `text` | `{ type: 'text', text: '一段说明' }` | | `image` | `src`、`caption?`、`alt?` | `{ type: 'image', src: 'stills/a.png', caption: '图 1' }` | | `table` | `head`、`rows` | `{ type: 'table', head: ['列 A','列 B'], rows: [['1','2']] }` | | `callout` | `text`、`label?`、`tone?` | `{ type: 'callout', label: '结论', text: '所以这样', tone: 'good' }` | | `timeline` | `items[]{title?,text}` | `{ type: 'timeline', items: [{ title: '第一步', text: '做什么' }] }` | | `kv` | `pairs`(`[键, 值]` 数组) | `{ type: 'kv', pairs: [['预设目录','.dsh/showme/presets/']] }` | | `code` | `text` | `{ type: 'code', text: 'const a = 1' }` | | `chips` | `items`(字符串或 `{text,accent?}`) | `{ type: 'chips', items: ['甲', { text: '乙', accent: true }] }` | | `stat` | `num`、`text?` | `{ type: 'stat', num: '42', text: '个片段' }` | | `rule` | — | `{ type: 'rule' }` | `tone` 可取 `good` / `warn` / `bad`(不写就是中性)。 `report` 每小节**默认自带一个轻回执**(判动词 `没意见` / `要改`)。 如果你确实不需要任何回执(纯图示),写 `sectionReceipt: false`。 **选择会自动存草稿**:用户选的判定、写了一半的备注,刷新 iframe / 切会话回来还在 (顶部会出现一条「已恢复 …·清空」的横幅)。这件事由内核与卡片负责,数据文件里不用写任何东西。 --- ## `doc` 形状 —— 纯文档(没有判定、没有回执) 长说明、机制讲解、结论汇总用它。**没有 `items`、没有判定控件、没有回执区**, 只有报头 + 小节 + 底部一个「复制全文」。 ```js window.SHOWME = { skin: 'soft', kicker: '说明', title: '草稿是怎么存的', lead: '一句话讲清', sections: [ { heading: '一、为什么页内存储不可用', intro: '可选引导语', blocks: [{ type: 'text', text: '沙箱是不透明源,`localStorage` 会直接抛异常。' }] }, { heading: '二、所以走卡片', blocks: [{ type: 'kv', pairs: [['落点', '.dsh/showme/drafts/']] }] }, ], } ``` - 外壳用 `templates/doc.html`(`window.SHOWME_SHAPE = 'doc'`); - 数据字段与 `report` **完全一样**(`sections[].blocks` 那 10 种块类型都可用); - 唯一的差别:`doc` 不渲染判定与回执,`items` 写了也不会被渲染——文档页不该有"表态"。 - 什么时候别用它:如果用户看完有话要说(认不认、选哪个、下一步做哪个),那要 `review` / `pick`。 --- ## 宿主差异:数据文件怎么被找到 内核默认**按页面自己的文件名**推数据文件名(`foo.html` → `foo.data.js`),这要求 **地址里有文件名**。展示这一页的三条路都满足:卡片、`侧栏打开`、`浏览器打开` (都走宿主镜像路由 `/api/showme/raw//…`)。 **有一条路不满足:原生文档预览**(卡片右下角那个路径按钮走的就是它)。 它把 HTML 塞进一个 **blob iframe**(地址只有一串 uuid),还只打包相对的 `.js` / `.css` —— 于是相对的**图片、下级页、CSS `url()`** 和这条文件名推导**一起失效**。 现象是: ``` 没找到数据文件:94d45dc1-3935-4e40-b3fc-57931b1d5350.data.js ``` 内核现在认得出这种宿主,会直接把话说清楚(不再让你去 `write` 一个 uuid 名字的文件)。 **要在那种宿主里也能起来**,就改成**静态引入数据** —— 把这一行放到 ` ``` (名字要和页面同名:`我的页面.html` ↔ `我的页面.data.js`。) 内核看到 `window.SHOWME` 已经在了就直接渲染,不再去取数据文件。 **静态引入在三条正常路径里同样有效**,所以这是"到处都能用"的写法; 代价是两处名字要一致。 --- ## 想画图:模板里没有图块,自己写页面 `review` / `pick` / `report` 是**条目式**的:一排可点名、可表态的东西(`doc` 是文档式,没有条目)。数据里的 `text` 块是**转义文本** (`` 会原样显示成尖括号),所以塞不进 HTML,也没有 `mermaid` 块 —— **这不是遗漏,是刻意的**: > `show_html` 会先把页面里**静态写着**的 mermaid 块解析一遍,语法错就让这次调用失败。 > 而这个检查读的是页面文件本身;由 `core.js` 在浏览器里拼出来的块,它看不到。 > 两边不一致(模板页能渲染坏图、手写页不能),比"模板不支持图"更难解释。 所以:**要流程图 / 时序图 / 状态机,就自己写这一页**(不套模板): ```html

调用链

sequenceDiagram
  participant U as 用户
  participant H as show_html
  participant C as 卡片
  U->>H: 写一页带图的页面
  H->>H: 预检每块 mermaid 语法
  H-->>U: 语法错 → 这次调用失败
  H->>C: 通过 → 页面进卡片
  C-->>U: 渲染并导出 PNG
``` > 别缩进 `
` 里的内容(正文要齐着左边写),图才不会带着前导空格。
> 这一页没有 `window.SHOWME`,`templates/core.js` 也不需要引 —— 内核管的是条目与回执,
> 不是图。要回执就照 skill §3 自己写一段内联 JS,或者另开一页用 `review` / `report` 骨架。
> **混着用不行**:数据里的 `text` 块是转义文本,图进不去。

图渲染完会自动导出 PNG 到 `.dsh/showme/mermaid/<页面短哈希>/<页面名>.<序号>.png`,
`show_html` 的返回值里就写着路径;想看自己画的图,`read_image` 打开它。

---

## 常见错误

| 症状 | 原因 |
|---|---|
| 页面显示「没找到数据文件」 | 数据文件名和页面不同名。`foo.html` 必须配 `foo.data.js` |
| 报的名字是一串 uuid(`94d45dc1-….data.js`) | 这一页被**原生文档预览**打开了(blob iframe 里没有文件名)。改用卡片 / `侧栏打开` / `浏览器打开`;见上面「宿主差异」 |
| 图片裂了 | 用了绝对路径或 URL。必须**相对于页面**(`stills/a.png`)。在原生文档预览里相对图片必然裂 —— 那是宿主限制 |
| 页面空白、没有内容 | 数据里没写 `window.SHOWME`,或 JSON 语法错了 |
| 想改样式 | **别改 `core.js`**:四份骨架共用它,改一处所有页面一起变。换 `skin` 就行 |
| 不知道自己该写什么 | 就是这一份。不要读 `core.js`——它是实现,不是接口 |

## 骨架不合适的场合

模板就是"条目式"的:一排可点名、可表态的东西。
**架构图、时序图、长文分析、并排对比**不是这个形状——那就自己写页面
(skill §3 的回执契约、§4 的通道、§5 的沙箱约束照样适用)。
模板是**可选骨架**,不是形式强制。