hd-garden 0.10.3 → 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.
- package/.agents/skills/garden-ui/SKILL.md +11 -10
- package/README.en.md +44 -6
- package/README.md +45 -7
- package/app/components/garden/GardenBlock.tsx +20 -4
- package/app/components/garden/blocks.tsx +6 -118
- package/app/globals.css +1 -0
- package/app/lib/garden-docs-meta.ts +3 -3
- package/app/lib/generated-content.ts +1 -1
- package/app/lib/i18n.ts +0 -6
- package/app/lib/remark-garden.mjs +61 -5
- package/app/styles/a2.css +25 -112
- package/app/styles/tokens.css +5 -0
- package/bin/garden.mjs +150 -112
- package/docs/handbook/ai-writing.md +33 -7
- package/docs/handbook/components/compose.md +354 -0
- package/docs/handbook/components/html.md +1 -1
- package/docs/handbook/components/image.md +1 -1
- package/docs/handbook/components/interactive.md +4 -1
- package/docs/handbook/components/link-card.md +1 -1
- package/docs/handbook/components.md +23 -35
- package/docs/handbook/design.md +25 -34
- package/docs/handbook/en/ai-writing.md +27 -7
- package/docs/handbook/en/components/compose.md +352 -0
- package/docs/handbook/en/components/html.md +1 -1
- package/docs/handbook/en/components/image.md +1 -1
- package/docs/handbook/en/components/interactive.md +4 -1
- package/docs/handbook/en/components.md +23 -36
- package/docs/handbook/en/design.md +25 -33
- package/docs/handbook/en/index.md +6 -5
- package/docs/handbook/en/reference/compose.md +99 -0
- package/docs/handbook/en/reference.md +2 -2
- package/docs/handbook/en/start.md +13 -0
- package/docs/handbook/en/workflow.md +47 -7
- package/docs/handbook/index.md +6 -5
- package/docs/handbook/reference/compose.md +99 -0
- package/docs/handbook/reference.md +2 -2
- package/docs/handbook/start.md +13 -0
- package/docs/handbook/workflow.md +47 -7
- package/editors/dist/typora/main.js +412 -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 +17 -3
- package/scripts/editors-kit.mjs +352 -0
- package/scripts/handbook-allowlist.mjs +0 -8
- package/scripts/handbook-facts.mjs +6 -36
- package/scripts/pack-editors.mjs +241 -0
- package/scripts/ui-cli.mjs +5 -1
- package/templates/garden-blocks.md +34 -31
- package/templates/garden-snippets.plist +3 -8
- package/templates/site/README.en.md +2 -0
- package/templates/site/README.md +2 -0
- package/templates/skills-en/garden-ui/SKILL.md +11 -10
- package/ui/README.md +1 -0
- package/ui/compose/compose.css +806 -0
- package/ui/compose/dil.mjs +443 -0
- package/ui/compose/examples.mjs +28 -0
- package/ui/compose/index.mjs +7 -0
- package/ui/compose/markup.mjs +364 -0
- package/ui/compose/parse.mjs +21 -0
- package/ui/compose/render.mjs +247 -0
- package/ui/core/cli-locale.mjs +4 -0
- package/ui/core/fence.mjs +13 -1
- package/ui/core/fix-prompt.mjs +1 -0
- package/ui/core/garden-fence.mjs +4 -1
- package/ui/core/scan.mjs +28 -2
- package/ui/core/transform.mjs +12 -0
- package/ui/core/validate.mjs +6 -4
- package/ui/garden/catalog.mjs +58 -35
- package/ui/garden/check.mjs +22 -6
- package/ui/garden/parse.mjs +26 -0
- package/ui/garden/snippets.mjs +101 -12
- package/ui/garden/word-count.mjs +10 -18
- package/ui/preview/dist/garden-ui.esm.js +158 -132
- package/ui/preview/dist/garden-ui.iife.js +157 -131
- package/ui/prompt/build.mjs +1 -0
- package/ui/prompt/recipes/compose.md +40 -0
- package/ui/prompt/rules.md +3 -3
- package/ui/react/CatalogPage.tsx +2 -2
- package/ui/react/runtime.tsx +5 -1
- package/docs/handbook/components/figure.md +0 -59
- package/docs/handbook/en/components/figure.md +0 -59
- package/docs/handbook/en/reference/figure.md +0 -84
- package/docs/handbook/reference/figure.md +0 -84
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderCompose } from "../../ui/compose/render.mjs";
|
|
1
2
|
import { diffToNewCode, protectMathIdentifiers } from "../../ui/garden/enhance.mjs";
|
|
2
3
|
import { classifyFence } from "../../ui/garden/parse.mjs";
|
|
3
4
|
|
|
@@ -29,12 +30,58 @@ function fenceNodeToRecord(node) {
|
|
|
29
30
|
return { lang: String(node.lang || "").toLowerCase(), meta: String(node.meta || ""), info: `${node.lang || ""} ${node.meta || ""}`.trim() };
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function
|
|
33
|
+
function htmlElement(value) {
|
|
34
|
+
return { type: "html", value };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const PAIR_ALT = new Set(["ChatGPT 原图", "ChatGPT original"]);
|
|
38
|
+
|
|
39
|
+
function pairImage(node) {
|
|
40
|
+
if (node?.type !== "paragraph" || !Array.isArray(node.children)) return null;
|
|
41
|
+
const images = node.children.filter((child) => child?.type === "image");
|
|
42
|
+
if (images.length !== 1 || node.children.some((child) => child?.type !== "image" && String(child.value ?? "").trim())) return null;
|
|
43
|
+
const image = images[0];
|
|
44
|
+
const alt = String(image.alt ?? "").trim();
|
|
45
|
+
return PAIR_ALT.has(alt) ? { image, alt } : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function element(tag, className, children) {
|
|
49
|
+
return {
|
|
50
|
+
type: "paragraph",
|
|
51
|
+
data: { hName: tag, hProperties: { className: [className] } },
|
|
52
|
+
children,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function composePair(image, alt, composeHtml) {
|
|
57
|
+
return element("div", "g-compose-pair", [
|
|
58
|
+
element("figure", "g-compose-pair-ref", [
|
|
59
|
+
element("figcaption", "g-compose-pair-k", [{ type: "text", value: alt }]),
|
|
60
|
+
image,
|
|
61
|
+
]),
|
|
62
|
+
element("div", "g-compose-pair-live", [
|
|
63
|
+
element("p", "g-compose-pair-k", [{ type: "text", value: "Garden Compose" }]),
|
|
64
|
+
htmlElement(composeHtml),
|
|
65
|
+
]),
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function rewriteChildren(node, nextScope) {
|
|
33
70
|
if (!Array.isArray(node.children)) return;
|
|
34
71
|
const next = [];
|
|
35
72
|
for (let index = 0; index < node.children.length; index += 1) {
|
|
36
73
|
const child = node.children[index];
|
|
37
74
|
const following = node.children[index + 1];
|
|
75
|
+
const pair = pairImage(child);
|
|
76
|
+
if (pair && following?.type === "code") {
|
|
77
|
+
const record = fenceNodeToRecord(following);
|
|
78
|
+
const kind = classifyFence({ ...record, body: following.value, line: 1, endLine: 1 });
|
|
79
|
+
if (kind.kind === "garden" && kind.block?.name === "compose" && !kind.block.error && kind.block.mode !== "play") {
|
|
80
|
+
next.push(composePair(pair.image, pair.alt, renderCompose(kind.block.props, { scope: nextScope() })));
|
|
81
|
+
index += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
38
85
|
if (child?.type === "code" && following?.type === "code" && String(following.lang || "").toLowerCase() === "diff") {
|
|
39
86
|
const current = fenceNodeToRecord(child);
|
|
40
87
|
const kind = classifyFence({ lang: current.lang, meta: current.meta, info: current.info });
|
|
@@ -51,6 +98,10 @@ function rewriteChildren(node) {
|
|
|
51
98
|
next.push(gardenElement({ legacy: true, from: kind.lang || record.lang, suggestion: kind.suggestion }));
|
|
52
99
|
continue;
|
|
53
100
|
}
|
|
101
|
+
if (kind.kind === "garden" && kind.block?.name === "compose" && !kind.block.error && kind.block.mode !== "play") {
|
|
102
|
+
next.push(htmlElement(renderCompose(kind.block.props, { scope: nextScope() })));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
54
105
|
if (kind.kind === "garden" && kind.block) {
|
|
55
106
|
next.push(gardenElement(kind.block));
|
|
56
107
|
continue;
|
|
@@ -61,20 +112,25 @@ function rewriteChildren(node) {
|
|
|
61
112
|
node.children = next;
|
|
62
113
|
}
|
|
63
114
|
|
|
64
|
-
function walk(node) {
|
|
115
|
+
function walk(node, nextScope) {
|
|
65
116
|
if (!node || typeof node !== "object") return;
|
|
66
117
|
if (node.type === "math" || node.type === "inlineMath") {
|
|
67
118
|
node.value = protectMathIdentifiers(node.value);
|
|
68
119
|
}
|
|
69
120
|
if (Array.isArray(node.children)) {
|
|
70
|
-
for (const child of node.children) walk(child);
|
|
71
|
-
rewriteChildren(node);
|
|
121
|
+
for (const child of node.children) walk(child, nextScope);
|
|
122
|
+
rewriteChildren(node, nextScope);
|
|
72
123
|
}
|
|
73
124
|
}
|
|
74
125
|
|
|
75
126
|
export function remarkGarden() {
|
|
127
|
+
let composeCount = 0;
|
|
128
|
+
const nextScope = () => {
|
|
129
|
+
composeCount += 1;
|
|
130
|
+
return `gc${composeCount}`;
|
|
131
|
+
};
|
|
76
132
|
return (tree) => {
|
|
77
|
-
walk(tree);
|
|
133
|
+
walk(tree, nextScope);
|
|
78
134
|
};
|
|
79
135
|
}
|
|
80
136
|
|
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. */
|
package/app/styles/tokens.css
CHANGED
|
@@ -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%);
|
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();
|