hd-garden 0.10.4 → 0.11.0

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.
Files changed (75) hide show
  1. package/.agents/skills/garden-ui/SKILL.md +11 -10
  2. package/README.en.md +1 -1
  3. package/README.md +2 -2
  4. package/app/components/garden/GardenBlock.tsx +20 -4
  5. package/app/components/garden/blocks.tsx +6 -118
  6. package/app/globals.css +1 -0
  7. package/app/lib/garden-docs-meta.ts +3 -3
  8. package/app/lib/generated-content.ts +1 -1
  9. package/app/lib/i18n.ts +0 -6
  10. package/app/lib/remark-garden.mjs +61 -5
  11. package/app/styles/a2.css +25 -112
  12. package/app/styles/tokens.css +5 -0
  13. package/docs/handbook/ai-writing.md +3 -3
  14. package/docs/handbook/components/compose.md +354 -0
  15. package/docs/handbook/components/html.md +1 -1
  16. package/docs/handbook/components/image.md +1 -1
  17. package/docs/handbook/components/interactive.md +4 -1
  18. package/docs/handbook/components/link-card.md +1 -1
  19. package/docs/handbook/components.md +23 -35
  20. package/docs/handbook/design.md +25 -34
  21. package/docs/handbook/en/ai-writing.md +3 -3
  22. package/docs/handbook/en/components/compose.md +352 -0
  23. package/docs/handbook/en/components/html.md +1 -1
  24. package/docs/handbook/en/components/image.md +1 -1
  25. package/docs/handbook/en/components/interactive.md +4 -1
  26. package/docs/handbook/en/components.md +23 -36
  27. package/docs/handbook/en/design.md +25 -33
  28. package/docs/handbook/en/index.md +5 -5
  29. package/docs/handbook/en/reference/compose.md +99 -0
  30. package/docs/handbook/en/reference.md +2 -2
  31. package/docs/handbook/en/workflow.md +8 -6
  32. package/docs/handbook/index.md +5 -5
  33. package/docs/handbook/reference/compose.md +99 -0
  34. package/docs/handbook/reference.md +2 -2
  35. package/docs/handbook/workflow.md +8 -6
  36. package/editors/dist/typora/main.js +164 -136
  37. package/editors/dist/vscode/garden-ui-0.1.0.vsix +0 -0
  38. package/package.json +14 -2
  39. package/scripts/handbook-allowlist.mjs +0 -8
  40. package/scripts/handbook-facts.mjs +6 -36
  41. package/scripts/ui-cli.mjs +5 -1
  42. package/templates/garden-blocks.md +34 -31
  43. package/templates/garden-snippets.plist +3 -8
  44. package/templates/skills-en/garden-ui/SKILL.md +11 -10
  45. package/ui/README.md +1 -0
  46. package/ui/compose/compose.css +806 -0
  47. package/ui/compose/dil.mjs +443 -0
  48. package/ui/compose/examples.mjs +28 -0
  49. package/ui/compose/index.mjs +7 -0
  50. package/ui/compose/markup.mjs +364 -0
  51. package/ui/compose/parse.mjs +21 -0
  52. package/ui/compose/render.mjs +247 -0
  53. package/ui/core/cli-locale.mjs +4 -0
  54. package/ui/core/fence.mjs +13 -1
  55. package/ui/core/fix-prompt.mjs +1 -0
  56. package/ui/core/garden-fence.mjs +4 -1
  57. package/ui/core/scan.mjs +28 -2
  58. package/ui/core/transform.mjs +12 -0
  59. package/ui/core/validate.mjs +6 -4
  60. package/ui/garden/catalog.mjs +58 -35
  61. package/ui/garden/check.mjs +22 -6
  62. package/ui/garden/parse.mjs +26 -0
  63. package/ui/garden/snippets.mjs +15 -13
  64. package/ui/garden/word-count.mjs +10 -18
  65. package/ui/preview/dist/garden-ui.esm.js +158 -132
  66. package/ui/preview/dist/garden-ui.iife.js +157 -131
  67. package/ui/prompt/build.mjs +1 -0
  68. package/ui/prompt/recipes/compose.md +40 -0
  69. package/ui/prompt/rules.md +3 -3
  70. package/ui/react/CatalogPage.tsx +2 -2
  71. package/ui/react/runtime.tsx +5 -1
  72. package/docs/handbook/components/figure.md +0 -59
  73. package/docs/handbook/en/components/figure.md +0 -59
  74. package/docs/handbook/en/reference/figure.md +0 -84
  75. package/docs/handbook/reference/figure.md +0 -84
