# MasterGo / D2C DSL 字段与解析约定

本文档约定从 MasterGo 导出 JSON（或 mcp_getDsl 返回的 dsl）中读取各字段的方式，与参考实现 **render.html**（UltimateRenderer）的解析逻辑对齐，保证布局推理与样式解析一致、可复现。

---

## 根节点与布局信息

* **根节点来源**（按优先级）：`data.nodes?.[0]` → `data.layers?.[0]` → `data.node` → `data.root` → `data.document?.children?.[0]`。
* **布局信息**：统一通过 `getLayout(node)` 读取，兼容两种字段名：
  * `node.layoutStyle` 或 `node.layout`；
  * 从中取 `width`、`height`、`relativeX`、`relativeY`、`x`、`y` 以及圆角、padding 等。
* **相对位置**：子节点相对父容器的位置取 `layout.relativeX ?? layout.x`、`layout.relativeY ?? layout.y`；仅当 JSON 中存在这些数值时才用于定位。

---

## 圆角 (border-radius)

* **四角数组**：`node.rectangleCornerRadii` 或 `layout.rectangleCornerRadii` 或 `node.cornerRadii`，顺序为 [topLeft, topRight, bottomRight, bottomLeft]；若为数组且长度 ≥ 4，则生成四值 `border-radius`。
* **四角分别**：`topLeftRadius` / `topRightRadius` / `bottomRightRadius` / `bottomLeftRadius`（或嵌套在 `cornerRadius` / `layout.cornerRadius` 下）。
* **单值**：`node.borderRadius` / `node.cornerRadius` / `layout.borderRadius` / `layout.cornerRadius`，或 `node.style.borderRadius`；若存在 `cornerRadiusStyleId`，则从 `styles[cornerRadiusStyleId]` 解析。
* **MasterGo 导出**：常为带单位字符串（如 `"16px"`、`"9px"`），需直接当 CSS 使用；纯数字可补 `px`。
* **溢出与裁剪**：当 `clipsContent` / `clipContent` 为 true，或 `overflow === 'hidden'|'clip'`，或 `type === 'FRAME'`，或存在非零圆角时，容器应设 `overflow: hidden`。

---

## 样式 Lookup (styles / paints)

* **样式字典**：`data.styles` 或 `data.paints`，以 styleId 为 key 查找。
* **resolveStyle(styleId)**：
  * 取 `styles[styleId].value`（可为数组则取首项）；
  * **颜色**：字符串直接使用（如 `#xxx`、`rgba(...)`）；
  * **图片**：`value` 为 `url(...)` 或对象含 `url` 时，解析出 URL 用于 Image 的 `src`；
  * **渐变**：对象含 `type: 'gradient'` 或 `gradientStops` 时，按 `gradientStops`（或 `stops`）与 `angle` 或 `startX/Y`、`endX/Y` 生成 `linear-gradient(...)`。

---

## 填充 (fill / fills)

* **收集 fill 引用**：支持 `node.fill`（单 ID）或 `node.fills` 数组；数组项可为字符串（styleId）或对象 `{ color }` / `{ paint }`。
* **应用规则**：
  * 纯色/渐变：应用到容器背景（backgroundColor / backgroundImage）；
  * **图片填充**：解析出 URL 后，使用 **Image** 组件（或等效）展示，设 `object-fit: cover`（或 Ray 等效），**不要**用 div 的 background-image 代替图片节点，以保证可访问性与层级一致。
* **冗余纯色矩形**：若节点为「仅纯色矩形」（无文字、无 path、无图片填充），且兄弟节点中存在带图片填充的节点，则该纯色矩形可过滤不生成，避免 D2C 常见冗余背景层。

---

## 描边 (stroke)

* **线宽**：`node.strokeWeight` 或 `node.strokeWidth` 或（当 `node.stroke` 为数字时）`node.stroke`。
* **颜色**：`node.strokeColor` 或（当 `node.stroke` 为字符串时）`node.stroke`，通过 `resolveStyle` 解析。
* 仅当同时存在有效线宽与颜色时才输出描边样式。

---

## 效果 (effect / box-shadow)

* **来源**：`node.effect` 或 `node.effects?.[0]`，值为 styleId。
* **解析**：`resolveEffect(effectId)` 从 `styles[effectId].value` 取字符串数组，从中提取 `box-shadow: ...` 部分并应用到节点。
* 仅使用 JSON 中已声明的 effect，不添加默认阴影。

---

## 子节点顺序与视觉流

* **排序**：子节点按**视觉流**排序后再参与 Flex 推导与 DOM 顺序：
  * 主序按 `relativeY`（或 `y`）升序；
  * **同行判定**：两节点 `relativeY` 差 &lt; 10px 视为同一行；同行内按 `relativeX` 升序；
  * 同位置时保留设计稿图层顺序（原数组下标升序）。
