mta-mcp 1.0.2 → 1.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mta-mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "MTA - 智能项目分析与编码规范管理 MCP 服务器",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,7 +52,148 @@ function getLayerColor(layer) {
52
52
  }
53
53
  ```
54
54
 
55
- ### 问题 2:Symbol 内部样式读取
55
+ ### 问题 2:fontWeight 数值不准确(重要)
56
+
57
+ **严重程度**: 🔴 高
58
+
59
+ **现象**:`layer.style.fontWeight` 返回的数值(如 8)不能直接映射到 Flutter/CSS 字重。
60
+
61
+ ```javascript
62
+ // ❌ 错误方式 - fontWeight 值不可靠
63
+ layer.style.fontWeight // 返回 8,但实际可能是 Semibold(w600) 而非 ExtraBold(w800)
64
+ ```
65
+
66
+ **原因**:Sketch API 的 `fontWeight` 属性返回的是内部权重值,不是标准的 100-900 字重值。
67
+
68
+ **解决方案**:通过 `sketchObject.font()` 获取真实的 PostScript 字体名称:
69
+
70
+ ```javascript
71
+ function getTextFontInfo(textLayer) {
72
+ const result = {
73
+ fontSize: textLayer.style.fontSize,
74
+ fontFamily: textLayer.style.fontFamily,
75
+ fontWeight: textLayer.style.fontWeight, // 仅供参考
76
+ fontName: null, // 关键:真实字体名称
77
+ displayName: null
78
+ };
79
+
80
+ // 获取真实字体名称
81
+ if (textLayer.sketchObject && textLayer.sketchObject.font) {
82
+ const font = textLayer.sketchObject.font();
83
+ if (font) {
84
+ result.fontName = String(font.fontName()); // 如 "PingFangSC-Semibold"
85
+ result.displayName = String(font.displayName()); // 如 "苹方-简 中粗体"
86
+ result.familyName = String(font.familyName()); // 如 "PingFang SC"
87
+ }
88
+ }
89
+
90
+ return result;
91
+ }
92
+ ```
93
+
94
+ **PostScript 字体名到 Flutter FontWeight 映射**:
95
+
96
+ | PostScript 后缀 | 中文名 | Flutter | CSS |
97
+ |----------------|--------|---------|-----|
98
+ | `-Thin` | 极细 | w100 | 100 |
99
+ | `-Ultralight` | 纤细 | w200 | 200 |
100
+ | `-Light` | 细体 | w300 | 300 |
101
+ | `-Regular` | 常规 | w400 | 400 |
102
+ | `-Medium` | 中等 | w500 | 500 |
103
+ | `-Semibold` | 中粗体 | w600 | 600 |
104
+ | `-Bold` | 粗体 | w700 | 700 |
105
+ | `-Heavy` | 特粗 | w800 | 800 |
106
+ | `-Black` | 黑体 | w900 | 900 |
107
+
108
+ ### 问题 3:图标必须从设计稿导出
109
+
110
+ **严重程度**: 🔴 高
111
+
112
+ **现象**:AI 可能会自己生成图标 SVG,而不是从设计稿中提取。
113
+
114
+ **正确做法**:使用 `sketch.export` API 导出设计稿中的图标:
115
+
116
+ ```javascript
117
+ const sketch = require('sketch');
118
+
119
+ function exportIconAsSVG(layer) {
120
+ // 导出为 Buffer
121
+ const buffer = sketch.export(layer, {
122
+ formats: 'svg',
123
+ output: false, // 关键:返回 Buffer 而非写文件
124
+ scales: '1'
125
+ });
126
+
127
+ // Buffer 转字符串
128
+ const svgString = buffer.toString('utf-8');
129
+
130
+ // 清理 SVG(移除 Sketch 生成的多余属性)
131
+ return svgString
132
+ .replace(/id="[^"]*"/g, '')
133
+ .replace(/sketch:type="[^"]*"/g, '')
134
+ .replace(/xmlns:sketch="[^"]*"/g, '')
135
+ .replace(/\s+/g, ' ')
136
+ .trim();
137
+ }
138
+ ```
139
+
140
+ **在 Flutter 中使用导出的 SVG**:
141
+
142
+ ```dart
143
+ // 方式1:内联 SVG(推荐小图标)
144
+ SvgPicture.string(
145
+ '''<svg>...</svg>''',
146
+ width: 24,
147
+ height: 24,
148
+ )
149
+
150
+ // 方式2:保存为文件(推荐大图标)
151
+ // 将导出的 SVG 保存到 assets/icons/ 目录
152
+ SvgPicture.asset('assets/icons/notification.svg')
153
+ ```
154
+
155
+ ### 问题 4:列表项间距测量
156
+
157
+ **严重程度**: 🔴 高
158
+
159
+ **现象**:列表中多个同类元素的间距可能不一致,需要逐个测量。
160
+
161
+ **测量脚本**:
162
+
163
+ ```javascript
164
+ function measureListSpacing(container) {
165
+ const children = container.layers;
166
+ const spacings = [];
167
+
168
+ for (let i = 0; i < children.length - 1; i++) {
169
+ const current = children[i];
170
+ const next = children[i + 1];
171
+
172
+ // 垂直间距 = 下一个元素的 y - (当前元素的 y + 高度)
173
+ const spacing = next.frame.y - (current.frame.y + current.frame.height);
174
+
175
+ spacings.push({
176
+ between: `${current.name} → ${next.name}`,
177
+ spacing: Math.round(spacing)
178
+ });
179
+ }
180
+
181
+ // 检查是否一致
182
+ const uniqueSpacings = [...new Set(spacings.map(s => s.spacing))];
183
+
184
+ return {
185
+ spacings,
186
+ isConsistent: uniqueSpacings.length === 1,
187
+ recommendedSpacing: uniqueSpacings.length === 1
188
+ ? uniqueSpacings[0]
189
+ : Math.round(spacings.reduce((a, b) => a + b.spacing, 0) / spacings.length)
190
+ };
191
+ }
192
+ ```
193
+
194
+ **注意**:如果设计稿中间距不一致,应与设计师确认是故意还是误差。代码中应使用统一间距。
195
+
196
+ ### 问题 5:Symbol 内部样式读取
56
197
 
57
198
  **严重程度**: 🟡 中
58
199
 
@@ -69,7 +210,7 @@ if (layer.type === 'SymbolInstance') {
69
210
  }
70
211
  ```