package/app/styles/a2.css CHANGED
@@ -73,8 +73,7 @@ body {
73
73
  .g-card,
74
74
  .g-timeline,
75
75
  .g-sandbox,
76
- .g-html,
77
- .g-figure {
76
+ .g-html {
78
77
  box-shadow: none;
79
78
  }
80
79
 
@@ -289,6 +288,17 @@ body {
289
288
  max-width: var(--col);
290
289
  }
291
290
 
291
+ /* The compare post puts two wide diagrams side by side. */
292
+ @media (min-width: 1260px) {
293
+ .post-reading-page:has(.g-compose-pair) .article-reading-layout {
294
+ grid-template-columns: 24px minmax(0, 1fr) var(--rail);
295
+ }
296
+
297
+ .post-reading-page:has(.g-compose-pair) .article-reading-column {
298
+ max-width: none;
299
+ }
300
+ }
301
+
292
302
  .post-reading-page .article-reading-layout > .toc {
293
303
  grid-column: 3;
294
304
  grid-row: 1;
@@ -785,6 +795,17 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
785
795
  background: transparent;
786
796
  }
787
797
 
798
+ .table-wrap td .markdown-image-trigger {
799
+ margin: 0;
800
+ }
801
+
802
+ .table-wrap td .markdown-image-trigger img {
803
+ width: 280px;
804
+ max-width: 100%;
805
+ height: auto;
806
+ border-radius: 6px;
807
+ }
808
+
788
809
  @media (min-width: 900px) {
789
810
  .ux-wide {
790
811
  margin-left: calc(-1 * var(--wl, 0px)) !important;
@@ -885,8 +906,6 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
885
906
  .g-cmp,
886
907
  .g-timeline,
887
908
  .g-chart,
888
- .g-figure,
889
- .g-explore,
890
909
  .g-html,
891
910
  .g-idea,
892
911
  .g-legacy,
@@ -895,77 +914,9 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
895
914
  box-shadow: none;
896
915
  }
897
916
 
898
- .g-explore {
899
- border: 1px solid var(--rule);
900
- border-radius: var(--r);
901
- padding: 12px 14px 14px;
902
- background: transparent;
903
- max-width: 100%;
904
- }
905
-
906
- .g-explore [role="tablist"] {
907
- display: flex;
908
- flex-wrap: wrap;
909
- gap: 6px;
910
- margin: 0;
911
- padding: 0;
912
- }
913
-
914
- .g-explore [role="tab"] {
915
- font: 500 14px / 1 var(--sans);
916
- padding: 9px 12px;
917
- min-height: 44px;
918
- border-radius: 999px;
919
- border: 1px solid var(--rule);
920
- background: transparent;
921
- color: var(--soft);
922
- cursor: pointer;
923
- }
924
-
925
- .g-explore [role="tab"][aria-selected="true"] {
926
- border-color: var(--blue);
927
- color: var(--blue);
928
- background: var(--blue-tint);
929
- }
930
-
931
- .dx-wrap {
932
- overflow: visible;
933
- margin-top: 10px;
934
- max-width: 100%;
935
- }
936
-
937
- .dx-wrap svg {
938
- display: block;
939
- width: 100%;
940
- max-width: 100%;
941
- height: auto;
942
- }
943
-
944
- .g-explore:not([data-sel="all"]) .dx-wrap [data-p]:not(.on),
945
- .g-explore:not([data-sel="all"]) .dx-wrap text:not(.on) {
946
- opacity: 0.22;
947
- }
948
-
949
- .g-explore .on {
950
- opacity: 1;
951
- }
952
-
953
- .dx-exp p {
954
- margin: 10px 0 0;
955
- }
956
-
957
- .g-explore figcaption {
958
- font-size: 13px;
959
- color: var(--muted);
960
- margin-top: 8px;
961
- text-align: left;
962
- }
963
-
964
917
  .g-sandbox,
965
918
  .g-play,
966
- .g-html,
967
- .g-explore,
968
- .dx-wrap {
919
+ .g-html {
969
920
  max-width: 100%;
970
921
  }
971
922
 
@@ -973,7 +924,6 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
973
924
  .g-stat,
974
925
  .g-cmp,
975
926
  .g-timeline,
976
- .g-figure,
977
927
  .g-html {
978
928
  border: 1px solid var(--rule);
979
929
  border-radius: var(--r);
@@ -982,8 +932,7 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
982
932
 
983
933
  .g-card,
984
934
  .g-cmp,
985
- .g-html,
986
- .g-figure {
935
+ .g-html {
987
936
  padding: 14px 16px;
988
937
  }
989
938
 
@@ -1163,7 +1112,6 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
1163
1112
  min-width: 3px;
1164
1113
  }
1165
1114
 
1166
- .g-figure svg,
1167
1115
  .g-mermaid svg,
1168
1116
  .dx-svg {
1169
1117
  display: block;
@@ -1172,16 +1120,6 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
1172
1120
  height: auto;
1173
1121
  }
1174
1122
 
1175
- .g-figure:not([data-sel="all"]) [data-p]:not(.on),
1176
- .g-figure:not([data-sel="all"]) text:not(.on) {
1177
- opacity: 0.22;
1178
- }
1179
-
1180
- .g-figure [data-p].on,
1181
- .g-figure text.on {
1182
- opacity: 1;
1183
- }
1184
-
1185
1123
  .g-mermaid {
1186
1124
  margin: 28px 0;
1187
1125
  padding: 16px;
@@ -1399,19 +1337,10 @@ html[data-theme="dark"] .markdown-body > p > img:not([class]) {
1399
1337
  padding-right: 44px;
1400
1338
  }
1401
1339
 
1402
- .ux-fs:not(.g-sandbox) > .ux-fsbar + .g-explore {
1403
- padding-right: 14px;
1404
- }
1405
-
1406
1340
  .ux-fs:not(.g-sandbox) > .ux-fsbar + .g-sandbox {
1407
1341
  padding-right: 0;
1408
1342
  }
1409
1343
 
1410
- .ux-fs:not(.g-sandbox) > .ux-fsbar + .g-explore > [role="tablist"] {
1411
- padding-right: 44px;
1412
- box-sizing: border-box;
1413
- }
1414
-
1415
1344
  .ux-fs:fullscreen,
1416
1345
  .ux-fs.ux-fs-on {
1417
1346
  background: var(--paper);
@@ -1793,13 +1722,6 @@ html[data-theme="dark"] .gf-scrim { background: rgb(0 0 0 / 45%); }
1793
1722
  font-family: var(--sans);
1794
1723
  }
1795
1724
 
1796
- .garden-doc-page .g-explore:not([data-sel="all"]) .dx-wrap [data-p]:not(.on),
1797
- .garden-doc-page .g-explore:not([data-sel="all"]) .dx-wrap text:not(.on),
1798
- .garden-doc-page .g-figure:not([data-sel="all"]) [data-p]:not(.on),
1799
- .garden-doc-page .g-figure:not([data-sel="all"]) text:not(.on) {
1800
- opacity: 0.56;
1801
- }
1802
-
1803
1725
  .garden-doc-page > .toc {
1804
1726
  margin-top: 8px;
1805
1727
  position: sticky;
@@ -2641,15 +2563,6 @@ html[data-theme="dark"] .ref-demo { --rd-s: #262624; --rd-p: #1f1e1d; --rd-i: #e
2641
2563
  .ref-demo-in .toc-mobile-trigger { opacity: 0; visibility: hidden; pointer-events: none; }
2642
2564
  }
2643
2565
 
2644
- /* figure reference page: the drawing needs room, so the example stacks
2645
- (preview on top, full width; code below). Scoped to the reference demo;
2646
- figures in articles are untouched. */
2647
- @media (min-width: 1024px) {
2648
- .ref-demo[data-name="figure"] { grid-template-columns: minmax(0, 1fr); background: var(--rd-p); }
2649
- .ref-demo[data-name="figure"] .ref-pane[data-p="v"] { grid-column: 1; grid-row: 1; }
2650
- .ref-demo[data-name="figure"] .ref-pane[data-p="c"] { grid-column: 1; grid-row: 2; background: var(--rd-s); border-top: 1px solid var(--rd-r); }
2651
- }
2652
-
2653
2566
  /* ===== v0.10.1 tip keep (after git checkout of a2.css from 9a80f7e5) ===== */
2654
2567
 
2655
2568
  /* List pages: PageIntro rides on .sh-head; keep eyebrow in the same vertical rhythm. */
@@ -33,6 +33,9 @@
33
33
  --accent-soft: var(--blue-tint);
34
34
  --selection: #e8e5db;
35
35
  --hl: #ffe58f;
36
+ /* Two lane-arrow emphases. Same hues as the note and tip callouts, not a second palette. */
37
+ --flow: #2c55b4;
38
+ --flow-back: #2d6a3e;
36
39
  --red: #d82d48;
37
40
  --red-text: #c2263f;
38
41
  --red-tint: #fbeef0;
@@ -94,6 +97,8 @@ html[data-theme="dark"] {
94
97
  --accent-soft: var(--blue-tint);
95
98
  --selection: rgb(238 236 231 / 18%);
96
99
  --hl: #5e4e17;
100
+ --flow: #93b2f4;
101
+ --flow-back: #8fcb9f;
97
102
  --red: #f29aab;
98
103
  --red-text: #f29aab;
99
104
  --red-tint: rgb(242 154 171 / 12%);
@@ -21,7 +21,7 @@ order: 2
21
21
  ```
22
22
  ````
23
23
 
24
- 草稿(`draft: true`)里可以保留未编译的想法;非草稿文章里还有未编译的想法时,`npx garden ui check` 和 `npx garden check` 都会报 `garden-idea` 错误。Agent 会根据内容把想法编译成 `garden interactive`、`card`、`stat`、`compare` 等内置组件,或者在一段话就能说清时直接写回普通段落。
24
+ 草稿(`draft: true`)里可以保留未编译的想法;非草稿文章里还有未编译的想法时,`npx garden ui check` 和 `npx garden check` 都会报 `garden-idea` 错误。Agent 会根据内容把想法编译成 `garden compose`、`card`、`stat`、`compare` 等内置组件,或者在一段话就能说清时直接写回普通段落。
25
25
 
26
26
  ## 2. 随包安装的三个 skill
27
27
 
@@ -112,8 +112,8 @@ npx garden editors install --typora
112
112
 
113
113
  不借助 AI 手写组件时,可以直接使用包内自带的模板与代码片段:
114
114
 
115
- - `make new` 创建的草稿底部自带一段 `<!-- 组件速查 ... -->` 注释,包含想法块和 12 个组件的示例,写完可直接删掉。
116
- - `templates/garden-blocks.md` 汇总了想法块与 12 个组件的适用场景(`when` / `whenNot`)和可复制示例;macOS 与 Typora / Alfred 用户可导入 `templates/garden-snippets.plist`,输入 `;gidea`(或 `;garden想法`)、`;gcard`、`;gstat`、`;gcompare`、`;gtimeline`、`;gchart`、`;gfigure`、`;ghtml`、`;ginteractive`、`;gfile`、`;grel`、`;glist`、`;gchoice` 即可展开对应围栏。
115
+ - `make new` 创建的草稿底部自带一段 `<!-- 组件速查 ... -->` 注释,包含想法块和各个组件的示例,写完可直接删掉。
116
+ - `templates/garden-blocks.md` 汇总了想法块与各个组件的适用场景(`when` / `whenNot`)和可复制示例;macOS 与 Typora / Alfred 用户可导入 `templates/garden-snippets.plist`,输入 `;gidea`(或 `;garden想法`)、`;gcard`、`;gstat`、`;gcompare`、`;gtimeline`、`;gchart`、`;gcompose`、`;ghtml`、`;ginteractive`、`;gfile`、`;grel`、`;glist`、`;gchoice` 即可展开对应围栏。`;gcompose` 展开的是带一句白话的 `garden compose`。
117
117
  - 编辑器插件的用法见上一节。`npx garden ui catalog` 在本地生成 `.garden/ui-catalog.html` 离线组件画廊。
118
118
 
119
119
  ## 8. 可选的独立管理后台
@@ -0,0 +1,354 @@
1
+ ---
2
+ title: 说明图(compose)
3
+ navTitle: 说明图
4
+ description: 在文章里写几句白话,让 AI 拼出静态说明图。
5
+ section: components
6
+ order: 36
7
+ badge: compose
8
+ ---
9
+
10
+ 写文章想画一张结构图时,输入 `/garden` 选「compose(想法)」。文章里会插入一个 `garden compose` 围栏。你在里面用白话写清结构,再对 AI 说「编译这篇」,AI 就会把这句话留在 `<intent>` 里,并用 Markdown 叶子和组件标签拼出静态说明图。
11
+
12
+ ## 怎么写
13
+
14
+ 围栏里只写你想让读者看见的结构,不要写样式。
15
+
16
+ 把四件事写清楚:
17
+
18
+ - **分几块**:是三列并排、从上到下五层,还是上下两行。
19
+ - **每块叫什么**:用图上要摆出来的短词。解释放在名字下面那一行,别把一整句话当标题。
20
+ - **谁连谁**:谁先到谁、从一处散开,还是几处收到一起。没有先后顺序,就直接写「这几块没有先后」。
21
+ - **哪块是重点**:直接点名,比如「中间那列是重点」「下面一行才是重点」。
22
+
23
+ 不要写颜色、圆角、灰胶囊、字重、间距、阴影、要不要撑满。外观由组件默认样式决定,写了反而乱。全部标签见 [组件参考 · compose](/garden/reference/compose)。
24
+
25
+ ## 最佳实践
26
+
27
+ 写围栏时,最常见的失误有两个:一是只写一个主题词,没给结构;二是把精力花在规定颜色、圆角和字重上。下面按五种常见结构,列出改写前后的写法。
28
+
29
+ **三列并排,中间接起来**
30
+
31
+ 只写「画一张权限图」,AI 不知道分几列、每列放什么;写成「三列等宽,中间用浅灰圆角,字重轻一点」,又全在规定外观。
32
+
33
+ 把三列的名字、每列底下的条目和中间的连接写出来:
34
+
35
+ ````markdown
36
+ ```garden compose
37
+ 三列:User 下面是 Alice、Bob;Role 下面是管理员、审核员;
38
+ Permission 下面是 orders:read、orders:approve。
39
+ Role 在中间,把用户和权限接起来。Role 是重点。
40
+ ```
41
+ ````
42
+
43
+ **先到一处,再散开**
44
+
45
+ 不要写「画个单点登录,箭头好看一点,间距均匀」。把谁先连向哪里、再同时分给哪几个系统写清楚:
46
+
47
+ ````markdown
48
+ ```garden compose
49
+ 用户浏览器先到身份提供方,再同时连到 HR、Finance、Project 三个系统。
50
+ 三个系统都信任同一个身份提供方。身份提供方是重点。
51
+ ```
52
+ ````
53
+
54
+ **自上而下分层**
55
+
56
+ 不要写「五张通栏卡片,第二张和第五张上色,箭头画细」。直接列出五层的名字、哪两层需要突出,以及是不是每次请求都按顺序走完:
57
+
58
+ ````markdown
59
+ ```garden compose
60
+ 从上到下五层:浏览器、身份服务、BFF、业务服务、授权决策。
61
+ 身份服务和授权决策是重点,其余是经过的路径。
62
+ 不是每次请求都按这个顺序走完。
63
+ ```
64
+ ````
65
+
66
+ **一件事的多个切面**
67
+
68
+ 不要写「两行两列加下面两张卡片,标题不要加粗」。把六个切面的短名、每块要回答的问题,以及「没有先后顺序」交代清楚:
69
+
70
+ ````markdown
71
+ ```garden compose
72
+ 用户发起请求后,先看四件事:认证(你是谁)、凭证(怎么证明)、
73
+ 授权(有没有权)、验证(数据合不合法)。
74
+ 落到业务和存储时,再看传输和保密。这六块没有严格的先后。
75
+ ```
76
+ ````
77
+
78
+ **上下两行对比**
79
+
80
+ 不要写「上面无框,下面灰色胶囊,贴着内容,不要撑满」。直接写哪一行是常规背景、哪一行是真正要强调的循环:
81
+
82
+ ````markdown
83
+ ```garden compose
84
+ 上面一行是常规交付:理解需求 → 设计方案 → 开发上线。
85
+ 下面一行是探索循环:提出假设 → 低成本验证 → 迭代或放弃。
86
+ 上面是背景,下面一行才是重点。
87
+ ```
88
+ ````
89
+
90
+ ## 能力示例
91
+
92
+ **下面这些图只是示例**,用来展示 `compose` 能拼出哪几类常见结构。图里的权限、登录、API 等词只是为了把格子填满,写文章时换成你自己的主题即可。
93
+
94
+ ### 三列中间接
95
+
96
+ 左右两列靠中间一列连起来,底下用 `<join>` 收成一句话。
97
+
98
+ ```garden compose
99
+ <intent>
100
+ 三列:User 下面是 Alice 和 Bob,Role 下面是管理员和审核员,Permission 下面是 orders:read 和 orders:approve。Role 在中间,把用户和权限接起来。Role 是要先看见的一层。
101
+ </intent>
102
+ <row align="center">
103
+ <stack align="center">
104
+ <title>User</title>
105
+ <text>Alice</text>
106
+ <text>Bob</text>
107
+ </stack>
108
+ <band tone="accent" align="center">
109
+ <title>Role</title>
110
+ <text>管理员</text>
111
+ <text>审核员</text>
112
+ </band>
113
+ <stack align="center">
114
+ <title>Permission</title>
115
+ <text>orders:read</text>
116
+ <text>orders:approve</text>
117
+ </stack>
118
+ </row>
119
+ <join>角色连接用户和权限</join>
120
+ <text tone="muted" align="center">概念关系:User ↔ Role ↔ Permission</text>
121
+ ```
122
+
123
+ ### 先汇再散
124
+
125
+ 先流向中间那一层,再用 `<fan>` 同时分到下面几列。
126
+
127
+ ```garden compose
128
+ <intent>
129
+ 用户浏览器先到身份提供方。身份提供方再同时连到 HR、Finance、Project。三个系统信任同一个身份提供方。身份提供方是要先看见的一层。
130
+ </intent>
131
+ <stack align="center">
132
+ <pill>用户浏览器</pill>
133
+ <arrow dir="down"/>
134
+ <band tone="accent" align="center">
135
+ <title>Identity Provider(IdP)</title>
136
+ <text>统一完成身份验证、维护登录状态、签发身份断言</text>
137
+ </band>
138
+ <fan n="3"/>
139
+ <row align="center">
140
+ <title>HR 系统</title>
141
+ <title>Finance 系统</title>
142
+ <title>Project 系统</title>
143
+ </row>
144
+ <text tone="muted" align="center">三个系统都信任同一个身份提供方</text>
145
+ </stack>
146
+ ```
147
+
148
+ ### 自上而下分层
149
+
150
+ 几层按顺序往下走,重点层用 `<band>`,经过的层只留文字。
151
+
152
+ ```garden compose
153
+ <intent>
154
+ 从上到下五层:浏览器、身份服务、BFF、业务服务、授权决策。身份服务和授权决策是要强调的两层,其余是经过的路径。不是每次请求都按这个顺序走完。
155
+ </intent>
156
+ <stack align="center">
157
+ <box frame="text" align="center">
158
+ <badge>①</badge>
159
+ <title>浏览器 / App</title>
160
+ <text>Cookie、Session、OAuth Client</text>
161
+ </box>
162
+ <arrow dir="down"/>
163
+ <band tone="accent" align="center">
164
+ <badge>②</badge>
165
+ <title>身份服务 IdP / IAM</title>
166
+ <text>Passkey、MFA、OIDC、会话管理</text>
167
+ </band>
168
+ <arrow dir="down"/>
169
+ <box frame="text" align="center">
170
+ <badge>③</badge>
171
+ <title>BFF / API Gateway</title>
172
+ <text>会话验证、Token 校验、入口授权控制</text>
173
+ </box>
174
+ <arrow dir="down"/>
175
+ <box frame="text" align="center">
176
+ <badge>④</badge>
177
+ <title>业务服务</title>
178
+ <text>订单、支付、账户、资源对象</text>
179
+ </box>
180
+ <arrow dir="down"/>
181
+ <band tone="tip" align="center">
182
+ <badge>⑤</badge>
183
+ <title>授权决策系统</title>
184
+ <text>RBAC + ABAC + ReBAC + Policy Engine</text>
185
+ </band>
186
+ <text tone="muted" align="start">逻辑职责示意:并非所有请求都按此顺序经过各层。身份服务通常在登录阶段交互,业务服务则在请求执行时调用授权决策能力。</text>
187
+ </stack>
188
+ ```
189
+
190
+ ### 一件事的多个切面
191
+
192
+ 没有严格先后的几块内容,用 `<row>` 和 `<box>` 分成上下两组并排摆开。
193
+
194
+ ```garden compose
195
+ <intent>
196
+ 用户要改订单地址。先分成认证、凭证、授权、验证,每块一个问题。然后再看传输和保密。这六块不是严格的先后步骤。
197
+ </intent>
198
+ <stack align="center">
199
+ <title>用户发起 API 请求</title>
200
+ <text tone="muted">例如:修改订单收货地址</text>
201
+ </stack>
202
+ <arrow dir="down"/>
203
+ <row>
204
+ <box>
205
+ <title>认证</title>
206
+ <text>你是谁?</text>
207
+ <text tone="muted">Authentication</text>
208
+ </box>
209
+ <box>
210
+ <title>凭证</title>
211
+ <text>如何证明你的身份?</text>
212
+ <text tone="muted">Credential</text>
213
+ </box>
214
+ </row>
215
+ <row>
216
+ <box>
217
+ <title>授权</title>
218
+ <text>你有权修改这笔订单吗?</text>
219
+ <text tone="muted">Authorization</text>
220
+ </box>
221
+ <box>
222
+ <title>验证</title>
223
+ <text>提交的数据是否合法?</text>
224
+ <text tone="muted">Verification</text>
225
+ </box>
226
+ </row>
227
+ <arrow dir="down"/>
228
+ <stack align="center">
229
+ <title>业务操作与数据存储</title>
230
+ </stack>
231
+ <row>
232
+ <box>
233
+ <title>传输</title>
234
+ <text>网络中能否被窃听、篡改、冒充?</text>
235
+ </box>
236
+ <box>
237
+ <title>保密</title>
238
+ <text>敏感数据泄露后能否被读取?</text>
239
+ </box>
240
+ </row>
241
+ <text tone="muted">概念示意:六者并不是严格串行的执行阶段,传输保护贯穿网络通信,保密措施也可能覆盖客户端与服务端。</text>
242
+ ```
243
+
244
+ ### 外框套子项
245
+
246
+ 大框里再摆一排徽章或两列要点,适合画「一层里面包含几条检查」。
247
+
248
+ ```garden compose
249
+ <intent>
250
+ 用户意图经过 Agent,再经过策略执行点,最后到工具执行器。策略点里是四条约束。策略点是要先看见的一层。
251
+ </intent>
252
+ <stack align="center">
253
+ <title>User Intent + Authorization</title>
254
+ <text tone="muted">明确任务目标、用户身份与授权范围</text>
255
+ <arrow dir="down"/>
256
+ <band align="center">
257
+ <title>Agent / LLM</title>
258
+ <text>负责理解、推理、规划和提出执行意图</text>
259
+ <row align="center">
260
+ <badge>Untrusted Context</badge>
261
+ <badge>Memory</badge>
262
+ <badge>Tool Results</badge>
263
+ </row>
264
+ </band>
265
+ <arrow dir="down"/>
266
+ <box align="center" mark="shield">
267
+ <title>Policy Enforcement Point</title>
268
+ <text tone="muted" align="center">独立于 LLM 的确定性执行控制</text>
269
+ <row align="center">
270
+ <text>权限与资源范围</text>
271
+ <text>操作参数校验</text>
272
+ </row>
273
+ <row align="center">
274
+ <text>任务意图约束</text>
275
+ <text>高风险操作审批</text>
276
+ </row>
277
+ </box>
278
+ <arrow dir="down"/>
279
+ <title>Tool Executor</title>
280
+ <text tone="muted">按最小权限执行、记录审计、支持中断</text>
281
+ </stack>
282
+ ```
283
+
284
+ ### 多角色来回交互
285
+
286
+ 几个角色按先后发消息、退回结果时,用 `<lanes>` 画成泳道。
287
+
288
+ ```garden compose
289
+ <intent>
290
+ 用户、客户端、授权服务器三条泳道,六步授权码加 PKCE。
291
+ </intent>
292
+ <lanes>
293
+ <actor>用户</actor>
294
+ <actor>客户端</actor>
295
+ <actor>授权服务器</actor>
296
+ <message from="1" to="2" tone="forward">点击登录 / 授权</message>
297
+ <message from="2" to="3" tone="forward">发起授权请求 + PKCE</message>
298
+ <message from="3" to="1" tone="back">用户身份验证与授权</message>
299
+ <message from="3" to="2" tone="back">重定向返回 Code</message>
300
+ <message from="2" to="3" tone="forward">Code + Verifier 换 Token</message>
301
+ <message from="3" to="2" tone="back">返回 Token</message>
302
+ </lanes>
303
+ <text tone="muted">简化示意:真实流程还包含浏览器跳转、回调绑定、授权码及令牌校验等细节。</text>
304
+ ```
305
+
306
+ ### 两行流程对比
307
+
308
+ 上面一行放常规做法当背景,中间加 `<divider/>`,下面一行用胶囊突出真正想讲的循环。
309
+
310
+ ```garden compose
311
+ <intent>
312
+ 上面一行是交付顺序:理解需求、设计方案、开发上线。下面一行才是要强调的决策循环:提出假设、低成本验证、迭代或放弃。上面是背景,下面是重点。
313
+ </intent>
314
+ <row align="center">
315
+ <title align="center">理解需求</title>
316
+ <arrow dir="right"/>
317
+ <title align="center">设计方案</title>
318
+ <arrow dir="right"/>
319
+ <title align="center">开发上线</title>
320
+ </row>
321
+ <divider/>
322
+ <row align="center">
323
+ <pill tone="muted">提出假设</pill>
324
+ <arrow dir="right"/>
325
+ <pill tone="muted">低成本验证</pill>
326
+ <arrow dir="right"/>
327
+ <pill tone="muted">迭代或放弃</pill>
328
+ </row>
329
+ <text tone="muted">第二行才是产品探索更强调的决策循环。</text>
330
+ ```
331
+
332
+ ## 可调参数(play)
333
+
334
+ 静态说明图还差一步、读者要拧参数时,仍写 `garden compose`,在围栏里用同一套 DIL 写法:
335
+
336
+ - `{@body const [n, setN] = DIL.useState(3)}` 声明状态
337
+ - `{@body const total = n * 24}` 派生值
338
+ - `{total}` / `value={n}` 插值
339
+ - `{#if …}{:else}{/if}`、`{#each … as item, i}{/each}` 控制流
340
+ - 小写控件:`<slider>`、`<choice>`、`<button>`、`<pie>` / `<chart>`、`<stat>`
341
+
342
+ 旧的 `garden interactive` 与 PascalCase `<State>` / `<Slider>` 仅作兼容,新文章不要再写。完整映射见仓库 `docs/compose-dil.md`。
343
+
344
+ ```garden compose #compose-play-demo
345
+ caption: 拧一下席位,看月费。
346
+ intent: |
347
+ 让读者调席位,看月费怎么变。
348
+ ---
349
+ {@body const [seats, setSeats] = DIL.useState(3)}
350
+ {@body const total = seats * 24}
351
+ <slider min={1} max={12} value={seats} onChange={setSeats} label="席位" />
352
+ <stat label="月费" value={total} unit="USD" />
353
+ ```
354
+
@@ -34,7 +34,7 @@ code: |
34
34
  ```
35
35
 
36
36
  - **什么时候用**:内置组件画不出来的画布动画、物理模拟或独立小工具。
37
- - **什么时候不用**:只需要拖动滑杆算公式时用[可调公式与参数](/garden/components/interactive);静态图表或结构图优先用对应组件。
37
+ - **什么时候不用**:只需要拖动滑杆算公式时用[可调公式与参数](/garden/components/interactive);说明图用 [compose](/garden/components/compose)。
38
38
 
39
39
  可以这样对 AI 说:
40
40
 
@@ -9,7 +9,7 @@ order: 45
9
9
  图片单独成段时会包进图框,并在下方显示图注。
10
10
 
11
11
  - **什么时候用**:这张图属于文章,读者需要看到图下说明。
12
- - **什么时候不用**:只是装饰时,把 `alt` 写成以 `image`、`截图` 或 `img` 开头的占位词,这样不会出现图注。要按部分高亮说明时用[分区结构图](/garden/components/figure);要提供下载时用[文件下载与预览](/garden/components/file)。
12
+ - **什么时候不用**:只是装饰时,把 `alt` 写成以 `image`、`截图` 或 `img` 开头的占位词,这样不会出现图注。要按部分点开说明时用[说明图](/garden/components/compose);要提供下载时用[文件下载与预览](/garden/components/file)。
13
13
 
14
14
  长文 `content/posts/my-post.md` 的配图放在同名目录 `content/posts/my-post/`。正文用 Markdown 图片语法引用,地址写成 `./my-post/diagram.svg`,方括号里的 `alt` 写成图注。
15
15
 
@@ -7,7 +7,10 @@ order: 38
7
7
  badge: interactive
8
8
  ---
9
9
 
10
- ```garden interactive #reading-time
10
+ > **已废弃。** 新文章请写 [`compose`](/garden/components/compose),用 ChatGPT 同款 DIL(`{@body}` / `{expr}` / `{#if}` / `{#each}` 与小写标签)。旧的 `garden interactive` 围栏仍会当作 compose play 解析以兼容,之后会像 `figure` 一样删掉。
11
+
12
+
13
+ ```garden compose #reading-time
11
14
  caption: 示例:按字数、阅读速度和配图数估算一篇文章的阅读时间。数字只用来演示组件,不是统计结果。
12
15
  ---
13
16
  <State words={6000} speed={400} images={8} />
@@ -47,4 +47,4 @@ https://spec.commonmark.org/
47
47
 
48
48
  随想 Front Matter 里的 `type: link` 用你写的 `title`、`description` 和 `image`,字段见[写文章与随想](/garden/writing)。
49
49
 
50
- 这不是 `garden` 围栏,没有字段表。12 个围栏组件的字段见[组件参考](/garden/reference)。
50
+ 这不是 `garden` 围栏,没有字段表。围栏组件的字段见[组件参考](/garden/reference)。