* 排序结果用于第一步的「视觉流与 Flex 推导」及最终 DOM 顺序。

---

## 文本 (TEXT)

* **内容**：`node.text?.[0]?.text` 或 `node.characters`。
* **字体**：从 `node.text[0].font` 的 styleId 取 `styles[font].value`，支持 `size`、`family`、`weight`、`lineHeight`。
* **颜色**：`node.textColor?.[0]?.color` 或 `node.fills?.[0]`，经 `resolveStyle` 得到颜色。
* **对齐**：水平 `node.textAlign` 或 `node.textAlignHorizontal`；垂直 `node.textAlignVertical`；单行垂直居中时若有高度且未指定 lineHeight，可设 `line-height` 与容器高度一致（如 `textMode === 'single-line'`）。

---

## 矢量与图形

* **Path**：`node.path` 为数组时，每项含 `data` 或 `d`、`fill`、可选 `stroke`/`strokeWidth`；fill/stroke 为 styleId 时经 `resolveStyle` 解析。涂鸦小程序中优先用 Image 展示导出图；若内联矢量需按 Ray 文档支持方式，禁止未支持的 Web `<svg>`。
* **椭圆/圆**：`type === 'ELLIPSE'` 或 `SVG_ELLIPSE`，且无 `path` 时，用 `border-radius: 50%` 近似。
* **推断圆形裁剪**：父容器为 GROUP/FRAME、宽高相等且含 ELLIPSE 子节点时，可视为圆形裁剪容器，子节点按圆角/裁剪处理。

---

## 蒙版 (mask: "alpha")

### 识别规则

当 GROUP/FRAME 的 `children` 中某个子节点带有 `"mask": "alpha"` 字段时，该节点为**蒙版节点**，其后的同级兄弟节点为**被裁剪节点**（内容层）。典型结构：

```json
{
  "type": "GROUP",
  "children": [
    {
      "type": "SVG_ELLIPSE",
      "mask": "alpha",
      "layoutStyle": { "width": 62, "height": 62, "relativeX": 0, "relativeY": 0 }
    },
    {
      "type": "LAYER",
      "name": "07",
      "layoutStyle": { "width": 129.9, "height": 67.9, "relativeX": -2.95, "relativeY": -2.95 },
      "fill": "paint_sa2:4542"
    }
  ]
}
```

**核心特征**：
* 蒙版节点（`mask: "alpha"`）定义裁剪形状，通常为 SVG_ELLIPSE（圆形）或 PATH（复杂形状）。
* 内容节点（LAYER/IMAGE）尺寸**大于**蒙版节点，且通过**负 relativeX/Y** 偏移定位，最终只显示蒙版形状内的部分。
* 蒙版节点自身不渲染，仅作为裁剪路径使用。

### 关键数据提取

对于每个蒙版 GROUP，需要提取以下数据用于代码生成：

| 数据 | 来源 | 用途 |
|------|------|------|
| 蒙版尺寸 | 蒙版节点的 `width`/`height` | CSS 容器尺寸 |
| 蒙版形状 | 蒙版节点的 `type`（ELLIPSE → 圆形，PATH → 复杂形状） | `border-radius` 或 `clip-path` |
| 内容尺寸 | 内容节点的 `width`/`height` | Image 组件的 `width`/`height` |
| 内容偏移 | 内容节点的 `relativeX`/`relativeY` | Image 组件的 `left`/`top`（absolute 定位） |

**注意**：每个被蒙版的图片**偏移和尺寸各不相同**，必须从 DSL 中逐一提取，**不能使用通用居中值**。例如同一网格中的场景图标：

```
场景 1: width=126.95, height=83.40, offsetX=-9.60, offsetY=-5.17
场景 9: width=129.90, height=67.90, offsetX=-2.95, offsetY=-2.95
场景 13: width=118.83, height=62.00, offsetX=-2.21, offsetY=0
```

### 代码生成规则

详见 → [02-styles.md](./02-styles.md#图片蒙版裁剪合法-absolute-场景三) 中的「图片蒙版裁剪」章节。

---

## 单位与数值

* **px 与 0**：仅当 JSON 中存在有效数值时才写入对应样式；未定义或非数字不写默认值（避免覆盖继承）。
* **输出到 Less**：设计稿坐标与长度在写入 **index.module.less** 时统一换算为 **rpx**，阈值（如 8px/10px）在换算后保持逻辑一致。

---

## 参考

* 字段与解析逻辑与 **render.html**（UltimateRenderer）保持一致，便于与高保真还原结果对照，并保证 D2C 输出与设计稿一致。
