hd-garden 0.8.0 → 0.8.2

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 (187) hide show
  1. package/.agents/skills/garden-ui/SKILL.md +2 -2
  2. package/.env.en.example +67 -0
  3. package/README.en.md +2 -2
  4. package/README.md +2 -2
  5. package/app/a2/[slug]/page.tsx +4 -3
  6. package/app/a2/page.tsx +4 -3
  7. package/app/about/page.tsx +8 -9
  8. package/app/admin/page.tsx +7 -6
  9. package/app/admin/preview/[...path]/page.tsx +2 -1
  10. package/app/admin/ui/blocks/page.tsx +2 -1
  11. package/app/admin/ui/catalog/page.tsx +2 -1
  12. package/app/admin/ui/evals/page.tsx +2 -1
  13. package/app/admin/ui/page.tsx +2 -1
  14. package/app/blog/[...path]/page.tsx +7 -6
  15. package/app/blog/page.tsx +4 -2
  16. package/app/columns/[column]/[...path]/page.tsx +8 -7
  17. package/app/columns/page.tsx +12 -8
  18. package/app/components/AdminUiShell.tsx +4 -3
  19. package/app/components/ArticleMeasure.tsx +6 -3
  20. package/app/components/ArticleReferences.tsx +4 -1
  21. package/app/components/ArticleTags.tsx +3 -2
  22. package/app/components/BlogArchive.tsx +20 -16
  23. package/app/components/Comments.tsx +16 -10
  24. package/app/components/ContentEmptyState.tsx +29 -28
  25. package/app/components/ExternalEmbed.tsx +15 -14
  26. package/app/components/GardenContentGuide.tsx +10 -2
  27. package/app/components/GardenDocsNav.tsx +4 -2
  28. package/app/components/GiscusComments.tsx +2 -1
  29. package/app/components/GithubLiveOverview.tsx +10 -8
  30. package/app/components/GithubProfile.tsx +31 -21
  31. package/app/components/HandbookLangSwitch.tsx +8 -4
  32. package/app/components/HandbookView.tsx +92 -9
  33. package/app/components/MarkdownArticle.tsx +31 -7
  34. package/app/components/MermaidDiagram.tsx +4 -3
  35. package/app/components/SiteChrome.tsx +3 -3
  36. package/app/components/SiteFooter.tsx +1 -1
  37. package/app/components/SiteHeader.tsx +13 -4
  38. package/app/components/ThoughtCard.tsx +16 -14
  39. package/app/components/UiBlock.tsx +3 -2
  40. package/app/components/Webmentions.tsx +11 -11
  41. package/app/components/WidgetChart.tsx +8 -6
  42. package/app/components/garden/FilePreview.tsx +2 -2
  43. package/app/components/garden/GardenBlock.tsx +7 -0
  44. package/app/components/garden/GardenFile.tsx +2 -2
  45. package/app/components/garden/RefDemo.tsx +62 -0
  46. package/app/components/garden/blocks.tsx +56 -8
  47. package/app/components/garden/patterns.tsx +497 -0
  48. package/app/garden/[...slug]/page.tsx +3 -2
  49. package/app/garden/en/page.tsx +7 -7
  50. package/app/garden/page.tsx +3 -2
  51. package/app/globals.css +49 -1
  52. package/app/lib/a2-meta.ts +26 -3
  53. package/app/lib/content.ts +2 -2
  54. package/app/lib/garden-docs-meta.ts +24 -0
  55. package/app/lib/github-id.mjs +8 -0
  56. package/app/lib/handbook-switch.mjs +12 -0
  57. package/app/lib/i18n.ts +401 -2
  58. package/app/lib/rehype-field-tables.mjs +47 -0
  59. package/app/lib/remark-a2.mjs +18 -8
  60. package/app/lib/remark-link-card.mjs +22 -0
  61. package/app/lib/remark-ref-demos.mjs +42 -0
  62. package/app/lib/tag-filter.ts +9 -2
  63. package/app/not-found.tsx +32 -4
  64. package/app/page.tsx +17 -14
  65. package/app/rss.xml/route.ts +2 -1
  66. package/app/styles/a2.css +338 -0
  67. package/app/styles/shell.css +15 -2
  68. package/app/tags/page.tsx +7 -5
  69. package/app/thoughts/[slug]/page.tsx +2 -1
  70. package/app/thoughts/page/[n]/page.tsx +1 -1
  71. package/app/thoughts/page.tsx +2 -1
  72. package/app/ui-parity/page.tsx +38 -22
  73. package/bin/garden.mjs +129 -19
  74. package/docs/handbook/ai-writing.md +2 -2
  75. package/docs/handbook/assets/reading-layout-en.svg +26 -0
  76. package/docs/handbook/components/card.md +43 -22
  77. package/docs/handbook/components/chart.md +10 -26
  78. package/docs/handbook/components/choice.md +42 -0
  79. package/docs/handbook/components/compare.md +32 -17
  80. package/docs/handbook/components/figure.md +33 -46
  81. package/docs/handbook/components/file.md +10 -15
  82. package/docs/handbook/components/html.md +10 -30
  83. package/docs/handbook/components/interactive.md +9 -35
  84. package/docs/handbook/components/list.md +65 -0
  85. package/docs/handbook/components/relations.md +95 -0
  86. package/docs/handbook/components/stat.md +10 -28
  87. package/docs/handbook/components/timeline.md +13 -28
  88. package/docs/handbook/components.md +210 -19
  89. package/docs/handbook/config.md +5 -3
  90. package/docs/handbook/deploy.md +1 -1
  91. package/docs/handbook/design.md +20 -12
  92. package/docs/handbook/en/ai-writing.md +2 -2
  93. package/docs/handbook/en/components/card.md +42 -21
  94. package/docs/handbook/en/components/chart.md +10 -26
  95. package/docs/handbook/en/components/choice.md +42 -0
  96. package/docs/handbook/en/components/compare.md +30 -15
  97. package/docs/handbook/en/components/figure.md +29 -42
  98. package/docs/handbook/en/components/file.md +9 -14
  99. package/docs/handbook/en/components/html.md +9 -29
  100. package/docs/handbook/en/components/interactive.md +8 -34
  101. package/docs/handbook/en/components/list.md +65 -0
  102. package/docs/handbook/en/components/relations.md +95 -0
  103. package/docs/handbook/en/components/stat.md +9 -27
  104. package/docs/handbook/en/components/timeline.md +9 -24
  105. package/docs/handbook/en/components.md +211 -19
  106. package/docs/handbook/en/config.md +32 -30
  107. package/docs/handbook/en/deploy.md +12 -12
  108. package/docs/handbook/en/design.md +19 -12
  109. package/docs/handbook/en/index.md +2 -2
  110. package/docs/handbook/en/reference/card.md +85 -0
  111. package/docs/handbook/en/reference/chart.md +68 -0
  112. package/docs/handbook/en/reference/choice.md +69 -0
  113. package/docs/handbook/en/reference/compare.md +59 -0
  114. package/docs/handbook/en/reference/figure.md +84 -0
  115. package/docs/handbook/en/reference/file/checklist.md +17 -0
  116. package/docs/handbook/en/reference/file.md +50 -0
  117. package/docs/handbook/en/reference/html.md +62 -0
  118. package/docs/handbook/en/reference/interactive.md +71 -0
  119. package/docs/handbook/en/reference/list.md +72 -0
  120. package/docs/handbook/en/reference/relations.md +113 -0
  121. package/docs/handbook/en/reference/stat.md +60 -0
  122. package/docs/handbook/en/reference/timeline.md +56 -0
  123. package/docs/handbook/en/reference.md +26 -0
  124. package/docs/handbook/en/start.md +2 -2
  125. package/docs/handbook/en/workflow.md +6 -5
  126. package/docs/handbook/en/writing.md +3 -2
  127. package/docs/handbook/index.md +1 -1
  128. package/docs/handbook/reference/card.md +85 -0
  129. package/docs/handbook/reference/chart.md +68 -0
  130. package/docs/handbook/reference/choice.md +69 -0
  131. package/docs/handbook/reference/compare.md +59 -0
  132. package/docs/handbook/reference/figure.md +84 -0
  133. package/docs/handbook/reference/file/checklist.md +17 -0
  134. package/docs/handbook/reference/file.md +50 -0
  135. package/docs/handbook/reference/html.md +62 -0
  136. package/docs/handbook/reference/interactive.md +71 -0
  137. package/docs/handbook/reference/list.md +72 -0
  138. package/docs/handbook/reference/relations.md +113 -0
  139. package/docs/handbook/reference/stat.md +60 -0
  140. package/docs/handbook/reference/timeline.md +56 -0
  141. package/docs/handbook/reference.md +26 -0
  142. package/docs/handbook/start.md +1 -1
  143. package/docs/handbook/workflow.md +6 -5
  144. package/docs/handbook/writing.md +3 -2
  145. package/next.config.ts +6 -0
  146. package/package.json +2 -1
  147. package/public/images/garden/ref-card-book.svg +1 -0
  148. package/public/images/garden/ref-card-lamp.svg +1 -0
  149. package/scripts/build-content.mjs +37 -16
  150. package/scripts/favicon-ico.mjs +9 -6
  151. package/scripts/file-preview.mjs +10 -7
  152. package/scripts/handbook-allowlist.mjs +18 -0
  153. package/scripts/handbook-facts.mjs +305 -11
  154. package/scripts/link-assets.mjs +7 -0
  155. package/scripts/new-post.mjs +18 -9
  156. package/scripts/prepare-pages-output.mjs +30 -18
  157. package/scripts/redirects.mjs +17 -6
  158. package/templates/garden-blocks.md +67 -0
  159. package/templates/garden-snippets.plist +61 -0
  160. package/templates/site/Makefile.en +45 -0
  161. package/templates/site/README.en.md +15 -0
  162. package/templates/site/site.config.en.yaml +55 -0
  163. package/templates/skills-en/garden-interactive-chart/SKILL.md +84 -0
  164. package/templates/skills-en/garden-interactive-chart/references/shell.html +49 -0
  165. package/templates/skills-en/garden-interactive-chart/references/style.md +61 -0
  166. package/templates/skills-en/garden-post-review/SKILL.md +63 -0
  167. package/templates/skills-en/garden-ui/SKILL.md +118 -0
  168. package/templates/workflows/pages.en.yml +195 -0
  169. package/templates/workflows/pages.yml +195 -0
  170. package/ui/garden/catalog.mjs +205 -4
  171. package/ui/garden/check.mjs +6 -0
  172. package/ui/garden/file-block.mjs +8 -7
  173. package/ui/garden/html-lang.mjs +73 -1
  174. package/ui/garden/markdown-links.mjs +7 -4
  175. package/ui/garden/parse.mjs +45 -0
  176. package/ui/garden/relations.mjs +252 -0
  177. package/ui/garden/search-index.mjs +6 -4
  178. package/ui/garden/source-icon.mjs +54 -0
  179. package/ui/parity-fixtures.mjs +77 -0
  180. package/ui/preview/dist/garden-ui.esm.js +41 -4
  181. package/ui/preview/dist/garden-ui.iife.js +41 -4
  182. package/ui/prompt/rules.md +28 -1
  183. package/ui/react/components/chart.tsx +1 -1
  184. package/ui/react/components/display.tsx +1 -1
  185. package/ui/react/lang.tsx +4 -0
  186. package/ui/react/parity.tsx +8 -6
  187. package/worker/index.ts +8 -2
