@szc-ft/mcp-szcd-client 0.25.0 → 0.26.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.
@@ -131,6 +131,27 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
131
131
  | 元素归属 | 凭 x/y 坐标判定("x=272 列都是同区域") | 看 `getMultipleNodeInfo` 返回的 `parent.id/name` 字段 |
132
132
  | 颜色提取 | 跳过 group 只取 text/rectangle | 同批提交 group ID,从 `style.fills` 取渐变背景 |
133
133
 
134
+ #### getMultipleNodeInfo 返回字段契约(sketch-mcp-server ≥ v1.6.1)
135
+
136
+ | 字段 | 适用类型 | 用途 |
137
+ |------|----------|------|
138
+ | `position:{x,y}` + `size:{width,height}` | 全部 | 步骤 2.6 表 1/2/3/4 的坐标推理 |
139
+ | `parent:{id,name,type}` | 全部(顶层除外) | 步骤 2.6 表 1 大区归属、Pass 2 质检 |
140
+ | `style.fills[].{color,gradient}` | rectangle / group / text | 表 5 样式映射 |
141
+ | `style.borders[].{color,thickness}` | rectangle / group | 表 5 样式映射 |
142
+ | `style.shadows[]` | rectangle / group | 表 5 样式映射 |
143
+ | `text.attributes[0].attributes.{fontFamily,fontSize,color,lineHeight}` | text | 字号/字重/颜色(fontFamily 编码字重,如 PingFangSC-Semibold → 600) |
144
+ | `children:[id]` | group / artboard | 推断容器子节点集合(与 parent 反向印证) |
145
+ | `isVisualContainer:bool` | group / artboard | true = 该 group 自身带 fills/borders/shadows,必须作为 UI 容器渲染(不是纯图层分组) |
146
+ | `shape.cornerRadius` | rectangle / oval | 圆角 |
147
+
148
+ **字段缺失自检**(任一项失败 → sketch-mcp-server 版本过旧,升级到 ≥ v1.6.1 后重跑):
149
+
150
+ - [ ] 任取一个 text 节点,`parent.id` 非空 ✅
151
+ - [ ] 任取一个 group 节点,`isVisualContainer` 类型是 boolean、`children` 是数组 ✅
152
+ - [ ] 任取一个 rectangle 节点,`style.fills` 数组存在(即使为空也要是 `[]` 不能 undefined) ✅
153
+ - [ ] 任取一个 text 节点,`text.attributes[0].attributes.fontFamily` 可读出(编码字重) ✅
154
+
134
155
  **实战经验沉淀(重要)**:
135
156
 
136
157
  **收获 1:首轮 `getMultipleNodeInfo` 必须全量拉取,不能挑**
@@ -145,15 +166,20 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
145
166
  错误做法:先查 text 拿自身样式 → 再根据 parent ID 单独查 rectangle(多走一步)
146
167
  正确做法:用 `queryNodes(type=["text","rectangle","group"])` 一次拿三类 ID,`getMultipleNodeInfo` 返回的 `parent` 字段直接告诉你归属关系
147
168
 
148
- **收获 3:元素归属必须走 group 父子关系,禁止 x 坐标平面判定**
169
+ **收获 3:归属判定走 x+y+parent 三轴联合,禁止单轴定论(坐标和 parent 互为纠错器)**
149
170
 
