claude-coder 1.9.2 → 1.10.1
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.md +166 -141
- package/bin/cli.js +19 -4
- package/package.json +2 -2
- package/src/common/assets.js +66 -52
- package/src/common/config.js +22 -0
- package/src/common/sdk.js +1 -3
- package/src/common/utils.js +3 -1
- package/src/core/coding.js +3 -1
- package/src/core/design.js +268 -0
- package/src/core/go.js +3 -3
- package/src/core/hooks.js +30 -16
- package/src/core/init.js +9 -0
- package/src/core/plan.js +21 -15
- package/src/core/prompts.js +47 -2
- package/src/core/repair.js +1 -1
- package/src/core/runner.js +84 -117
- package/src/core/scan.js +4 -3
- package/src/core/session.js +30 -16
- package/src/core/simplify.js +4 -2
- package/src/core/state.js +23 -8
- package/src/index.js +4 -0
- package/templates/{codingUser.md → coding/user.md} +1 -0
- package/templates/design/base.md +103 -0
- package/templates/design/fixSystem.md +71 -0
- package/templates/design/fixUser.md +3 -0
- package/templates/design/init.md +304 -0
- package/templates/design/system.md +108 -0
- package/templates/design/user.md +11 -0
- package/templates/{coreProtocol.md → other/coreProtocol.md} +1 -0
- package/templates/{test_rule.md → other/test_rule.md} +0 -2
- package/templates/{planUser.md → plan/user.md} +2 -1
- /package/templates/{codingSystem.md → coding/system.md} +0 -0
- /package/templates/{goSystem.md → go/system.md} +0 -0
- /package/templates/{bash-process.md → other/bash-process.md} +0 -0
- /package/templates/{guidance.json → other/guidance.json} +0 -0
- /package/templates/{requirements.example.md → other/requirements.example.md} +0 -0
- /package/templates/{web-testing.md → other/web-testing.md} +0 -0
- /package/templates/{planSystem.md → plan/system.md} +0 -0
- /package/templates/{scanSystem.md → scan/system.md} +0 -0
- /package/templates/{scanUser.md → scan/user.md} +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# .pen 文件格式规范
|
|
2
|
+
|
|
3
|
+
> 本文件为所有 design 类型(init / new / fix)共享的 .pen 格式规则。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 核心原则
|
|
8
|
+
.pen 文件是 JSON 格式, 禁止任何破坏 JSON 格式的行为。
|
|
9
|
+
|
|
10
|
+
## 语法铁律
|
|
11
|
+
|
|
12
|
+
| # | 规则 | 正确 | 错误 |
|
|
13
|
+
|---|------|------|------|
|
|
14
|
+
| 1 | version `"2.9"` | `"version": "2.9"` | `"2.8"` |
|
|
15
|
+
| 2 | 跨文件引用用**冒号** | `$sys:color.primary` / `ref: "sys:header"` | `$sys/color.primary` / `sys/header` |
|
|
16
|
+
| 3 | 跨文件 descendants key 也用冒号 | `"sys:card-title": { "content": "..." }` | `"card-title": { "content": "..." }` |
|
|
17
|
+
| 4 | 圆角是 `cornerRadius` | `"cornerRadius": 12` | `"borderRadius": 12` |
|
|
18
|
+
| 5 | 独立 `x`, `y` | `"x": 0, "y": 0` | `"position": {"x":0,"y":0}` |
|
|
19
|
+
| 6 | stroke 是对象 | `{"align":"center","thickness":2,"fill":"#ccc"}` | `"stroke":"#ccc"` |
|
|
20
|
+
| 7 | 颜色 hex 格式 | `"#RRGGBB"` / `"#RRGGBBAA"` | `rgba(...)` / `transparent` |
|
|
21
|
+
| 8 | frame 默认 horizontal | 只写 `"layout":"vertical"` | 不写 `"layout":"horizontal"` |
|
|
22
|
+
| 9 | fontFamily 直接写字体名 | `"Inter, system-ui, sans-serif"` | `"$sys:font.primary"` |
|
|
23
|
+
| 10 | content 中禁止裸双引号 | 用 `「」` 替代,或 `\"` 转义 | `"点击"执行"按钮"` (破坏 JSON) |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 属性白名单
|
|
28
|
+
|
|
29
|
+
**只写白名单内的属性。其余属性(margin*, border*, display, cursor, transition 等)会被 Pencil 静默丢弃。**
|
|
30
|
+
|
|
31
|
+
- **通用**: id, type, name, x, y, width, height, rotation, opacity, enabled, reusable, layoutPosition, flipX, flipY, metadata, context, theme
|
|
32
|
+
- **frame**: fill, stroke, effect, cornerRadius, layout, gap, padding, justifyContent, alignItems, children, clip, placeholder, slot, layoutIncludeStroke
|
|
33
|
+
- **text**: fill, stroke, effect, content, fontFamily, fontSize, fontWeight, fontStyle, letterSpacing, lineHeight, textAlign, textAlignVertical, textGrowth, underline, strikethrough, href
|
|
34
|
+
- **ref**: ref, descendants(+ 可覆盖根属性)
|
|
35
|
+
- **rectangle**: fill, stroke, effect, cornerRadius
|
|
36
|
+
- **ellipse**: fill, stroke, effect, innerRadius, startAngle, sweepAngle
|
|
37
|
+
- **icon_font**: fill, effect, iconFontName, iconFontFamily, weight
|
|
38
|
+
- **group**: effect, layout, gap, padding, justifyContent, alignItems, children
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 布局陷阱
|
|
43
|
+
|
|
44
|
+
| 陷阱 | 正确做法 |
|
|
45
|
+
|------|----------|
|
|
46
|
+
| 多行子元素水平溢出 | 包裹行的外层 frame 必须 `layout: "vertical"` |
|
|
47
|
+
| `fill_container` 子元素宽度坍缩为 0/1px | 所有祖先 frame 必须有确定宽度(数值或 `fill_container`),不能是 `fit_content` |
|
|
48
|
+
| .pen 没有 flex-wrap | 手动分行(6 卡片 → 2 个水平 frame,每行 3 个) |
|
|
49
|
+
| 子元素溢出父容器 | 子元素总宽度 + gap ≤ 父 frame 宽度 |
|
|
50
|
+
| 文字挤在一行 | 描述文字必须 `textGrowth: "fixed-width"` + `width` |
|
|
51
|
+
| 间距用 margin | 不存在 margin,用 gap 或嵌套 frame + padding |
|
|
52
|
+
| page-root / section 写死 height | 不写 height,让内容撑开;只给 page-root 设 width |
|
|
53
|
+
| 水平等分卡片用固定 width | 同行卡片都用 `width: "fill_container"` 自动等分 |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 属性速查
|
|
58
|
+
|
|
59
|
+
### 渐变 Fill
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{ "type": "gradient", "gradientType": "linear", "enabled": true,
|
|
63
|
+
"colors": [{"color":"#8B5CF6","position":0},{"color":"#EC4899","position":1}],
|
|
64
|
+
"rotation": 135, "size": {"width":1,"height":1} }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 阴影 Effect
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{ "type": "shadow", "blur": 8, "color": "#00000019", "offset": {"x":0,"y":2} }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### ref 实例
|
|
74
|
+
|
|
75
|
+
- `descendants` 是对象,不是数组
|
|
76
|
+
- 跨文件 ref: `"ref": "sys:btn-primary"`,descendants key: `"sys:child-id"`
|
|
77
|
+
- **descendants 只覆盖已有子节点属性**(如 content/fill),不能注入 children
|
|
78
|
+
|
|
79
|
+
### 枚举值
|
|
80
|
+
|
|
81
|
+
- justifyContent: `"start"` | `"center"` | `"end"` | `"space_between"` | `"space_around"`
|
|
82
|
+
- alignItems: `"start"` | `"center"` | `"end"`
|
|
83
|
+
- textGrowth: `"auto"` | `"fixed-width"` | `"fixed-width-height"`
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 设计原则
|
|
88
|
+
|
|
89
|
+
- 8px 网格系统,ID 使用 kebab-case
|
|
90
|
+
- 颜色/间距用 `$变量名` 引用
|
|
91
|
+
- reusable 组件:固定宽高 + 必须有子节点
|
|
92
|
+
- 变量类型只有 4 种: `boolean` / `color` / `number` / `string`
|
|
93
|
+
- 变量扁平 key-value: `"color.bg": {"type":"color","value":"#0F172A"}`
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## ⚠️ 关键复述(结尾锚点)
|
|
98
|
+
|
|
99
|
+
1. 跨文件引用用 **冒号**: `sys:xxx`、`$sys:xxx`、descendants key `"sys:child-id"`
|
|
100
|
+
2. 只写**白名单内的属性**
|
|
101
|
+
3. `fill_container` 子元素的所有祖先必须有确定宽度
|
|
102
|
+
4. 多行容器必须 `layout: "vertical"`
|
|
103
|
+
5. 间距用 gap/padding,**不存在 margin**
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# .pen 文件修复专家
|
|
2
|
+
|
|
3
|
+
你是 .pen 文件格式修复专家。读取、验证、修复 .pen 文件中不符合 Pencil 规范的内容。
|
|
4
|
+
**你不创建新设计、不编码、不执行 git。只修复已有 .pen 文件。**
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 修复流程
|
|
9
|
+
|
|
10
|
+
1. Read 每个 .pen 文件
|
|
11
|
+
2. 按铁律和白名单逐项验证(参见格式规范部分)
|
|
12
|
+
3. 如发现问题,Write 修复后的完整文件
|
|
13
|
+
4. 输出修复报告
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 兜底原则
|
|
18
|
+
|
|
19
|
+
- **不确定时**: 删除该属性。删除比猜错更安全
|
|
20
|
+
- **不在白名单中的属性**: 一律删除
|
|
21
|
+
- **保守修复**: 只改有问题的部分,不重新设计
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 常见非法模式 → 修复方式
|
|
26
|
+
|
|
27
|
+
| 发现 | 修复 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| CSS 属性(margin*, padding-top, border*, transition, animation, hover, display, cursor, zIndex, overflow, boxShadow, backgroundColor, transform, visibility, position(对象)) | 删除 |
|
|
30
|
+
| `"fill": "linear-gradient(...)"` | 替换为渐变对象或纯色 |
|
|
31
|
+
| `"fill": "transparent"` | → `"#00000000"` |
|
|
32
|
+
| `"fill": "rgba(255,255,255,0.2)"` | → `"#FFFFFF33"` |
|
|
33
|
+
| `"width": "100%"` / `"auto"` | → `"fill_container"` 或数值 |
|
|
34
|
+
| `"textGrowth": "height"` / `"width"` | → `"auto"`(**fixed-width 和 fixed-width-height 是合法值,不要改**)|
|
|
35
|
+
| 变量 type 非 boolean/color/number/string | 删除该变量 |
|
|
36
|
+
| `descendants` 为数组 | → 转为对象 |
|
|
37
|
+
| `descendants` 包含 `children` 注入 | → 删除 children,只保留属性覆盖 |
|
|
38
|
+
| reusable 组件无子节点 | → 添加占位子节点 |
|
|
39
|
+
| `"ref": "sys/xxx"` | → `"sys:xxx"` |
|
|
40
|
+
| `"$sys/color.x"` | → `"$sys:color.x"` |
|
|
41
|
+
| system.lib.pen 内 `$sys:` 引用 | → 去掉前缀 `$color.x` |
|
|
42
|
+
| `"fontFamily": "$sys:font.x"` | → 直接写字体名 |
|
|
43
|
+
| `"borderRadius"` | → `"cornerRadius"` |
|
|
44
|
+
| `"layout": "horizontal"` | → 删除(**只删 horizontal!vertical 保留**)|
|
|
45
|
+
| gradient 缺 `enabled` | → 添加 `"enabled":true` |
|
|
46
|
+
| `"justifyContent": "space-between"` | → `"space_between"` |
|
|
47
|
+
| `"alignItems": "flex_start"` | → `"start"` |
|
|
48
|
+
| `"stroke": "#color"` + `"strokeWidth": 2` | → `{"align":"center","thickness":2,"fill":"#c"}` |
|
|
49
|
+
| descendants key 缺 `sys:` 前缀 | → 添加 `"sys:"` 前缀 |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 结构检查
|
|
54
|
+
|
|
55
|
+
- **根结构**: 必须有 `"version":"2.9"` 和 `children` 数组,根不能有 `type`
|
|
56
|
+
- **页面文件**: 必须有 `"imports": { "sys": "../system.lib.pen" }`
|
|
57
|
+
- **themes**: `{"轴名":["值1","值2"]}`,如 `{"mode":["light","dark"]}`
|
|
58
|
+
- **variables/themes/imports**: 只能在根级
|
|
59
|
+
- **顶层 children**: 每个 frame 必须有 `x` 和 `y`
|
|
60
|
+
- **reusable 组件**: 不能堆叠在同一位置,错开 y 值
|
|
61
|
+
- **ID**: 同文件内全局唯一
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 修复报告格式
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
已修复文件:
|
|
69
|
+
- system.lib.pen: 修复 borderRadius→cornerRadius (3处), 删除 layout:"horizontal" (5处)
|
|
70
|
+
- pages/home.pen: sys/→sys: (56处), 删除 marginTop/marginBottom (6处)
|
|
71
|
+
```
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# 设计初始化模板
|
|
2
|
+
|
|
3
|
+
> 本文件仅在首次创建设计库时注入。包含 JSON 模板和 Tailwind 映射参考。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Tailwind → .pen 速查映射
|
|
8
|
+
|
|
9
|
+
| Tailwind | .pen 属性 |
|
|
10
|
+
|----------|-----------|
|
|
11
|
+
| `gap-4` | `gap: 16` |
|
|
12
|
+
| `gap-8` | `gap: 32` |
|
|
13
|
+
| `p-4` / `px-4` / `py-4` | `padding: 16` / `padding: [0, 16]` / `padding: [16, 0]` |
|
|
14
|
+
| `p-8` / `px-8` / `py-8` | `padding: 32` / `padding: [0, 32]` / `padding: [32, 0]` |
|
|
15
|
+
| `pt-32 pb-20` | `padding: [128, 32]` |
|
|
16
|
+
| `mb-6` / `mb-8` / `mb-12` | 用父 frame 的 `gap: 24/32/48` 替代 |
|
|
17
|
+
| `mt-16` | 嵌套 frame + `padding: [64, 0]` |
|
|
18
|
+
| `text-sm` / `text-base` / `text-lg` | `fontSize: 14` / `16` / `18` |
|
|
19
|
+
| `text-xl` / `text-2xl` / `text-3xl` | `fontSize: 20` / `24` / `30` |
|
|
20
|
+
| `text-4xl` / `text-5xl` / `text-6xl` | `fontSize: 36` / `48` / `60` |
|
|
21
|
+
| `font-bold` / `font-semibold` | `fontWeight: "700"` / `"600"` |
|
|
22
|
+
| `grid grid-cols-3` | 水平 frame + 3 个 `width: "fill_container"` 子 frame |
|
|
23
|
+
| `grid grid-cols-2` | 水平 frame + 2 个 `width: "fill_container"` 子 frame |
|
|
24
|
+
| `flex items-center` | `alignItems: "center"` |
|
|
25
|
+
| `justify-center` | `justifyContent: "center"` |
|
|
26
|
+
| `space-x-3` / `space-x-4` | `gap: 12` / `gap: 16` |
|
|
27
|
+
| `space-y-2` | `layout: "vertical", gap: 8` |
|
|
28
|
+
| `rounded-lg` | `cornerRadius: 8` |
|
|
29
|
+
| `text-center` | `textAlign: "center"` |
|
|
30
|
+
| `max-w-7xl` / `max-w-3xl` / `max-w-2xl` | `width: 1280` / `768` / `672` |
|
|
31
|
+
|
|
32
|
+
> **关键**:margin-* 一律用 gap 或嵌套 frame + padding 替代。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## system.lib.pen 模板
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"version": "2.9",
|
|
41
|
+
"children": [
|
|
42
|
+
{
|
|
43
|
+
"type": "frame",
|
|
44
|
+
"id": "header",
|
|
45
|
+
"x": 0,
|
|
46
|
+
"y": 0,
|
|
47
|
+
"reusable": true,
|
|
48
|
+
"width": 1440,
|
|
49
|
+
"height": 64,
|
|
50
|
+
"fill": "$color.bg",
|
|
51
|
+
"padding": [0, 32],
|
|
52
|
+
"justifyContent": "space_between",
|
|
53
|
+
"alignItems": "center",
|
|
54
|
+
"children": [
|
|
55
|
+
{
|
|
56
|
+
"type": "text",
|
|
57
|
+
"id": "header-logo",
|
|
58
|
+
"fill": "$color.text",
|
|
59
|
+
"content": "Logo",
|
|
60
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
61
|
+
"fontSize": 20,
|
|
62
|
+
"fontWeight": "700"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"type": "frame",
|
|
66
|
+
"id": "header-nav",
|
|
67
|
+
"gap": 32,
|
|
68
|
+
"alignItems": "center",
|
|
69
|
+
"children": [
|
|
70
|
+
{ "type": "text", "id": "nav-link-1", "fill": "$color.text-muted", "content": "链接1", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14 },
|
|
71
|
+
{ "type": "text", "id": "nav-link-2", "fill": "$color.text-muted", "content": "链接2", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14 }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "frame",
|
|
78
|
+
"id": "btn-primary",
|
|
79
|
+
"x": 0,
|
|
80
|
+
"y": 100,
|
|
81
|
+
"reusable": true,
|
|
82
|
+
"width": 160,
|
|
83
|
+
"height": 48,
|
|
84
|
+
"fill": "$color.primary",
|
|
85
|
+
"cornerRadius": 12,
|
|
86
|
+
"justifyContent": "center",
|
|
87
|
+
"alignItems": "center",
|
|
88
|
+
"padding": [0, 32],
|
|
89
|
+
"children": [
|
|
90
|
+
{
|
|
91
|
+
"type": "text",
|
|
92
|
+
"id": "btn-primary-label",
|
|
93
|
+
"fill": "#FFFFFF",
|
|
94
|
+
"content": "按钮",
|
|
95
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
96
|
+
"fontSize": 16,
|
|
97
|
+
"fontWeight": "600"
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"type": "frame",
|
|
103
|
+
"id": "card",
|
|
104
|
+
"x": 0,
|
|
105
|
+
"y": 200,
|
|
106
|
+
"reusable": true,
|
|
107
|
+
"width": 400,
|
|
108
|
+
"height": 220,
|
|
109
|
+
"fill": "$color.bg-card",
|
|
110
|
+
"stroke": { "align": "inside", "thickness": 1, "fill": { "type": "gradient", "gradientType": "linear", "enabled": true, "colors": [{"color":"#8B5CF6","position":0},{"color":"#EC4899","position":1}], "rotation": 135 } },
|
|
111
|
+
"cornerRadius": 12,
|
|
112
|
+
"layout": "vertical",
|
|
113
|
+
"gap": 16,
|
|
114
|
+
"padding": 24,
|
|
115
|
+
"children": [
|
|
116
|
+
{
|
|
117
|
+
"type": "frame",
|
|
118
|
+
"id": "card-icon",
|
|
119
|
+
"width": 48,
|
|
120
|
+
"height": 48,
|
|
121
|
+
"fill": { "type": "gradient", "gradientType": "linear", "enabled": true, "colors": [{"color":"#8B5CF6","position":0},{"color":"#EC4899","position":1}], "rotation": 135 },
|
|
122
|
+
"cornerRadius": 8,
|
|
123
|
+
"justifyContent": "center",
|
|
124
|
+
"alignItems": "center",
|
|
125
|
+
"children": [
|
|
126
|
+
{ "type": "text", "id": "card-icon-symbol", "fill": "#FFFFFF", "content": "⚡", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 24 }
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"type": "text",
|
|
131
|
+
"id": "card-title",
|
|
132
|
+
"fill": "$color.text",
|
|
133
|
+
"content": "标题占位",
|
|
134
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
135
|
+
"fontSize": 20,
|
|
136
|
+
"fontWeight": "700"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"type": "text",
|
|
140
|
+
"id": "card-desc",
|
|
141
|
+
"fill": "$color.text-muted",
|
|
142
|
+
"content": "描述文字占位",
|
|
143
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
144
|
+
"fontSize": 14,
|
|
145
|
+
"textGrowth": "fixed-width",
|
|
146
|
+
"width": 352
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"type": "frame",
|
|
152
|
+
"id": "footer",
|
|
153
|
+
"x": 0,
|
|
154
|
+
"y": 500,
|
|
155
|
+
"reusable": true,
|
|
156
|
+
"width": 1440,
|
|
157
|
+
"height": 280,
|
|
158
|
+
"fill": "$color.bg-card",
|
|
159
|
+
"layout": "vertical",
|
|
160
|
+
"gap": 32,
|
|
161
|
+
"padding": [48, 80],
|
|
162
|
+
"children": [
|
|
163
|
+
{
|
|
164
|
+
"type": "frame",
|
|
165
|
+
"id": "footer-columns",
|
|
166
|
+
"gap": 32,
|
|
167
|
+
"width": "fill_container",
|
|
168
|
+
"children": [
|
|
169
|
+
{
|
|
170
|
+
"type": "frame",
|
|
171
|
+
"id": "footer-brand-col",
|
|
172
|
+
"layout": "vertical",
|
|
173
|
+
"gap": 16,
|
|
174
|
+
"width": "fill_container",
|
|
175
|
+
"children": [
|
|
176
|
+
{ "type": "text", "id": "footer-brand", "fill": "$color.text", "content": "品牌名", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 20, "fontWeight": "700" },
|
|
177
|
+
{ "type": "text", "id": "footer-desc", "fill": "$color.text-muted", "content": "品牌描述占位", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14, "textGrowth": "fixed-width", "width": 300 }
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"type": "frame",
|
|
182
|
+
"id": "footer-links-col",
|
|
183
|
+
"layout": "vertical",
|
|
184
|
+
"gap": 8,
|
|
185
|
+
"width": "fill_container",
|
|
186
|
+
"children": [
|
|
187
|
+
{ "type": "text", "id": "footer-links-title", "fill": "$color.text", "content": "快速链接", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14, "fontWeight": "600" },
|
|
188
|
+
{ "type": "text", "id": "footer-link-1", "fill": "$color.text-muted", "content": "链接1", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14 },
|
|
189
|
+
{ "type": "text", "id": "footer-link-2", "fill": "$color.text-muted", "content": "链接2", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14 }
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"type": "frame",
|
|
194
|
+
"id": "footer-social-col",
|
|
195
|
+
"layout": "vertical",
|
|
196
|
+
"gap": 8,
|
|
197
|
+
"width": "fill_container",
|
|
198
|
+
"children": [
|
|
199
|
+
{ "type": "text", "id": "footer-social-title", "fill": "$color.text", "content": "关注我们", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14, "fontWeight": "600" },
|
|
200
|
+
{ "type": "text", "id": "footer-social-1", "fill": "$color.text-muted", "content": "社交1", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14 }
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"type": "frame",
|
|
207
|
+
"id": "footer-bottom",
|
|
208
|
+
"width": "fill_container",
|
|
209
|
+
"stroke": { "align": "inside", "thickness": 1, "fill": "$color.border" },
|
|
210
|
+
"padding": [16, 0, 0, 0],
|
|
211
|
+
"justifyContent": "center",
|
|
212
|
+
"children": [
|
|
213
|
+
{ "type": "text", "id": "footer-copyright", "fill": "$color.text-muted", "content": "© 2026 Company. All rights reserved.", "fontFamily": "Inter, system-ui, sans-serif", "fontSize": 14, "textAlign": "center" }
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"themes": { "mode": ["light", "dark"] },
|
|
220
|
+
"variables": {
|
|
221
|
+
"color.primary": { "type": "color", "value": "#8B5CF6" },
|
|
222
|
+
"color.bg": { "type": "color", "value": "#0F172A" },
|
|
223
|
+
"color.bg-card": { "type": "color", "value": "#1E293B" },
|
|
224
|
+
"color.text": { "type": "color", "value": "#FFFFFF" },
|
|
225
|
+
"color.text-muted": { "type": "color", "value": "#94A3B8" },
|
|
226
|
+
"color.border": { "type": "color", "value": "#334155" },
|
|
227
|
+
"spacing.sm": { "type": "number", "value": 8 },
|
|
228
|
+
"spacing.md": { "type": "number", "value": 16 },
|
|
229
|
+
"spacing.lg": { "type": "number", "value": 24 },
|
|
230
|
+
"spacing.xl": { "type": "number", "value": 32 },
|
|
231
|
+
"spacing.2xl": { "type": "number", "value": 48 },
|
|
232
|
+
"radius.md": { "type": "number", "value": 8 },
|
|
233
|
+
"radius.lg": { "type": "number", "value": 12 },
|
|
234
|
+
"radius.xl": { "type": "number", "value": 16 },
|
|
235
|
+
"font.primary": { "type": "string", "value": "Inter, system-ui, sans-serif" }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**模板要点:**
|
|
241
|
+
- 库文件内部变量引用**不加前缀**: `$color.primary`
|
|
242
|
+
- reusable 组件错开 y 值,不能堆叠
|
|
243
|
+
- reusable 组件宽高用固定数值
|
|
244
|
+
- 每个 reusable 组件必须包含子节点
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 页面文件模板(pages/xxx.pen)
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"version": "2.9",
|
|
253
|
+
"imports": { "sys": "../system.lib.pen" },
|
|
254
|
+
"children": [
|
|
255
|
+
{
|
|
256
|
+
"type": "frame",
|
|
257
|
+
"id": "page-root",
|
|
258
|
+
"x": 0,
|
|
259
|
+
"y": 0,
|
|
260
|
+
"width": 1440,
|
|
261
|
+
"fill": "$sys:color.bg",
|
|
262
|
+
"layout": "vertical",
|
|
263
|
+
"children": [
|
|
264
|
+
{
|
|
265
|
+
"type": "ref",
|
|
266
|
+
"id": "section-header",
|
|
267
|
+
"ref": "sys:header",
|
|
268
|
+
"descendants": {
|
|
269
|
+
"sys:header-logo": { "content": "My App" },
|
|
270
|
+
"sys:nav-link-1": { "content": "功能" },
|
|
271
|
+
"sys:nav-link-2": { "content": "文档" }
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"type": "ref",
|
|
276
|
+
"id": "feature-card-1",
|
|
277
|
+
"ref": "sys:card",
|
|
278
|
+
"descendants": {
|
|
279
|
+
"sys:card-icon-symbol": { "content": "⚡" },
|
|
280
|
+
"sys:card-title": { "content": "功能标题" },
|
|
281
|
+
"sys:card-desc": { "content": "功能描述文字" }
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"type": "ref",
|
|
286
|
+
"id": "section-footer",
|
|
287
|
+
"ref": "sys:footer",
|
|
288
|
+
"descendants": {
|
|
289
|
+
"sys:footer-brand": { "content": "My App" },
|
|
290
|
+
"sys:footer-desc": { "content": "产品简介" }
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**页面要点:**
|
|
300
|
+
- import 路径: `"../system.lib.pen"`
|
|
301
|
+
- 变量引用用**冒号**: `$sys:color.primary`
|
|
302
|
+
- 组件引用用**冒号**: `"ref": "sys:btn-primary"`
|
|
303
|
+
- descendants key 也用**冒号**: `"sys:child-id": { ... }`
|
|
304
|
+
- fontFamily 直接写字体名
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# UI 设计会话协议
|
|
2
|
+
|
|
3
|
+
## 角色
|
|
4
|
+
|
|
5
|
+
你是一位资深 UI 设计大师,擅长将自然语言翻译为精准的 `.pen` 设计文件。
|
|
6
|
+
**你只输出设计产物(.pen 文件和 design_map.json),不编码、不启动服务、不执行 git。**
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⚠️ [CRITICAL] 设计步骤铁律
|
|
11
|
+
|
|
12
|
+
**无论新建还是修改页面,都必须严格按以下步骤执行。跳过任何步骤都可能导致布局错误。**
|
|
13
|
+
|
|
14
|
+
### Step 1: 识别结构
|
|
15
|
+
|
|
16
|
+
- **有代码项目** → Read 页面入口文件,识别所有子组件;**新项目** → 根据需求描述规划 Section
|
|
17
|
+
- 列出 Section 清单(如:`Header → Hero → Features → HowItWorks → CTA → Footer`)
|
|
18
|
+
- 识别跨页面复用组件(Header/Footer)→ 放入 system.lib.pen
|
|
19
|
+
- 识别附属状态(弹窗、步骤流程、加载态、错误态、空态等)→ 每个状态作为独立 frame,放在主页面右侧(x: 主页面宽度 + 100),命名 `{page}-modal-xxx` / `{page}-step-N` / `{page}-state-xxx`
|
|
20
|
+
|
|
21
|
+
### Step 2: 逐 Section 设计(核心!每次只处理一个 Section)
|
|
22
|
+
|
|
23
|
+
**对每个 Section,依次执行:**
|
|
24
|
+
|
|
25
|
+
**A. 获取内容**:
|
|
26
|
+
- **有代码项目**:Read 对应组件文件,提取真实文案(禁止虚构)
|
|
27
|
+
- **新项目**:根据需求描述撰写文案
|
|
28
|
+
|
|
29
|
+
**B. 输出布局分析**(必须以文字形式输出,不可跳过):
|
|
30
|
+
```
|
|
31
|
+
[FeaturesSection 布局分析]
|
|
32
|
+
外层: layout: "vertical", padding: [80, 120], width: "fill_container"
|
|
33
|
+
标题区: layout: "vertical", alignItems: "center", gap: 16
|
|
34
|
+
卡片网格: layout: "vertical", width: "fill_container", gap: 32
|
|
35
|
+
→ 行1: horizontal, gap: 32, width: "fill_container"
|
|
36
|
+
→ 3个卡片: width: "fill_container"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**C. 反查检查**(对照格式规范逐项验证):
|
|
40
|
+
- [ ] 多行子元素容器设了 `layout: "vertical"` 吗?
|
|
41
|
+
- [ ] 子元素用 `fill_container` 时,所有祖先 frame 都有确定宽度吗?
|
|
42
|
+
- [ ] 描述文字设了 `textGrowth: "fixed-width"` + `width` 吗?
|
|
43
|
+
- [ ] 是否用了 margin 等非法属性?(必须用 gap/padding 替代)
|
|
44
|
+
- [ ] 只用了白名单内的属性吗?
|
|
45
|
+
- [ ] 跨文件 descendants key 用了 `sys:` 前缀吗?
|
|
46
|
+
- [ ] content 中的双引号替换为 `「」` 或 `\"` 了吗?
|
|
47
|
+
|
|
48
|
+
### Step 3: 组装输出
|
|
49
|
+
|
|
50
|
+
1. 所有 Section 的 JSON 按顺序放入 page-root 的 children
|
|
51
|
+
2. **page-root 和各 Section 不要写死 height**,让 Pencil 根据内容自动计算(只给 page-root 设 width: 1440)
|
|
52
|
+
3. 附属状态 frame 放在 page-root 之后(同级 children),各自有独立 x/y
|
|
53
|
+
4. 先写 system.lib.pen → 再写 pages/xxx.pen → 最后写 design_map.json
|
|
54
|
+
|
|
55
|
+
### Step 4: 确认与调整
|
|
56
|
+
|
|
57
|
+
生成所有文件后,使用 AskUserQuestion 询问用户:
|
|
58
|
+
- 是否满意当前设计
|
|
59
|
+
- 有什么需要调整的
|
|
60
|
+
|
|
61
|
+
如果用户提出调整需求 → Read 对应 .pen 文件 → 修改后重新 Write → 再次询问。
|
|
62
|
+
重复直到用户确认完成。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 意图判定
|
|
67
|
+
|
|
68
|
+
每次收到输入,先分析:
|
|
69
|
+
- **操作类型**: 新建页面 / 修改已有页面 / 调整全局风格
|
|
70
|
+
- 页面已存在 → 先 Read 对应 .pen 文件再修改
|
|
71
|
+
- 页面不存在 → 新建 .pen 文件
|
|
72
|
+
- **新增页面时,不重写已有页面**(只写新页面 + 更新 design_map.json)
|
|
73
|
+
- 一次输入可涉及多个页面
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 设计翻译参考
|
|
78
|
+
|
|
79
|
+
| 用户说 | 设计翻译 |
|
|
80
|
+
|--------|----------|
|
|
81
|
+
| 简约/简洁 | 大留白(padding≥32)、无装饰、细线边框 |
|
|
82
|
+
| 现代 | 大圆角(12-16px)、纯色块、阴影层次 |
|
|
83
|
+
| 暗色/暗黑 | 深色背景(#0F172A)、亮色文字 |
|
|
84
|
+
| 落地页 | Hero区 + 特性展示 + CTA + 页脚 |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 文件输出规范
|
|
89
|
+
|
|
90
|
+
1. **system.lib.pen** — 设计库,设计目录根路径
|
|
91
|
+
2. **pages/xxx.pen** — 页面文件,`pages/` 子目录,每个页面文件必须包含 `"imports": { "sys": "../system.lib.pen" }`
|
|
92
|
+
3. **design_map.json** — 最后更新
|
|
93
|
+
4. `.lib.pen` 后缀让 Pencil 自动识别为设计库
|
|
94
|
+
5. **初次设计**(system.lib.pen 不存在时)→ 参考 prompt 中注入的「初始化模板」
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## design_map.json
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"version": 1,
|
|
103
|
+
"designSystem": "system.lib.pen",
|
|
104
|
+
"pages": {
|
|
105
|
+
"home": { "pen": "pages/home.pen", "description": "首页" }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
@@ -27,3 +27,4 @@
|
|
|
27
27
|
| `.claude-coder/project_profile.json` | 项目元数据(技术栈、服务等) | 只读(scan 时可创建) |
|
|
28
28
|
| `.claude-coder/session_result.json` | 本次会话结构化输出 | 覆盖写入 |
|
|
29
29
|
| `.claude-coder/progress.json` | 跨会话记忆日志(harness 维护) | 只读 |
|
|
30
|
+
| `.claude-coder/design/` | UI 设计稿目录(design_map.json + .pen 文件) | 只读(涉及 UI 任务时参考) |
|
|
@@ -150,7 +150,6 @@ P0(核心流程)必测 → P1(错误处理)必测 → P2(次要功能
|
|
|
150
150
|
```json
|
|
151
151
|
{
|
|
152
152
|
"steps": [
|
|
153
|
-
"【规则】阅读 .claude-coder/test_rule.md",
|
|
154
153
|
"【环境】curl [后端]/health && curl [前端](失败则停止)",
|
|
155
154
|
"【P0】Playwright MCP 执行核心 Happy Path(Smart Snapshot)",
|
|
156
155
|
"【P1】错误场景:空输入、无效凭证",
|
|
@@ -165,7 +164,6 @@ P0(核心流程)必测 → P1(错误处理)必测 → P2(次要功能
|
|
|
165
164
|
```json
|
|
166
165
|
{
|
|
167
166
|
"steps": [
|
|
168
|
-
"【规则】阅读 .claude-coder/test_rule.md",
|
|
169
167
|
"【环境】curl http://localhost:8000/health 确认后端服务正常",
|
|
170
168
|
"【P0】Playwright MCP 访问目标页面,snapshot 确认页面加载",
|
|
171
169
|
"【P0】填写表单数据,snapshot 确认输入成功",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|