@@ -0,0 +1,61 @@
1
+ # Garden chart visuals
2
+
3
+ Follow the tokens in the site's `app/globals.css`, not the dark green glow of whatever article you are referencing.
4
+
5
+ ## Tokens
6
+
7
+ ```css
8
+ :root {
9
+ --paper: #f8f8f6;
10
+ --paper-deep: #eeeeeb;
11
+ --card: #ffffff;
12
+ --ink: #202124;
13
+ --ink-soft: #34383d;
14
+ --muted: #65707d;
15
+ --faint: #a1a6ad;
16
+ --line: #deded9;
17
+ --line-strong: #c9c9c2;
18
+ --accent: #d82d48;
19
+ --accent-soft: #f9e8eb;
20
+ --new: #1f7a4d;
21
+ --new-soft: #e5f4ec;
22
+ }
23
+
24
+ :root[data-theme="dark"] {
25
+ --paper: #171819;
26
+ --paper-deep: #202224;
27
+ --card: #1e2022;
28
+ --ink: #ededeb;
29
+ --ink-soft: #d8d8d4;
30
+ --muted: #a6adb6;
31
+ --faint: #747b84;
32
+ --line: #35383b;
33
+ --line-strong: #4b4f54;
34
+ --accent: #ff5d76;
35
+ --accent-soft: #3b242a;
36
+ --new: #5ee0a0;
37
+ --new-soft: #1a3a2c;
38
+ }
39
+ ```
40
+
41
+ Use the accent on one or two focal points at most. Newly computed uses `--new`, waste or repetition uses `--accent`, cached uses `--muted`.
42
+
43
+ ## Type and density
44
+
45
+ ```css
46
+ font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
47
+ font-size: 13px;
48
+ line-height: 1.5;
49
+ ```
50
+
51
+ Do not add Google Fonts. The text column is about 700px wide; draw for 640–680. Use multiples of 4 for control heights and spacing. No shadows, no large radii (4px at most on cells).
52
+
53
+ ## Semantic colors
54
+
55
+ | Marker | Use |
56
+ | --- | --- |
57
+ | `new` / `append` | Cells newly computed at this beat |
58
+ | `waste` | Past tokens that are recomputed |
59
+ | `cached` | Read from the cache, not projected again |
60
+ | `idle` | Placeholder, visually gone |
61
+ | `repeat` / `unique` | Repeated vs unique in an inspect grid |
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: garden-post-review
3
+ description: >-
4
+ Reviews one specified Garden post in three steps: fix script-reported
5
+ structure errors, fix widgets with the chart skill, then suggest prose
6
+ improvements without editing. Use when the user asks to review or
7
+ proofread a Garden blog post.
8
+ ---
9
+
10
+ # Review a Garden post
11
+
12
+ Review only the post the user named. Do the three steps in order; each step sees the post as the previous step left it.
13
+
14
+ Before you start, run `npx garden ui scan content/posts/path.md`. If any block has `status` `uncompiled`, `stale` or `error`, handle it with [garden-ui](../garden-ui/SKILL.md) first, then go to step 1.
15
+
16
+ Hard check command (from the site root):
17
+
18
+ ```bash
19
+ npx garden check content/posts/path.md
20
+ ```
21
+
22
+ stdout is JSON. It passes only when `ok` is true. Do not re-check titles, dates or the reference list yourself.
23
+
24
+ ## 1. The script reports errors; fix only those
25
+
26
+ Run `garden check`. If there are `errors`, change only those entries, then run the same command again until `ok` is true.
27
+
28
+ - Touch only the places the errors point to.
29
+ - Do not fix structural issues the script did not report.
30
+ - `references` is recommended further reading. It does not have to match the links in the body one to one. Do not add an entry because the body has a link, and do not remove one because the body never mentions it.
31
+ - `reference-heading`: move the flagged reference-list heading into `references` and delete the heading.
32
+ - If a widget file is missing, only fix a mistyped `src`. Do not build a new chart in this step.
33
+
34
+ ## 2. Review and fix charts with the chart skill
35
+
36
+ Do this step only when the post has a `widget`. Read [garden-interactive-chart](../garden-interactive-chart/SKILL.md) first.
37
+
38
+ You may change the HTML in the same-name directory and the `caption` of its fence. Leave the surrounding prose alone. `caption` must be a one-sentence claim.
39
+
40
+ Run `garden check` again afterwards, until it passes.
41
+
42
+ Skip this step when there is no `widget`.
43
+
44
+ ## 3. Read the whole post; suggestions only
45
+
46
+ `garden check` passes and the charts are fixed. Read the whole post. Do not change any file.
47
+
48
+ There is one goal: does the post start from first principles, stay consistent, and close the loop on one point? After reading, the reader should be able to say what this is, why it has to be this way, and how that "why" lands in a piece of practice. Practice means code, an incident, a measurement, or a derivation put to work. It verifies the same chain; it is not a separate tutorial.
49
+
50
+ First principles means reasoning up from facts, constraints or invariants that cannot be broken down further, not from "everyone does it this way" or a metaphor. After taking it apart, put it back together: listing the holes in existing approaches without reaching a conclusion is not finished. Analogies and industry practice may appear, but only as a foil for the derivation, never as a substitute for it.
51
+
52
+ Write a suggestion only when:
53
+
54
+ - The opening stands on a term, a convention or a metaphor, and the reader cannot see which fact constrains the problem
55
+ - A later mechanism, term or conclusion cannot be derived from the earlier facts, or the same word means different things in different places
56
+ - A point stops at "what it is": there is no why, or the why never lands in practice that can be checked
57
+ - The practice and the principles talk past each other and are not the same chain
58
+ - The post spreads across several neighbouring topics without bringing one of them home. One point explained fully beats many points touched lightly
59
+ - Numbers, comparisons or "usually" claims do not grow out of the derivation, the practice or a source
60
+
61
+ Each suggestion gives: the heading it is under, where the loop breaks, and how it could be closed. If there are none, say so. Do not turn the post into a problem–solution template, and do not ask for a "does not apply" note on every paragraph. If a translation already restates the original's derivation, do not ask the author to invent exercises.
62
+
63
+ This step must not change the post, the charts or the front matter.
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: garden-ui
3
+ description: >-
4
+ Compiles the `garden` idea blocks in one Garden post into garden components
5
+ (garden interactive, or card / stat / compare / ... , or prose, or garden
6
+ html), fixes broken garden interactive 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.
10
+ ---
11
+
12
+ # Compile Garden interactive blocks
13
+
14
+ The author writes a one-sentence idea in an unnamed `garden` fence. This skill turns it into `garden interactive` or another `garden` component. The interactive components and design rules are in the output of `npx garden ui prompt`. The purpose and examples of the 12 `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.
15
+
16
+ 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
+
18
+ Run every command from the site root. If no post path was given, ask only for the path; do not scan the whole repo.
19
+
20
+ ## Triggers and scope
21
+
22
+ | The user says | Blocks to handle |
23
+ | --- | --- |
24
+ | Compile this post: `<path>`, compile ideas | Blocks with status `uncompiled`, `stale` or `error` |
25
+ | Recompile `<id>` | Only this block, whatever its status |
26
+ | Pastes a `garden ui check` fix prompt | The blocks listed in the prompt |
27
+ | Where could this post use interaction | Read-only suggestion mode, see the end; do not edit the post |
28
+
29
+ ## Workflow
30
+
31
+ 1. Scan:
32
+
33
+ ```bash
34
+ npx garden ui scan content/posts/path.md
35
+ ```
36
+
37
+ stdout is JSON. `lang` is `garden-idea` (an idea), `garden-interactive`, or the older `intent` / `ui`. Each block has `lang`, `id`, `status`, `startLine`, `hash` (hash of the whole fence, used as `--base` on write-back), `intentHash` (hash of the author's words), `intent` (the author's words), `caption`, `section` (the heading it sits under) and `diagnostics`. Do not find fences yourself with regular expressions.
38
+
39
+ 2. Read the rules:
40
+
41
+ ```bash
42
+ npx garden ui prompt
43
+ ```
44
+
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.
46
+
47
+ 3. For each block, read the author's words and its section, then pick one:
48
+ - **Write it back as prose**: when a sentence, a table or a Mermaid diagram says it clearly.
49
+ - **Write `garden interactive`**: when the reader needs to drag, click or step through it to understand.
50
+ - **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`), a labelled structure diagram (`figure`), 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
+ - **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`.
53
+
54
+ 4. When writing `garden interactive`:
55
+ - Format: the fence header is `garden interactive #<id>`. Above `---` go `caption`, `intent` and an optional `expect`; below `---` goes the component tree from `npx garden ui prompt`.
56
+ - Put the author's words into `intent` verbatim; do not change a single character.
57
+ - `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.
58
+ - Known points in the words ("512 MiB at batch 1") go into `expect`.
59
+ - Data lives in the block. Use `Data` references only when the words mention a data file that already exists in the same-name directory.
60
+ - 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.
61
+
62
+ 5. Write each block back with `apply`, passing the new content on standard input:
63
+
64
+ ````bash
65
+ npx garden ui apply content/posts/path.md --id <id> --base <hash> <<'GARDEN_UI'
66
+ ```garden interactive #id
67
+ caption: ...
68
+ intent: |
69
+ ...
70
+ ---
71
+ ...
72
+ ```
73
+ GARDEN_UI
74
+ ````
75
+
76
+ - Idea blocks have no id; locate them with `--intent-hash <intentHash>`, or `--at <startLine>` when the words repeat.
77
+ - 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.
78
+ - The new content must not contain any uncompiled ideas.
79
+ - On success stdout is `{"ok":true,...}`. On failure stderr is JSON; act on `code`:
80
+ - `apply-invalid`: the new content failed validation. Fix it from `diagnostics` and write again.
81
+ - `apply-base`: the block changed after the scan. Run `scan` again and use the new `hash`.
82
+ - `apply-not-found` / `apply-ambiguous`: the block was not found or not unique. Run `scan` again and locate it another way.
83
+ - Line numbers shift after each write-back, so run `scan` again before the next block.
84
+
85
+ 6. When everything is written:
86
+
87
+ ```bash
88
+ npx garden ui check content/posts/path.md
89
+ npx garden check content/posts/path.md
90
+ ```
91
+
92
+ 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.
93
+
94
+ 7. Reply with a table:
95
+
96
+ | id | choice | recipe | assumptions | expect |
97
+ | --- | --- | --- | --- | --- |
98
+ | kv-memory | garden interactive | param bench | default sequence is 4K | 512 MiB at batch 1 |
99
+
100
+ "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.
101
+
102
+ ## Don't
103
+
104
+ - Edit the post file directly. The post changes only through `apply`; HTML files referenced by `garden html` may be created directly.
105
+ - 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.
106
+ - Use remote URLs (images, scripts, data).
107
+ - Change prose outside the target blocks.
108
+
109
+ ## Suggestion mode: where could this post use interaction
110
+
111
+ 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:
112
+
113
+ - A formula or numbers that change with parameters (param bench, converter)
114
+ - A multi-step process (stepper, flow)
115
+ - The difference between two approaches (comparison)
116
+ - A set of cells, bytes or a memory layout (grid)
117
+
118
+ 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.
@@ -0,0 +1,195 @@
1
+ name: Verify and publish blog
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: publish-blog
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ build:
17
+ if: github.event.repository.name == format('{0}.github.io', github.repository_owner) || vars.SITE_URL != '' || vars.PAGES_REPOSITORY != ''
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+
22
+ - uses: actions/setup-node@v7
23
+ with:
24
+ node-version: 22
25
+ cache: npm
26
+
27
+ - run: npm ci
28
+
29
+ - name: Resolve the public URL
30
+ env:
31
+ SITE_URL_OVERRIDE: ${{ vars.SITE_URL }}
32
+ ADMIN_URL_OVERRIDE: ${{ vars.ADMIN_URL }}
33
+ GISCUS_REPO: ${{ vars.GISCUS_REPO }}
34
+ GISCUS_REPO_ID: ${{ vars.GISCUS_REPO_ID }}
35
+ GISCUS_CATEGORY: ${{ vars.GISCUS_CATEGORY }}
36
+ GISCUS_CATEGORY_ID: ${{ vars.GISCUS_CATEGORY_ID }}
37
+ GISCUS_DISABLED: ${{ vars.GISCUS_DISABLED }}
38
+ WEBMENTION_IO_DOMAIN: ${{ vars.WEBMENTION_IO_DOMAIN }}
39
+ ANALYTICS_DOMAIN: ${{ vars.ANALYTICS_DOMAIN }}
40
+ ANALYTICS_SCRIPT: ${{ vars.ANALYTICS_SCRIPT }}
41
+ run: |
42
+ owner="$GITHUB_REPOSITORY_OWNER"
43
+ default_url="https://$owner.github.io"
44
+ giscus_ready=1
45
+ if [ -z "$GISCUS_REPO" ] || [ "$GISCUS_REPO" = "owner/repository" ]; then giscus_ready=0; fi
46
+ if [ -z "$GISCUS_REPO_ID" ] || [ "$GISCUS_REPO_ID" = "R_xxxxxxxxxx" ]; then giscus_ready=0; fi
47
+ if [ -z "$GISCUS_CATEGORY" ]; then giscus_ready=0; fi
48
+ if [ -z "$GISCUS_CATEGORY_ID" ] || [ "$GISCUS_CATEGORY_ID" = "DIC_xxxxxxxxxx" ]; then giscus_ready=0; fi
49
+ if [ "$giscus_ready" != 1 ]; then
50
+ if [ "$GISCUS_DISABLED" = "1" ]; then
51
+ GISCUS_REPO=
52
+ GISCUS_REPO_ID=
53
+ GISCUS_CATEGORY=
54
+ GISCUS_CATEGORY_ID=
55
+ echo "GISCUS_DISABLED=1; building without comments."
56
+ else
57
+ echo "Comments are not configured yet; stopping the build."
58
+ echo "1. Prepare a public GitHub repository and enable Discussions."
59
+ echo "2. Open https://giscus.app, install the Giscus App, and pick the repository and category."
60
+ echo "3. Under Settings -> Secrets and variables -> Actions -> Variables, set:"
61
+ echo " GISCUS_REPO, GISCUS_REPO_ID, GISCUS_CATEGORY, GISCUS_CATEGORY_ID"
62
+ echo "To build without comments, set GISCUS_DISABLED to 1. Empty values and the placeholder owner/repository will not pass."
63
+ exit 1
64
+ fi
65
+ fi
66
+ {
67
+ echo "SITE_URL=${SITE_URL_OVERRIDE:-$default_url}"
68
+ echo "NEXT_PUBLIC_SITE_URL=${SITE_URL_OVERRIDE:-$default_url}"
69
+ echo "ADMIN_URL=$ADMIN_URL_OVERRIDE"
70
+ echo "GISCUS_REPO=$GISCUS_REPO"
71
+ echo "GISCUS_REPO_ID=$GISCUS_REPO_ID"
72
+ echo "GISCUS_CATEGORY=$GISCUS_CATEGORY"
73
+ echo "GISCUS_CATEGORY_ID=$GISCUS_CATEGORY_ID"
74
+ echo "NEXT_PUBLIC_GISCUS_REPO=$GISCUS_REPO"
75
+ echo "NEXT_PUBLIC_GISCUS_REPO_ID=$GISCUS_REPO_ID"
76
+ echo "NEXT_PUBLIC_GISCUS_CATEGORY=$GISCUS_CATEGORY"
77
+ echo "NEXT_PUBLIC_GISCUS_CATEGORY_ID=$GISCUS_CATEGORY_ID"
78
+ echo "WEBMENTION_IO_DOMAIN=$WEBMENTION_IO_DOMAIN"
79
+ echo "NEXT_PUBLIC_ANALYTICS_DOMAIN=$ANALYTICS_DOMAIN"
80
+ echo "NEXT_PUBLIC_ANALYTICS_SCRIPT=$ANALYTICS_SCRIPT"
81
+ echo "STATIC_EXPORT=1"
82
+ } >> "$GITHUB_ENV"
83
+
84
+ - name: Build the public site
85
+ env:
86
+ GITHUB_TOKEN: ${{ github.token }}
87
+ run: npm run build
88
+
89
+ - name: Validate the static output
90
+ run: npm run pages:validate
91
+
92
+ - name: Configure GitHub Pages
93
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
94
+ uses: actions/configure-pages@v6
95
+
96
+ - name: Upload the GitHub Pages artifact
97
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
98
+ uses: actions/upload-pages-artifact@v5
99
+ with:
100
+ path: dist/client
101
+
102
+ - name: Upload the public-repository artifact
103
+ if: ${{ vars.PAGES_REPOSITORY != '' }}
104
+ uses: actions/upload-artifact@v7
105
+ with:
106
+ name: static-site
107
+ path: dist/client
108
+ include-hidden-files: true
109
+ if-no-files-found: error
110
+ retention-days: 1
111
+
112
+ deploy-pages:
113
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
114
+ needs: build
115
+ permissions:
116
+ pages: write
117
+ id-token: write
118
+ environment:
119
+ name: github-pages
120
+ url: ${{ steps.deployment.outputs.page_url }}
121
+ runs-on: ubuntu-latest
122
+ steps:
123
+ - name: Deploy the GitHub Pages artifact
124
+ id: deployment
125
+ uses: actions/deploy-pages@v5
126
+
127
+ publish-repository:
128
+ if: ${{ vars.PAGES_REPOSITORY != '' }}
129
+ needs: build
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v7
133
+
134
+ - uses: actions/setup-node@v7
135
+ with:
136
+ node-version: 22
137
+ cache: npm
138
+
139
+ - run: npm ci
140
+
141
+ - name: Check the split-repository configuration
142
+ env:
143
+ PAGES_DEPLOY_KEY: ${{ secrets.PAGES_DEPLOY_KEY }}
144
+ TARGET_REPOSITORY: ${{ vars.PAGES_REPOSITORY }}
145
+ run: |
146
+ test -n "$PAGES_DEPLOY_KEY" || { echo "Missing PAGES_DEPLOY_KEY secret"; exit 1; }
147
+ [[ "$TARGET_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || {
148
+ echo "Invalid PAGES_REPOSITORY format"
149
+ exit 1
150
+ }
151
+
152
+ - uses: actions/download-artifact@v8
153
+ with:
154
+ name: static-site
155
+ path: static-site
156
+
157
+ - name: Check out the public output repository
158
+ uses: actions/checkout@v7
159
+ with:
160
+ repository: ${{ vars.PAGES_REPOSITORY }}
161
+ ssh-key: ${{ secrets.PAGES_DEPLOY_KEY }}
162
+ path: pages-repository
163
+ fetch-depth: 1
164
+
165
+ - name: Start a clean rolling deployment
166
+ working-directory: pages-repository
167
+ env:
168
+ TARGET_REPOSITORY: ${{ vars.PAGES_REPOSITORY }}
169
+ run: |
170
+ expected_remote="git@github.com:$TARGET_REPOSITORY.git"
171
+ actual_remote="$(git remote get-url origin)"
172
+ test "$actual_remote" = "$expected_remote" || {
173
+ echo "Refusing to publish to unexpected repository: $actual_remote"
174
+ exit 1
175
+ }
176
+ echo "PAGES_PREVIOUS_TREE=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_ENV"
177
+ git checkout --orphan pages-deploy
178
+ git rm -q -r -f .
179
+
180
+ - name: Prepare generated-only repository contents
181
+ run: npx garden pages:prepare static-site pages-repository
182
+
183
+ - name: Publish the rolling deployment commit
184
+ working-directory: pages-repository
185
+ run: |
186
+ git config user.name "github-actions[bot]"
187
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
188
+ git add -A
189
+ new_tree="$(git write-tree)"
190
+ if [ "$new_tree" = "$PAGES_PREVIOUS_TREE" ]; then
191
+ echo "Static output is unchanged; skipping publish"
192
+ exit 0
193
+ fi
194
+ git commit -m "Deploy ${GITHUB_SHA}"
195
+ git push --force origin HEAD:main
@@ -0,0 +1,195 @@
1
+ name: Verify and publish blog
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: publish-blog
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ build:
17
+ if: github.event.repository.name == format('{0}.github.io', github.repository_owner) || vars.SITE_URL != '' || vars.PAGES_REPOSITORY != ''
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+
22
+ - uses: actions/setup-node@v7
23
+ with:
24
+ node-version: 22
25
+ cache: npm
26
+
27
+ - run: npm ci
28
+
29
+ - name: Resolve the public URL
30
+ env:
31
+ SITE_URL_OVERRIDE: ${{ vars.SITE_URL }}
32
+ ADMIN_URL_OVERRIDE: ${{ vars.ADMIN_URL }}
33
+ GISCUS_REPO: ${{ vars.GISCUS_REPO }}
34
+ GISCUS_REPO_ID: ${{ vars.GISCUS_REPO_ID }}
35
+ GISCUS_CATEGORY: ${{ vars.GISCUS_CATEGORY }}
36
+ GISCUS_CATEGORY_ID: ${{ vars.GISCUS_CATEGORY_ID }}
37
+ GISCUS_DISABLED: ${{ vars.GISCUS_DISABLED }}
38
+ WEBMENTION_IO_DOMAIN: ${{ vars.WEBMENTION_IO_DOMAIN }}
39
+ ANALYTICS_DOMAIN: ${{ vars.ANALYTICS_DOMAIN }}
40
+ ANALYTICS_SCRIPT: ${{ vars.ANALYTICS_SCRIPT }}
41
+ run: |
42
+ owner="$GITHUB_REPOSITORY_OWNER"
43
+ default_url="https://$owner.github.io"
44
+ giscus_ready=1
45
+ if [ -z "$GISCUS_REPO" ] || [ "$GISCUS_REPO" = "owner/repository" ]; then giscus_ready=0; fi
46
+ if [ -z "$GISCUS_REPO_ID" ] || [ "$GISCUS_REPO_ID" = "R_xxxxxxxxxx" ]; then giscus_ready=0; fi
47
+ if [ -z "$GISCUS_CATEGORY" ]; then giscus_ready=0; fi
48
+ if [ -z "$GISCUS_CATEGORY_ID" ] || [ "$GISCUS_CATEGORY_ID" = "DIC_xxxxxxxxxx" ]; then giscus_ready=0; fi
49
+ if [ "$giscus_ready" != 1 ]; then
50
+ if [ "$GISCUS_DISABLED" = "1" ]; then
51
+ GISCUS_REPO=
52
+ GISCUS_REPO_ID=
53
+ GISCUS_CATEGORY=
54
+ GISCUS_CATEGORY_ID=
55
+ echo "GISCUS_DISABLED=1,本次构建不开放评论。"
56
+ else
57
+ echo "评论还没有配置,构建已停止。"
58
+ echo "1. 准备一个公开 GitHub 仓库,并开启 Discussions。"
59
+ echo "2. 打开 https://giscus.app/zh-CN ,安装 Giscus App,选中该仓库和分类。"
60
+ echo "3. 在 Settings → Secrets and variables → Actions → Variables 写入:"
61
+ echo " GISCUS_REPO、GISCUS_REPO_ID、GISCUS_CATEGORY、GISCUS_CATEGORY_ID"
62
+ echo "确定不开放评论时,把 GISCUS_DISABLED 设为 1。留空和示例值 owner/repository 都不会通过。"
63
+ exit 1
64
+ fi
65
+ fi
66
+ {
67
+ echo "SITE_URL=${SITE_URL_OVERRIDE:-$default_url}"
68
+ echo "NEXT_PUBLIC_SITE_URL=${SITE_URL_OVERRIDE:-$default_url}"
69
+ echo "ADMIN_URL=$ADMIN_URL_OVERRIDE"
70
+ echo "GISCUS_REPO=$GISCUS_REPO"
71
+ echo "GISCUS_REPO_ID=$GISCUS_REPO_ID"
72
+ echo "GISCUS_CATEGORY=$GISCUS_CATEGORY"
73
+ echo "GISCUS_CATEGORY_ID=$GISCUS_CATEGORY_ID"
74
+ echo "NEXT_PUBLIC_GISCUS_REPO=$GISCUS_REPO"
75
+ echo "NEXT_PUBLIC_GISCUS_REPO_ID=$GISCUS_REPO_ID"
76
+ echo "NEXT_PUBLIC_GISCUS_CATEGORY=$GISCUS_CATEGORY"
77
+ echo "NEXT_PUBLIC_GISCUS_CATEGORY_ID=$GISCUS_CATEGORY_ID"
78
+ echo "WEBMENTION_IO_DOMAIN=$WEBMENTION_IO_DOMAIN"
79
+ echo "NEXT_PUBLIC_ANALYTICS_DOMAIN=$ANALYTICS_DOMAIN"
80
+ echo "NEXT_PUBLIC_ANALYTICS_SCRIPT=$ANALYTICS_SCRIPT"
81
+ echo "STATIC_EXPORT=1"
82
+ } >> "$GITHUB_ENV"
83
+
84
+ - name: Build the public site
85
+ env:
86
+ GITHUB_TOKEN: ${{ github.token }}
87
+ run: npm run build
88
+
89
+ - name: Validate the static output
90
+ run: npm run pages:validate
91
+
92
+ - name: Configure GitHub Pages
93
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
94
+ uses: actions/configure-pages@v6
95
+
96
+ - name: Upload the GitHub Pages artifact
97
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
98
+ uses: actions/upload-pages-artifact@v5
99
+ with:
100
+ path: dist/client
101
+
102
+ - name: Upload the public-repository artifact
103
+ if: ${{ vars.PAGES_REPOSITORY != '' }}
104
+ uses: actions/upload-artifact@v7
105
+ with:
106
+ name: static-site
107
+ path: dist/client
108
+ include-hidden-files: true
109
+ if-no-files-found: error
110
+ retention-days: 1
111
+
112
+ deploy-pages:
113
+ if: ${{ vars.PAGES_REPOSITORY == '' }}
114
+ needs: build
115
+ permissions:
116
+ pages: write
117
+ id-token: write
118
+ environment:
119
+ name: github-pages
120
+ url: ${{ steps.deployment.outputs.page_url }}
121
+ runs-on: ubuntu-latest
122
+ steps:
123
+ - name: Deploy the GitHub Pages artifact
124
+ id: deployment
125
+ uses: actions/deploy-pages@v5
126
+
127
+ publish-repository:
128
+ if: ${{ vars.PAGES_REPOSITORY != '' }}
129
+ needs: build
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v7
133
+
134
+ - uses: actions/setup-node@v7
135
+ with:
136
+ node-version: 22
137
+ cache: npm
138
+
139
+ - run: npm ci
140
+
141
+ - name: Check the split-repository configuration
142
+ env:
143
+ PAGES_DEPLOY_KEY: ${{ secrets.PAGES_DEPLOY_KEY }}
144
+ TARGET_REPOSITORY: ${{ vars.PAGES_REPOSITORY }}
145
+ run: |
146
+ test -n "$PAGES_DEPLOY_KEY" || { echo "缺少 PAGES_DEPLOY_KEY Secret"; exit 1; }
147
+ [[ "$TARGET_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || {
148
+ echo "PAGES_REPOSITORY 格式无效"
149
+ exit 1
150
+ }
151
+
152
+ - uses: actions/download-artifact@v8
153
+ with:
154
+ name: static-site
155
+ path: static-site
156
+
157
+ - name: Check out the public output repository
158
+ uses: actions/checkout@v7
159
+ with:
160
+ repository: ${{ vars.PAGES_REPOSITORY }}
161
+ ssh-key: ${{ secrets.PAGES_DEPLOY_KEY }}
162
+ path: pages-repository
163
+ fetch-depth: 1
164
+
165
+ - name: Start a clean rolling deployment
166
+ working-directory: pages-repository
167
+ env:
168
+ TARGET_REPOSITORY: ${{ vars.PAGES_REPOSITORY }}
169
+ run: |
170
+ expected_remote="git@github.com:$TARGET_REPOSITORY.git"
171
+ actual_remote="$(git remote get-url origin)"
172
+ test "$actual_remote" = "$expected_remote" || {
173
+ echo "拒绝发布到非目标仓库:$actual_remote"
174
+ exit 1
175
+ }
176
+ echo "PAGES_PREVIOUS_TREE=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_ENV"
177
+ git checkout --orphan pages-deploy
178
+ git rm -q -r -f .
179
+
180
+ - name: Prepare generated-only repository contents
181
+ run: npx garden pages:prepare static-site pages-repository
182
+
183
+ - name: Publish the rolling deployment commit
184
+ working-directory: pages-repository
185
+ run: |
186
+ git config user.name "github-actions[bot]"
187
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
188
+ git add -A
189
+ new_tree="$(git write-tree)"
190
+ if [ "$new_tree" = "$PAGES_PREVIOUS_TREE" ]; then
191
+ echo "静态产物没有变化,无需发布"
192
+ exit 0
193
+ fi
194
+ git commit -m "Deploy ${GITHUB_SHA}"
195
+ git push --force origin HEAD:main