hd-garden 0.14.3 → 0.14.5
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-compose/SKILL.md +3 -3
- package/.agents/skills/garden-compose/references/layout-recipes.md +19 -4
- package/.agents/skills/garden-compose/references/measured-connectors.js +94 -0
- package/.agents/skills/garden-compose/references/rendering-model.md +10 -3
- package/.agents/skills/garden-compose/references/visual-selection.md +1 -0
- package/.agents/skills/garden-compose/references/workflow.md +7 -0
- package/app/components/PostReadingView.tsx +1 -1
- package/app/styles/a2.css +13 -1
- package/app/styles/shell.css +4 -4
- package/bin/garden.mjs +8 -2
- package/docs/handbook/components/compose.md +2 -0
- package/docs/handbook/en/components/compose.md +2 -0
- package/docs/handbook/en/reference/compose.md +7 -0
- package/docs/handbook/reference/compose.md +7 -0
- package/editors/dist/garden-typora-0.14.5.zip +0 -0
- package/editors/dist/typora/main.js +76 -77
- package/editors/dist/typora/manifest.json +7 -3
- package/editors/dist/typora/style.css +31 -18
- package/editors/dist/vscode/{garden-ui-0.14.3.vsix → garden-ui-0.14.5.vsix} +0 -0
- package/package.json +3 -2
- package/scripts/handbook-facts.mjs +5 -4
- package/scripts/pack-editors.mjs +8 -1
- package/scripts/rendered-site.mjs +130 -0
- package/scripts/sync-editor-version.mjs +5 -5
- package/templates/skills-en/garden-compose/SKILL.md +3 -3
- package/ui/compose/compose.css +6 -8
- package/ui/host.mjs +0 -1
- package/ui/preview/dist/garden-ui.css +31 -18
- package/ui/preview/dist/garden-ui.esm.js +77 -78
- package/ui/preview/dist/garden-ui.iife.js +77 -78
- package/ui/react/components/chart.tsx +2 -1
- package/ui/react/components/sandbox.tsx +3 -0
- package/ui/react/ui.css +12 -9
|
@@ -8,14 +8,14 @@ description: "将原文与一句效果或参考图,编译为 Garden Compose
|
|
|
8
8
|
## 短决策链:问题 → 约束 → 推荐路线 → 原文 → 验证
|
|
9
9
|
1. 作者只需原文+一句效果/参考图+可选不要什么。分开事实、效果与修改范围;保留措辞、顺序、完整段落和资源,不补摘要、数据、因果。普通 Markdown 足够就不加组件。
|
|
10
10
|
2. 选型必读 `references/visual-selection.md`。AI 内部先判断移动阅读任务(扫读/顺序/对照/空间关系),必读 `references/mobile-contract.md`,写桌面+窄屏约束卡,再取**唯一推荐路线**;不是限定合法组件的白名单。只在事实关系、隐藏内容、阅读顺序等重大取舍不明时询问;其余按已记录默认收敛,不让作者填技术表单。
|
|
11
|
-
3.
|
|
11
|
+
3. 原文关键数值+标签/单位用`metric`;独立解释类别用`equal-cards`,不造数值。标题+徽标用`row align="center" justify="between" wrap="nowrap"`:justify管主轴、align管交叉轴,标题保留内容宽;nowrap仅约束局部,长标题仍须窄屏验收。按路线只读 `references/layout-recipes.md` 对应小节;陌生组合/参考图再读 `references/rendering-model.md`。需要新组合时读 `references/composition-patterns.md`,仍先建立共享容器。frame/tone/size是正交选择,不因微调换组件家族。
|
|
12
12
|
4. 作者组件只用 `garden compose`;内部静态/语义/交互能力按需读 `references/static-play.md`。语义字段、图标、允许子项以 `npx --no-install garden ui schema` 为准,按需存到 `.garden`,不预读整本目录。Markdown/安全HTML岛保存完整块内容;受限表达式不等于任意JS。
|
|
13
13
|
|
|
14
14
|
## 写入与验证
|
|
15
15
|
1. 必读 `references/workflow.md`:scan,保存id/行号、完整围栏base hash与原文。首次插入意图块和已有块apply不同;未编译意图不是完成品。
|
|
16
|
-
2.
|
|
16
|
+
2. 先按 `references/rendering-model.md` 量宿主可用宽度与各层padding,再生成候选;不把主题/运行时挤压误诊成原文过长。正文默认继承文章动态字体/字号/行高;不缩字凑版。显式size只响应明确要求或真实层级。框读frames、颜色读colors;不要把场景框、内部边框和底色混为一谈。
|
|
17
17
|
3. 属性级微调优先,但当前apply仍替换完整围栏:候选文件+稳定id(没有才用scan行号)+原完整base hash;冲突即重读,不绕过保护。不误伤邻块或改写intent原话。
|
|
18
18
|
4. `scripts/check.mjs` 与 `garden check`保持原契约;有内部约束卡可另跑 `scripts/lint-layout.mjs`(workflow说明)。风险提示不等于语法错误;静态属性可能被丢弃,解析通过不等于生效。
|
|
19
|
-
5. 所有新布局都默认承担移动端责任,不等作者指出手机问题。按mobile-contract先验最窄屏和放大正文,再在完整主题与真实CSS顺序下检查桌面、computed
|
|
19
|
+
5. 所有新布局都默认承担移动端责任,不等作者指出手机问题。按mobile-contract先验最窄屏和放大正文,再在完整主题与真实CSS顺序下检查桌面、computed样式、几何及阅读密度;交互另测正常首页Link进入、返回再进、后退/前进与直接链接;sandbox不能以强刷或_top代替修复。静态构建自动验导出资源,也可用 `garden verify --rendered` 重跑,但它不证明浏览器导航。分别报告:结构/解析、渲染、浏览器几何/交互、独立AI生成试验。没有对应证据就写未验证。
|
|
20
20
|
|
|
21
21
|
脚本仅使用已安装本地引擎,不下载包、不默认调用模型或导出全目录。缺依赖就报告;可复用维护经验只提出候选,不在文章任务里自行改技能。
|
|
@@ -14,7 +14,7 @@ Use `<grid cols={3} gap={3}>` with three direct `<card>` children. `cols` accept
|
|
|
14
14
|
|
|
15
15
|
三个直接 card 子元素配 `grid cols={3} gap={3}`;cols 为 1–6。等宽来自等分网格;当前主题在视口 ≤800px 时收为单列,不强制窄屏横排。
|
|
16
16
|
|
|
17
|
-
- `gap` and `padding`: 0–4 → 0/4/8/12/16px. A restrained consistent default is `padding={3} radius="md" border="subtle" background="
|
|
17
|
+
- `gap` and `padding`: 0–4 → 0/4/8/12/16px. A restrained consistent default is `padding={3} radius="md" border="subtle" background="transparent"` on each card. Preserve grouping borders; do not add fill merely because items are peers. Choose explicit `background="surface"` only for intentional reading groups or requested hierarchy. / 默认透明并保留合理分组边框;内卡surface按阅读分组或明确层级需要选择,不是所有并列项都加底。
|
|
18
18
|
- Supported radii: none/sm/md/lg/xl/full. Borders: true/false/subtle/none/strong. Backgrounds include surface, surface-secondary, surface-accent, surface-tip, surface-muted, paper, well, transparent. Consult tokens only when needed.
|
|
19
19
|
- Equal width does not mean equal word count, forced line breaks or fixed heights. Grid items stretch within their row by normal CSS grid behavior; content remains natural, without height clamps or filler text. / 不为等宽删改原文、补字或强插换行,也不把多行内容裁成固定高度。
|
|
20
20
|
- Icons and semantic colors are optional, not mandatory decoration. Do not invent icons or inject arbitrary CSS. / 图标与颜色可选,不强行装饰。
|
|
@@ -30,15 +30,15 @@ Use `<grid cols={3} gap={3}>` with three direct `<card>` children. `cols` accept
|
|
|
30
30
|
</intent>
|
|
31
31
|
<figure frame="none">
|
|
32
32
|
<grid cols={3} gap={3}>
|
|
33
|
-
<card padding={3} radius="md" border="subtle" background="
|
|
33
|
+
<card padding={3} radius="md" border="subtle" background="transparent">
|
|
34
34
|
<title>人员</title>
|
|
35
35
|
<text>解决的是团队到底需要哪几类人,各自管什么、不管什么,他们如何进行合作。</text>
|
|
36
36
|
</card>
|
|
37
|
-
<card padding={3} radius="md" border="subtle" background="
|
|
37
|
+
<card padding={3} radius="md" border="subtle" background="transparent">
|
|
38
38
|
<title>流程</title>
|
|
39
39
|
<text>从冒出一个点子到软件上线,先迈左脚还是先迈右脚,怎么用最低成本试错。</text>
|
|
40
40
|
</card>
|
|
41
|
-
<card padding={3} radius="md" border="subtle" background="
|
|
41
|
+
<card padding={3} radius="md" border="subtle" background="transparent">
|
|
42
42
|
<title>产品</title>
|
|
43
43
|
<text>一款真正的好产品本身长什么样,有哪些绝不能碰的死穴。</text>
|
|
44
44
|
</card>
|
|
@@ -113,3 +113,18 @@ Use this for independent explanations, not a workflow. Derive the alignment hier
|
|
|
113
113
|
- centered-chip:只承诺局部贴标。完整问句、长标题、双语标签和放大字体必须实测;不自动把所有正文居中。长扫描清单优先开始对齐的连续阅读,必要时用局部重点居中,不通过sm来压高度。
|
|
114
114
|
- prose-list / frameless:保持真实标记、顺序、正文基线和自然段落;去掉无意义的多层场景表面不是删掉所有必要分组。
|
|
115
115
|
同一树能响应式重排时使用已有机制;若需要尚未支持的断点条件对齐/拓扑,不复制两套文字隐藏一套、不发明媒体属性。采用兼容两端且保真的最小方案并注明视觉取舍;无法满足作者明确硬约束时标未完成,而非让作者做设备调参。
|
|
116
|
+
|
|
117
|
+
## Source-backed metrics · 原文指标
|
|
118
|
+
Use `metric` for a source-backed number plus label/unit/caption; use equal-cards for independent explanatory categories. Do not invent values or convert prose-only peers into metrics. / 原文数值+标签/单位/说明用metric,独立解释类别用equal-cards;不造数据。
|
|
119
|
+
|
|
120
|
+
## Title and badge · 标题与徽标
|
|
121
|
+
Use `row align="center" justify="between" wrap="nowrap"` with a title and badge. Explicit justify controls distribution; align controls the cross axis. Between/around title leaves remain content-sized; nowrap keeps this local row, not the whole article, together. Verify long titles at the actual narrow width. / between让标题与徽标两端分布,align管交叉轴;标题保留内容宽,nowrap只约束这一局部,长标题仍须窄屏实测。
|
|
122
|
+
|
|
123
|
+
## Measured DOM connectors · 可复用的实测连线
|
|
124
|
+
|
|
125
|
+
- First choose native `relations/entity/edge` when its compact ring and detail interaction match the reader's task. It is not an arbitrary-position graph router. / 环形关系+详情合适时优先原生relations,不承诺自由拓扑排版。
|
|
126
|
+
- Custom topology, branch ports or exact placement: keep the same Compose fence and use an offline `sandbox`. Keep labels in ordinary HTML, inheriting its body font; SVG draws **lines only**, never a whole text diagram scaled to fit. The iframe is an isolated document: article web fonts are not automatically available. / 自定义拓扑用同一compose入口内的sandbox;文字留在DOM,SVG只画线,不缩放整张文字图。iframe不自动继承文章web字体,须如实验证。
|
|
127
|
+
- Reuse `measured-connectors.js` inside the sandbox's literal JS, then call `mountMeasuredConnectors(root, [{from:'a',to:'b'}, {from:'a',to:'c',route:'elbow'}, {from:'b',to:'c',route:'elbow',dashed:true}])`. The root needs `position:relative`; node elements need unique `data-node` ids. CSS owns layout, the recipe owns measurement. No new engine component/API or network import is involved. / 根容器定位,节点唯一id;CSS布局与测量连线分工,不发明组件或远程依赖。
|
|
128
|
+
- It uses ResizeObserver, font readiness and SVG screen-coordinate conversion. It returns a disposer; remount on topology changes. Supported for a stable node set: straight edges by default; `route:"elbow"` draws an axis-aligned, midpoint-bend path between measured boundary anchors; `dashed:true` adds a 6/4 dash pattern independently. All update on node/root resize, font readiness and position-only node style/class changes; dragging controls and animation are not supplied. **Not supported:** automatic obstacle avoidance, edge labels, arrowheads, dragging or automatic graph layout. Nodes must not overlap. Choose a readable non-crossing arrangement; if hard requirements need these missing behaviors, implement and test them explicitly rather than claiming this recipe supplies them. / 支持实测直线、`route:'elbow'`轴对齐中点转折与独立`dashed:true`虚线;基于真实节点框重测两端,随字体/容器重排及节点style/class移动更新;不提供自动避障、边标签、箭头、拖拽或自动排图,不能把端点测试当成完整复杂图验收。
|
|
129
|
+
- Protect readable node widths before choosing a narrow layout. For spatial relationships that must retain their arrangement, wrap the graph in a labelled, keyboard-focusable local viewport (`max-width:100%; overflow-x:auto`); a two-column graph can use `min-width:34em` and natural word wrapping (`overflow-wrap:break-word; word-break:normal`). Font enlargement increases that floor, not shrinks type. Resize the viewport, not below the node floor. If the task permits reflow, explicitly reroute edges so they do not run through intervening nodes. This is recipe CSS, not a new engine API. / 空间关系需保留时用有标签、可键盘访问的局部横滚;节点保留可读最小宽,正文自然换词,不以anywhere挤成碎词、不缩字。可重排的任务另验改序与连线,不把直排后穿过中间节点当成功。
|
|
130
|
+
- Verify actual endpoints, elbow segment axes and dash style after reflow and enlarged text at 300/344/375/430 and desktop widths; inspect crossings, reading order, scrollbar ownership and fullscreen separately. `tests/measured-connectors-browser.test.mjs` verifies the foundation, not every topology. / 按真实节点框验端点,另审交叉、阅读顺序、滚动归属与全屏;测试基础能力不等于所有拓扑都已验收。
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Offline sandbox recipe, not a Compose component or an automatic graph router.
|
|
2
|
+
// root: positioned DOM container; nodes: unique data-node ids.
|
|
3
|
+
// edges: {from,to,route?:"straight"|"elbow",dashed?:boolean}; no obstacle router.
|
|
4
|
+
function mountMeasuredConnectors(root, edges) {
|
|
5
|
+
const namespace = 'http://www.w3.org/2000/svg';
|
|
6
|
+
const nodes = new Map([...root.querySelectorAll('[data-node]')].map(node => [node.dataset.node, node]));
|
|
7
|
+
if (nodes.size !== root.querySelectorAll('[data-node]').length) throw new Error('Duplicate connector node id');
|
|
8
|
+
for (const edge of edges) {
|
|
9
|
+
if (!nodes.has(edge.from) || !nodes.has(edge.to)) throw new Error('Unknown connector node id');
|
|
10
|
+
if (edge.route !== undefined && !['straight', 'elbow'].includes(edge.route)) throw new Error('Unknown connector route');
|
|
11
|
+
if (edge.dashed !== undefined && typeof edge.dashed !== 'boolean') throw new Error('Connector dashed must be boolean');
|
|
12
|
+
}
|
|
13
|
+
const svg = document.createElementNS(namespace, 'svg');
|
|
14
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
15
|
+
svg.classList.add('connector-lines');
|
|
16
|
+
Object.assign(svg.style, { position: 'absolute', inset: '0', width: '100%', height: '100%', overflow: 'visible', pointerEvents: 'none' });
|
|
17
|
+
const paths = edges.map(edge => {
|
|
18
|
+
const line = document.createElementNS(namespace, 'path');
|
|
19
|
+
line.dataset.from = edge.from;
|
|
20
|
+
line.dataset.to = edge.to;
|
|
21
|
+
line.dataset.route = edge.route ?? 'straight';
|
|
22
|
+
if (edge.dashed) line.setAttribute('stroke-dasharray', '6 4');
|
|
23
|
+
line.setAttribute('fill', 'none');
|
|
24
|
+
line.setAttribute('stroke', 'currentColor');
|
|
25
|
+
line.setAttribute('stroke-width', '1.5');
|
|
26
|
+
svg.append(line);
|
|
27
|
+
return line;
|
|
28
|
+
});
|
|
29
|
+
root.prepend(svg);
|
|
30
|
+
let active = true;
|
|
31
|
+
let scheduled = 0;
|
|
32
|
+
function draw() {
|
|
33
|
+
scheduled = 0;
|
|
34
|
+
if (!active || !svg.isConnected) return;
|
|
35
|
+
const matrix = svg.getScreenCTM();
|
|
36
|
+
if (!matrix) return;
|
|
37
|
+
const inverse = matrix.inverse();
|
|
38
|
+
for (const [index, edge] of edges.entries()) {
|
|
39
|
+
const from = nodes.get(edge.from).getBoundingClientRect();
|
|
40
|
+
const to = nodes.get(edge.to).getBoundingClientRect();
|
|
41
|
+
const a = { x: from.x + from.width / 2, y: from.y + from.height / 2 };
|
|
42
|
+
const b = { x: to.x + to.width / 2, y: to.y + to.height / 2 };
|
|
43
|
+
// Prefer a genuinely separated axis: tall peer cards can overlap vertically.
|
|
44
|
+
// A center-only choice could send the first elbow segment back into its node.
|
|
45
|
+
const separatedX = from.right <= to.left || to.right <= from.left;
|
|
46
|
+
const separatedY = from.bottom <= to.top || to.bottom <= from.top;
|
|
47
|
+
const horizontal = separatedX !== separatedY ? separatedX : Math.abs(b.x - a.x) > Math.abs(b.y - a.y);
|
|
48
|
+
if (horizontal) {
|
|
49
|
+
const forward = b.x > a.x;
|
|
50
|
+
a.x = forward ? from.right : from.left;
|
|
51
|
+
b.x = forward ? to.left : to.right;
|
|
52
|
+
} else {
|
|
53
|
+
const forward = b.y > a.y;
|
|
54
|
+
a.y = forward ? from.bottom : from.top;
|
|
55
|
+
b.y = forward ? to.top : to.bottom;
|
|
56
|
+
}
|
|
57
|
+
const start = new DOMPoint(a.x, a.y).matrixTransform(inverse);
|
|
58
|
+
const end = new DOMPoint(b.x, b.y).matrixTransform(inverse);
|
|
59
|
+
const points = [start];
|
|
60
|
+
if (edge.route === 'elbow') {
|
|
61
|
+
// Mid-gap orthogonal route, recalculated from actual node bounds.
|
|
62
|
+
// Intentionally no obstacle search or automatic node placement.
|
|
63
|
+
if (horizontal) {
|
|
64
|
+
const x = (start.x + end.x) / 2;
|
|
65
|
+
points.push({ x, y: start.y }, { x, y: end.y });
|
|
66
|
+
} else {
|
|
67
|
+
const y = (start.y + end.y) / 2;
|
|
68
|
+
points.push({ x: start.x, y }, { x: end.x, y });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
points.push(end);
|
|
72
|
+
paths[index].setAttribute('d', points.map((point, i) => `${i ? 'L' : 'M'}${point.x},${point.y}`).join(' '));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function schedule() { if (active && !scheduled) scheduled = requestAnimationFrame(draw); }
|
|
76
|
+
const observer = new ResizeObserver(schedule);
|
|
77
|
+
observer.observe(root);
|
|
78
|
+
for (const node of nodes.values()) observer.observe(node);
|
|
79
|
+
const changes = new MutationObserver(schedule);
|
|
80
|
+
// Position-only style/class changes do not trigger ResizeObserver.
|
|
81
|
+
// Do not observe path `d` writes: measurement must not reschedule itself.
|
|
82
|
+
changes.observe(root, { attributes: true, attributeFilter: ['style', 'class'], subtree: true });
|
|
83
|
+
window.addEventListener('resize', schedule);
|
|
84
|
+
document.fonts?.ready.then(schedule);
|
|
85
|
+
schedule();
|
|
86
|
+
return () => {
|
|
87
|
+
active = false;
|
|
88
|
+
cancelAnimationFrame(scheduled);
|
|
89
|
+
observer.disconnect();
|
|
90
|
+
changes.disconnect();
|
|
91
|
+
window.removeEventListener('resize', schedule);
|
|
92
|
+
svg.remove();
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -6,14 +6,19 @@
|
|
|
6
6
|
- 关系决定共同容器:一个图标对应的标题与所有正文要属于一个重复单元;标题和正文必须同在右侧容器。分隔整条目时 divider 放两条 row 之间,而非右栏内。
|
|
7
7
|
- row 默认 wrap/stretch、8px gap;直接 card/stack/title/text 共分可用宽度,icon 固定不缩。窄屏默认会改行流;需要持续图标栏用 wrap="nowrap" 并测试不溢出。
|
|
8
8
|
- row > stack 有 **12px 顶部 padding**;不能假定任意嵌套同样表现。group 为 width:100%、min-width:0 的纵向容器,能在固定 icon 旁收缩,支持 gap,避开该上下文顶距。prose 不能凭空赋予未实现的 gap 行为。
|
|
9
|
-
- align="center"
|
|
9
|
+
- row 的 align 管交叉轴,justify 管主轴;显式 justify 始终优先,不被 align="center" 或徽标的默认居中覆盖。文字仍可能继承 text-align。当前 title/text 的显式 align="start" 会保留并输出 data-align。用一个共享 group 和显式开始对齐,不要把 title 独立成横向等分栏、正文又放回全宽。
|
|
10
10
|
- icon 名查 schema/icons 实际目录;不存在报错。SVG width 属性不等于最终像素:当前 CSS sm13、md15、lg18、xl22px,小卡上下文另有覆盖,必须测 computed/rect。
|
|
11
11
|
- 多段正文分别放 text,或用 markdown/html 岛保存真实 p。text 内空行是 inline break,不是独立段落。图标签有截断/行钳制时不能塞完整正文去丢字。
|
|
12
12
|
|
|
13
|
+
## Diagnose the host before rewriting content · 先诊断宿主
|
|
14
|
+
Measure viewport → `.article-reading-column` → `.garden-ui-body` → iframe; record each width and computed padding. Do not infer content width from a desktop breakpoint. The reading track takes priority over side rails; a frameless runtime has no body inset; fullscreen controls occupy a top band outside the frame. Post mobile TOC entries are in normal flow before the article. / 依次量视口、正文栏、运行时正文与iframe,不先删字或缩字。侧栏让位正文,frame="none"不再叠运行时内距,全屏按钮占外部顶栏,手机目录入口在正文前正常排版。
|
|
15
|
+
|
|
16
|
+
For a title and trailing badge, use `<row align="center" justify="between" wrap="nowrap"><title align="start">…</title><badge>…</badge></row>`. Its text/title children are content-sized and may shrink/wrap naturally, not width:100% columns. Explicit fills, scene framing and fullscreen are independent choices. / 标题+尾部徽标直接组合已有属性;不靠换组件、固定标题宽度或缩字绕开。
|
|
17
|
+
|
|
13
18
|
## 集中禁令与风险分级
|
|
14
19
|
- **必选结构(已选icon-rail)**:icon和一个group同属row,标题与全部正文/列表同属group;分隔线在row之间。不要正文逃出右栏,不用row>stack替代group来碰运气。
|
|
15
20
|
- **无效属性**:stack不因direction="row"变横向;用真实row。未知静态属性可能归一化丢失,当前lint不能从归一化树找回它们;对照源属性与schema,缺少未知属性诊断是引擎需求,不谎称已机器覆盖。
|
|
16
|
-
- **风险而非全局错误**:row>stack的12px顶距、居中row直接title/text的flex:1、center
|
|
21
|
+
- **风险而非全局错误**:row>stack的12px顶距、居中row直接title/text的flex:1、center仍可能向后代继承文字居中(但不覆盖显式justify)、nowrap长链、显式size和叠框。这些在其他语境可能有意,必须结合内部约束卡判断,不能一律禁用。
|
|
17
22
|
- **箭头看实际绘制方向**:right是源意图,不必等于当前屏幕方向。真实SVG默认向下;桌面旋转为right,≤800px取消旋转配合默认row转column。先看computed transform/屏幕向量,不把data-dir当几何证据。nowrap让row保持横向却不自动保留箭头旋转,须专项检查。
|
|
18
23
|
- **必须浏览器**:icon glyph光学齐平不是节点矩形相等;内容宽居中、完整主题字号/列表间距、溢出与手机回退不能从AST推断。用computed与rect及截图分别取证。
|
|
19
24
|
|
|
@@ -50,7 +55,7 @@ The authoritative, demand-loaded catalog is `npx garden ui schema`: actual seman
|
|
|
50
55
|
|
|
51
56
|
First identify what belongs together. A heading and all its paragraphs must share a container. Next choose columns and proportions, then alignments and typography. Only then add surfaces and decorative details.
|
|
52
57
|
|
|
53
|
-
A static `row` wraps by default. Its direct text/title/stack/card children share available width (`flex: 1 1 0`), whereas an icon is content-sized. A heading placed directly in that row is a sibling column, not the heading of a paragraph below the row. `align="center"`
|
|
58
|
+
A static `row` wraps by default. Its direct text/title/stack/card children share available width (`flex: 1 1 0`), whereas an icon is content-sized. A heading placed directly in that row is a sibling column, not the heading of a paragraph below the row. `align="center"` controls the cross axis and can inherit centered text; an explicit `justify` independently controls the main axis and takes precedence over default badge centering. A centered stack propagates text alignment. For an editorial list use explicit start alignment on children of a shared right-hand group; do not center each title independently.
|
|
54
59
|
|
|
55
60
|
Direct `row > stack` has 12px top padding in the current theme. A `group` is a width:100%, min-width:0 vertical container that shrinks beside the fixed icon and reads gap without that contextual padding; use it when icon and heading must begin on the same top line. Explicit `align="start"` survives normalization and emits data-align.
|
|
56
61
|
|
|
@@ -118,6 +123,8 @@ Use `markdown` or `html` islands for full paragraphs, lists, headings, blockquot
|
|
|
118
123
|
|
|
119
124
|
Safe HTML supports allowlisted local `style` properties, including useful layout and typography; inspect the generated schema for the actual set. Unsupported tags/properties, events, unsafe URLs, executable scripts and network CSS functions report diagnostics. Do not expand the article allowlist into arbitrary execution. An intentional executable demo uses `sandbox` inside the **same Compose entry**, with literal html/css/js and no parent origin access. Sandbox layout is its own document, not inherited article prose.
|
|
120
125
|
|
|
126
|
+
For measured node connections, load layout-recipes.md and measured-connectors.js: straight, midpoint orthogonal elbow and optional dashed edges follow real DOM bounds. This is a sandbox recipe, not automatic graph layout or obstacle avoidance.
|
|
127
|
+
|
|
121
128
|
## Verification levels
|
|
122
129
|
|
|
123
130
|
1. Run `npx garden ui check <file>` (and `npx garden check <file>` for real file paths). Preserve original text/resources and inspect diagnostics.
|
|
@@ -14,6 +14,7 @@ Load when choosing how to explain source content, not for every repair. Use the
|
|
|
14
14
|
| 图标窄栏、标题正文共左缘 | icon-rail:stack重复row(icon+group),divider在row间 | 不把正文放row外;不当流程 |
|
|
15
15
|
| 短阶段从左到右 | flow-row:row+真实顺序箭头;窄屏/长内容回退flow-stack | 不靠缩字/nowrap强撑手机 |
|
|
16
16
|
| 长阶段或纵向顺序 | flow-stack:stack+下箭头 | 无顺序的条目不加箭头 |
|
|
17
|
+
| 原文关键数值+标签/单位 | metric:保留原始值、单位与说明 | 不造数据;无数值的解释类别仍用equal-cards |
|
|
17
18
|
| 并列等宽卡片 | equal-cards:grid+直接card | 非真实共同维度不走compare |
|
|
18
19
|
| 图标贴标题,两者一起居中 | centered-chip:内容宽标题壳+内部图标标题行,下方正文独立 | 不只给全宽row设center;候选须浏览器验收 |
|
|
19
20
|
| 跟正文一样的列表 | prose-list:普通Markdown;场景内用markdown岛 | 不写假圆点;不为列表手感换到compare |
|
|
@@ -44,3 +44,10 @@ node .agents/skills/garden-compose/scripts/lint-layout.mjs content/posts/post.md
|
|
|
44
44
|
## Mobile acceptance · AI内部,不要求作者补规格
|
|
45
45
|
几何卡补可选`"mobile":{"readingMode":"sequence","reflow":true}`;readingMode可选sequence/scan-list/comparison/spatial,reflow为布尔。lint只在显式reflow的flow-row中拒绝场景顶层流向row的nowrap(不误禁内部贴标row);icon-rail局部nowrap不被误禁。scan-list的成组居中只给风险提示,不凭AST断言过高或难读。
|
|
46
46
|
浏览器验证遵守mobile-contract:记录实际CSS viewport、内容宽、正常/放大正文、每层滚动与实际箭头向量,并人工审查整组阅读节奏。保留首稿和源hash,失败后的修订单独编号,不能把修后结果算首轮成功。默认check参数/退出状态不变,mobile字段不是runtime DIL属性。
|
|
47
|
+
|
|
48
|
+
## Built artifact gate · 最终产物门
|
|
49
|
+
`STATIC_EXPORT=1 garden build` now checks the final export automatically. `garden verify --rendered` reruns it without rebuilding: actual HTML stylesheet/script links, literal JS imports (including lazy chunks), Compose/runtime CSS and per-page client navigation payloads. Missing assets or corrupt payloads fail. / 静态构建后自动检查真实HTML/CSS/JS依赖及导航payload,缺失即失败;不是只看源代码导入。
|
|
50
|
+
|
|
51
|
+
This is asset integrity, **not** visual or lifecycle acceptance. Complete-theme browser tests and the real site's normal Link → article → home → re-entry → history/direct sequence remain separate gates. Record iframe height messages from its own contentWindow, srcdoc length and computed geometry; a populated srcdoc alone does not prove execution. Do not claim navigation fixed from a direct-link pass. / 资源完整≠样式生效≠生命周期通过;iframe需记录实际消息、srcdoc与几何,不能凭直接链接成功宣称客户端导航已修好。
|
|
52
|
+
|
|
53
|
+
Measured connector recipes: verify boundary endpoints after enlarged labels and container resize; for elbow edges also verify every segment is axis-aligned, and dashed edges retain their dash style. Do not infer obstacle avoidance from endpoint tests. / 连线需验实测端点、肘线各段轴对齐与虚线样式;另审交叉,不能据此宣称自动避障。
|
|
@@ -110,6 +110,7 @@ export function PostReadingView({
|
|
|
110
110
|
) : null}
|
|
111
111
|
</header>
|
|
112
112
|
<div className="article-reading-layout">
|
|
113
|
+
<TableOfContents headings={headings} />
|
|
113
114
|
<div className="article-reading-column">
|
|
114
115
|
<ArticleMeasure />
|
|
115
116
|
<div className="e-content">
|
|
@@ -125,7 +126,6 @@ export function PostReadingView({
|
|
|
125
126
|
/>
|
|
126
127
|
<Comments slug={commentsSlug} />
|
|
127
128
|
</div>
|
|
128
|
-
<TableOfContents headings={headings} />
|
|
129
129
|
</div>
|
|
130
130
|
</article>
|
|
131
131
|
</main>
|
package/app/styles/a2.css
CHANGED
|
@@ -274,7 +274,7 @@ body {
|
|
|
274
274
|
--rail: 200px;
|
|
275
275
|
--gap: 72px;
|
|
276
276
|
display: grid;
|
|
277
|
-
grid-template-columns: minmax(0,
|
|
277
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, var(--col)) minmax(0, 1fr);
|
|
278
278
|
column-gap: var(--gap);
|
|
279
279
|
justify-content: center;
|
|
280
280
|
align-items: start;
|
|
@@ -300,6 +300,8 @@ body {
|
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
.post-reading-page .article-reading-layout > .toc {
|
|
303
|
+
min-width: 0;
|
|
304
|
+
overflow-wrap: anywhere;
|
|
303
305
|
grid-column: 3;
|
|
304
306
|
grid-row: 1;
|
|
305
307
|
margin-top: 0;
|
|
@@ -1582,6 +1584,16 @@ html[data-theme="dark"] .g-quiz-out.ok { color: #8fd1a0; }
|
|
|
1582
1584
|
html[data-theme="dark"] .g-quiz-out.no { color: #f29aab; }
|
|
1583
1585
|
.g-quiz-ex { margin-top: 8px; font-size: 15px; color: var(--soft); }
|
|
1584
1586
|
.g-play { border: 1px solid var(--rule); border-radius: var(--r); padding: 14px 16px; margin: 24px 0; background: transparent; }
|
|
1587
|
+
/* Fullscreen controls own a top band, not a second frame or a narrow text rail. */
|
|
1588
|
+
.ux-fs > .ux-fsbar + .g-play { padding: 44px 0 0; border: 0; }
|
|
1589
|
+
.g-play > .garden-ui { margin: 0; }
|
|
1590
|
+
@media (max-width: 900px) {
|
|
1591
|
+
.post-reading-page .toc-mobile-trigger,
|
|
1592
|
+
.post-reading-page .toc-nojs-trigger {
|
|
1593
|
+
position: static;
|
|
1594
|
+
margin: 0 0 16px;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1585
1597
|
.g-play-f { margin: 0 0 10px; font-size: 14px; color: var(--soft); }
|
|
1586
1598
|
.g-range { display: grid; grid-template-columns: 110px minmax(0, 1fr) 64px; gap: 12px; align-items: center; font-size: 14px; color: var(--muted); margin: 6px 0; }
|
|
1587
1599
|
.g-range input { width: 100%; accent-color: var(--blue); min-height: 32px; }
|
package/app/styles/shell.css
CHANGED
|
@@ -36,15 +36,15 @@ img,svg{max-width:100%}
|
|
|
36
36
|
/* ---------- header ---------- */
|
|
37
37
|
.sh-header{position:sticky;top:0;z-index:40;background:var(--paper);border-bottom:1px solid transparent;transition:border-color .2s ease}
|
|
38
38
|
.sh-header.is-scrolled{border-bottom-color:var(--rule)}
|
|
39
|
-
.sh-bar{max-width:var(--shell);margin:0 auto;height:var(--hdr);padding:0 32px;display:flex;align-items:center;gap:28px}
|
|
40
|
-
.sh-word{display:inline-flex;align-items:center;gap:10px;font:600 var(--fs-lead)/1 var(--serif);color:var(--ink);text-decoration:none;white-space:
|
|
39
|
+
.sh-bar{max-width:var(--shell);margin:0 auto;min-height:var(--hdr);height:auto;padding:0 32px;display:flex;align-items:center;gap:28px}
|
|
40
|
+
.sh-word{display:inline-flex;align-items:center;gap:10px;font:600 var(--fs-lead)/1 var(--serif);color:var(--ink);text-decoration:none;white-space:normal;overflow-wrap:anywhere;min-width:0;flex:0 1 auto;line-height:1.2;padding:8px 0}
|
|
41
41
|
.sh-mark{display:inline-flex;flex:none}
|
|
42
42
|
.sh-mark svg{display:block;height:26px;width:auto}
|
|
43
|
-
.sh-nav{display:flex;gap:4px;margin-left:auto}
|
|
43
|
+
.sh-nav{display:flex;flex-shrink:0;gap:4px;margin-left:auto}
|
|
44
44
|
.sh-nav a{font-size:15px;color:var(--soft);text-decoration:none;padding:8px 12px;border-radius:var(--r);transition:color .15s ease,background-color .15s ease}
|
|
45
45
|
.sh-nav a:hover{color:var(--ink);background:var(--tint)}
|
|
46
46
|
.sh-nav a[aria-current="page"],.sh-nav a[aria-current="true"]{color:var(--blue);font-weight:600}
|
|
47
|
-
.sh-tools{display:flex;align-items:center;gap:8px}
|
|
47
|
+
.sh-tools{display:flex;flex-shrink:0;align-items:center;gap:8px}
|
|
48
48
|
/* A hairline box so the entry reads as a field, not a stray word. */
|
|
49
49
|
.sh-search{display:inline-flex;align-items:center;gap:6px;box-sizing:border-box;height:34px;padding:0 10px 0 12px;border:1px solid var(--rule);background:transparent;color:var(--soft);border-radius:8px;font:400 15px/1 var(--sans);cursor:pointer;appearance:none;transition:color .15s ease,border-color .15s ease}
|
|
50
50
|
.sh-search:hover{color:var(--ink);border-color:var(--rule-strong)}
|
package/bin/garden.mjs
CHANGED
|
@@ -495,7 +495,7 @@ function help() {
|
|
|
495
495
|
garden files 读取 Compose <file> 里远程文件的大小,写入 file-sizes.json(build 时会自动做)
|
|
496
496
|
garden init [目录] 创建只含稿和配置的新站点
|
|
497
497
|
garden check <文章.md> 检查一篇博文的硬性结构
|
|
498
|
-
garden verify [文章.md|--diff] 全量/单篇/改动文档严格预检(包含草稿;make check 使用)
|
|
498
|
+
garden verify [文章.md|--diff|--rendered] 全量/单篇/改动文档严格预检(包含草稿;make check 使用)
|
|
499
499
|
garden ui <子命令> Intelligent UI:scan、check、apply、prompt、schema、catalog
|
|
500
500
|
garden hooks 安装提交前检查(检查暂存稿件里的 ui 块)
|
|
501
501
|
garden editors install 安装 Intelligent UI 插件到 VS Code / Cursor 与 Typora(--vscode、--typora、--yes)
|
|
@@ -544,6 +544,7 @@ async function main() {
|
|
|
544
544
|
runNode("scripts/html-lang.mjs");
|
|
545
545
|
// 404.html also under basePath when the host serves that folder alone.
|
|
546
546
|
runNode("scripts/static-404.mjs");
|
|
547
|
+
runNode("scripts/rendered-site.mjs");
|
|
547
548
|
}
|
|
548
549
|
break;
|
|
549
550
|
case "links":
|
|
@@ -565,7 +566,12 @@ async function main() {
|
|
|
565
566
|
runNode("scripts/fetch-embed.mjs", rest);
|
|
566
567
|
break;
|
|
567
568
|
case "verify":
|
|
568
|
-
|
|
569
|
+
if (rest[0] === "--rendered") {
|
|
570
|
+
if (rest.length !== 1) fail("Usage: garden verify --rendered");
|
|
571
|
+
runNode("scripts/rendered-site.mjs");
|
|
572
|
+
} else {
|
|
573
|
+
runNode("scripts/check-site.mjs", rest);
|
|
574
|
+
}
|
|
569
575
|
break;
|
|
570
576
|
case "check":
|
|
571
577
|
runNode("scripts/check-post.mjs", rest);
|
|
@@ -24,6 +24,8 @@ badge: compose
|
|
|
24
24
|
|
|
25
25
|
先说明视觉关系与重点,再使用受支持的属性表达必要样式。默认样式不保证复刻参考图,必须检查真实排版。
|
|
26
26
|
|
|
27
|
+
静态 Compose 最外层场景默认背景透明,边框、圆角与内边距仍保留。无底色不等于无外框;需要去掉整个场景外框时,使用唯一最外层 `<figure frame="none">…</figure>`。内部容器显式指定的背景、tone 与节点填色不受影响;全屏或弹层的背景由宿主单独处理。
|
|
28
|
+
|
|
27
29
|
**完整标签与属性**见 [组件参考 · compose](/garden/reference/compose);**全部图标名**见 [compose 图标](/garden/reference/compose-icons)。
|
|
28
30
|
|
|
29
31
|
## 写法对照
|
|
@@ -24,6 +24,8 @@ Cover four things:
|
|
|
24
24
|
|
|
25
25
|
Describe the visual relationships and desired emphasis. Defaults are a starting point, not a substitute for checking alignment and hierarchy. Explicit styling must use supported controls.
|
|
26
26
|
|
|
27
|
+
The outer static Compose scene is transparent by default; its border, corner radius, and padding remain. A transparent background does not remove the frame. To remove the entire scene frame, use one outer `<figure frame="none">…</figure>`. Explicit container backgrounds, tones, and node fills are unchanged. The host handles fullscreen or overlay backgrounds separately.
|
|
28
|
+
|
|
27
29
|
**Every tag and shared token**: [compose reference](/garden/en/reference/compose). **Every icon name**: [compose icons](/garden/en/reference/compose-icons).
|
|
28
30
|
|
|
29
31
|
## Rewrite patterns
|
|
@@ -109,3 +109,10 @@ Safe HTML supports allowlisted local `style` properties, including useful layout
|
|
|
109
109
|
3. Inspect the complete-theme render: icon rail fixed, heading and **all** paragraphs share the same right edge/left edge, no centered headings or full-width body beneath a detached icon row, dividers outside rows, no unintended cards/arrows, no horizontal overflow.
|
|
110
110
|
4. Check desktop and narrow screenshots plus geometry/computed styles; exercise choices, relations, file previews, state updates and sandbox behavior when used.
|
|
111
111
|
5. A hand-authored passing scene proves engine capability, not that an independent model reliably produces it from a short prompt. Report those claims separately.
|
|
112
|
+
|
|
113
|
+
## Host and exported-artifact acceptance
|
|
114
|
+
Explicit `justify` owns the main axis; `align` owns the cross axis. A title and trailing badge can use `row align="center" justify="between"`. Reading width takes priority over side rails; frameless runtimes do not add body padding; fullscreen controls use a separate top band. The post's mobile contents entry stays in normal flow. Measure the real column and iframe before changing content or type size.
|
|
115
|
+
|
|
116
|
+
Use native relations for ring-and-detail interactions. Custom topology stays in the same Compose entry, using an offline sandbox, HTML labels and connector-only SVG. The author skill includes a measured-endpoint recipe, not an automatic layout/obstacle router. An isolated iframe does not automatically inherit article web fonts.
|
|
117
|
+
|
|
118
|
+
Static builds check final HTML links, CSS/JS and lazy dependencies, Compose styles and navigation payloads. Rerun with `garden verify --rendered`. Asset integrity is not visual or client-lifecycle proof: test normal homepage Link entry, return/re-entry, history and direct links separately.
|
|
@@ -111,3 +111,10 @@ Safe HTML supports allowlisted local `style` properties, including useful layout
|
|
|
111
111
|
3. Inspect the complete-theme render: icon rail fixed, heading and **all** paragraphs share the same right edge/left edge, no centered headings or full-width body beneath a detached icon row, dividers outside rows, no unintended cards/arrows, no horizontal overflow.
|
|
112
112
|
4. Check desktop and narrow screenshots plus geometry/computed styles; exercise choices, relations, file previews, state updates and sandbox behavior when used.
|
|
113
113
|
5. A hand-authored passing scene proves engine capability, not that an independent model reliably produces it from a short prompt. Report those claims separately.
|
|
114
|
+
|
|
115
|
+
## 宿主与最终产物验收
|
|
116
|
+
显式 `justify` 管主轴,`align` 管交叉轴;标题+徽标可直接使用 `row align="center" justify="between"`。正文栏优先于侧栏,frame="none"不叠运行时内距,全屏按钮使用独立顶栏;手机目录入口随正文正常排版。先量真实正文/iframe宽度,不用缩字补救宿主挤压。
|
|
117
|
+
|
|
118
|
+
复杂连线:原生relations适用于环形关系与详情;自定义拓扑使用同一compose内的离线sandbox,普通HTML承载文字,SVG只画线。作者skill提供实测端点配方,不提供自动排图/避障。iframe字体须单独验证,不能声称隔离文档自动继承文章web字体。
|
|
119
|
+
|
|
120
|
+
静态构建自动检查最终HTML引用的CSS/JS及懒加载依赖、Compose样式与导航payload;`garden verify --rendered`可单独重跑。它不证明视觉或客户端导航;仍需测试首页正常Link、返回再入、历史及直接链接。
|
|
Binary file
|