71
212
 
72
- ### 问题 3:阴影数组顺序
213
+ ### 问题 6:阴影数组顺序
73
214
 
74
215
  **严重程度**: 🟢 低
75
216
 
@@ -346,4 +487,11 @@ function convertGradient(gradient, framework) {
346
487
 
347
488
  **维护者**: MTA工作室
348
489
  **创建日期**: 2026-01-20
349
- **最后更新**: 2026-01-20
490
+ **最后更新**: 2026-01-21
491
+
492
+ ### 更新日志
493
+
494
+ | 版本 | 日期 | 更新内容 |
495
+ |------|------|---------|
496
+ | v1.0 | 2026-01-20 | 初始版本 |
497
+ | v1.1 | 2026-01-21 | 新增:fontWeight不可靠问题及fontName获取方案、图标导出规范、列表间距测量脚本 |
@@ -83,16 +83,53 @@ color: "#1C2B45" // 丢失透明度
83
83
 
84
84
  设计稿中的字重名称需要映射为数值:
85
85
 
86
- | 设计稿名称 | 数值 | Flutter | CSS |
87
- |-----------|------|---------|-----|
88
- | Thin | 100 | FontWeight.w100 | font-weight: 100 |
89
- | Light | 300 | FontWeight.w300 | font-weight: 300 |
90
- | Regular | 400 | FontWeight.w400 | font-weight: 400 |
91
- | Medium | 500 | FontWeight.w500 | font-weight: 500 |
92
- | SemiBold | 600 | FontWeight.w600 | font-weight: 600 |
93
- | Bold | 700 | FontWeight.w700 | font-weight: 700 |
86
+ | 设计稿名称 | PostScript 后缀 | 数值 | Flutter | CSS |
87
+ |-----------|----------------|------|---------|-----|
88
+ | Thin | -Thin | 100 | FontWeight.w100 | font-weight: 100 |
89
+ | Ultralight | -Ultralight | 200 | FontWeight.w200 | font-weight: 200 |
90
+ | Light | -Light | 300 | FontWeight.w300 | font-weight: 300 |
91
+ | Regular | -Regular | 400 | FontWeight.w400 | font-weight: 400 |
92
+ | Medium | -Medium | 500 | FontWeight.w500 | font-weight: 500 |
93
+ | SemiBold | -Semibold | 600 | FontWeight.w600 | font-weight: 600 |
94
+ | Bold | -Bold | 700 | FontWeight.w700 | font-weight: 700 |
95
+ | Heavy/ExtraBold | -Heavy | 800 | FontWeight.w800 | font-weight: 800 |
96
+ | Black | -Black | 900 | FontWeight.w900 | font-weight: 900 |
94
97
 
95
- #### 4. 行高计算差异
98
+ > ⚠️ **重要**:Sketch API 的 `fontWeight` 属性返回的数值(如 8)不可靠!必须通过 `sketchObject.font().fontName()` 获取真实的 PostScript 字体名称(如 `PingFangSC-Semibold`),然后根据后缀映射。
99
+
100
+ #### 4. 图标必须从设计稿导出
101
+
102
+ **绝对禁止**:AI 自己生成图标 SVG
103
+
104
+ **正确做法**:使用设计工具 API 导出设计稿中的图标
105
+
106
+ ```javascript
107
+ // Sketch 导出示例
108
+ const buffer = sketch.export(iconLayer, {
109
+ formats: 'svg',
110
+ output: false,
111
+ scales: '1'
112
+ });
113
+ const svgString = buffer.toString('utf-8');
114
+ ```
115
+
116
+ **原因**:
117
+ - 设计师精心调整过图标的线条粗细、比例、细节
118
+ - AI 生成的图标风格可能与设计稿不一致
119
+ - 图标是品牌视觉的重要组成部分
120
+
121
+ #### 5. 列表间距必须逐个测量
122
+
123
+ **问题**:同一列表中的元素间距可能不完全一致
124
+
125
+ **正确做法**:测量每对相邻元素的间距,取多数值或与设计师确认
126
+
127
+ ```javascript
128
+ // 间距计算公式
129
+ spacing = nextElement.y - (currentElement.y + currentElement.height)
130
+ ```
131
+
132
+ #### 6. 行高计算差异
96
133
 
97
134
  不同框架对行高的处理不同:
98
135
 
@@ -356,6 +393,7 @@ function getFigmaColor(paint) {
356
393
  | 版本 | 日期 | 更新内容 |
357
394
  |------|------|---------|
358
395
  | v1.0 | 2026-01-20 | 初始版本,包含渐变色读取问题解决方案 |
396
+ | v1.1 | 2026-01-21 | 新增:字重映射完整表(含PostScript后缀)、图标导出强制要求、列表间距测量规范 |
359
397
 
360
398
  ---
361
399
 
@@ -70,6 +70,11 @@ templates/
70
70
  │ ├── api-layer/
71
71
  │ └── themes/
72
72
  ├── react/ # React 项目模板(预留)
73
+ ├── design-measurement/ # 设计稿测量模板 ✅ 新增
74
+ │ ├── README.md # 使用指南
75
+ │ ├── component-measurement.js # 组件测量
76
+ │ ├── style-extraction.js # 样式提取
77
+ │ └── gap-measurement.js # 间距测量
73
78
  └── common/ # 跨框架通用模板
74
79
  └── types/ # TypeScript 类型定义 ✅
75
80
  ```
@@ -108,6 +113,15 @@ templates/
108
113
  |------|--------|------|------|
109
114
  | `common/types` | types, 类型 | TypeScript 通用类型 | ✅ 可用 |
110
115
 
116
+ ### 设计稿测量模板 🆕
117
+
118
+ | 模板 | 关键词 | 说明 | 状态 |
119
+ |------|--------|------|------|
120
+ | `design-measurement` | 测量, measure | 设计稿测量脚本合集 | ✅ 可用 |
121
+ | `component-measurement.js` | 组件, 边距 | 组件尺寸+相对边距 | ✅ 可用 |
122
+ | `style-extraction.js` | 样式, 渐变, 阴影 | 完整样式提取 | ✅ 可用 |
123
+ | `gap-measurement.js` | 间距, gap | 同级元素间距 | ✅ 可用 |
124
+
111
125
  ---
112
126
 
113
127
  ## 🔧 模板规范
@@ -0,0 +1,81 @@
1
+ # 🎨 设计稿测量模板
2
+
3
+ > 适用于 Sketch MCP、Figma 等设计工具的测量脚本模板
4
+
5
+ ---
6
+
7
+ ## 🤖 Agent 使用指令
8
+
9
+ **触发词**:
10
+ - "测量选中的组件"
11
+ - "获取设计稿边距"
12
+ - "分析图层样式"
13
+ - "获取渐变色参数"
14
+
15
+ **自动匹配模板**:
16
+
17
+ | 场景 | 对应模板 |
18
+ |------|----------|
19
+ | 分析整体结构 | `structure-overview.js` |
20
+ | 测量单个组件 | `component-measurement.js` |
21
+ | 计算间距关系 | `gap-measurement.js` |
22
+ | 提取样式参数 | `style-extraction.js` |
23
+ | 分析嵌套Symbol | `nested-symbol-analysis.js` |
24
+
25
+ ---
26
+
27
+ ## 📁 模板文件
28
+
29
+ ### 1. structure-overview.js
30
+ 用途:分析选中元素的整体结构和层级
31
+
32
+ ### 2. component-measurement.js
33
+ 用途:测量单个组件的尺寸、边距、内边距(自动计算相对值)
34
+
35
+ ### 3. gap-measurement.js
36
+ 用途:计算同级元素之间的间距
37
+
38
+ ### 4. style-extraction.js
39
+ 用途:提取颜色、渐变、阴影、圆角等样式参数
40
+
41
+ ### 5. nested-symbol-analysis.js
42
+ 用途:递归分析 Symbol 内部嵌套结构
43
+
44
+ ---
45
+
46
+ ## 🔧 使用方式
47
+
48
+ ### 方式一:直接调用 MCP 工具
49
+ ```
50
+ 调用 get_standard_by_id({ id: 'sketch-mcp' }) 获取完整脚本
51
+ ```
52
+
53
+ ### 方式二:复制模板脚本
54
+ ```javascript
55
+ // 从模板文件中复制脚本到 mcp_sketch_run_code
56
+ ```
57
+
58
+ ---
59
+
60
+ ## ⚠️ 核心原则
61
+
62
+ 1. **测量 = 记录 + 计算**
63
+ - 记录绝对坐标
64
+ - 立即计算相对边距
65
+ - 转换为目标框架可用的值
66
+
67
+ 2. **坐标系转换**
68
+ - iOS SafeArea 偏移:44px(刘海屏)/ 20px(非刘海)
69
+ - Android 状态栏:24-48dp
70
+ - Web:通常不需要
71
+
72
+ 3. **渐变角度转换**
73
+ - Sketch: from{x,y} → to{x,y}(0-1 范围)
74
+ - CSS: `linear-gradient(angle, ...)`
75
+ - Flutter: `Alignment(x, y)`(-1 到 1 范围)
76
+
77
+ ---
78
+
79
+ **维护者**: MTA工作室
80
+ **版本**: v1.0
81
+ **创建日期**: 2025-01-21
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 组件测量模板 - 自动计算相对边距
3
+ * 用途:测量单个组件的完整布局参数
4
+ *
5
+ * 使用方式:在 Sketch 中选中目标组件,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (!sel.length) {
11
+ console.log('请先选中一个组件')
12
+ } else {
13
+ const layer = sel[0]
14
+ const parent = layer.parent
15
+
16
+ // 基础信息
17
+ console.log('=== 组件测量结果 ===')
18
+ console.log(`名称: ${layer.name}`)
19
+ console.log(`类型: ${layer.type}`)
20
+
21
+ // 尺寸信息
22
+ console.log(`\n--- 尺寸 ---`)
23
+ console.log(`宽度: ${layer.frame.width}`)
24
+ console.log(`高度: ${layer.frame.height}`)
25
+
26
+ // 绝对坐标(供参考)
27
+ console.log(`\n--- 绝对坐标(供参考)---`)
28
+ console.log(`x: ${layer.frame.x}`)
29
+ console.log(`y: ${layer.frame.y}`)
30
+
31
+ // ⭐ 关键:计算相对边距(可直接使用)
32
+ console.log(`\n--- 相对边距(可直接使用)---`)
33
+ if (parent && parent.frame) {
34
+ const paddingLeft = layer.frame.x - parent.frame.x
35
+ const paddingTop = layer.frame.y - parent.frame.y
36
+ const paddingRight = (parent.frame.x + parent.frame.width) - (layer.frame.x + layer.frame.width)
37
+ const paddingBottom = (parent.frame.y + parent.frame.height) - (layer.frame.y + layer.frame.height)
38
+
39
+ console.log(`距父容器左边: ${paddingLeft}`)
40
+ console.log(`距父容器顶部: ${paddingTop}`)
41
+ console.log(`距父容器右边: ${paddingRight}`)
42
+ console.log(`距父容器底部: ${paddingBottom}`)
43
+ console.log(`父容器: ${parent.name} (${parent.frame.width}x${parent.frame.height})`)
44
+ } else {
45
+ console.log('无父容器或为顶层元素')
46
+ }
47
+
48
+ // 框架转换提示
49
+ console.log(`\n--- 框架转换 ---`)
50
+ console.log(`Flutter: padding: EdgeInsets.fromLTRB(${layer.frame.x}, ${layer.frame.y}, 0, 0)`)
51
+ console.log(`CSS: padding: ${layer.frame.y}px ${layer.frame.x}px;`)
52
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 间距测量模板 - 计算同级元素间距
3
+ * 用途:测量多个元素之间的垂直/水平间距
4
+ *
5
+ * 使用方式:在 Sketch 中选中多个同级元素,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (sel.length < 2) {
11
+ console.log('请选中至少2个元素来计算间距')
12
+ } else {
13
+ // 按 Y 坐标排序(从上到下)
14
+ const sortedByY = [...sel].sort((a, b) => a.frame.y - b.frame.y)
15
+
16
+ console.log('=== 垂直间距测量 ===')
17
+ console.log(`选中元素数量: ${sel.length}`)
18
+
19
+ for (let i = 0; i < sortedByY.length - 1; i++) {
20
+ const current = sortedByY[i]
21
+ const next = sortedByY[i + 1]
22
+
23
+ // 计算垂直间距 = 下一个元素顶部 - 当前元素底部
24
+ const gap = next.frame.y - (current.frame.y + current.frame.height)
25
+
26
+ console.log(`\n${current.name} ↓ ${next.name}`)
27
+ console.log(` 垂直间距: ${gap}px`)
28
+ console.log(` CSS: margin-bottom: ${gap}px; 或 gap: ${gap}px;`)
29
+ console.log(` Flutter: SizedBox(height: ${gap})`)
30
+ }
31
+
32
+ // 按 X 坐标排序(从左到右)
33
+ const sortedByX = [...sel].sort((a, b) => a.frame.x - b.frame.x)
34
+
35
+ console.log('\n=== 水平间距测量 ===')
36
+
37
+ for (let i = 0; i < sortedByX.length - 1; i++) {
38
+ const current = sortedByX[i]
39
+ const next = sortedByX[i + 1]
40
+
41
+ // 计算水平间距 = 下一个元素左边 - 当前元素右边
42
+ const gap = next.frame.x - (current.frame.x + current.frame.width)
43
+
44
+ console.log(`\n${current.name} → ${next.name}`)
45
+ console.log(` 水平间距: ${gap}px`)
46
+ console.log(` CSS: margin-right: ${gap}px; 或 gap: ${gap}px;`)
47
+ console.log(` Flutter: SizedBox(width: ${gap})`)
48
+ }
49
+
50
+ // 汇总信息
51
+ console.log('\n=== 汇总 ===')
52
+ const verticalGaps = []
53
+ const horizontalGaps = []
54
+
55
+ for (let i = 0; i < sortedByY.length - 1; i++) {
56
+ const gap = sortedByY[i + 1].frame.y - (sortedByY[i].frame.y + sortedByY[i].frame.height)
57
+ verticalGaps.push(gap)
58
+ }
59
+
60
+ for (let i = 0; i < sortedByX.length - 1; i++) {
61
+ const gap = sortedByX[i + 1].frame.x - (sortedByX[i].frame.x + sortedByX[i].frame.width)
62
+ horizontalGaps.push(gap)
63
+ }
64
+
65
+ const uniqueVGaps = [...new Set(verticalGaps)]
66
+ const uniqueHGaps = [...new Set(horizontalGaps)]
67
+
68
+ if (uniqueVGaps.length === 1) {
69
+ console.log(`垂直间距统一: ${uniqueVGaps[0]}px → 可使用 flex gap 或 Column mainAxisSpacing`)
70
+ } else {
71
+ console.log(`垂直间距不统一: ${verticalGaps.join(', ')} → 需要单独设置`)
72
+ }
73
+
74
+ if (uniqueHGaps.length === 1) {
75
+ console.log(`水平间距统一: ${uniqueHGaps[0]}px → 可使用 flex gap 或 Row mainAxisSpacing`)
76
+ } else {
77
+ console.log(`水平间距不统一: ${horizontalGaps.join(', ')} → 需要单独设置`)
78
+ }
79
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 样式提取模板 - 完整样式参数
3
+ * 用途:提取颜色、渐变、阴影、圆角等样式
4
+ *
5
+ * 使用方式:在 Sketch 中选中目标元素,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (!sel.length) {
11
+ console.log('请先选中一个元素')
12
+ } else {
13
+ const layer = sel[0]
14
+ const style = layer.style
15
+
16
+ console.log('=== 样式提取结果 ===')
17
+ console.log(`元素: ${layer.name}`)
18
+
19
+ // 圆角
20
+ if (layer.style?.corners) {
21
+ console.log(`\n--- 圆角 ---`)
22
+ console.log(`圆角: ${JSON.stringify(layer.style.corners.radii)}`)
23
+ }
24
+
25
+ // 填充(颜色/渐变)
26
+ if (style?.fills?.length) {
27
+ console.log(`\n--- 填充 ---`)
28
+ style.fills.forEach((fill, i) => {
29
+ if (fill.fillType === 'Color') {
30
+ console.log(`填充${i}: 纯色 ${fill.color}`)
31
+ // 转换提示
32
+ const hex = fill.color
33
+ console.log(` CSS: background: ${hex};`)
34
+ console.log(` Flutter: Color(0xFF${hex.replace('#', '').toUpperCase()})`)
35
+ } else if (fill.fillType === 'Gradient') {
36
+ console.log(`填充${i}: 渐变`)
37
+ console.log(` 类型: ${fill.gradient.gradientType}`)
38
+ console.log(` 起点: from(${fill.gradient.from.x}, ${fill.gradient.from.y})`)
39
+ console.log(` 终点: to(${fill.gradient.to.x}, ${fill.gradient.to.y})`)
40
+ console.log(` 色标:`)
41
+ fill.gradient.stops.forEach((stop, j) => {
42
+ console.log(` ${j}: ${stop.color} at ${(stop.position * 100).toFixed(0)}%`)
43
+ })
44
+
45
+ // 计算渐变角度
46
+ const dx = fill.gradient.to.x - fill.gradient.from.x
47
+ const dy = fill.gradient.to.y - fill.gradient.from.y
48
+ const angle = Math.round(Math.atan2(dy, dx) * 180 / Math.PI + 90)
49
+
50
+ // CSS 转换
51
+ const colors = fill.gradient.stops.map(s => `${s.color} ${(s.position * 100).toFixed(0)}%`).join(', ')
52
+ console.log(` CSS: linear-gradient(${angle}deg, ${colors})`)
53
+
54
+ // Flutter 转换
55
+ const fromX = (fill.gradient.from.x * 2 - 1).toFixed(2)
56
+ const fromY = (fill.gradient.from.y * 2 - 1).toFixed(2)
57
+ const toX = (fill.gradient.to.x * 2 - 1).toFixed(2)
58
+ const toY = (fill.gradient.to.y * 2 - 1).toFixed(2)
59
+ console.log(` Flutter: begin: Alignment(${fromX}, ${fromY}), end: Alignment(${toX}, ${toY})`)
60
+ }
61
+ })
62
+ }
63
+
64
+ // 边框
65
+ if (style?.borders?.length) {
66
+ console.log(`\n--- 边框 ---`)
67
+ style.borders.forEach((border, i) => {
68
+ console.log(`边框${i}: ${border.color} 宽度=${border.thickness}`)
69
+ console.log(` CSS: border: ${border.thickness}px solid ${border.color};`)
70
+ })
71
+ }
72
+
73
+ // 阴影
74
+ if (style?.shadows?.length) {
75
+ console.log(`\n--- 阴影 ---`)
76
+ style.shadows.forEach((shadow, i) => {
77
+ console.log(`阴影${i}:`)
78
+ console.log(` 颜色: ${shadow.color}`)
79
+ console.log(` 偏移: x=${shadow.x}, y=${shadow.y}`)
80
+ console.log(` 模糊: ${shadow.blur}`)
81
+ console.log(` 扩展: ${shadow.spread}`)
82
+ console.log(` CSS: box-shadow: ${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.spread}px ${shadow.color};`)
83
+ })
84
+ }
85
+
86
+ // 内阴影
87
+ if (style?.innerShadows?.length) {
88
+ console.log(`\n--- 内阴影 ---`)
89
+ style.innerShadows.forEach((shadow, i) => {
90
+ console.log(`内阴影${i}:`)
91
+ console.log(` 颜色: ${shadow.color}`)
92
+ console.log(` 偏移: x=${shadow.x}, y=${shadow.y}`)
93
+ console.log(` 模糊: ${shadow.blur}`)
94
+ console.log(` CSS: box-shadow: inset ${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.color};`)
95
+ })
96
+ }
97
+
98
+ // 文字样式(如果是文本)
99
+ if (layer.type === 'Text') {
100
+ console.log(`\n--- 文字样式 ---`)
101
+ const textStyle = layer.style
102
+ console.log(`内容: ${layer.text}`)
103
+ console.log(`字体: ${textStyle.fontFamily || 'default'}`)
104
+ console.log(`字号: ${textStyle.fontSize}`)
105
+ console.log(`行高: ${textStyle.lineHeight || 'auto'}`)
106
+ console.log(`颜色: ${textStyle.textColor}`)
107
+ console.log(`对齐: ${textStyle.alignment}`)
108
+ }
109
+ }