hd-garden 0.10.3 → 0.10.4
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.
- package/README.en.md +43 -5
- package/README.md +43 -5
- package/bin/garden.mjs +150 -112
- package/docs/handbook/ai-writing.md +30 -4
- package/docs/handbook/en/ai-writing.md +24 -4
- package/docs/handbook/en/index.md +1 -0
- package/docs/handbook/en/start.md +13 -0
- package/docs/handbook/en/workflow.md +39 -1
- package/docs/handbook/index.md +1 -0
- package/docs/handbook/start.md +13 -0
- package/docs/handbook/workflow.md +39 -1
- package/editors/dist/typora/main.js +384 -0
- package/editors/dist/typora/manifest.json +12 -0
- package/editors/dist/typora/style.css +1215 -0
- package/editors/dist/vscode/garden-ui-0.1.0.vsix +0 -0
- package/eslint.config.mjs +1 -0
- package/package.json +4 -2
- package/scripts/editors-kit.mjs +352 -0
- package/scripts/pack-editors.mjs +241 -0
- package/templates/site/README.en.md +2 -0
- package/templates/site/README.md +2 -0
- package/ui/garden/snippets.mjs +89 -2
package/README.en.md
CHANGED
|
@@ -17,9 +17,46 @@ npm install
|
|
|
17
17
|
make dev
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
The published npm package is `hd-garden`; once installed inside a site, you can run `npx garden` (do not run `npx garden init` directly, as `garden` on npm is an unrelated package). Running `make dev` for the first time creates `.env.local`, installs the three AI skills under `.agents/skills/`, installs the pre-commit check in `.git/hooks/pre-commit`, and starts the dev server at `http://localhost:3000` (with the built-in handbook at `http://localhost:3000/garden/en`).
|
|
20
|
+
The published npm package is `hd-garden`; once installed inside a site, you can run `npx garden` (do not run `npx garden init` directly, as `garden` on npm is an unrelated package). `init` copies the VS Code / Cursor and Typora Intelligent UI plugins into the site and, in an interactive terminal, asks whether to install each one (see the next section). Running `make dev` for the first time creates `.env.local`, installs the three AI skills under `.agents/skills/`, installs the pre-commit check in `.git/hooks/pre-commit`, and starts the dev server at `http://localhost:3000` (with the built-in handbook at `http://localhost:3000/garden/en`).
|
|
21
21
|
|
|
22
|
-
## 2.
|
|
22
|
+
## 2. Write interactives in VS Code, Cursor, and Typora
|
|
23
|
+
|
|
24
|
+
The Intelligent UI plugins ship inside `hd-garden`. You do not compile them in the site. They insert a one-sentence idea, preview the rendered component in the editor, open the component catalog, and hand the post to the `garden-ui` skill to compile.
|
|
25
|
+
|
|
26
|
+
`npx hd-garden init` copies the files to `.garden/editors/` (a `.vsix` for VS Code / Cursor, and the Typora plugin). In an interactive terminal it asks about VS Code / Cursor first, then Typora. The first `npx garden sync` (`make dev` syncs first) asks again if that prompt has not happened yet. `y` installs; pressing Enter skips.
|
|
27
|
+
|
|
28
|
+
With no TTY (a pipe or a CI log), with `CI=1`, or with `GARDEN_SKIP_EDITORS=1`, the command does not wait for input. It prints the install command below. You can run it later, and running it again is safe:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx garden editors install # Ask again about VS Code and Typora
|
|
32
|
+
npx garden editors install --yes # Install both, without asking
|
|
33
|
+
npx garden editors install --vscode # VS Code / Cursor only
|
|
34
|
+
npx garden editors install --typora # Typora only
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### VS Code / Cursor
|
|
38
|
+
|
|
39
|
+
- **Insert an idea**: `Cmd+Alt+I` on macOS, `Ctrl+Alt+I` on Windows and Linux. The Command Palette and the editor context menu have the same Garden command. It inserts a plain `garden` fence: a short brief for the `garden-ui` skill, which compiles it later. In Typora, type `/` and choose `/garden`; the next list gives each component a small preview image.
|
|
40
|
+
- **Preview**: Open the Markdown preview. A `ui` fence renders the same component as the blog. An `intent` fence renders as an idea card. The extension reads sidecar data files next to the post.
|
|
41
|
+
- **Check**: Errors show up in the Problems panel and as squiggles in the source. A CodeLens above each block says whether it is ok, broken, stale, or not compiled yet.
|
|
42
|
+
- **Catalog**: The Garden catalog command inserts a recipe at the cursor when you choose to start from it. The same catalog from the terminal is `npx garden ui catalog` (it writes `.garden/ui-catalog.html`).
|
|
43
|
+
- **Compile**: Copy the compile prompt. On an English site (`locale: en_US`) it is `Compile this post:` plus the path. Paste that to an Agent that has the `garden-ui` skill. The skill runs `npx garden ui scan`, reads `npx garden ui prompt`, and writes the fence back with `npx garden ui apply`. The plugin does not call a model and does not store a key.
|
|
44
|
+
|
|
45
|
+
When `code` or `cursor` is on `PATH`, `garden editors install` runs `--install-extension`. If both commands exist, it installs into both. Without a CLI, open the Extensions view, choose the `...` menu, then **Install from VSIX**, and select the file under `.garden/editors/vscode/`.
|
|
46
|
+
|
|
47
|
+
### Typora
|
|
48
|
+
|
|
49
|
+
Typora has no official plugin API. Install [typora-community-plugin](https://github.com/typora-community-plugin/typora-community-plugin/blob/main/docs/en-us/user-guide/1a-installation.md) first (on macOS it patches `index.html` inside `Typora.app`). `garden editors install` looks in the usual locations. If the framework is missing, it still copies the plugin and prints that prerequisite.
|
|
50
|
+
|
|
51
|
+
The plugin is copied to `~/.typora/community-plugins/plugins/hedon954.garden-intelligent-ui/`. On macOS it is also copied to `~/Library/Application Support/abnerworks.Typora/plugins/plugins/hedon954.garden-intelligent-ui/`, which is the directory the Installed Plugins list reads. Open Typora and enable Garden Intelligent UI under Settings, Installed plugins.
|
|
52
|
+
|
|
53
|
+
Use it the same way as VS Code: leaving a fence previews `ui` / `intent` (as with Mermaid, click into the fence to edit the source; clicks on sliders and buttons stay in the preview). Type `/`, choose `/garden`, or use the insert command (`Ctrl+Alt+I` on Windows and Linux; on macOS, Option rewrites the key, so record a shortcut under Settings, Keyboard Shortcuts). Copy compile prompt hands the post to the `garden-ui` skill.
|
|
54
|
+
|
|
55
|
+
A Typora upgrade overwrites the community framework inside the app. Reinstall typora-community-plugin, then enable this plugin again. The fences stay in the Markdown, and `make dev` still previews them.
|
|
56
|
+
|
|
57
|
+
The Obsidian plugin is not installed by this command.
|
|
58
|
+
|
|
59
|
+
## 3. Describe components in plain language (humans and Agents share the same CLI)
|
|
23
60
|
|
|
24
61
|
Where you want a component or interactive calculation, write an unnamed `garden` fence describing what the reader should adjust and see:
|
|
25
62
|
|
|
@@ -31,7 +68,7 @@ Where you want a component or interactive calculation, write an unnamed `garden`
|
|
|
31
68
|
|
|
32
69
|
Ask your Agent to compile the post using the bundled `garden-ui` skill: it runs `npx garden ui scan` to locate uncompiled or broken blocks, reads the component catalog from `npx garden ui prompt`, generates a `garden interactive` block (or `card`, `stat`, `compare`, `timeline`, `chart`, `figure`, `html`, `file`, `relations`, `list`, `choice`, or a `diff` block right below a code snippet), and writes only that fence back via `npx garden ui apply`. If you run `git commit` before compiling, the pre-commit hook runs `npx --no-install garden ui check --staged --quiet`, stops the commit, and prints a ready-to-paste fix prompt for your AI.
|
|
33
70
|
|
|
34
|
-
##
|
|
71
|
+
## 4. Common commands
|
|
35
72
|
|
|
36
73
|
```bash
|
|
37
74
|
make new SLUG=writing-pace TITLE="Writing Pace" # Create a draft post in content/posts/
|
|
@@ -42,9 +79,10 @@ make update MESSAGE="Add writing-pace post" # Check, commit, and push to ori
|
|
|
42
79
|
npx garden check content/posts/writing-pace.md # Validate front matter, components, and assets
|
|
43
80
|
npx garden ui check --staged # Check staged posts for uncompiled ideas or UI errors
|
|
44
81
|
npx garden ui catalog # Generate an offline component gallery at .garden/ui-catalog.html
|
|
82
|
+
npx garden editors install --yes # Install the Intelligent UI plugins into VS Code / Cursor and Typora
|
|
45
83
|
```
|
|
46
84
|
|
|
47
|
-
##
|
|
85
|
+
## 5. Excalidraw
|
|
48
86
|
|
|
49
87
|
Point a Markdown image at the `.excalidraw` source. `garden build` exports one light flat SVG beside that file, with a transparent background, and embeds that SVG in the article so its text can be selected. No separate export command.
|
|
50
88
|
|
|
@@ -54,7 +92,7 @@ Point a Markdown image at the `.excalidraw` source. `garden build` exports one l
|
|
|
54
92
|
|
|
55
93
|
The first page that references `.excalidraw` downloads the exporter (Chromium is not required; fonts are embedded in the SVG). See [Local Image](docs/handbook/en/components/image.md).
|
|
56
94
|
|
|
57
|
-
##
|
|
95
|
+
## 6. Handbook and reference docs
|
|
58
96
|
|
|
59
97
|
Start `make dev` and open `/garden/en` (Chinese at `/garden`), or read the markdown sources directly:
|
|
60
98
|
|
package/README.md
CHANGED
|
@@ -17,11 +17,48 @@ npm install
|
|
|
17
17
|
make dev
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
公开包名是 `hd-garden`,装进站点后可使用 `npx garden`(不要直接运行未安装的 `npx garden init`,npm 上的 `garden`
|
|
20
|
+
公开包名是 `hd-garden`,装进站点后可使用 `npx garden`(不要直接运行未安装的 `npx garden init`,npm 上的 `garden` 是另一个包)。`init` 会把 VS Code / Cursor 与 Typora 的 Intelligent UI 插件放进站点,并在交互终端里分别询问要不要装上(见下一节)。首次运行 `make dev` 会生成 `.env.local`、补齐 `.agents/skills/` 下的三个 AI skill,并在 `.git/hooks/pre-commit` 安装提交前检查,默认预览地址是 `http://localhost:3000`(使用说明在 `http://localhost:3000/garden`)。
|
|
21
21
|
|
|
22
22
|
如果在本地使用了 npm 镜像,请把 `registry` 留在 `~/.npmrc`,不要让镜像域名写进 `package-lock.json`(提交前可运行 `node node_modules/hd-garden/scripts/check-lockfile-registry.mjs` 检查)。
|
|
23
23
|
|
|
24
|
-
## 2.
|
|
24
|
+
## 2. 在 VS Code、Cursor 和 Typora 里写交互
|
|
25
|
+
|
|
26
|
+
Intelligent UI 插件随 `hd-garden` 一起发布,不用在站点里编译源码。它做四件事:插入一句想法、在编辑器里预览画好的组件、打开组件清单,以及把「编译这篇」交给 `garden-ui` skill。
|
|
27
|
+
|
|
28
|
+
`npx hd-garden init` 把产物复制到站点的 `.garden/editors/`(VS Code 的 `.vsix`,以及 Typora 插件目录)。终端可交互时,会先问要不要装进 VS Code / Cursor,再问要不要装进 Typora。第一次 `npx garden sync`(`make dev` 会先同步)如果还没问过,会再问一次。回答 `y` 才会安装;直接回车是不装。
|
|
29
|
+
|
|
30
|
+
没有 TTY(管道或 CI 日志)、`CI=1`,或设置了 `GARDEN_SKIP_EDITORS=1` 时,命令不会停下来等输入,只打印下面这条安装命令。之后随时可以装,重复执行也安全:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx garden editors install # 再问一次 VS Code 和 Typora
|
|
34
|
+
npx garden editors install --yes # 两个都装,不再询问
|
|
35
|
+
npx garden editors install --vscode # 只装 VS Code / Cursor
|
|
36
|
+
npx garden editors install --typora # 只装 Typora
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### VS Code / Cursor
|
|
40
|
+
|
|
41
|
+
- **插入想法**:macOS 用 `Cmd+Alt+I`,Windows / Linux 用 `Ctrl+Alt+I`,或命令「插入 Garden 块」。插入的是不带组件名的 `garden` 围栏,正文用中文告诉你要写什么,交给 `garden-ui` skill 编译。Typora 里先输入 `/` 选 `/garden`,再从带小预览图的列表里选组件。
|
|
42
|
+
- **预览**:打开 Markdown 预览。`ui` 围栏画成和博客一样的组件,`intent` 围栏显示成想法卡片。同名目录里的数据文件由扩展读取。
|
|
43
|
+
- **检查**:错误进「问题」面板,并在源码上画波浪线。每个块上方的 CodeLens 标出正常、有错、过期或未编译。
|
|
44
|
+
- **组件清单**:命令「查看组件清单」。点「用它开始」把配方插进当前稿件。终端里的同一份清单是 `npx garden ui catalog`(生成 `.garden/ui-catalog.html`)。
|
|
45
|
+
- **编译**:命令「复制编译提示词」。中文站点复制 `编译这篇:<相对路径>`,英文站点(`locale: en_US`)复制 `Compile this post: <相对路径>`。把这句话发给装了 `garden-ui` skill 的 Agent,由它 `npx garden ui scan`、读 `npx garden ui prompt`、再 `npx garden ui apply`。插件不调用模型,也不保存密钥。
|
|
46
|
+
|
|
47
|
+
本机有 `code` 或 `cursor` 命令时,`garden editors install` 会执行 `--install-extension`。两个命令都在时,会各装一次。没有命令行时,打开扩展视图,点右上角「…」,选 **Install from VSIX**(从 VSIX 安装),文件在 `.garden/editors/vscode/`。
|
|
48
|
+
|
|
49
|
+
### Typora
|
|
50
|
+
|
|
51
|
+
Typora 没有官方插件接口。先按 [typora-community-plugin 的安装说明](https://github.com/typora-community-plugin/typora-community-plugin/blob/main/docs/en-us/user-guide/1a-installation.md) 装上社区框架(macOS 上它会修改 `Typora.app` 里的 `index.html`)。`garden editors install` 会检查常见安装位置;没找到时仍会复制插件,并打印这个前提。
|
|
52
|
+
|
|
53
|
+
插件文件复制到 `~/.typora/community-plugins/plugins/hedon954.garden-intelligent-ui/`。macOS 同时写到 `~/Library/Application Support/abnerworks.Typora/plugins/plugins/hedon954.garden-intelligent-ui/`。打开 Typora,在「设置 → 已安装插件」里勾选 Garden Intelligent UI。
|
|
54
|
+
|
|
55
|
+
用法和 VS Code 同一套:光标离开围栏时预览 `ui` / `intent`(和 Mermaid 一样,点进围栏编辑源码;点滑杆或按钮不会跳进源码);先输入 `/` 再选 `/garden`,或用命令「插入 Garden 块」,都会插入想法围栏(Windows / Linux 默认 `Ctrl+Alt+I`,macOS 的 Option 会改写按键,请到「设置 → 快捷键」里自己录);「Garden:复制编译提示词」交给 `garden-ui` skill。
|
|
56
|
+
|
|
57
|
+
升级 Typora 会覆盖社区框架改过的程序文件,插件跟着失效。需要重新安装 typora-community-plugin,再勾选本插件。稿件里的围栏原文还在,也可以用 `make dev` 预览。
|
|
58
|
+
|
|
59
|
+
Obsidian 插件这一轮不随包安装。
|
|
60
|
+
|
|
61
|
+
## 3. 用一句话写组件(人和 Agent 用同一套工具)
|
|
25
62
|
|
|
26
63
|
在正文想放组件的位置写一个不带名字的 `garden` 围栏,只描述想让读者做什么、看到什么:
|
|
27
64
|
|
|
@@ -33,7 +70,7 @@ make dev
|
|
|
33
70
|
|
|
34
71
|
让 Agent 按站点里的 `garden-ui` skill 处理这篇稿件:Agent 先用 `npx garden ui scan` 读取待编译块的哈希,再读 `npx garden ui prompt` 的组件规则,生成 `garden interactive`(或 `card`、`stat`、`compare`、`timeline`、`chart`、`figure`、`html`、`file`、`relations`、`list`、`choice` 等内置组件,或在完整代码下方生成 `diff`),最后用 `npx garden ui apply` 只替换该围栏。如果你在编译前直接 `git commit`,pre-commit 钩子会运行同一条 `npx --no-install garden ui check --staged --quiet` 拦下提交,并打印一段可直接粘贴给 AI 的修复提示词。
|
|
35
72
|
|
|
36
|
-
##
|
|
73
|
+
## 4. 常用命令
|
|
37
74
|
|
|
38
75
|
```bash
|
|
39
76
|
make new SLUG=writing-pace TITLE="慢慢写" # 在 content/posts/ 下创建文章草稿
|
|
@@ -44,9 +81,10 @@ make update MESSAGE="新增一篇文章" # 检查通过后提交并
|
|
|
44
81
|
npx garden check content/posts/writing-pace.md # 单独检查一篇文章的标题、组件与资源路径
|
|
45
82
|
npx garden ui check --staged # 检查暂存区稿件中的想法块与交互组件
|
|
46
83
|
npx garden ui catalog # 在 .garden/ui-catalog.html 生成离线组件清单
|
|
84
|
+
npx garden editors install --yes # 把 Intelligent UI 插件装进 VS Code / Cursor 与 Typora
|
|
47
85
|
```
|
|
48
86
|
|
|
49
|
-
##
|
|
87
|
+
## 5. Excalidraw
|
|
50
88
|
|
|
51
89
|
正文用 Markdown 图片指向 `.excalidraw` 源文件。`garden build` 会在同目录导出一张浅色扁平 SVG,画布背景透明,并把这张 SVG 嵌进正文,图里的文字可以选中。不需要单独的导出命令。
|
|
52
90
|
|
|
@@ -56,7 +94,7 @@ npx garden ui catalog # 在 .garden/ui-catalog.html 生
|
|
|
56
94
|
|
|
57
95
|
页面里第一次出现 `.excalidraw` 时,构建会下载导出程序(不需要 Chromium;字体写进 SVG)。说明见[本地图片](docs/handbook/components/image.md)。
|
|
58
96
|
|
|
59
|
-
##
|
|
97
|
+
## 6. 使用说明与深入文档
|
|
60
98
|
|
|
61
99
|
本地运行 `make dev` 后访问 `/garden`(英文版在 `/garden/en`),或直接阅读仓库内的文档:
|
|
62
100
|
|
package/bin/garden.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
+
import { editorsInstall, offerEditorsOnce } from "../scripts/editors-kit.mjs";
|
|
7
8
|
import { gardenPackageRoot, gardenSiteRoot } from "../scripts/garden-paths.mjs";
|
|
8
9
|
import { langOf, siteCliLang } from "../ui/core/cli-locale.mjs";
|
|
9
10
|
|
|
@@ -196,29 +197,30 @@ function copyIfMissing(from, to) {
|
|
|
196
197
|
return true;
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
function ensureSiteKit(destinationRoot = siteRoot) {
|
|
200
|
-
if (samePath(destinationRoot, gardenPackageRoot)) return;
|
|
200
|
+
async function ensureSiteKit(destinationRoot = siteRoot) {
|
|
201
201
|
const lang = siteCliLang(destinationRoot);
|
|
202
|
+
if (!samePath(destinationRoot, gardenPackageRoot)) {
|
|
203
|
+
const added = [];
|
|
204
|
+
if (
|
|
205
|
+
copyIfMissing(
|
|
206
|
+
path.join(gardenPackageRoot, "templates", "site", lang === "en" ? "Makefile.en" : "Makefile"),
|
|
207
|
+
path.join(destinationRoot, "Makefile"),
|
|
208
|
+
)
|
|
209
|
+
) {
|
|
210
|
+
added.push("Makefile");
|
|
211
|
+
}
|
|
212
|
+
if (added.length) {
|
|
213
|
+
process.stdout.write(
|
|
214
|
+
lang === "en"
|
|
215
|
+
? `Added missing site files: ${added.join(", ")}\n`
|
|
216
|
+
: `已补齐站点文件:${added.join("、")}\n`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
202
219
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
copyIfMissing(
|
|
206
|
-
path.join(gardenPackageRoot, "templates", "site", lang === "en" ? "Makefile.en" : "Makefile"),
|
|
207
|
-
path.join(destinationRoot, "Makefile"),
|
|
208
|
-
)
|
|
209
|
-
) {
|
|
210
|
-
added.push("Makefile");
|
|
220
|
+
installSkill(destinationRoot, { onlyMissing: true, lang });
|
|
221
|
+
if (process.env.GARDEN_SKIP_HOOKS !== "1") installHook(destinationRoot, { quiet: true, lang });
|
|
211
222
|
}
|
|
212
|
-
|
|
213
|
-
process.stdout.write(
|
|
214
|
-
lang === "en"
|
|
215
|
-
? `Added missing site files: ${added.join(", ")}\n`
|
|
216
|
-
: `已补齐站点文件:${added.join("、")}\n`,
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
installSkill(destinationRoot, { onlyMissing: true, lang });
|
|
221
|
-
if (process.env.GARDEN_SKIP_HOOKS !== "1") installHook(destinationRoot, { quiet: true, lang });
|
|
223
|
+
await offerEditorsOnce(destinationRoot, lang);
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
const HOOK_MARKER = "# garden: ui check";
|
|
@@ -272,19 +274,19 @@ function installHook(destinationRoot = siteRoot, { quiet = false, force = false,
|
|
|
272
274
|
);
|
|
273
275
|
}
|
|
274
276
|
|
|
275
|
-
function prepare() {
|
|
277
|
+
async function prepare() {
|
|
276
278
|
ensureEnv();
|
|
277
279
|
ensurePublic();
|
|
278
280
|
fs.mkdirSync(path.join(siteRoot, "content", "posts"), { recursive: true });
|
|
279
281
|
fs.mkdirSync(path.join(siteRoot, "content", "thoughts"), { recursive: true });
|
|
280
|
-
ensureSiteKit();
|
|
282
|
+
await ensureSiteKit();
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
function sync() {
|
|
285
|
+
async function sync() {
|
|
284
286
|
if (!fs.existsSync(path.join(siteRoot, "site.config.yaml"))) {
|
|
285
287
|
fail(`当前目录不是 Garden 站点(缺少 site.config.yaml):${siteRoot}`);
|
|
286
288
|
}
|
|
287
|
-
prepare();
|
|
289
|
+
await prepare();
|
|
288
290
|
runNode("scripts/build-content.mjs");
|
|
289
291
|
}
|
|
290
292
|
|
|
@@ -313,8 +315,8 @@ function ensureDevGitHubLogin() {
|
|
|
313
315
|
fs.writeFileSync(local, text.endsWith("\n") ? text : `${text}\n`);
|
|
314
316
|
}
|
|
315
317
|
|
|
316
|
-
function runVinext(vinextArgs, { copyDist = false } = {}) {
|
|
317
|
-
sync();
|
|
318
|
+
async function runVinext(vinextArgs, { copyDist = false } = {}) {
|
|
319
|
+
await sync();
|
|
318
320
|
const cwd = vinextRoot();
|
|
319
321
|
const result = spawnSync(process.execPath, [resolveVinext(), ...vinextArgs], {
|
|
320
322
|
cwd,
|
|
@@ -378,7 +380,7 @@ function writeEngineDependency(destination) {
|
|
|
378
380
|
fs.writeFileSync(sitePkgPath, `${JSON.stringify(sitePkg, null, 2)}\n`);
|
|
379
381
|
}
|
|
380
382
|
|
|
381
|
-
function initSite() {
|
|
383
|
+
async function initSite() {
|
|
382
384
|
const { destination, lang } = parseInitArgs(rest);
|
|
383
385
|
const template = path.join(gardenPackageRoot, "templates", "site");
|
|
384
386
|
if (!fs.existsSync(template)) fail("Garden 包里缺少 templates/site。");
|
|
@@ -405,9 +407,10 @@ function initSite() {
|
|
|
405
407
|
}
|
|
406
408
|
}
|
|
407
409
|
writeEngineDependency(destination);
|
|
410
|
+
process.stdout.write(lang === "en" ? `Created site: ${destination}\n` : `已创建站点:${destination}\n`);
|
|
411
|
+
installSkill(destination, { lang });
|
|
412
|
+
await offerEditorsOnce(destination, lang);
|
|
408
413
|
if (lang === "en") {
|
|
409
|
-
process.stdout.write(`Created site: ${destination}\n`);
|
|
410
|
-
installSkill(destination, { lang });
|
|
411
414
|
process.stdout.write("Next steps:\n");
|
|
412
415
|
process.stdout.write(` cd ${destination}\n`);
|
|
413
416
|
process.stdout.write(" npm install\n");
|
|
@@ -417,8 +420,6 @@ function initSite() {
|
|
|
417
420
|
printGiscusGuide("en");
|
|
418
421
|
return;
|
|
419
422
|
}
|
|
420
|
-
process.stdout.write(`已创建站点:${destination}\n`);
|
|
421
|
-
installSkill(destination);
|
|
422
423
|
process.stdout.write("下一步:\n");
|
|
423
424
|
process.stdout.write(` cd ${destination}\n`);
|
|
424
425
|
process.stdout.write(" npm install\n");
|
|
@@ -428,6 +429,35 @@ function initSite() {
|
|
|
428
429
|
printGiscusGuide("zh");
|
|
429
430
|
}
|
|
430
431
|
|
|
432
|
+
function editorInstallFlags(argv) {
|
|
433
|
+
const known = new Set(["--vscode", "--typora", "--yes"]);
|
|
434
|
+
for (const arg of argv) {
|
|
435
|
+
if (!known.has(arg)) {
|
|
436
|
+
const lang = siteCliLang(siteRoot);
|
|
437
|
+
fail(lang === "en" ? `Unknown option: ${arg}` : `未知参数:${arg}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
vscode: argv.includes("--vscode"),
|
|
442
|
+
typora: argv.includes("--typora"),
|
|
443
|
+
yes: argv.includes("--yes"),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function editorsCommand(argv) {
|
|
448
|
+
const lang = siteCliLang(siteRoot);
|
|
449
|
+
const sub = argv[0];
|
|
450
|
+
if (sub !== "install") {
|
|
451
|
+
fail(
|
|
452
|
+
lang === "en"
|
|
453
|
+
? "Usage: garden editors install [--vscode] [--typora] [--yes]"
|
|
454
|
+
: "用法:garden editors install [--vscode] [--typora] [--yes]",
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const status = await editorsInstall(siteRoot, lang, editorInstallFlags(argv.slice(1)));
|
|
458
|
+
if (status) process.exit(status);
|
|
459
|
+
}
|
|
460
|
+
|
|
431
461
|
function packagedSkills() {
|
|
432
462
|
const root = path.join(gardenPackageRoot, ".agents", "skills");
|
|
433
463
|
if (!fs.existsSync(root)) return [];
|
|
@@ -473,6 +503,7 @@ function help() {
|
|
|
473
503
|
garden check <文章.md> 检查一篇博文的硬性结构
|
|
474
504
|
garden ui <子命令> Intelligent UI:scan、check、apply、prompt、schema、catalog
|
|
475
505
|
garden hooks 安装提交前检查(检查暂存稿件里的 ui 块)
|
|
506
|
+
garden editors install 安装 Intelligent UI 插件到 VS Code / Cursor 与 Typora(--vscode、--typora、--yes)
|
|
476
507
|
garden skill 把引擎自带的 skill 装进当前站点
|
|
477
508
|
garden pages:validate 检查 dist/client
|
|
478
509
|
garden pages:prepare 写入公开产物仓库
|
|
@@ -480,85 +511,92 @@ function help() {
|
|
|
480
511
|
`);
|
|
481
512
|
}
|
|
482
513
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
sync
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
514
|
+
async function main() {
|
|
515
|
+
switch (command) {
|
|
516
|
+
case "help":
|
|
517
|
+
case "--help":
|
|
518
|
+
case "-h":
|
|
519
|
+
help();
|
|
520
|
+
break;
|
|
521
|
+
case "sync":
|
|
522
|
+
await sync();
|
|
523
|
+
break;
|
|
524
|
+
case "dev":
|
|
525
|
+
ensureDevGitHubLogin();
|
|
526
|
+
await runVinext(["dev", ...rest]);
|
|
527
|
+
break;
|
|
528
|
+
case "build":
|
|
529
|
+
// Favicons are refetched on every build; failures fall back to link-assets.json.
|
|
530
|
+
if (process.env.GARDEN_OFFLINE !== "1") runNode("scripts/link-assets.mjs");
|
|
531
|
+
// Remote image sizes: only new images are probed; the rest come from image-sizes.json.
|
|
532
|
+
if (process.env.GARDEN_OFFLINE !== "1") runNode("scripts/image-sizes.mjs");
|
|
533
|
+
// Sizes of remote files in garden file blocks; failures fall back to file-sizes.json.
|
|
534
|
+
if (process.env.GARDEN_OFFLINE !== "1") runNode("scripts/file-sizes.mjs");
|
|
535
|
+
await runVinext(["build", ...rest], { copyDist: true });
|
|
536
|
+
// /favicon.ico from the site's own icon (brand.favicon).
|
|
537
|
+
runNode("scripts/favicon-ico.mjs");
|
|
538
|
+
// Pages for old addresses listed under redirects in site.config.yaml (static export only).
|
|
539
|
+
if (process.env.STATIC_EXPORT === "1") {
|
|
540
|
+
// What the export wrote for empty dynamic routes (ui/garden/static-params.mjs).
|
|
541
|
+
runNode("scripts/static-placeholders.mjs");
|
|
542
|
+
runNode("scripts/redirects.mjs");
|
|
543
|
+
// <html lang="en"> on the English handbook pages.
|
|
544
|
+
runNode("scripts/html-lang.mjs");
|
|
545
|
+
// 404.html also under basePath when the host serves that folder alone.
|
|
546
|
+
runNode("scripts/static-404.mjs");
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
case "links":
|
|
550
|
+
runNode("scripts/link-assets.mjs");
|
|
551
|
+
break;
|
|
552
|
+
case "images":
|
|
553
|
+
runNode("scripts/image-sizes.mjs");
|
|
554
|
+
break;
|
|
555
|
+
case "files":
|
|
556
|
+
runNode("scripts/file-sizes.mjs");
|
|
557
|
+
break;
|
|
558
|
+
case "start":
|
|
559
|
+
await runVinext(["start", ...rest]);
|
|
560
|
+
break;
|
|
561
|
+
case "new":
|
|
562
|
+
runNode("scripts/new-post.mjs", rest);
|
|
563
|
+
break;
|
|
564
|
+
case "embed":
|
|
565
|
+
runNode("scripts/fetch-embed.mjs", rest);
|
|
566
|
+
break;
|
|
567
|
+
case "check":
|
|
568
|
+
runNode("scripts/check-post.mjs", rest);
|
|
569
|
+
break;
|
|
570
|
+
case "ui":
|
|
571
|
+
runNode("scripts/ui-cli.mjs", rest, { cwd: process.cwd() });
|
|
572
|
+
break;
|
|
573
|
+
case "hooks":
|
|
574
|
+
installHook(siteRoot, { force: rest.includes("--force") });
|
|
575
|
+
break;
|
|
576
|
+
case "editors":
|
|
577
|
+
await editorsCommand(rest);
|
|
578
|
+
break;
|
|
579
|
+
case "init":
|
|
580
|
+
await initSite();
|
|
581
|
+
break;
|
|
582
|
+
case "skill":
|
|
583
|
+
case "skills":
|
|
584
|
+
installSkill();
|
|
585
|
+
break;
|
|
586
|
+
case "pages:validate":
|
|
587
|
+
runNode("scripts/prepare-pages-output.mjs", [
|
|
588
|
+
rest[0] ?? path.join(siteRoot, "dist", "client"),
|
|
589
|
+
]);
|
|
590
|
+
break;
|
|
591
|
+
case "pages:prepare":
|
|
592
|
+
runNode("scripts/prepare-pages-output.mjs", rest);
|
|
593
|
+
break;
|
|
594
|
+
case "webmentions":
|
|
595
|
+
runNode("scripts/send-webmentions.mjs", rest);
|
|
596
|
+
break;
|
|
597
|
+
default:
|
|
598
|
+
fail(`未知命令:${command}`);
|
|
599
|
+
}
|
|
564
600
|
}
|
|
601
|
+
|
|
602
|
+
await main();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 用 AI 写作
|
|
3
3
|
navTitle: 用 AI 写作
|
|
4
|
-
description:
|
|
4
|
+
description: 用编辑器插件插入想法并预览,交给 Agent 编译;提交前由同一个脚本检查。
|
|
5
5
|
section: getting-started
|
|
6
6
|
order: 2
|
|
7
7
|
---
|
|
@@ -82,15 +82,41 @@ pages:
|
|
|
82
82
|
|
|
83
83
|
Agent 按 `garden-ui` skill 会把这个想法块替换成一段紧挨着完整代码的 `diff` 围栏。页面渲染时会把完整代码和 `diff` 合成一个带「代码 / 对比上一版」切换按钮的代码框,手写 `diff` 的规则见 [写文章与随想](/garden/writing)。
|
|
84
84
|
|
|
85
|
-
## 6.
|
|
85
|
+
## 6. 在编辑器里插入、预览,再交给 skill 编译
|
|
86
|
+
|
|
87
|
+
VS Code、Cursor 和 Typora 的 Intelligent UI 插件把上面的流程放到写稿的地方。插件随 `hd-garden` 发布。`npx hd-garden init` 把产物复制到 `.garden/editors/`,并在交互终端里询问要不要安装。第一次 `npx garden sync` 如果还没问过,会再问一次。非交互环境、`CI=1` 或 `GARDEN_SKIP_EDITORS=1` 只打印提示,不阻塞。手动安装:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx garden editors install
|
|
91
|
+
npx garden editors install --yes
|
|
92
|
+
npx garden editors install --vscode
|
|
93
|
+
npx garden editors install --typora
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
装好之后,一篇组件通常是这四步:
|
|
97
|
+
|
|
98
|
+
1. **插入想法。** 在要放组件的位置运行「插入 Garden 块」(VS Code / Cursor 的快捷键是 macOS `Cmd+Alt+I`,Windows / Linux `Ctrl+Alt+I`)。Typora 里输入 `/` 后选 `/garden`,下一层列表每一项有一张小预览图;命令的快捷键在 Windows / Linux 是 `Ctrl+Alt+I`,macOS 需要在「设置 → 快捷键」里自己录。插入的是给 `garden-ui` skill 编译的想法围栏,例如选「资源卡」得到:
|
|
99
|
+
|
|
100
|
+
````markdown
|
|
101
|
+
```garden
|
|
102
|
+
做成资源卡。一篇论文、一个仓库或一本书,读者需要标题、链接和一句说明。请写上链接、标题、一句说明。
|
|
103
|
+
```
|
|
104
|
+
````
|
|
105
|
+
2. **预览。** 打开 Markdown 预览,或在 Typora 里把光标移出围栏。`intent` 显示成想法卡片;skill 写回 `ui` 之后,预览画成和博客一样的组件。错误会标在源码上(VS Code / Cursor 的「问题」面板和 CodeLens)。
|
|
106
|
+
3. **看清单。** 命令「查看组件清单」,或在终端运行 `npx garden ui catalog` 打开 `.garden/ui-catalog.html`。清单里的「用它开始」把配方插进当前稿件,适合已经知道要用哪一种组件的时候。
|
|
107
|
+
4. **编译。** 「复制编译提示词」。中文站点得到 `编译这篇:<相对路径>`,英文站点得到 `Compile this post: <相对路径>`。把这句话发给 `garden-ui` skill。Agent 用 `npx garden ui scan` 定位,用 `npx garden ui prompt` 读规则,用 `npx garden ui apply` 写回。插件不调用模型。
|
|
108
|
+
|
|
109
|
+
**Typora 的前提。** 先安装 [typora-community-plugin](https://github.com/typora-community-plugin/typora-community-plugin/blob/main/docs/en-us/user-guide/1a-installation.md)。`npx garden editors install --typora` 把插件复制到 `~/.typora/community-plugins/plugins/hedon954.garden-intelligent-ui/`(macOS 同时写到 `~/Library/Application Support/abnerworks.Typora/plugins/plugins/hedon954.garden-intelligent-ui/`),然后在「设置 → 已安装插件」里勾选 Garden Intelligent UI。升级 Typora 会覆盖社区框架,需要重装框架后再勾选。没找到 `code` 或 `cursor` 时,用扩展视图的 Install from VSIX 选择 `.garden/editors/vscode/` 里的文件。安装细节见 [语法检查与快捷输入](/garden/workflow)。
|
|
110
|
+
|
|
111
|
+
## 7. 不使用 Agent 时的快捷输入
|
|
86
112
|
|
|
87
113
|
不借助 AI 手写组件时,可以直接使用包内自带的模板与代码片段:
|
|
88
114
|
|
|
89
115
|
- `make new` 创建的草稿底部自带一段 `<!-- 组件速查 ... -->` 注释,包含想法块和 12 个组件的示例,写完可直接删掉。
|
|
90
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` 即可展开对应围栏。
|
|
91
|
-
-
|
|
117
|
+
- 编辑器插件的用法见上一节。`npx garden ui catalog` 在本地生成 `.garden/ui-catalog.html` 离线组件画廊。
|
|
92
118
|
|
|
93
|
-
##
|
|
119
|
+
## 8. 可选的独立管理后台
|
|
94
120
|
|
|
95
121
|
Garden 引擎仓库内置了可选的 `/admin` 管理后台(位于 `app/admin` 与 `worker/`),可通过 `npm run admin:local`、`npm run admin:cloudflare` 或 Docker 单独部署。配置环境变量后,后台提供以下网页端功能:
|
|
96
122
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Writing with AI
|
|
3
3
|
navTitle: Writing with AI
|
|
4
|
-
description:
|
|
4
|
+
description: Insert and preview ideas with the editor plugins, let an Agent compile them, and validate staged files with the same CLI.
|
|
5
5
|
section: getting-started
|
|
6
6
|
order: 2
|
|
7
7
|
---
|
|
@@ -82,15 +82,35 @@ Compared with the previous version: split the title into two lines with " | " in
|
|
|
82
82
|
|
|
83
83
|
Following the `garden-ui` skill, the Agent replaces that idea fence with a `diff` fence right below the code block. The page merges the adjacent code and `diff` fences into a single code frame with a toggle between the full code and the diff; see [Writing Posts & Thoughts](/garden/en/writing) for the hand-written `diff` format.
|
|
84
84
|
|
|
85
|
-
## 6.
|
|
85
|
+
## 6. Insert, Preview, and Compile from the Editor
|
|
86
|
+
|
|
87
|
+
The VS Code, Cursor, and Typora Intelligent UI plugins put the flow above in the editor. They ship inside `hd-garden`. `npx hd-garden init` copies them to `.garden/editors/` and asks whether to install them when the terminal is interactive. The first `npx garden sync` asks again if that prompt has not happened yet. A non-interactive shell, `CI=1`, or `GARDEN_SKIP_EDITORS=1` prints the hint and does not block. Install later with:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx garden editors install
|
|
91
|
+
npx garden editors install --yes
|
|
92
|
+
npx garden editors install --vscode
|
|
93
|
+
npx garden editors install --typora
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A component then takes four steps:
|
|
97
|
+
|
|
98
|
+
1. **Insert an idea.** Where the component should go, run the Garden insert command (`Cmd+Alt+I` on macOS, `Ctrl+Alt+I` on Windows and Linux in VS Code / Cursor). In Typora, type `/` and choose `/garden` to open a picker with a small preview image on each row; the command shortcut is `Ctrl+Alt+I` on Windows and Linux, and macOS needs a shortcut recorded under Settings, Keyboard Shortcuts. The insertion is a plain `garden` fence whose body tells the `garden-ui` skill which component to compile and which facts to include.
|
|
99
|
+
2. **Preview.** Open the Markdown preview, or in Typora move the cursor out of the fence. An `intent` block shows as an idea card. After the skill writes a `ui` block, the preview matches the blog. VS Code / Cursor also report errors in Problems and in the CodeLens above the block.
|
|
100
|
+
3. **Open the catalog.** Use the catalog command, or run `npx garden ui catalog` and open `.garden/ui-catalog.html`. Starting from a recipe inserts it at the cursor when you already know which component you want.
|
|
101
|
+
4. **Compile.** Copy the compile prompt. On an English site the clipboard text is `Compile this post:` plus the post path. Paste it to the `garden-ui` skill. The Agent locates the block with `npx garden ui scan`, reads the rules with `npx garden ui prompt`, and writes it back with `npx garden ui apply`. The plugin does not call a model.
|
|
102
|
+
|
|
103
|
+
**Typora prerequisite.** Install [typora-community-plugin](https://github.com/typora-community-plugin/typora-community-plugin/blob/main/docs/en-us/user-guide/1a-installation.md) first. `npx garden editors install --typora` copies the plugin to `~/.typora/community-plugins/plugins/hedon954.garden-intelligent-ui/`. On macOS it also copies to `~/Library/Application Support/abnerworks.Typora/plugins/plugins/hedon954.garden-intelligent-ui/`. Enable Garden Intelligent UI under Settings, Installed plugins. A Typora upgrade overwrites the community framework; reinstall the framework, then enable the plugin again. If `code` or `cursor` is not on `PATH`, use Install from VSIX in the Extensions view and select the file under `.garden/editors/vscode/`. Install details are in [Checks & Shortcuts](/garden/en/workflow).
|
|
104
|
+
|
|
105
|
+
## 7. Templates and Snippets Without an Agent
|
|
86
106
|
|
|
87
107
|
When writing components by hand without AI, you can use the templates and snippets shipped in the package:
|
|
88
108
|
|
|
89
109
|
- Every draft created by `make new` includes an HTML comment at the bottom with copy-ready examples for idea fences and all 12 components; delete the comment whenever you are done.
|
|
90
110
|
- `templates/garden-blocks.md` lists `when` / `whenNot` guidance and copyable templates for every block; macOS, Typora, and Alfred users can import `templates/garden-snippets.plist` to expand `;gidea`, `;gcard`, `;gstat`, `;gcompare`, `;gtimeline`, `;gchart`, `;gfigure`, `;ghtml`, `;ginteractive`, `;gfile`, `;grel`, `;glist`, and `;gchoice`.
|
|
91
|
-
-
|
|
111
|
+
- Editor plugin usage is in the previous section. `npx garden ui catalog` writes an offline component catalog at `.garden/ui-catalog.html`.
|
|
92
112
|
|
|
93
|
-
##
|
|
113
|
+
## 8. Optional Standalone Admin
|
|
94
114
|
|
|
95
115
|
The Garden engine includes an optional `/admin` console (under `app/admin` and `worker/`) that can be deployed separately with `npm run admin:local`, `npm run admin:cloudflare`, or Docker. Once configured via environment variables, it provides:
|
|
96
116
|
|
|
@@ -187,6 +187,7 @@ make dev
|
|
|
187
187
|
Run `make update` when a post is ready; it runs the full check and build before committing and pushing. Read the chapters for details:
|
|
188
188
|
|
|
189
189
|
- **[Quick Start](/garden/en/start)**: Directory layout, creating posts with `make new`, previewing drafts, and daily commands.
|
|
190
|
+
- **[Writing with AI](/garden/en/ai-writing)** and **[Checks & Shortcuts](/garden/en/workflow)**: Insert an idea, preview the component, and open the catalog in VS Code, Cursor, or Typora, and install the plugins with `npx garden editors install`.
|
|
190
191
|
- **[Site & Column Config](/garden/en/config)**: Every field in `site.config.yaml`, custom SVG icons, and ordering series in `content/columns.yaml`.
|
|
191
192
|
- **[Deploy to GitHub Pages](/garden/en/deploy)**: A single GitHub Actions workflow that supports both one public repository and a private source repo paired with a public Pages repo.
|
|
192
193
|
- **[Writing Posts & Thoughts](/garden/en/writing)** and **[How the Reading Layout Works](/garden/en/design)**: Front Matter fields, three-tier adaptive widths, and color tokens. Link cards, source chips, and local images are on [Components Overview](/garden/en/components).
|