hd-garden 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,16 +2,20 @@
2
2
  name: garden-ui
3
3
  description: >-
4
4
  Compiles the `garden` idea blocks in one Garden post into garden components
5
- (garden compose, or card / stat / compare / ... , or prose, or garden
5
+ (garden compose DIL, or card / stat / compare / ... , or prose, or garden
6
6
  html), fixes broken garden compose blocks, and suggests where a post could
7
- use interaction. Older intent / ui blocks are handled the same way. Use when
8
- the user says 编译这篇 / 编译想法 / 重编译 <id> / 这篇哪里适合交互, or pastes a
9
- garden ui check fix prompt.
7
+ use interaction. Older intent / ui / interactive blocks are handled the same
8
+ way. Use when the user says 编译这篇 / 编译想法 / 重编译 <id> / 这篇哪里适合交互,
9
+ or pastes a garden ui check fix prompt.
10
10
  ---
11
11
 
12
12
  # 编译 Garden 交互块
13
13
 
14
- 作者在稿里用不带名字的 `garden` 围栏写一句想法,这个 skill 把它变成 `garden compose`、`garden compose` 或别的 `garden` 组件。`garden compose` 若还是白话,就在同一围栏里填 YAML 树,不要改围栏类型。交互组件和设计规则在 `npx garden ui prompt` 的输出里,各 `garden` 组件的用途和示例在 `node_modules/hd-garden/templates/garden-blocks.md`(在引擎仓库里是 `templates/garden-blocks.md`),以这两份为准,不凭记忆写组件。
14
+ 作者在稿里用不带名字的 `garden` 围栏写一句想法,这个 skill 把它变成 `garden compose`、别的 `garden` 组件,或写回正文。
15
+
16
+ **`garden compose` 写的是 DIL,不是 YAML:** 叶子是 Markdown,结构是 compose 目录里的**小写标签** + 设计 token。作者原话放进 `<intent>`(或块头 `intent:`)。围栏类型始终是 `garden compose`——静态说明图与可调 play 都是它。不要发明 YAML 槽,不要用 interactive 的 PascalCase 当作者输入。
17
+
18
+ 交互组件、compose 目录和设计规则在 `npx garden ui prompt` 的输出里;各 `garden` 组件的用途和示例在 `node_modules/hd-garden/templates/garden-blocks.md`(在引擎仓库里是 `templates/garden-blocks.md`)。以这两份为准,不凭记忆写组件。改完引擎后若本 skill 或 prompt 过期,先 `npx garden ui prompt` 再写。
15
19
 
16
20
  作者提交时,提交前钩子跑的是同一个 `npx garden ui check`。你用来找块、写回、自查的命令,和拦住作者提交的命令是同一套。
17
21
 
@@ -42,22 +46,23 @@ description: >-
42
46
  npx garden ui prompt
43
47
  ```
44
48
 
45
- 里面是组件目录、配方和设计规则。按配方的骨架写,不发明目录里没有的组件或属性。
49
+ 里面是 compose 目录(小写标签 + token)、interactive PascalCase 参考(兼容)、配方和设计规则。按配方的骨架写,不发明目录里没有的组件或属性。
46
50
 
47
51
  3. 每块读原话和所在小节,三选一:
48
52
  - **写回正文**:一句话、一张表或一张 Mermaid 就能说清时。
49
- - **写 `garden compose`**:读者需要拖、点、翻才能看懂时。
53
+ - **写 `garden compose`(说明图)**:读者需要看见结构、关系、重点时。Markdown 叶子 + 小写目录标签;不要 YAML。
54
+ - **写 `garden compose`(play)**:读者需要拖、点、翻才能看懂时。仍用 `garden compose`;可用 `{@body}`、`{expr}`、`{#if}`/`{#each}` 与小写控件(`slider`/`choice`/`button`/`stat`…)。遗留 `garden interactive` + PascalCase 仅作兼容。
50
55
  - **写一段 `diff`**:想法紧跟在一段完整代码后面,说的是「和上一版比改了什么」时。按原话写出 diff,用一段普通 Markdown 写回(只有这一个 `diff` 围栏)。写回后它紧挨着那段代码,页面会合成「代码 / 对比上一版」切换;diff 里的新行要和上面的完整代码一致。
51
- - **写别的 `garden` 组件**:一张或多张资源卡(`card` / `cards`)、几个指标(`stat`)、两种做法并排或好坏对照(`compare` / `verdict: good|bad`)、一条时间线(`timeline`)、一张静态图表(`chart`)、一张说明图(`compose`)、一份下载文件(`file`)、3~6 个要素的关系图(`relations`)、紧凑图标列表或分步列表(`list`)、两三个选项的选择卡(`choice`)。字段与校验约束见 `npx garden ui prompt` 与 `templates/garden-blocks.md`。
52
- - **编译 `garden compose`**:`lang` 是 `garden-compose` 且 `status` 是 `uncompiled` 时,围栏头保持 `garden compose`。把扫描到的原话原样放进 `intent`,正文写成 YAML 树(`steps` + `notes`,或 `tree`)。不要改成 interactive,不要写 HTML、SVG 或脚本。配方在 `npx garden ui prompt` 的说明图一节。
56
+ - **写别的 `garden` 组件**:一张或多张资源卡(`card` / `cards`)、几个指标(`stat`)、两种做法并排或好坏对照(`compare` / `verdict: good|bad`)、一条时间线(`timeline`)、一张静态图表(`chart`)、一份下载文件(`file`)、3~6 个要素的关系图(`relations`)、紧凑图标列表或分步列表(`list`)、两三个选项的选择卡(`choice`)。字段与校验约束见 `npx garden ui prompt` 与 `templates/garden-blocks.md`。
57
+ - **编译已有 `garden compose`**:`lang` 是 `garden-compose` 且 `status` 是 `uncompiled` 时,围栏头保持 `garden compose`。把扫描到的原话原样放进 `<intent>`(或块头 `intent:`),正文写成 DIL(Markdown + 小写标签;play 时加 `{@body}` 等)。不要改成 interactive,不要写任意 HTML、SVG 或脚本。配方在 `npx garden ui prompt` 的说明图一节。
53
58
  - **写 `garden html`**:目录表达不了的画布动画、音频、物理模拟等。按 [garden-interactive-chart](../garden-interactive-chart/SKILL.md) 在同名目录写 HTML,用 `title` 写下判断。
54
59
 
55
60
  4. 写 `garden compose` 时:
56
- - 格式:围栏头写 `garden compose #<id>`;`---` 上面是 `caption`、`intent`、可选的 `expect`;`---` 下面是 `npx garden ui prompt` 里的组件树。
57
- - 原话原样放进 `intent`,一个字都不改。
58
- - `caption` 摘原话里的判断。原话没有判断句,就根据小节内容补一句,并在回复里说明。
59
- - 原话里有已知点("batch 1 时是 512 MiB"),写进 `expect`。
60
- - 数据写在块里。原话提到同名目录里已有的数据文件时,才用 `Data` 引用。
61
+ - 格式:围栏头写 `garden compose`(可选 `#<id>`);作者原话进 `<intent>…</intent>` 或块头 `intent:`;静态说明图直接写 Markdown + 标签树;play 可在 `---` 块头写 `caption` / `intent`,正文用 `{@body}` 与小写控件。
62
+ - 原话原样放进 intent,一个字都不改。
63
+ - 芯片内部 `gap={0}`/`gap={1}`;章节堆叠 `gap={2}`/`gap={3}`;日横幅等宽;phrasing 叶子保留前导 `*`。
64
+ - play 时:`caption` 摘原话里的判断;原话有已知点可写 `expect`(若引擎/校验支持)。
65
+ - 不要写色值、宽度、任意 HTML/SVG/脚本;图标用 `<icon name="…"/>`(名字须在目录里)。
61
66
  - 块太大就拆成两块,各自就近放在要讲的段落旁边,不挪到别的文件。
62
67
 
63
68
  5. 每块用 `apply` 写回,新内容从标准输入给:
@@ -65,17 +70,16 @@ description: >-
65
70
  ````bash
66
71
  npx garden ui apply content/posts/路径.md --id <id> --base <hash> <<'GARDEN_UI'
67
72
  ```garden compose #id
68
- caption: ...
69
- intent: |
70
- ...
71
- ---
73
+ <intent>
72
74
  ...
75
+ </intent>
76
+ <row gap={2}>...</row>
73
77
  ```
74
78
  GARDEN_UI
75
79
  ````
76
80
 
77
81
  - 想法块没有 id,用 `--intent-hash <intentHash>` 定位;原话重复时改用 `--at <startLine>`。
78
- - 新内容可以是一个 `garden` 组件围栏(interactive、card、stat……),或一段普通 Markdown(写回正文时)。旧稿里的 `ui` 块也可以按原格式写回。
82
+ - 新内容可以是一个 `garden` 组件围栏(compose、interactive、card、stat……),或一段普通 Markdown(写回正文时)。旧稿里的 `ui` 块也可以按原格式写回。
79
83
  - 新内容里不能再有没编译的想法。
80
84
  - 成功时 stdout 是 `{"ok":true,...}`。失败时 stderr 是 JSON,按 `code` 处理:
81
85
  - `apply-invalid`:新内容没通过校验,按 `diagnostics` 改了再写。
@@ -96,7 +100,7 @@ description: >-
96
100
 
97
101
  | id | 选择 | 配方 | 假设 | expect |
98
102
  | --- | --- | --- | --- | --- |
99
- | kv-memory | garden compose | 参数台 | 默认序列取 4K | batch 1 时 512 MiB |
103
+ | kv-memory | garden compose play | 参数台 | 默认序列取 4K | batch 1 时 512 MiB |
100
104
 
101
105
  "假设"写原话没说、你替作者定下的东西(默认值、范围、补的 caption)。没有就写"无"。
102
106
 
@@ -106,14 +110,16 @@ description: >-
106
110
  - 编造原话里没有的数字。需要数字但原话没给时,用原话里的公式算,或者问作者。
107
111
  - 使用远程地址(图片、脚本、数据)。
108
112
  - 改动目标块之外的正文。
113
+ - 把 compose 写成 YAML 槽,或禁止作者/模型使用合法的 `{@body}` / `{expr}` / `{#if}` / `{#each}`(play 需要时)。
109
114
 
110
115
  ## 建议模式:这篇哪里适合交互
111
116
 
112
117
  只读,不改稿。先运行 `npx garden ui prompt` 读设计规则,再通读全文,挑出纯文字或贴图说不清的段落:
113
118
 
114
- - 公式或数字随参数变化(参数台、换算器)
119
+ - 公式或数字随参数变化(compose play / 参数台)
115
120
  - 多步过程(步进器、流程)
116
121
  - 两种做法的差别(对比)
117
122
  - 一组格子、字节或内存布局(格子图)
123
+ - 结构关系说明图(compose 静态 DIL)
118
124
 
119
125
  每处给出:所在小节、为什么文字不够、一段可以直接粘贴到该段落后面的 `garden` 想法块草稿。草稿只写读者要做什么、看到什么,以及原文里已有的数字,不写组件名。最多 5 处;没有合适的就说没有。
@@ -186,7 +186,7 @@ series:
186
186
  - name: \u5EF6\u8FDF
187
187
  data: [120, 85]
188
188
  unit: ms
189
- \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\uFF08\u5361\u7247\u3001\u7BAD\u5934\u3001\u6D41\u7A0B\uFF09\uFF0C\u6216\u8BFB\u8005\u8981\u62E7\u6ED1\u6746/\u9009\u9879\u624D\u80FD\u770B\u61C2\u7684\u53EF\u8C03\u56FE\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["title","caption","intent","expect","id","steps","notes","tree","children"],example:`\`\`\`garden compose
189
+ \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\u6216\u53EF\u8C03 play\uFF1B\u5199 DIL\uFF08Markdown+\u6807\u7B7E\uFF09\uFF0C\u4E0D\u662F YAML\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["caption","intent","expect","id"],example:`\`\`\`garden compose
190
190
  ${Gp}
191
191
  \`\`\``,triggers:["gcompose"]},html:{label:"\u81EA\u5B9A\u4E49 HTML",when:"\u76EE\u5F55\u91CC\u7684\u7EC4\u4EF6\u753B\u4E0D\u51FA\u6765\u7684\u793A\u610F\u56FE\u6216\u5C0F\u5DE5\u5177\uFF0C\u5199\u5728\u540C\u540D\u76EE\u5F55\u7684 HTML \u91CC\u3002",whenNot:"\u80FD\u7528 card\u3001chart\u3001compose \u6216\u6B63\u6587\u8BF4\u6E05\u65F6\uFF0C\u4E0D\u8981\u5199 HTML\u3002",keys:["src","code","title","height"],example:"```garden html\nsrc: ./demo.html\ntitle: \u793A\u610F\u56FE\n```",triggers:["ghtml"]},interactive:{label:"\u4EA4\u4E92\uFF08\u5DF2\u5E76\u5165 compose\uFF09",deprecated:!0,when:"\u65E7\u7A3F\u517C\u5BB9\u3002\u65B0\u6587\u7AE0\u8BF7\u5199 garden compose\uFF08{@body}/{expr}/{#if}/{#each}\uFF09\u3002",whenNot:"\u65B0\u6587\u7AE0\u4E0D\u8981\u518D\u5199 interactive\u3002",keys:["id","caption","intent","expect"],example:`\`\`\`garden compose #kv-memory
192
192
  caption: \u663E\u5B58\u4E0E batch\u3001\u5E8F\u5217\u957F\u5EA6\u90FD\u6210\u6B63\u6BD4\u3002
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-garden",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "private": false,
5
5
  "description": "用 Markdown 写作、用 GitHub Pages 发布的个人博客引擎。",
6
6
  "license": "MIT",
@@ -89,7 +89,7 @@ unit: ms
89
89
 
90
90
  ## compose · 说明图与可调参数
91
91
 
92
- 静态说明图(卡片、箭头、流程),或读者要拧滑杆/选项才能看懂的可调图。
92
+ 静态说明图或可调 play;写 DIL(Markdown+标签),不是 YAML。
93
93
 
94
94
  不该用:放一张真图或自定义 HTML 时,用图片或 garden html。
95
95
 
@@ -2,16 +2,20 @@
2
2
  name: garden-ui
3
3
  description: >-
4
4
  Compiles the `garden` idea blocks in one Garden post into garden components
5
- (garden compose, or card / stat / compare / ... , or prose, or garden
5
+ (garden compose DIL, or card / stat / compare / ... , or prose, or garden
6
6
  html), fixes broken garden compose blocks, and suggests where a post could
7
- use interaction. Older intent / ui blocks are handled the same way. Use when
8
- the user says compile this post / compile ideas / recompile <id> / where could
9
- this post use interaction, or pastes a garden ui check fix prompt.
7
+ use interaction. Older intent / ui / interactive blocks are handled the same
8
+ way. Use when the user says compile this post / compile ideas / recompile <id>
9
+ / where could this post use interaction, or pastes a garden ui check fix prompt.
10
10
  ---
11
11
 
12
12
  # Compile Garden interactive blocks
13
13
 
14
- The author writes a one-sentence idea in an unnamed `garden` fence. This skill turns it into `garden compose`, `garden compose`, or another `garden` component. If a `garden compose` fence is still prose, fill a YAML tree in that same fence and do not change the fence type. The interactive components and design rules are in the output of `npx garden ui prompt`. The purpose and examples of the `garden` components are in `node_modules/hd-garden/templates/garden-blocks.md` (in the engine repo, `templates/garden-blocks.md`). Treat those two as the source of truth; never write components from memory.
14
+ The author writes a one-sentence idea in an unnamed `garden` fence. This skill turns it into `garden compose`, another `garden` component, or plain prose.
15
+
16
+ **`garden compose` is DIL, not YAML:** leaves are Markdown; structure is **lowercase catalog tags** + design tokens. Put the author's words in `<intent>` (or header `intent:`). The fence type stays `garden compose` for both static diagrams and tunable play. Do not invent YAML slots; do not treat interactive PascalCase as author input.
17
+
18
+ Interactive components, the compose catalog and design rules are in the output of `npx garden ui prompt`. The purpose and examples of the `garden` components are in `node_modules/hd-garden/templates/garden-blocks.md` (in the engine repo, `templates/garden-blocks.md`). Treat those two as the source of truth; never write components from memory. After an engine refresh, run `npx garden ui prompt` again before writing.
15
19
 
16
20
  When the author commits, the pre-commit hook runs the same `npx garden ui check`. The commands you use to find blocks, write them back and self-check are the same ones that stop the author's commit.
17
21
 
@@ -42,22 +46,23 @@ Run every command from the site root. If no post path was given, ask only for th
42
46
  npx garden ui prompt
43
47
  ```
44
48
 
45
- It contains the component catalog, recipes and design rules. Follow the recipe skeletons; do not invent components or properties that are not in the catalog.
49
+ It contains the compose catalog (lowercase tags + tokens), the interactive PascalCase reference (compat), recipes and design rules. Follow the recipe skeletons; do not invent components or properties that are not in the catalog.
46
50
 
47
51
  3. For each block, read the author's words and its section, then pick one:
48
52
  - **Write it back as prose**: when a sentence, a table or a Mermaid diagram says it clearly.
49
- - **Write `garden compose`**: when the reader needs to drag, click or step through it to understand.
53
+ - **Write `garden compose` (diagram)**: when the reader needs to see structure, relationships or emphasis. Markdown leaves + lowercase catalog tags; no YAML.
54
+ - **Write `garden compose` (play)**: when the reader needs to drag, click or step through it to understand. Still `garden compose`; use `{@body}`, `{expr}`, `{#if}`/`{#each}` and lowercase controls (`slider`/`choice`/`button`/`stat`…). Legacy `garden interactive` + PascalCase is compat only.
50
55
  - **Write a `diff`**: when the idea follows a complete code block and is about "what changed since the last version". Write the diff the words describe as plain Markdown (just this one `diff` fence). Once written back it sits right after that code, and the page turns them into a "code / compare with previous" toggle. New lines in the diff must match the complete code above.
51
- - **Write another `garden` component**: one or more resource cards (`card` / `cards`), a few metrics (`stat`), two approaches side by side or a good/bad comparison (`compare` / `verdict: good|bad`), a timeline (`timeline`), a static chart (`chart`), an explanation diagram (`compose`), a downloadable file (`file`), a relationship diagram of 3–6 elements (`relations`), a compact icon list or step list (`list`), or a choice card with two or three options (`choice`). Fields and validation rules are in `npx garden ui prompt` and `templates/garden-blocks.md`.
52
- - **Compile `garden compose`**: when `lang` is `garden-compose` and `status` is `uncompiled`, keep the fence header as `garden compose`. Put the scanned sentence into `intent` unchanged and write a YAML tree (`steps` + `notes`, or `tree`). Do not switch it to interactive, and do not write HTML, SVG, or scripts. The recipe is the explanation-diagram section of `npx garden ui prompt`.
56
+ - **Write another `garden` component**: one or more resource cards (`card` / `cards`), a few metrics (`stat`), two approaches side by side or a good/bad comparison (`compare` / `verdict: good|bad`), a timeline (`timeline`), a static chart (`chart`), a downloadable file (`file`), a relationship diagram of 3–6 elements (`relations`), a compact icon list or step list (`list`), or a choice card with two or three options (`choice`). Fields and validation rules are in `npx garden ui prompt` and `templates/garden-blocks.md`.
57
+ - **Compile an existing `garden compose`**: when `lang` is `garden-compose` and `status` is `uncompiled`, keep the fence header as `garden compose`. Put the scanned sentence into `<intent>` (or header `intent:`) unchanged and write DIL (Markdown + lowercase tags; for play add `{@body}` etc.). Do not switch it to interactive, and do not write arbitrary HTML, SVG, or scripts. The recipe is the explanation-diagram section of `npx garden ui prompt`.
53
58
  - **Write `garden html`**: canvas animation, audio, physics simulations and anything else the catalog cannot express. Write the HTML in the same-name directory following [garden-interactive-chart](../garden-interactive-chart/SKILL.md), and put the claim in `title`.
54
59
 
55
60
  4. When writing `garden compose`:
56
- - Format: the fence header is `garden compose #<id>`. Above `---` go `caption`, `intent` and an optional `expect`; below `---` goes the component tree from `npx garden ui prompt`.
57
- - Put the author's words into `intent` verbatim; do not change a single character.
58
- - `caption` takes the claim from the author's words. If there is no claim, write one from the section and say so in your reply.
59
- - Known points in the words ("512 MiB at batch 1") go into `expect`.
60
- - Data lives in the block. Use `Data` references only when the words mention a data file that already exists in the same-name directory.
61
+ - Format: fence header `garden compose` (optional `#<id>`); author's words in `<intent>…</intent>` or header `intent:`; static diagrams are Markdown + tag trees; play may use a `---` header with `caption` / `intent` and a body with `{@body}` plus lowercase controls.
62
+ - Put the author's words into intent verbatim; do not change a single character.
63
+ - Chip interiors use `gap={0}`/`gap={1}`; section stacks use `gap={2}`/`gap={3}`; full-width day rows; phrasing leaves keep a leading `*`.
64
+ - For play: `caption` takes the claim from the author's words; known points may go into `expect` when the engine supports it.
65
+ - Do not write color values, widths, or arbitrary HTML/SVG/scripts; icons use `<icon name="…"/>` (name must be in the catalog).
61
66
  - If a block gets too big, split it into two, each placed next to the paragraph it explains. Do not move it to another file.
62
67
 
63
68
  5. Write each block back with `apply`, passing the new content on standard input:
@@ -65,55 +70,56 @@ Run every command from the site root. If no post path was given, ask only for th
65
70
  ````bash
66
71
  npx garden ui apply content/posts/path.md --id <id> --base <hash> <<'GARDEN_UI'
67
72
  ```garden compose #id
68
- caption: ...
69
- intent: |
70
- ...
71
- ---
73
+ <intent>
72
74
  ...
75
+ </intent>
76
+ <row gap={2}>...</row>
73
77
  ```
74
78
  GARDEN_UI
75
79
  ````
76
80
 
77
81
  - Idea blocks have no id; locate them with `--intent-hash <intentHash>`, or `--at <startLine>` when the words repeat.
78
- - The new content can be one `garden` component fence (interactive, card, stat, …) or plain Markdown (when writing it back as prose). Old `ui` blocks may also be written back in their original format.
82
+ - The new content can be one `garden` component fence (compose, interactive, card, stat, …) or plain Markdown (when writing it back as prose). Old `ui` blocks may also be written back in their original format.
79
83
  - The new content must not contain any uncompiled ideas.
80
84
  - On success stdout is `{"ok":true,...}`. On failure stderr is JSON; act on `code`:
81
- - `apply-invalid`: the new content failed validation. Fix it from `diagnostics` and write again.
82
- - `apply-base`: the block changed after the scan. Run `scan` again and use the new `hash`.
83
- - `apply-not-found` / `apply-ambiguous`: the block was not found or not unique. Run `scan` again and locate it another way.
84
- - Line numbers shift after each write-back, so run `scan` again before the next block.
85
+ - `apply-base`: the block changed after the scan. Re-`scan` and use the new `hash`.
86
+ - `apply-invalid`: the new content failed validation; fix from `diagnostics` and retry.
87
+ - `apply-not-found` / `apply-ambiguous`: cannot locate uniquely; re-`scan` and change how you locate it.
88
+ - Line numbers shift after earlier write-backs; re-`scan` before each next block.
85
89
 
86
- 6. When everything is written:
90
+ 6. After every block:
87
91
 
88
92
  ```bash
89
93
  npx garden ui check content/posts/path.md
90
94
  npx garden check content/posts/path.md
91
95
  ```
92
96
 
93
- The first is the pre-commit check and must pass; the second must report `ok` true. If the same block fails three times in a row, stop and report the diagnostics to the user as they are.
97
+ The first is what the pre-commit hook runs and must pass; the second must report `ok: true`. If the same block fails three times in a row, stop and report the diagnostics to the user unchanged.
94
98
 
95
99
  7. Reply with a table:
96
100
 
97
- | id | choice | recipe | assumptions | expect |
101
+ | id | choice | recipe | assumption | expect |
98
102
  | --- | --- | --- | --- | --- |
99
- | kv-memory | garden compose | param bench | default sequence is 4K | 512 MiB at batch 1 |
103
+ | kv-memory | garden compose play | param bench | default seq 4K | 512 MiB at batch 1 |
100
104
 
101
- "Assumptions" lists what the author's words did not say and you decided for them (defaults, ranges, an added caption). Write "none" if there are none.
105
+ "assumption" is anything the words did not say that you fixed for the author (defaults, ranges, a filled-in caption). Write "none" when there is nothing.
102
106
 
103
- ## Don't
107
+ ## Forbidden
104
108
 
105
- - Edit the post file directly. The post changes only through `apply`; HTML files referenced by `garden html` may be created directly.
106
- - Invent numbers the author did not give. If a number is needed and missing, compute it from a formula in the words or ask the author.
107
- - Use remote URLs (images, scripts, data).
108
- - Change prose outside the target blocks.
109
+ - Editing the post file directly. Posts change only through `apply`; HTML files referenced by `garden html` may be created directly.
110
+ - Inventing numbers that are not in the author's words. When a number is needed and missing, derive it from a formula in the words, or ask the author.
111
+ - Using remote URLs (images, scripts, data).
112
+ - Changing prose outside the target block.
113
+ - Writing compose as YAML slots, or forbidding legal `{@body}` / `{expr}` / `{#if}` / `{#each}` when play needs them.
109
114
 
110
115
  ## Suggestion mode: where could this post use interaction
111
116
 
112
- Read-only; do not edit the post. Run `npx garden ui prompt` to read the design rules, then read the whole post and pick paragraphs that plain text or a static image cannot explain well:
117
+ Read-only; do not edit the post. First run `npx garden ui prompt` for the design rules, then read the whole post and pick paragraphs that prose or a pasted image cannot explain:
113
118
 
114
- - A formula or numbers that change with parameters (param bench, converter)
115
- - A multi-step process (stepper, flow)
116
- - The difference between two approaches (comparison)
117
- - A set of cells, bytes or a memory layout (grid)
119
+ - formulas or numbers that change with a parameter (compose play / param bench)
120
+ - multi-step processes (stepper, flow)
121
+ - differences between two approaches (compare)
122
+ - grids, bytes or memory layouts (inspect grid)
123
+ - structural relationship diagrams (static compose DIL)
118
124
 
119
- For each, give: the section, why text is not enough, and a draft `garden` idea block that can be pasted right after that paragraph. The draft says only what the reader does and sees, plus numbers already in the post; do not name components. Five at most; if none fit, say so.
125
+ For each: the section, why prose is not enough, and a draft `garden` idea fence the author can paste after that paragraph. The draft only says what the reader should do and see, plus numbers already in the source — no component names. At most 5 places; say none if none fit.
package/ui/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  | `core/` | 解析、表达式、目录、校验、静态快照。不依赖 React |
13
13
  | `host.mjs` | 宿主契约和 `<Sandbox>` 的 iframe 协议 |
14
14
  | `react/` | `UiFigure` 和目录里的组件,样式在 `react/ui.css` |
15
- | `compose/` | `garden compose` 的 YAML 树、静态 HTML 和样式。不读文章,不引用宿主 |
15
+ | `compose/` | `garden compose` 的 DIL(Markdown + 小写目录标签)、静态 HTML 和样式。不读文章,不引用宿主 |
16
16
  | `prompt/` | 写给模型的目录说明、配方和规则。宿主负责发送 |
17
17
  | `preview/` | 编辑器里的预览挂载 |
18
18
  | `evals/` | 评测样本,由宿主的工作台读取 |
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Markdown section injected into `garden ui prompt` for compose DIL.
3
+ * Generated from CATALOG_TAGS + tokens + icon count so it cannot drift.
4
+ */
5
+
6
+ import { CATALOG_TAGS } from "./markup.mjs";
7
+ import { ICON_COUNT } from "./icons.mjs";
8
+ import {
9
+ ALIGNS,
10
+ BACKGROUNDS,
11
+ BORDERS,
12
+ JUSTIFIES,
13
+ RADII,
14
+ SIZES,
15
+ SPACING,
16
+ TONES,
17
+ WRAPS,
18
+ } from "./tokens.mjs";
19
+
20
+ const GROUPS = [
21
+ {
22
+ title: "布局",
23
+ tags: [
24
+ "box", "card", "row", "stack", "columns", "grid", "grid-item", "pill", "band",
25
+ "steps", "step", "lanes", "compare", "item", "callout", "tabs", "tab",
26
+ "list", "list-item", "table", "tr", "th", "td", "thead", "tbody",
27
+ "group", "field", "header", "footer", "figure", "code", "pre",
28
+ ],
29
+ },
30
+ {
31
+ title: "文字与标注",
32
+ tags: [
33
+ "title", "text", "caption", "badge", "note", "actor", "intent", "join",
34
+ "label", "kbd", "link", "prose", "message", "option",
35
+ ],
36
+ },
37
+ {
38
+ title: "空标签 / 控件快照",
39
+ tags: [
40
+ "arrow", "fan", "divider", "separator", "kpi", "icon", "spacer", "image",
41
+ "avatar", "progress", "meter", "checkbox", "radio", "toggle", "switch",
42
+ "input", "select", "textarea", "button", "slider", "stat", "empty",
43
+ ],
44
+ },
45
+ ];
46
+
47
+ function list(set) {
48
+ return [...set].sort().map((v) => `\`${v}\``).join(" ");
49
+ }
50
+
51
+ /** Compose DIL catalog for `garden ui prompt` (between rules and interactive reference). */
52
+ export function composeCatalogReference() {
53
+ const known = new Set(CATALOG_TAGS);
54
+ const grouped = new Set();
55
+ const lines = [
56
+ "## compose 目录(小写标签 + token)",
57
+ "",
58
+ "作者只写白话想法;AI 编译成 **Markdown 叶子 + 小写目录标签 + 设计 token**。围栏类型始终是 `garden compose`。作者原话放进 `<intent>`,不画出来。",
59
+ "",
60
+ "这和下面的 **interactive PascalCase** 目录不是同一套:compose 用小写标签;interactive 仅作旧 play / 兼容。",
61
+ "",
62
+ `当前 **${CATALOG_TAGS.length}** 个标签;图标 **${ICON_COUNT}** 个名字(\`<icon name="check-circle"/>\`)。未知标签或未知图标名 → 编译失败。`,
63
+ "",
64
+ "### 共用 token",
65
+ "",
66
+ `- \`padding\` / \`gap\`:${list(SPACING)}(4px × n)`,
67
+ `- \`background\`:${list(BACKGROUNDS)}`,
68
+ `- \`radius\`:${list(RADII)}`,
69
+ `- \`border\`:${list(BORDERS)}`,
70
+ `- \`size\`:${list(SIZES)}`,
71
+ `- \`tone\`:${list(TONES)}(callout 另有 \`note\`/\`warn\`;lanes message 另有 \`forward\`/\`back\`)`,
72
+ `- \`align\`:${list(ALIGNS)}`,
73
+ `- \`justify\`(row):${list(JUSTIFIES)}`,
74
+ `- \`wrap\`(row):${list(WRAPS)}`,
75
+ "",
76
+ "### 写法要点",
77
+ "",
78
+ "- 叶子是 Markdown(标题、段落、`**强调**`、列表);结构是目录标签。不要写 YAML 槽、色值、HTML、SVG、脚本或宽度。",
79
+ "- 芯片内部间距用 `gap={0}` / `gap={1}`;章节堆叠用 `gap={2}` / `gap={3}` 及以上。",
80
+ "- 日横幅等宽:`row` 里每个 day 芯片 `flex:1`(`nowrap` + `size=\"sm\"`),不要用 between 把空日挤成内容宽。",
81
+ "- tip 钉在右边:`justify=\"between\"`,tip/`caption` 侧 `flex:0`。",
82
+ "- phrasing 叶子(title/text/caption/badge/…)保留单独的前导 `*`(脚注标记);不要剥掉。",
83
+ "- **play(可调参数)** 仍用 `garden compose`:可用 `{@body}`、`{expr}`、`{#if}`/`{#each}` 与小写控件(`slider`/`choice`/`button`/`stat`…)。静态 diagram 路径不解析这些构造。",
84
+ "",
85
+ ];
86
+
87
+ for (const group of GROUPS) {
88
+ const tags = group.tags.filter((tag) => known.has(tag));
89
+ for (const tag of tags) grouped.add(tag);
90
+ lines.push(`### ${group.title}`, "", tags.map((tag) => `\`${tag}\``).join(" "), "");
91
+ }
92
+
93
+ const leftover = CATALOG_TAGS.filter((tag) => !grouped.has(tag));
94
+ if (leftover.length) {
95
+ lines.push("### 其他", "", leftover.map((tag) => `\`${tag}\``).join(" "), "");
96
+ }
97
+
98
+ return lines.join("\n");
99
+ }
@@ -523,7 +523,12 @@ export function catalogSchema() {
523
523
 
524
524
  /** Markdown reference that `garden ui prompt` inserts between rules and recipes. */
525
525
  export function catalogReference() {
526
- const lines = ["## 组件参考", ""];
526
+ const lines = [
527
+ "## interactive 组件参考(PascalCase,兼容)",
528
+ "",
529
+ "仅用于遗留 `garden interactive` 围栏。新的可调参数请写 `garden compose` play(见上方 compose 目录与 `{@body}`)。",
530
+ "",
531
+ ];
527
532
  for (const group of GROUPS) {
528
533
  lines.push(`### ${group}`, "");
529
534
  for (const [name, spec] of Object.entries(COMPONENTS)) {
@@ -34,8 +34,8 @@ const zh = {
34
34
  promptRuleCheck: "- 不改作者原话的意思。改完运行 npx garden ui check <稿件>,直到通过。",
35
35
  promptRuleOffline: "- 如果你不能访问这个仓库,就只回复每块修好后的完整围栏。",
36
36
  promptItem: (i, file, start, end, target) => `${i}. ${file},第 ${start}–${end} 行,${target}`,
37
- promptGardenIdea: " - garden-idea:这段想法还没编译,请按原话编译成 garden interactive 或其他 garden 组件(或判断后写回正文)。",
38
- promptCompose: " - compose-uncompiled:围栏类型保持 garden compose。把原话原样放进 intent,并在同一围栏里写 YAML 树。不要改成别的组件,也不要写 HTML 或脚本。",
37
+ promptGardenIdea: " - garden-idea:这段想法还没编译,请按原话编译成 garden compose(DIL:Markdown+小写标签;可调参数用 play/{@body})或其他 garden 组件(或判断后写回正文)。遗留 interactive 仅作兼容。",
38
+ promptCompose: " - compose-uncompiled:围栏类型保持 garden compose。把原话原样放进 <intent>,正文写成 DIL(Markdown + 小写目录标签;play 可用 {@body}/{expr}/{#if}/{#each})。不要写成 YAML 槽,不要改成别的组件,也不要写任意 HTML 或脚本。",
39
39
  promptIntent: " - intent-uncompiled:这段想法还没编译,请按原话编译成 ui 块(或判断后写回正文)。",
40
40
  promptDiag: (line, code, message) => ` - 第 ${line} 行 ${code}:${message}`,
41
41
  promptFence: "当前围栏:",
@@ -58,8 +58,8 @@ const en = {
58
58
  promptRuleCheck: "- Keep the author's meaning. When done, run npx garden ui check <post> until it passes.",
59
59
  promptRuleOffline: "- If you cannot access this repository, reply with only the finished fence for each block.",
60
60
  promptItem: (i, file, start, end, target) => `${i}. ${file}, lines ${start}–${end}, ${target}`,
61
- promptGardenIdea: " - garden-idea: this idea is not compiled yet; compile it into garden interactive or another garden component (or put the sentence back into the prose).",
62
- promptCompose: " - compose-uncompiled: keep the fence type garden compose. Put the author's sentence in intent unchanged and fill a YAML tree in the same fence. Do not switch components, and do not write HTML or scripts.",
61
+ promptGardenIdea: " - garden-idea: this idea is not compiled yet; compile it into garden compose (DIL: Markdown + lowercase tags; tunable play with {@body}) or another garden component (or put the sentence back into the prose). Legacy interactive is compat only.",
62
+ promptCompose: " - compose-uncompiled: keep the fence type garden compose. Put the author's sentence in <intent> unchanged and write DIL (Markdown + lowercase catalog tags; play may use {@body}/{expr}/{#if}/{#each}). Do not write YAML slots, switch components, or write arbitrary HTML or scripts.",
63
63
  promptIntent: " - intent-uncompiled: this idea is not compiled yet; compile it into a ui block (or put the sentence back into the prose).",
64
64
  promptDiag: (line, code, message) => ` - line ${line} ${code}: ${message}`,
65
65
  promptFence: "Current fence:",
@@ -5,7 +5,7 @@ import { parse as parseYaml, stringify } from "yaml";
5
5
  * `intent` / `ui` ones:
6
6
  * ```garden an idea, one sentence to compile before publishing
7
7
  * ```garden interactive #id a compiled interactive block
8
- * ```garden compose an idea until the body is a YAML tree
8
+ * ```garden compose an idea until the body is DIL (Markdown + catalog tags)
9
9
  * Other `garden <name>` fences (card, stat, ...) are checked by `garden check`.
10
10
  */
11
11
  export function gardenKind(fence) {
package/ui/core/scan.mjs CHANGED
@@ -13,7 +13,7 @@ export function checkInteractiveFence(block, options = {}) {
13
13
  return { ...check, diagnostics: check.diagnostics.map((item) => ({ ...item, line: read.toFileLine(item.line) })) };
14
14
  }
15
15
 
16
- /** A `garden compose` fence whose body is still prose, or a YAML tree that fails the catalog. */
16
+ /** A `garden compose` fence whose body is still prose, or a DIL tree that fails the catalog. */
17
17
  export function checkComposeFence(block) {
18
18
  const result = parseCompose(block.body);
19
19
  if (result.uncompiled) return { ok: false, uncompiled: true, diagnostics: [], doc: null };
@@ -101,9 +101,9 @@ unit: ms
101
101
  },
102
102
  compose: {
103
103
  label: "说明图与可调参数",
104
- when: "静态说明图(卡片、箭头、流程),或读者要拧滑杆/选项才能看懂的可调图。",
104
+ when: "静态说明图或可调 play;写 DIL(Markdown+标签),不是 YAML。",
105
105
  whenNot: "放一张真图或自定义 HTML 时,用图片或 garden html。",
106
- keys: ["title", "caption", "intent", "expect", "id", "steps", "notes", "tree", "children"],
106
+ keys: ["caption", "intent", "expect", "id"],
107
107
  example: `\`\`\`garden compose\n${COMPOSE_EXAMPLE_BODY}\n\`\`\``,
108
108
  triggers: ["gcompose"],
109
109
  },
@@ -346,7 +346,7 @@ series:
346
346
  - name: \u5EF6\u8FDF
347
347
  data: [120, 85]
348
348
  unit: ms
349
- \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\uFF08\u5361\u7247\u3001\u7BAD\u5934\u3001\u6D41\u7A0B\uFF09\uFF0C\u6216\u8BFB\u8005\u8981\u62E7\u6ED1\u6746/\u9009\u9879\u624D\u80FD\u770B\u61C2\u7684\u53EF\u8C03\u56FE\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["title","caption","intent","expect","id","steps","notes","tree","children"],example:`\`\`\`garden compose
349
+ \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\u6216\u53EF\u8C03 play\uFF1B\u5199 DIL\uFF08Markdown+\u6807\u7B7E\uFF09\uFF0C\u4E0D\u662F YAML\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["caption","intent","expect","id"],example:`\`\`\`garden compose
350
350
  ${em}
351
351
  \`\`\``,triggers:["gcompose"]},html:{label:"\u81EA\u5B9A\u4E49 HTML",when:"\u76EE\u5F55\u91CC\u7684\u7EC4\u4EF6\u753B\u4E0D\u51FA\u6765\u7684\u793A\u610F\u56FE\u6216\u5C0F\u5DE5\u5177\uFF0C\u5199\u5728\u540C\u540D\u76EE\u5F55\u7684 HTML \u91CC\u3002",whenNot:"\u80FD\u7528 card\u3001chart\u3001compose \u6216\u6B63\u6587\u8BF4\u6E05\u65F6\uFF0C\u4E0D\u8981\u5199 HTML\u3002",keys:["src","code","title","height"],example:"```garden html\nsrc: ./demo.html\ntitle: \u793A\u610F\u56FE\n```",triggers:["ghtml"]},interactive:{label:"\u4EA4\u4E92\uFF08\u5DF2\u5E76\u5165 compose\uFF09",deprecated:!0,when:"\u65E7\u7A3F\u517C\u5BB9\u3002\u65B0\u6587\u7AE0\u8BF7\u5199 garden compose\uFF08{@body}/{expr}/{#if}/{#each}\uFF09\u3002",whenNot:"\u65B0\u6587\u7AE0\u4E0D\u8981\u518D\u5199 interactive\u3002",keys:["id","caption","intent","expect"],example:`\`\`\`garden compose #kv-memory
352
352
  caption: \u663E\u5B58\u4E0E batch\u3001\u5E8F\u5217\u957F\u5EA6\u90FD\u6210\u6B63\u6BD4\u3002
@@ -346,7 +346,7 @@ series:
346
346
  - name: \u5EF6\u8FDF
347
347
  data: [120, 85]
348
348
  unit: ms
349
- \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\uFF08\u5361\u7247\u3001\u7BAD\u5934\u3001\u6D41\u7A0B\uFF09\uFF0C\u6216\u8BFB\u8005\u8981\u62E7\u6ED1\u6746/\u9009\u9879\u624D\u80FD\u770B\u61C2\u7684\u53EF\u8C03\u56FE\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["title","caption","intent","expect","id","steps","notes","tree","children"],example:`\`\`\`garden compose
349
+ \`\`\``,triggers:["gchart"]},compose:{label:"\u8BF4\u660E\u56FE\u4E0E\u53EF\u8C03\u53C2\u6570",when:"\u9759\u6001\u8BF4\u660E\u56FE\u6216\u53EF\u8C03 play\uFF1B\u5199 DIL\uFF08Markdown+\u6807\u7B7E\uFF09\uFF0C\u4E0D\u662F YAML\u3002",whenNot:"\u653E\u4E00\u5F20\u771F\u56FE\u6216\u81EA\u5B9A\u4E49 HTML \u65F6\uFF0C\u7528\u56FE\u7247\u6216 garden html\u3002",keys:["caption","intent","expect","id"],example:`\`\`\`garden compose
350
350
  ${em}
351
351
  \`\`\``,triggers:["gcompose"]},html:{label:"\u81EA\u5B9A\u4E49 HTML",when:"\u76EE\u5F55\u91CC\u7684\u7EC4\u4EF6\u753B\u4E0D\u51FA\u6765\u7684\u793A\u610F\u56FE\u6216\u5C0F\u5DE5\u5177\uFF0C\u5199\u5728\u540C\u540D\u76EE\u5F55\u7684 HTML \u91CC\u3002",whenNot:"\u80FD\u7528 card\u3001chart\u3001compose \u6216\u6B63\u6587\u8BF4\u6E05\u65F6\uFF0C\u4E0D\u8981\u5199 HTML\u3002",keys:["src","code","title","height"],example:"```garden html\nsrc: ./demo.html\ntitle: \u793A\u610F\u56FE\n```",triggers:["ghtml"]},interactive:{label:"\u4EA4\u4E92\uFF08\u5DF2\u5E76\u5165 compose\uFF09",deprecated:!0,when:"\u65E7\u7A3F\u517C\u5BB9\u3002\u65B0\u6587\u7AE0\u8BF7\u5199 garden compose\uFF08{@body}/{expr}/{#if}/{#each}\uFF09\u3002",whenNot:"\u65B0\u6587\u7AE0\u4E0D\u8981\u518D\u5199 interactive\u3002",keys:["id","caption","intent","expect"],example:`\`\`\`garden compose #kv-memory
352
352
  caption: \u663E\u5B58\u4E0E batch\u3001\u5E8F\u5217\u957F\u5EA6\u90FD\u6210\u6B63\u6BD4\u3002
@@ -1,4 +1,5 @@
1
1
  import { catalogReference } from "../core/catalog.mjs";
2
+ import { composeCatalogReference } from "../compose/prompt-catalog.mjs";
2
3
 
3
4
  export const RECIPE_FILES = [
4
5
  "param-bench.md",
@@ -13,13 +14,21 @@ export const RECIPE_FILES = [
13
14
  "compose.md",
14
15
  ];
15
16
 
17
+ export { composeCatalogReference };
18
+
16
19
  /**
17
- * `garden ui prompt`: rules + component reference generated from the catalog
18
- * + recipes. The admin workbench appends `studio` for its output contract.
20
+ * `garden ui prompt`: rules + compose DIL catalog + interactive PascalCase
21
+ * reference + recipes. The admin workbench appends `studio` for its output contract.
19
22
  * @param {{ rules: string, recipes: string[], studio?: string | null }} parts
20
23
  */
21
24
  export function buildPrompt({ rules, recipes, studio = null }) {
22
- const parts = [rules.trim(), catalogReference().trim(), "## 配方", ...recipes.map((recipe) => recipe.trim())];
25
+ const parts = [
26
+ rules.trim(),
27
+ composeCatalogReference().trim(),
28
+ catalogReference().trim(),
29
+ "## 配方",
30
+ ...recipes.map((recipe) => recipe.trim()),
31
+ ];
23
32
  if (studio) parts.push(studio.trim());
24
33
  return `${parts.join("\n\n---\n\n")}\n`;
25
34
  }
@@ -1,25 +1,33 @@
1
- # 配方:说明图(garden compose)
1
+ # 配方:说明图与可调参数(garden compose)
2
2
 
3
- **读者卡在哪:** 一条结构被画成一张图。读者要看见谁和谁有关、哪一块是重点。
3
+ **读者卡在哪:** 一条结构被画成一张图;或读者要拧参数看结论怎么变。
4
4
 
5
- **什么时候用:** 几列名单、从一个点散开、从上到下的几层、一次事情的几面、或上面一行顺序、下面一行才是重点。
5
+ **什么时候用:** 几列名单、从一个点散开、从上到下的几层、一次事情的几面、上面一行顺序下面一行才是重点;或默认状态就要能看出结论、拖一下才多懂一点。
6
6
 
7
- **什么时候不用:** 读者要拖参数(`garden compose`)、要一张真图或 Mermaid、或正文列表已经说清。
7
+ **什么时候不用:** 正文列表 / Mermaid / `garden list` / `garden relations` 已经说清;一次乘法留在正文;需要画布动画时用遗留 `garden interactive` + `<Sandbox>`(或 `garden html`)。
8
8
 
9
9
  **围栏:** 类型始终是 `garden compose`。作者原话放进 `<intent>`,一个字不改。不要改成别的组件。正文还是一句白话时,不要发明 YAML 槽。
10
10
 
11
- **模型写的是标记,不是槽:** 叶子是 Markdown(`#` 标题、段落、`**强调**`、列表)。结构是标签。Markdown 和标签编进同一棵树。
11
+ **模型写的是 DIL,不是槽:** 叶子是 Markdown(`#` 标题、段落、`**强调**`、列表)。结构是 **小写目录标签** + **设计 token 属性**。Markdown 和标签编进同一棵树。完整标签 / token / 图标名见上方「compose 目录」——不要用手抄短名单替代。
12
12
 
13
- **目录里的标签:** `box`、`row`、`stack`、`grid`、`title`、`text`、`badge`、`pill`、`band`、`arrow`、`fan`、`join`、`lanes`、`divider`、`steps`、`step`、`note`、`compare`、`callout`、`kpi`。
13
+ ## 静态 diagram
14
14
 
15
- - `<box frame="band" tone="accent">` 是要先看见的一层。`frame` 还有 `card`、`text`、`pill`。`tone` 只有 `plain`、`accent`、`tip`、`muted`。
16
- - `<arrow dir="down"/>` 或 `dir="right"`。`<fan n="3"/>` 下面的 `<row>` 要有同样格数。`<join>一句说明</join>` 是收到一处。
17
- - `<lanes>` 里写 `<actor>` 和 `<message from="1" to="2" tone="forward">`。`tone` 是 `forward` 或 `back`。
18
- - `<steps>` 里每步一个 `<step id="sync">`,要点开的说明写 `<note>`(这些 note 收成 notes)。
19
- - `<compare>` 里 2 到 4 个 `<item>`。`<callout tone="tip">` 的 tone 是 `note`、`tip`、`warn`。
20
- - 不要写色值、HTML、SVG、脚本,也不要写 `{@body}`、`{#if}` 或 `{表达式}`。颜色走主题变量。窄屏会收成一列。
15
+ - 布局:`box` / `card` / `row` / `stack` / `columns` / `grid` / `pill` / `band` / `steps` / `lanes` / `compare` / `callout`…
16
+ - 文字:`title` / `text` / `caption` / `badge` / `note` / `actor` / `join`…
17
+ - 空标签:`arrow` / `fan` / `divider` / `kpi` / `icon`…
18
+ - token:`padding`/`gap` 0–4;`background` surface…;`radius`;`border`;`size`;`tone`(`plain`/`accent`/`tip`/`muted`;callout 另有 `note`/`warn`;lanes message 另有 `forward`/`back`);`align`;`justify`;`wrap`
19
+ - 芯片内部间距用 `gap={0}` / `gap={1}`;章节堆叠用 `gap={2}` / `gap={3}` 及以上
20
+ - 日横幅等宽:`row` 里每个 day 芯片 `flex:1`(`nowrap` + `size="sm"`)
21
+ - tip 钉右边:`justify="between"`,tip/`caption` 侧 `flex:0`
22
+ - phrasing 叶子(title/text/caption/badge/…)保留单独的前导 `*`(脚注标记)
23
+ - `<icon name="shield"/>` 等:名字必须在 Lucide 目录里
24
+ - `<arrow dir="down"/>` 或 `dir="right"`。`<fan n="3"/>` 下面的 `<row>` 要有同样格数。`<join>一句说明</join>` 是收到一处
25
+ - `<lanes>` 里写 `<actor>` 和 `<message from="1" to="2" tone="forward">`
26
+ - `<steps>` 里每步一个 `<step id="sync">`,要点开的说明写 `<note>`
27
+ - `<compare>` 里 2 到 4 个 `<item>`。`<callout tone="tip">` 的 tone 是 `note`、`tip`、`warn`
28
+ - 不要写色值、HTML、SVG、脚本或宽度。颜色走主题变量。窄屏会收成一列
21
29
 
22
- **骨架:**
30
+ **骨架(静态):**
23
31
 
24
32
  ````md
25
33
  ```garden compose
@@ -38,3 +46,30 @@
38
46
  ````
39
47
 
40
48
  并排时用 `<row>` 包 `<box>`、`<arrow dir="right"/>`、再一个 `<box>`。`id` 用字母开头。
49
+
50
+ ## play(可调参数)
51
+
52
+ 仍写 `garden compose`。可用:
53
+
54
+ - `{@body const [n, setN] = DIL.useState(3)}` 声明状态
55
+ - `{@body const total = n * 24}` 派生值
56
+ - `{total}` / `value={n}` 插值
57
+ - `{#if …}{:else}{/if}`、`{#each … as item, i}{/each}` 控制流
58
+ - 小写控件:`<slider>`、`<choice>`、`<button>`、`<stat>`、`<pie>` / `<chart>`…
59
+
60
+ 旧的 `garden interactive` 与 PascalCase 仅作兼容;不要把 PascalCase 当成 compose 作者输入。
61
+
62
+ **骨架(play):**
63
+
64
+ ````md
65
+ ```garden compose #compose-play-demo
66
+ caption: 拧一下席位,看月费。
67
+ intent: |
68
+ 让读者调席位,看月费怎么变。
69
+ ---
70
+ {@body const [seats, setSeats] = DIL.useState(3)}
71
+ {@body const total = seats * 24}
72
+ <slider min={1} max={12} value={seats} onChange={setSeats} label="席位" />
73
+ <stat label="月费" value={total} unit="USD" />
74
+ ```
75
+ ````
@@ -27,7 +27,7 @@
27
27
  - 正反对照模式:给条目加 `verdict: bad` 或 `verdict: good`,改为上下堆叠并显示红叉或绿勾,不受 60 字 `compare-long` 限制。
28
28
  - `garden timeline`:可选 `title`,必填 `items` 数组(至少 2 项,每项含可选 `date`、必填 `title`、可选 `text`、`source`)。
29
29
  - `garden chart`:可选 `type`(`"bar"`)、`title`、`unit`、`x`、`xLabel`、`yLabel`、`caption`,必填 `data`(`{ x, y, label? }[]`,至少 2 项)或 `series`(配 `x` 标签数组)。自带可展开数据表,同节不要重复手写相同数据的 Markdown 表。
30
- - `garden compose`:不是 YAML 槽。叶子是 Markdown(标题、段落、`**强调**`、列表),结构是目录里的标签(`box`、`row`、`stack`、`grid`、`title`、`text`、`badge`、`arrow`、`fan`、`join`、`lanes`、`divider`、`steps`、`pill`、`band`)。作者原话放进 `<intent>`,不画出来。`tone` 只有 `plain`、`accent`、`tip`、`muted`。不要写脚本、色值、宽度,也不要写 `{@body}` 或表达式。
30
+ - `garden compose`:不是 YAML 槽。叶子是 Markdown(标题、段落、`**强调**`、列表),结构是 **compose 目录**里的小写标签(完整列表见下方「compose 目录」:约 68 个标签 + Lucide 图标名 + token)。作者原话放进 `<intent>`,不画出来。芯片内部用 `gap={0}`/`gap={1}`,章节堆叠用 `gap={2}`/`gap={3}`;日横幅等宽用 `flex:1`;tip 钉右边用 `justify="between"`;phrasing 叶子保留前导 `*`。不要写脚本、色值、任意 HTML/SVG 或宽度。**静态 diagram** 只写 Markdown+标签;**play(可调参数)** 仍用同一围栏,可用 `{@body}`、`{expr}`、`{#if}`/`{#each}` 与小写控件(`slider`/`choice`/`button`/`stat`…)。不要用 interactive 的 PascalCase 当作者输入。
31
31
  - `garden html`:`src`(同名子目录下的本地 `.html`)或 `code` 二选一,配 `title`、可选 `height`。
32
32
  - `garden file`:单文件填 `src`(可选 `title`、`note`),多文件填 `files` 数组(每项含 `src`、可选 `title`、`note`)。本地路径相对当前 Markdown 且不能越出 `content/`(否则报 `file-outside`),文件不存在报 `missing-asset`,单文件超过 `5 MB` 报 `file-large` 警告;不要手写 `resolved`(报 `garden-file`)。
33
33
  - `garden relations`:可选 `title`、`caption`,必填 `items`(3~6 项,每项含 `title`、`text`,节点无强调色选项),在 `items` 与 `links` 之间可选 `center: <序号|标题>`,可选 `links` 数组。
@@ -39,7 +39,7 @@
39
39
  - 可用 `icon` 名(23 个):`search`、`eye`、`heart`、`chat`、`shield`、`wallet`、`bell`、`check`、`x`、`star`、`flag`、`clock`、`idea`、`book`、`link`、`user`、`chart`、`gear`、`file`、`pin`、`flask`、`target`、`question`。
40
40
  - `garden choice`:可选 `title`、`caption`,必填 `items` 数组(2~3 项,每项含 `icon`、`value`、`text`、`label`、`reply`),可选 `none: { label, reply }`(在卡片下方渲染为单行兜底文字按钮)。整组支持键盘方向键切换,窄屏下卡片单列纵向堆叠。
41
41
 
42
- interactive 的正文仍用下面的组件和属性。那是 interactive 的内部零件,不是文章里的名字。
42
+ 遗留 `garden interactive` 的正文仍用下面的 PascalCase 组件和属性(见「interactive 组件参考」)。新的可调参数优先写 `garden compose` play(`{@body}` + 小写控件)。PascalCase 不是 compose 作者输入。
43
43
 
44
44
  # 把作者的意图写成 Garden 的 interactive 块
45
45