150
- 实战教训:`'主题'text(x=272,y=149)` 被错误识别为内容区块标题,实际是左侧树标题(与树节点同 x=272 列)。同列坐标不代表同区域。
171
+ 实战教训:
172
+ - 反例 A:`'主题'text(x=272,y=149)` 单看 x 在右侧内容区列,但 `parent.name=leftTreeHeader` 强语义 → 归左树(情形 C:parent 修正坐标)
173
+ - 反例 B(反馈 #11):`'段码含义'text(x=300,y=257)` 坐标落在 `渐变容器(x=240..960, y=189..297)` 内,但 parent 是兄弟编组 `cataMain` → 视觉上属于渐变容器内(情形 B:图层拆开,按坐标渲染)
151
174
 
152
175
  正确做法:
153
- - 步骤 2 拿到完整节点树后,先识别顶层 group 分区(通常 2-5 个,对应 LeftTree区/Query+Table区/信息栏区/标题栏区)
154
- - `getMultipleNodeInfo` v1.5.0+ **自动返回** `parent: { id, name, type }` 字段,按 parent 归属到具体分区
155
- - "主题"这类标题文本,先看 `parent.name` 是否带 "LeftTree" / "树" / "PageContent" 等容器关键字,不要看 x 坐标
156
- - 坐标仅作为同 parent 内子元素排序兜底使用
176
+ - 步骤 2.5 4 步先 `mode="tree"` 看顶层 group 分区(通常 2-5 个)
177
+ - 步骤 2.6 做双向校验:Pass 1 坐标推假设 Pass 2 parent 质检 四类情形 A/B/C/D 处理
178
+ - 大区归属:`parent.name` `LeftTree/树/PageContent/sidebar/cataMain` 等强语义关键字时优先于 x 分布
179
+ - 容器归属:必须 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]`,单轴判定无效
180
+ - 跨分区不比 y;同分区内按 y 升序;同行(y 差<8px)按 x 升序
181
+
182
+ 详细决策矩阵见步骤 2.6。
157
183
 
158
184
  **收获 4:信息栏/卡片容器必须先取 group 再取子元素**
159
185
 
@@ -199,14 +225,129 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
199
225
 
200
226
  - **兜底方案**:① 用 `extractBitmaps` 提取的 PNG/PDF 资源;② `matchIconFromName` 的 Top-1(若 source=compound);③ unmatched 时按 parent 上下文 + 上表选 antd `@ant-design/icons` 复合图标;④ 在代码注释中标注"⚠️ 矢量图标用 antd 图标替代,原始 Sketch 为 shapePath"
201
227
 
202
- **提示(sketch-mcp-server v1.5.0+)**:
228
+ **收获 8:rectangle 的 fill/border 必须以内联样式或精确选择器落到 DOM,禁止借用通用 CSS class**
229
+
230
+ 实战教训(反馈 #11):5 个段码视觉块的颜色(#f2f8fd/#f5ffed/#fffbe6/#f5f6f7)完整地从 rectangle 节点拿到了,但代码却用了 `.beforebg{background:#ebf0ff}` 这种与设计稿无对应关系的通用 class,导致 5 个块颜色全错。
231
+
232
+ 正确做法:
233
+ - 步骤 2.6 的样式映射表(表 5)必须为每个"颜色块"列出 rectangle 数据 → 内联 style 的对应关系
234
+ - 写代码时直接用 `<div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}>`
235
+ - 若必须用 CSS class,类名要能反向追到 Sketch 节点(如 `.segmentBlock-blue` 而非 `.beforebg`),且 class 内容必须**精确等于** rectangle 的 fill/border
236
+ - 反例排查:写完代码后 grep 一下 CSS class 名,出现 `.bg1/.box2/.before*` 这类无语义类名、且颜色和设计稿对不上 → 几乎确定是凭空兜底
237
+
238
+ ### 步骤2.6:布局推理(必做,写代码前的最后一道关)
239
+
240
+ ⚠️ 这一步专治"数据全拿到却放错位置"。反馈 #11 的 5 处错位全部由跳过此步骤导致。
241
+
242
+ **核心思路**:x+y 坐标先画"视觉假设",parent 字段做质检纠错。两套信号**互为纠错器**,不是单方向"谁优先"。
243
+
244
+ **输入**:步骤 2.5 产出的 `text + rectangle + group` 节点全量数据(含 `position.{x,y}`、`size.{width,height}`、`parent.{id,name,type}`、`style`、`isVisualContainer`)
245
+
246
+ #### Pass 1:坐标推假设(产出 5 张表)
247
+
248
+ 每张表都必须输出,即使某类节点为空也要写"无"。
249
+
250
+ ##### 表 1:分区识别表(x+y+parent 联合,定大区)
251
+
252
+ ```
253
+ | 节点 name | x | y | parent.name | 推断分区 | 决策依据 |
254
+ |----------------|-----|-----|------------------|--------------|-------------------------|
255
+ | 主题(text) | 272 | 149 | leftTreeHeader | 左树标题区 | parent 强语义压过 x 列 |
256
+ | 段码定义(text) | 320 | 200 | cataMain | 右侧内容-顶部 | parent 含 PageContent 类语义 |
257
+ ```
258
+
259
+ 判定流程:
260
+ 1. 按 x 把画板切成 1-3 列(x<300 → 左区、x≥300 → 右区,或按设计稿实际边界)
261
+ 2. 用 `parent.name` 校验:含 `LeftTree/树/sidebar/cataMain/PageContent/header/footer` 等关键字时以 parent 为准
262
+ 3. 冲突时遵循"收获 3"的 A/B/C/D 矩阵(见下方 Pass 2)
263
+
264
+ → 此表输出**布局类型**:UpDown(单列)/ LeftRight(双列)/ LeftMiddleRight(三列)
265
+
266
+ ##### 表 2:纵向顺序表(同分区内按 y)
267
+
268
+ ```
269
+ | 分区 | 节点 | y | 推断角色 |
270
+ |------------|---------------|-----|-------------------|
271
+ | 右侧内容区 | 段码定义 | 200 | 卡片 1 |
272
+ | 右侧内容区 | Tabs | 420 | Tabs(type=card) |
273
+ | 右侧内容区 | 配置说明 | 478 | 卡片 2(Tabs 下方) |
274
+ ```
275
+
276
+ 铁律:**同分区内**渲染顺序按 y 升序。跨分区不比 y(左树第 100 行 y 和右内容区第 1 行 y 没有可比性)。
277
+
278
+ ##### 表 3:横向排列表(同行按 x,y 差<8px 视为同行)
279
+
280
+ ```
281
+ | 分区/容器 | 节点 | x | y | 推断角色 |
282
+ |-----------------|--------------|-----|-----|-------------------------|
283
+ | Query 操作栏 | 名称输入框 | 320 | 50 | Form.Item col-span=6 |
284
+ | Query 操作栏 | 状态下拉 | 540 | 50 | Form.Item col-span=6 |
285
+ | Query 操作栏 | 查询按钮 | 980 | 50 | 操作按钮(右对齐) |
286
+ ```
287
+
288
+ 判定流程:
289
+ 1. y 差<8px 识别同一行
290
+ 2. 同行按 x 升序排
291
+ 3. 计算 x 间距推算 antd Col span(24 栅格)或 flex
292
+ 4. 最右元素若 `x + width` 接近容器右边界 → 右对齐(justify-content: flex-end)
293
+
294
+ ##### 表 4:容器归属表(x+y 双轴边界检测)
295
+
296
+ 仅对 `isVisualContainer === true` 的 group 和带 fill/border 的 rectangle 做检测。
297
+
298
+ ```
299
+ | 容器 | x..x+w | y..y+h | 落入子元素 |
300
+ |------------------|----------|----------|----------------------------------|
301
+ | 渐变容器(group) | 240..960 | 189..297 | 段码含义(x=300,y=257) ✅ |
302
+ | 段码块-蓝(rect) | 320..420 | 320..360 | (无子节点,仅样式) |
303
+ ```
304
+
305
+ 判定式:子元素 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]` → 渲染在容器内部。**单轴判定无效**。
306
+
307
+ ##### 表 5:样式映射表(rectangle/group fill → UI 组件内联样式)
308
+
309
+ ```
310
+ | Sketch 节点 | 数据来源 | bg/border | 映射到代码 |
311
+ |----------------------|--------------------------|--------------------------|-----------------------------------------------------------|
312
+ | 段码块-蓝(rectangle) | style.fills[0]/borders[0]| bg #f2f8fd / b #d9eafb | <div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}> |
313
+ | 渐变容器(group) | style.fills[0].gradient | linear #f3faff→#f8fdff | <div style={{background:'linear-gradient(180deg,#f3faff,#f8fdff)'}}> |
314
+ | Tab 激活态 | rect bg #fff border #0079f7 | bg #fff / b #0079f7 | Tabs type="card" + .segmentTabs .ant-tabs-tab-active 覆盖 |
315
+ ```
316
+
317
+ 铁律(见收获 8):rectangle 的 fill/border 必须以**内联样式**或精确选择器落到代码,**禁止**用 `.beforebg{background:#ebf0ff}` 这类与设计稿无对应关系的 CSS class。
318
+
319
+ #### Pass 2:parent 质检(按 A/B/C/D 矩阵处理冲突)
320
+
321
+ 逐行扫表 1,对照 parent 字段:
322
+
323
+ | 情形 | 坐标判定 | parent 判定 | 处理 |
324
+ |------|----------|-------------|------|
325
+ | A | 在容器 X 内 | parent 链含 X | ✅ 双向一致,直接采用 |
326
+ | B | 在容器 X 内 | parent 是兄弟节点 Y | ⚠️ **视觉容器优先**(设计师把图层拆开了)→ 按坐标渲染到 X 内 |
327
+ | C | 不在容器 X 内 | parent.name 强语义 = X | ⚠️ **parent 优先**(绝对定位/溢出布局)→ 按 parent 归属,加 `position:absolute` 标注 |
328
+ | D | 双向都不落 | parent 是顶层 | 独立元素,按坐标走纵向顺序 |
329
+
330
+ 输出**最终归属表**:在表 1 末尾追加 `情形(A/B/C/D)` 列。
331
+
332
+ #### Pass 3:自检清单(写代码前必跑)
333
+
334
+ - [ ] 表 1 是否按 x 分布判定了布局类型(UpDown / LeftRight / LeftMiddleRight)?
335
+ - [ ] 表 2 同分区内是否按 y 排序,没有跨分区比 y?
336
+ - [ ] 表 3 同行元素(y 差<8px)是否按 x 排序,计算了栅格 span 或右对齐?
337
+ - [ ] 表 4 容器归属是否做了 x+y 双轴检测,不是只看一轴?
338
+ - [ ] 表 5 所有"颜色块/卡片/渐变"样式都来自 rectangle/group 的 style.fills/borders,没有出现凭空 CSS class?
339
+ - [ ] Pass 2 冲突行是否标了 A/B/C/D 情形和决策依据?
340
+
341
+ 任一项 ❌ → 回 Pass 1 补;禁止跳到步骤 3 前未输出全部 5 张表 + 最终归属表。
342
+
343
+ **提示(sketch-mcp-server v1.6.1+)**:
203
344
  - 大型 .sketch 文件先 `queryNodes(pageId, type="artboard")` 锁定目标画板,避免一次性加载整个文档
204
345
  - `queryNodes(pageId, type=["text","rectangle","group"])` 一次拿三类节点 ID(type 字段支持数组),跳过 `getPageStructure(mode="ids")` 单独调用
205
346
  - `getPageStructure(pageId, mode="tree", maxDepth=2-3)` 用于看分区层级;`mode="summary"` 用于按类型统计概况
206
- - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段,元素归属直接走 parent,禁止用 x/y 坐标
347
+ - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段 + group/artboard 额外有 `children` 和 `isVisualContainer` —— 步骤 2.6 的双向校验同时用 parent x/y,不是只挑一个
207
348
  - `matchIconFromName` 评分制返回 Top-3 + `bestMatch.source === "compound"` 强映射,shapePath 节点优先走它
208
349
  - 仅当结构化样式数据仍不足(如复杂渐变、阴影)时,才回退到 `renderNodeAsBase64` + `analyze_design_image`
209
- - 如果 sketch-mcp-server 工具不可用,提示用户安装:`npm install -g @szc-ft/sketch-mcp-server`
350
+ - 如果 sketch-mcp-server 工具不可用或版本 < v1.6.1(parent / isVisualContainer 字段缺失),提示用户升级:`npm install -g @szc-ft/sketch-mcp-server@latest`
210
351
 
211
352
  ### 步骤3:分析设计稿(条件执行)
212
353
 
@@ -332,6 +473,7 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
332
473
  - ProTable/ProForm 等使用 valueType 配置列/字段类型
333
474
  - 复合组件配合 TemplateMode 使用实现标准页面布局
334
475
 
476
+
335
477
  **交互节点**:代码生成后,主动询问用户是否需要调整:
336
478
  - "代码已生成,是否需要调整样式或交互细节?"
337
479
  - "是否需要补充表单校验规则?"
@@ -127,6 +127,27 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
127
127
  | 元素归属 | 凭 x/y 坐标判定("x=272 列都是同区域") | 看 `getMultipleNodeInfo` 返回的 `parent.id/name` 字段 |
128
128
  | 颜色提取 | 跳过 group 只取 text/rectangle | 同批提交 group ID,从 `style.fills` 取渐变背景 |
129
129
 
130
+ #### getMultipleNodeInfo 返回字段契约(sketch-mcp-server ≥ v1.6.1)
131
+
132
+ | 字段 | 适用类型 | 用途 |
133
+ |------|----------|------|
134
+ | `position:{x,y}` + `size:{width,height}` | 全部 | 步骤 2.6 表 1/2/3/4 的坐标推理 |
135
+ | `parent:{id,name,type}` | 全部(顶层除外) | 步骤 2.6 表 1 大区归属、Pass 2 质检 |
136
+ | `style.fills[].{color,gradient}` | rectangle / group / text | 表 5 样式映射 |
137
+ | `style.borders[].{color,thickness}` | rectangle / group | 表 5 样式映射 |
138
+ | `style.shadows[]` | rectangle / group | 表 5 样式映射 |
139
+ | `text.attributes[0].attributes.{fontFamily,fontSize,color,lineHeight}` | text | 字号/字重/颜色(fontFamily 编码字重,如 PingFangSC-Semibold → 600) |
140
+ | `children:[id]` | group / artboard | 推断容器子节点集合(与 parent 反向印证) |
141
+ | `isVisualContainer:bool` | group / artboard | true = 该 group 自身带 fills/borders/shadows,必须作为 UI 容器渲染(不是纯图层分组) |
142
+ | `shape.cornerRadius` | rectangle / oval | 圆角 |
143
+
144
+ **字段缺失自检**(任一项失败 → sketch-mcp-server 版本过旧,升级到 ≥ v1.6.1 后重跑):
145
+
146
+ - [ ] 任取一个 text 节点,`parent.id` 非空 ✅
147
+ - [ ] 任取一个 group 节点,`isVisualContainer` 类型是 boolean、`children` 是数组 ✅
148
+ - [ ] 任取一个 rectangle 节点,`style.fills` 数组存在(即使为空也要是 `[]` 不能 undefined) ✅
149
+ - [ ] 任取一个 text 节点,`text.attributes[0].attributes.fontFamily` 可读出(编码字重) ✅
150
+
130
151
  **实战经验沉淀(重要)**:
131
152
 
132
153
  **收获 1:首轮 `getMultipleNodeInfo` 必须全量拉取,不能挑**
@@ -141,15 +162,20 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
141
162
  错误做法:先查 text 拿自身样式 → 再根据 parent ID 单独查 rectangle(多走一步)
142
163
  正确做法:用 `queryNodes(type=["text","rectangle","group"])` 一次拿三类 ID,`getMultipleNodeInfo` 返回的 `parent` 字段直接告诉你归属关系
143
164
 
144
- **收获 3:元素归属必须走 group 父子关系,禁止 x 坐标平面判定**
165
+ **收获 3:归属判定走 x+y+parent 三轴联合,禁止单轴定论(坐标和 parent 互为纠错器)**
145
166
 
146
- 实战教训:`'主题'text(x=272,y=149)` 被错误识别为内容区块标题,实际是左侧树标题(与树节点同 x=272 列)。同列坐标不代表同区域。
167
+ 实战教训:
168
+ - 反例 A:`'主题'text(x=272,y=149)` 单看 x 在右侧内容区列,但 `parent.name=leftTreeHeader` 强语义 → 归左树(情形 C:parent 修正坐标)
169
+ - 反例 B(反馈 #11):`'段码含义'text(x=300,y=257)` 坐标落在 `渐变容器(x=240..960, y=189..297)` 内,但 parent 是兄弟编组 `cataMain` → 视觉上属于渐变容器内(情形 B:图层拆开,按坐标渲染)
147
170
 
148
171
  正确做法:
149
- - 步骤 2 拿到完整节点树后,先识别顶层 group 分区(通常 2-5 个,对应 LeftTree区/Query+Table区/信息栏区/标题栏区)
150
- - `getMultipleNodeInfo` v1.5.0+ **自动返回** `parent: { id, name, type }` 字段,按 parent 归属到具体分区
151
- - "主题"这类标题文本,先看 `parent.name` 是否带 "LeftTree" / "树" / "PageContent" 等容器关键字,不要看 x 坐标
152
- - 坐标仅作为同 parent 内子元素排序兜底使用
172
+ - 步骤 2.5 4 步先 `mode="tree"` 看顶层 group 分区(通常 2-5 个)
173
+ - 步骤 2.6 做双向校验:Pass 1 坐标推假设 Pass 2 parent 质检 四类情形 A/B/C/D 处理
174
+ - 大区归属:`parent.name` `LeftTree/树/PageContent/sidebar/cataMain` 等强语义关键字时优先于 x 分布
175
+ - 容器归属:必须 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]`,单轴判定无效
176
+ - 跨分区不比 y;同分区内按 y 升序;同行(y 差<8px)按 x 升序
177
+
178
+ 详细决策矩阵见步骤 2.6。
153
179
 
154
180
  **收获 4:信息栏/卡片容器必须先取 group 再取子元素**
155
181
 
@@ -195,14 +221,129 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
195
221
 
196
222
  - **兜底方案**:① 用 `extractBitmaps` 提取的 PNG/PDF 资源;② `matchIconFromName` 的 Top-1(若 source=compound);③ unmatched 时按 parent 上下文 + 上表选 antd `@ant-design/icons` 复合图标;④ 在代码注释中标注"⚠️ 矢量图标用 antd 图标替代,原始 Sketch 为 shapePath"
197
223
 
198
- **提示(sketch-mcp-server v1.5.0+)**:
224
+ **收获 8:rectangle 的 fill/border 必须以内联样式或精确选择器落到 DOM,禁止借用通用 CSS class**
225
+
226
+ 实战教训(反馈 #11):5 个段码视觉块的颜色(#f2f8fd/#f5ffed/#fffbe6/#f5f6f7)完整地从 rectangle 节点拿到了,但代码却用了 `.beforebg{background:#ebf0ff}` 这种与设计稿无对应关系的通用 class,导致 5 个块颜色全错。
227
+
228
+ 正确做法:
229
+ - 步骤 2.6 的样式映射表(表 5)必须为每个"颜色块"列出 rectangle 数据 → 内联 style 的对应关系
230
+ - 写代码时直接用 `<div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}>`
231
+ - 若必须用 CSS class,类名要能反向追到 Sketch 节点(如 `.segmentBlock-blue` 而非 `.beforebg`),且 class 内容必须**精确等于** rectangle 的 fill/border
232
+ - 反例排查:写完代码后 grep 一下 CSS class 名,出现 `.bg1/.box2/.before*` 这类无语义类名、且颜色和设计稿对不上 → 几乎确定是凭空兜底
233
+
234
+ ### 步骤2.6:布局推理(必做,写代码前的最后一道关)
235
+
236
+ ⚠️ 这一步专治"数据全拿到却放错位置"。反馈 #11 的 5 处错位全部由跳过此步骤导致。
237
+
238
+ **核心思路**:x+y 坐标先画"视觉假设",parent 字段做质检纠错。两套信号**互为纠错器**,不是单方向"谁优先"。
239
+
240
+ **输入**:步骤 2.5 产出的 `text + rectangle + group` 节点全量数据(含 `position.{x,y}`、`size.{width,height}`、`parent.{id,name,type}`、`style`、`isVisualContainer`)
241
+
242
+ #### Pass 1:坐标推假设(产出 5 张表)
243
+
244
+ 每张表都必须输出,即使某类节点为空也要写"无"。
245
+
246
+ ##### 表 1:分区识别表(x+y+parent 联合,定大区)
247
+
248
+ ```
249
+ | 节点 name | x | y | parent.name | 推断分区 | 决策依据 |
250
+ |----------------|-----|-----|------------------|--------------|-------------------------|
251
+ | 主题(text) | 272 | 149 | leftTreeHeader | 左树标题区 | parent 强语义压过 x 列 |
252
+ | 段码定义(text) | 320 | 200 | cataMain | 右侧内容-顶部 | parent 含 PageContent 类语义 |
253
+ ```
254
+
255
+ 判定流程:
256
+ 1. 按 x 把画板切成 1-3 列(x<300 → 左区、x≥300 → 右区,或按设计稿实际边界)
257
+ 2. 用 `parent.name` 校验:含 `LeftTree/树/sidebar/cataMain/PageContent/header/footer` 等关键字时以 parent 为准
258
+ 3. 冲突时遵循"收获 3"的 A/B/C/D 矩阵(见下方 Pass 2)
259
+
260
+ → 此表输出**布局类型**:UpDown(单列)/ LeftRight(双列)/ LeftMiddleRight(三列)
261
+
262
+ ##### 表 2:纵向顺序表(同分区内按 y)
263
+
264
+ ```
265
+ | 分区 | 节点 | y | 推断角色 |
266
+ |------------|---------------|-----|-------------------|
267
+ | 右侧内容区 | 段码定义 | 200 | 卡片 1 |
268
+ | 右侧内容区 | Tabs | 420 | Tabs(type=card) |
269
+ | 右侧内容区 | 配置说明 | 478 | 卡片 2(Tabs 下方) |
270
+ ```
271
+
272
+ 铁律:**同分区内**渲染顺序按 y 升序。跨分区不比 y(左树第 100 行 y 和右内容区第 1 行 y 没有可比性)。
273
+
274
+ ##### 表 3:横向排列表(同行按 x,y 差<8px 视为同行)
275
+
276
+ ```
277
+ | 分区/容器 | 节点 | x | y | 推断角色 |
278
+ |-----------------|--------------|-----|-----|-------------------------|
279
+ | Query 操作栏 | 名称输入框 | 320 | 50 | Form.Item col-span=6 |
280
+ | Query 操作栏 | 状态下拉 | 540 | 50 | Form.Item col-span=6 |
281
+ | Query 操作栏 | 查询按钮 | 980 | 50 | 操作按钮(右对齐) |
282
+ ```
283
+
284
+ 判定流程:
285
+ 1. y 差<8px 识别同一行
286
+ 2. 同行按 x 升序排
287
+ 3. 计算 x 间距推算 antd Col span(24 栅格)或 flex
288
+ 4. 最右元素若 `x + width` 接近容器右边界 → 右对齐(justify-content: flex-end)
289
+
290
+ ##### 表 4:容器归属表(x+y 双轴边界检测)
291
+
292
+ 仅对 `isVisualContainer === true` 的 group 和带 fill/border 的 rectangle 做检测。
293
+
294
+ ```
295
+ | 容器 | x..x+w | y..y+h | 落入子元素 |
296
+ |------------------|----------|----------|----------------------------------|
297
+ | 渐变容器(group) | 240..960 | 189..297 | 段码含义(x=300,y=257) ✅ |
298
+ | 段码块-蓝(rect) | 320..420 | 320..360 | (无子节点,仅样式) |
299
+ ```
300
+
301
+ 判定式:子元素 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]` → 渲染在容器内部。**单轴判定无效**。
302
+
303
+ ##### 表 5:样式映射表(rectangle/group fill → UI 组件内联样式)
304
+
305
+ ```
306
+ | Sketch 节点 | 数据来源 | bg/border | 映射到代码 |
307
+ |----------------------|--------------------------|--------------------------|-----------------------------------------------------------|
308
+ | 段码块-蓝(rectangle) | style.fills[0]/borders[0]| bg #f2f8fd / b #d9eafb | <div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}> |
309
+ | 渐变容器(group) | style.fills[0].gradient | linear #f3faff→#f8fdff | <div style={{background:'linear-gradient(180deg,#f3faff,#f8fdff)'}}> |
310
+ | Tab 激活态 | rect bg #fff border #0079f7 | bg #fff / b #0079f7 | Tabs type="card" + .segmentTabs .ant-tabs-tab-active 覆盖 |
311
+ ```
312
+
313
+ 铁律(见收获 8):rectangle 的 fill/border 必须以**内联样式**或精确选择器落到代码,**禁止**用 `.beforebg{background:#ebf0ff}` 这类与设计稿无对应关系的 CSS class。
314
+
315
+ #### Pass 2:parent 质检(按 A/B/C/D 矩阵处理冲突)
316
+
317
+ 逐行扫表 1,对照 parent 字段:
318
+
319
+ | 情形 | 坐标判定 | parent 判定 | 处理 |
320
+ |------|----------|-------------|------|
321
+ | A | 在容器 X 内 | parent 链含 X | ✅ 双向一致,直接采用 |
322
+ | B | 在容器 X 内 | parent 是兄弟节点 Y | ⚠️ **视觉容器优先**(设计师把图层拆开了)→ 按坐标渲染到 X 内 |
323
+ | C | 不在容器 X 内 | parent.name 强语义 = X | ⚠️ **parent 优先**(绝对定位/溢出布局)→ 按 parent 归属,加 `position:absolute` 标注 |
324
+ | D | 双向都不落 | parent 是顶层 | 独立元素,按坐标走纵向顺序 |
325
+
326
+ 输出**最终归属表**:在表 1 末尾追加 `情形(A/B/C/D)` 列。
327
+
328
+ #### Pass 3:自检清单(写代码前必跑)
329
+
330
+ - [ ] 表 1 是否按 x 分布判定了布局类型(UpDown / LeftRight / LeftMiddleRight)?
331
+ - [ ] 表 2 同分区内是否按 y 排序,没有跨分区比 y?
332
+ - [ ] 表 3 同行元素(y 差<8px)是否按 x 排序,计算了栅格 span 或右对齐?
333
+ - [ ] 表 4 容器归属是否做了 x+y 双轴检测,不是只看一轴?
334
+ - [ ] 表 5 所有"颜色块/卡片/渐变"样式都来自 rectangle/group 的 style.fills/borders,没有出现凭空 CSS class?
335
+ - [ ] Pass 2 冲突行是否标了 A/B/C/D 情形和决策依据?
336
+
337
+ 任一项 ❌ → 回 Pass 1 补;禁止跳到步骤 3 前未输出全部 5 张表 + 最终归属表。
338
+
339
+ **提示(sketch-mcp-server v1.6.1+)**:
199
340
  - 大型 .sketch 文件先 `queryNodes(pageId, type="artboard")` 锁定目标画板,避免一次性加载整个文档
200
341
  - `queryNodes(pageId, type=["text","rectangle","group"])` 一次拿三类节点 ID(type 字段支持数组),跳过 `getPageStructure(mode="ids")` 单独调用
201
342
  - `getPageStructure(pageId, mode="tree", maxDepth=2-3)` 用于看分区层级;`mode="summary"` 用于按类型统计概况
202
- - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段,元素归属直接走 parent,禁止用 x/y 坐标
343
+ - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段 + group/artboard 额外有 `children` 和 `isVisualContainer` —— 步骤 2.6 的双向校验同时用 parent x/y,不是只挑一个
203
344
  - `matchIconFromName` 评分制返回 Top-3 + `bestMatch.source === "compound"` 强映射,shapePath 节点优先走它
204
345
  - 仅当结构化样式数据仍不足(如复杂渐变、阴影)时,才回退到 `renderNodeAsBase64` + `analyze_design_image`
205
- - 如果 sketch-mcp-server 工具不可用,提示用户安装:`npm install -g @szc-ft/sketch-mcp-server`
346
+ - 如果 sketch-mcp-server 工具不可用或版本 < v1.6.1(parent / isVisualContainer 字段缺失),提示用户升级:`npm install -g @szc-ft/sketch-mcp-server@latest`
206
347
 
207
348
  ### 步骤3:分析设计稿(条件执行)
208
349
 
@@ -328,6 +469,7 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
328
469
  - ProTable/ProForm 等使用 valueType 配置列/字段类型
329
470
  - 复合组件配合 TemplateMode 使用实现标准页面布局
330
471
 
472
+
331
473
  **交互节点**:代码生成后,主动询问用户是否需要调整:
332
474
  - "代码已生成,是否需要调整样式或交互细节?"
333
475
  - "是否需要补充表单校验规则?"
@@ -202,6 +202,27 @@ Ant Design → ProComponents → Cover 层 → Wrapper 层 → ProPackages →
202
202
  | 元素归属 | 凭 x/y 坐标判定("x=272 列都是同区域") | 看 `getMultipleNodeInfo` 返回的 `parent.id/name` 字段 |
203
203
  | 颜色提取 | 跳过 group 只取 text/rectangle | 同批提交 group ID,从 `style.fills` 取渐变背景 |
204
204
 
205
+ #### getMultipleNodeInfo 返回字段契约(sketch-mcp-server ≥ v1.6.1)
206
+
207
+ | 字段 | 适用类型 | 用途 |
208
+ |------|----------|------|
209
+ | `position:{x,y}` + `size:{width,height}` | 全部 | 步骤 2.6 表 1/2/3/4 的坐标推理 |
210
+ | `parent:{id,name,type}` | 全部(顶层除外) | 步骤 2.6 表 1 大区归属、Pass 2 质检 |
211
+ | `style.fills[].{color,gradient}` | rectangle / group / text | 表 5 样式映射 |
212
+ | `style.borders[].{color,thickness}` | rectangle / group | 表 5 样式映射 |
213
+ | `style.shadows[]` | rectangle / group | 表 5 样式映射 |
214
+ | `text.attributes[0].attributes.{fontFamily,fontSize,color,lineHeight}` | text | 字号/字重/颜色(fontFamily 编码字重,如 PingFangSC-Semibold → 600) |
215
+ | `children:[id]` | group / artboard | 推断容器子节点集合(与 parent 反向印证) |
216
+ | `isVisualContainer:bool` | group / artboard | true = 该 group 自身带 fills/borders/shadows,必须作为 UI 容器渲染(不是纯图层分组) |
217
+ | `shape.cornerRadius` | rectangle / oval | 圆角 |
218
+
219
+ **字段缺失自检**(任一项失败 → sketch-mcp-server 版本过旧,升级到 ≥ v1.6.1 后重跑):
220
+
221
+ - [ ] 任取一个 text 节点,`parent.id` 非空 ✅
222
+ - [ ] 任取一个 group 节点,`isVisualContainer` 类型是 boolean、`children` 是数组 ✅
223
+ - [ ] 任取一个 rectangle 节点,`style.fills` 数组存在(即使为空也要是 `[]` 不能 undefined) ✅
224
+ - [ ] 任取一个 text 节点,`text.attributes[0].attributes.fontFamily` 可读出(编码字重) ✅
225
+
205
226
  **实战经验沉淀(重要)**:
206
227
 
207
228
  **收获 1:首轮 `getMultipleNodeInfo` 必须全量拉取,不能挑**
@@ -216,15 +237,20 @@ Ant Design → ProComponents → Cover 层 → Wrapper 层 → ProPackages →
216
237
  错误做法:先查 text 拿自身样式 → 再根据 parent ID 单独查 rectangle(多走一步)
217
238
  正确做法:用 `queryNodes(type=["text","rectangle","group"])` 一次拿三类 ID,`getMultipleNodeInfo` 返回的 `parent` 字段直接告诉你归属关系
218
239
 
219
- **收获 3:元素归属必须走 group 父子关系,禁止 x 坐标平面判定**
240
+ **收获 3:归属判定走 x+y+parent 三轴联合,禁止单轴定论(坐标和 parent 互为纠错器)**
220
241
 
221
- 实战教训:`'主题'text(x=272,y=149)` 被错误识别为内容区块标题,实际是左侧树标题(与树节点同 x=272 列)。同列坐标不代表同区域。
242
+ 实战教训:
243
+ - 反例 A:`'主题'text(x=272,y=149)` 单看 x 在右侧内容区列,但 `parent.name=leftTreeHeader` 强语义 → 归左树(情形 C:parent 修正坐标)
244
+ - 反例 B(反馈 #11):`'段码含义'text(x=300,y=257)` 坐标落在 `渐变容器(x=240..960, y=189..297)` 内,但 parent 是兄弟编组 `cataMain` → 视觉上属于渐变容器内(情形 B:图层拆开,按坐标渲染)
222
245
 
223
246
  正确做法:
224
- - 步骤 2 拿到完整节点树后,先识别顶层 group 分区(通常 2-5 个,对应 LeftTree区/Query+Table区/信息栏区/标题栏区)
225
- - `getMultipleNodeInfo` v1.5.0+ **自动返回** `parent: { id, name, type }` 字段,按 parent 归属到具体分区
226
- - "主题"这类标题文本,先看 `parent.name` 是否带 "LeftTree" / "树" / "PageContent" 等容器关键字,不要看 x 坐标
227
- - 坐标仅作为同 parent 内子元素排序兜底使用
247
+ - 步骤 2.5 4 步先 `mode="tree"` 看顶层 group 分区(通常 2-5 个)
248
+ - 步骤 2.6 做双向校验:Pass 1 坐标推假设 Pass 2 parent 质检 四类情形 A/B/C/D 处理
249
+ - 大区归属:`parent.name` `LeftTree/树/PageContent/sidebar/cataMain` 等强语义关键字时优先于 x 分布
250
+ - 容器归属:必须 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]`,单轴判定无效
251
+ - 跨分区不比 y;同分区内按 y 升序;同行(y 差<8px)按 x 升序
252
+
253
+ 详细决策矩阵见步骤 2.6。
228
254
 
229
255
  **收获 4:信息栏/卡片容器必须先取 group 再取子元素**
230
256
 
@@ -270,14 +296,129 @@ Ant Design → ProComponents → Cover 层 → Wrapper 层 → ProPackages →
270
296
 
271
297
  - **兜底方案**:① 用 `extractBitmaps` 提取的 PNG/PDF 资源;② `matchIconFromName` 的 Top-1(若 source=compound);③ unmatched 时按 parent 上下文 + 上表选 antd `@ant-design/icons` 复合图标;④ 在代码注释中标注"⚠️ 矢量图标用 antd 图标替代,原始 Sketch 为 shapePath"
272
298
 
273
- **提示(sketch-mcp-server v1.5.0+)**:
299
+ **收获 8:rectangle 的 fill/border 必须以内联样式或精确选择器落到 DOM,禁止借用通用 CSS class**
300
+
301
+ 实战教训(反馈 #11):5 个段码视觉块的颜色(#f2f8fd/#f5ffed/#fffbe6/#f5f6f7)完整地从 rectangle 节点拿到了,但代码却用了 `.beforebg{background:#ebf0ff}` 这种与设计稿无对应关系的通用 class,导致 5 个块颜色全错。
302
+
303
+ 正确做法:
304
+ - 步骤 2.6 的样式映射表(表 5)必须为每个"颜色块"列出 rectangle 数据 → 内联 style 的对应关系
305
+ - 写代码时直接用 `<div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}>`
306
+ - 若必须用 CSS class,类名要能反向追到 Sketch 节点(如 `.segmentBlock-blue` 而非 `.beforebg`),且 class 内容必须**精确等于** rectangle 的 fill/border
307
+ - 反例排查:写完代码后 grep 一下 CSS class 名,出现 `.bg1/.box2/.before*` 这类无语义类名、且颜色和设计稿对不上 → 几乎确定是凭空兜底
308
+
309
+ ### 步骤2.6:布局推理(必做,写代码前的最后一道关)
310
+
311
+ ⚠️ 这一步专治"数据全拿到却放错位置"。反馈 #11 的 5 处错位全部由跳过此步骤导致。
312
+
313
+ **核心思路**:x+y 坐标先画"视觉假设",parent 字段做质检纠错。两套信号**互为纠错器**,不是单方向"谁优先"。
314
+
315
+ **输入**:步骤 2.5 产出的 `text + rectangle + group` 节点全量数据(含 `position.{x,y}`、`size.{width,height}`、`parent.{id,name,type}`、`style`、`isVisualContainer`)
316
+
317
+ #### Pass 1:坐标推假设(产出 5 张表)
318
+
319
+ 每张表都必须输出,即使某类节点为空也要写"无"。
320
+
321
+ ##### 表 1:分区识别表(x+y+parent 联合,定大区)
322
+
323
+ ```
324
+ | 节点 name | x | y | parent.name | 推断分区 | 决策依据 |
325
+ |----------------|-----|-----|------------------|--------------|-------------------------|
326
+ | 主题(text) | 272 | 149 | leftTreeHeader | 左树标题区 | parent 强语义压过 x 列 |
327
+ | 段码定义(text) | 320 | 200 | cataMain | 右侧内容-顶部 | parent 含 PageContent 类语义 |
328
+ ```
329
+
330
+ 判定流程:
331
+ 1. 按 x 把画板切成 1-3 列(x<300 → 左区、x≥300 → 右区,或按设计稿实际边界)
332
+ 2. 用 `parent.name` 校验:含 `LeftTree/树/sidebar/cataMain/PageContent/header/footer` 等关键字时以 parent 为准
333
+ 3. 冲突时遵循"收获 3"的 A/B/C/D 矩阵(见下方 Pass 2)
334
+
335
+ → 此表输出**布局类型**:UpDown(单列)/ LeftRight(双列)/ LeftMiddleRight(三列)
336
+
337
+ ##### 表 2:纵向顺序表(同分区内按 y)
338
+
339
+ ```
340
+ | 分区 | 节点 | y | 推断角色 |
341
+ |------------|---------------|-----|-------------------|
342
+ | 右侧内容区 | 段码定义 | 200 | 卡片 1 |
343
+ | 右侧内容区 | Tabs | 420 | Tabs(type=card) |
344
+ | 右侧内容区 | 配置说明 | 478 | 卡片 2(Tabs 下方) |
345
+ ```
346
+
347
+ 铁律:**同分区内**渲染顺序按 y 升序。跨分区不比 y(左树第 100 行 y 和右内容区第 1 行 y 没有可比性)。
348
+
349
+ ##### 表 3:横向排列表(同行按 x,y 差<8px 视为同行)
350
+
351
+ ```
352
+ | 分区/容器 | 节点 | x | y | 推断角色 |
353
+ |-----------------|--------------|-----|-----|-------------------------|
354
+ | Query 操作栏 | 名称输入框 | 320 | 50 | Form.Item col-span=6 |
355
+ | Query 操作栏 | 状态下拉 | 540 | 50 | Form.Item col-span=6 |
356
+ | Query 操作栏 | 查询按钮 | 980 | 50 | 操作按钮(右对齐) |
357
+ ```
358
+
359
+ 判定流程:
360
+ 1. y 差<8px 识别同一行
361
+ 2. 同行按 x 升序排
362
+ 3. 计算 x 间距推算 antd Col span(24 栅格)或 flex
363
+ 4. 最右元素若 `x + width` 接近容器右边界 → 右对齐(justify-content: flex-end)
364
+
365
+ ##### 表 4:容器归属表(x+y 双轴边界检测)
366
+
367
+ 仅对 `isVisualContainer === true` 的 group 和带 fill/border 的 rectangle 做检测。
368
+
369
+ ```
370
+ | 容器 | x..x+w | y..y+h | 落入子元素 |
371
+ |------------------|----------|----------|----------------------------------|
372
+ | 渐变容器(group) | 240..960 | 189..297 | 段码含义(x=300,y=257) ✅ |
373
+ | 段码块-蓝(rect) | 320..420 | 320..360 | (无子节点,仅样式) |
374
+ ```
375
+
376
+ 判定式:子元素 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]` → 渲染在容器内部。**单轴判定无效**。
377
+
378
+ ##### 表 5:样式映射表(rectangle/group fill → UI 组件内联样式)
379
+
380
+ ```
381
+ | Sketch 节点 | 数据来源 | bg/border | 映射到代码 |
382
+ |----------------------|--------------------------|--------------------------|-----------------------------------------------------------|
383
+ | 段码块-蓝(rectangle) | style.fills[0]/borders[0]| bg #f2f8fd / b #d9eafb | <div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}> |
384
+ | 渐变容器(group) | style.fills[0].gradient | linear #f3faff→#f8fdff | <div style={{background:'linear-gradient(180deg,#f3faff,#f8fdff)'}}> |
385
+ | Tab 激活态 | rect bg #fff border #0079f7 | bg #fff / b #0079f7 | Tabs type="card" + .segmentTabs .ant-tabs-tab-active 覆盖 |
386
+ ```
387
+
388
+ 铁律(见收获 8):rectangle 的 fill/border 必须以**内联样式**或精确选择器落到代码,**禁止**用 `.beforebg{background:#ebf0ff}` 这类与设计稿无对应关系的 CSS class。
389
+
390
+ #### Pass 2:parent 质检(按 A/B/C/D 矩阵处理冲突)
391
+
392
+ 逐行扫表 1,对照 parent 字段:
393
+
394
+ | 情形 | 坐标判定 | parent 判定 | 处理 |
395
+ |------|----------|-------------|------|
396
+ | A | 在容器 X 内 | parent 链含 X | ✅ 双向一致,直接采用 |
397
+ | B | 在容器 X 内 | parent 是兄弟节点 Y | ⚠️ **视觉容器优先**(设计师把图层拆开了)→ 按坐标渲染到 X 内 |
398
+ | C | 不在容器 X 内 | parent.name 强语义 = X | ⚠️ **parent 优先**(绝对定位/溢出布局)→ 按 parent 归属,加 `position:absolute` 标注 |
399
+ | D | 双向都不落 | parent 是顶层 | 独立元素,按坐标走纵向顺序 |
400
+
401
+ 输出**最终归属表**:在表 1 末尾追加 `情形(A/B/C/D)` 列。
402
+
403
+ #### Pass 3:自检清单(写代码前必跑)
404
+
405
+ - [ ] 表 1 是否按 x 分布判定了布局类型(UpDown / LeftRight / LeftMiddleRight)?
406
+ - [ ] 表 2 同分区内是否按 y 排序,没有跨分区比 y?
407
+ - [ ] 表 3 同行元素(y 差<8px)是否按 x 排序,计算了栅格 span 或右对齐?
408
+ - [ ] 表 4 容器归属是否做了 x+y 双轴检测,不是只看一轴?
409
+ - [ ] 表 5 所有"颜色块/卡片/渐变"样式都来自 rectangle/group 的 style.fills/borders,没有出现凭空 CSS class?
410
+ - [ ] Pass 2 冲突行是否标了 A/B/C/D 情形和决策依据?
411
+
412
+ 任一项 ❌ → 回 Pass 1 补;禁止跳到步骤 3 前未输出全部 5 张表 + 最终归属表。
413
+
414
+ **提示(sketch-mcp-server v1.6.1+)**:
274
415
  - 大型 .sketch 文件先 `queryNodes(pageId, type="artboard")` 锁定目标画板,避免一次性加载整个文档
275
416
  - `queryNodes(pageId, type=["text","rectangle","group"])` 一次拿三类节点 ID(type 字段支持数组),跳过 `getPageStructure(mode="ids")` 单独调用
276
417
  - `getPageStructure(pageId, mode="tree", maxDepth=2-3)` 用于看分区层级;`mode="summary"` 用于按类型统计概况
277
- - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段,元素归属直接走 parent,禁止用 x/y 坐标
418
+ - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段 + group/artboard 额外有 `children` 和 `isVisualContainer` —— 步骤 2.6 的双向校验同时用 parent x/y,不是只挑一个
278
419
  - `matchIconFromName` 评分制返回 Top-3 + `bestMatch.source === "compound"` 强映射,shapePath 节点优先走它
279
420
  - 仅当结构化样式数据仍不足(如复杂渐变、阴影)时,才回退到 `renderNodeAsBase64` + `{{TOOL:analyze_design_image}}`
280
- - 如果 sketch-mcp-server 工具不可用,提示用户安装:`npm install -g @szc-ft/sketch-mcp-server`
421
+ - 如果 sketch-mcp-server 工具不可用或版本 < v1.6.1(parent / isVisualContainer 字段缺失),提示用户升级:`npm install -g @szc-ft/sketch-mcp-server@latest`
281
422
 
282
423
  ### 步骤3:分析设计稿(条件执行)
283
424
 
@@ -478,9 +619,21 @@ Ant Design → ProComponents → Cover 层 → Wrapper 层 → ProPackages →
478
619
  | 组件 | 依赖的 szcd 组件 | 依赖的 hooks |
479
620
  |------|-----------------|-------------|
480
621
 
622
+ ### 布局顺序(来自步骤 2.6 表 2 压缩,按 y 排序)
623
+ [页面级垂直顺序,如:TitleAndBack → 段码定义卡片 → Tabs → 配置说明卡片]
624
+
625
+ ### 关键样式映射(来自步骤 2.6 表 5,仅列非默认样式)
626
+ | UI 元素 | Sketch 数据来源 | 落地方式 |
627
+ |---------|----------------|----------|
628
+ | 段码块×5 | 5 个 rectangle 的 fills[0]/borders[0] | 内联 style |
629
+ | 渐变容器 | group(isVisualContainer) 的 fills[0].gradient | 内联 linear-gradient |
630
+ | Tab 激活态 | rectangle bg #fff border #0079f7 | type="card" + .segmentTabs 覆盖 |
631
+
481
632
  ### 备选方案(如有)
482
633
  [说明]
483
634
  ```
635
+
636
+ 用户在确认环节即可发现"段码块颜色没列上"或"Tab 样式漏识别",避免反馈 #11 那样要等代码生成后反复修。
484
637
  <!-- /INCLUDE:enhanced -->
485
638
 
486
639
  <!-- INCLUDE:full -->
@@ -511,6 +664,22 @@ Ant Design → ProComponents → Cover 层 → Wrapper 层 → ProPackages →
511
664
  - Hooks 按依赖关系正确引入(如 useLeftTree、useTable)
512
665
  <!-- /INCLUDE:enhanced -->
513
666
 
667
+ <!-- INCLUDE:enhanced -->
668
+ #### 步骤 6 末尾必做:样式落地校验
669
+
670
+ 代码生成完成后,**逐行核对**步骤 2.6 表 5(样式映射表)的每一项是否真落到代码里:
671
+
672
+ ```
673
+ | 样式映射表行 | 代码中对应位置 | 校验 |
674
+ |---------------------------------------------|-------------------------------------------------------------|------|
675
+ | 段码块-蓝 bg #f2f8fd border #d9eafb | CreateOrEdit.tsx:L120 `<div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}>` | ✅ |
676
+ | 渐变容器 linear #f3faff→#f8fdff | CreateOrEdit.tsx:L98 `style={{background:'linear-gradient(...)'}}` | ✅ |
677
+ | Tab 激活态 bg #fff border #0079f7 | styles.less:L45 `.segmentTabs .ant-tabs-tab-active{...}` | ✅ |
678
+ ```
679
+
680
+ ❌ 如果某行找不到对应代码位置 → 直接补到代码里再继续,**禁止说"代码已生成"前留缺口**。这是反馈 #11"5 个段码块全错"的根因——所有颜色都拿到了,但没逐一回查是否落到了代码上。
681
+ <!-- /INCLUDE:enhanced -->
682
+
514
683
  <!-- EXCLUDE:enhanced -->
515
684
  **交互节点**:代码生成后,主动询问用户是否需要调整:
516
685
  - "代码已生成,是否需要调整样式或交互细节?"