styimat 1.4.0 → 1.6.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.
Files changed (4) hide show
  1. package/README.md +263 -24
  2. package/package.json +1 -1
  3. package/styimat.js +1451 -1380
  4. package/styimat.min.js +154 -126
package/styimat.js CHANGED
@@ -1,3 +1,7 @@
1
+ /*!
2
+ * MIT License
3
+ * Copyright (c) 2025 王小玗
4
+ */
1
5
  /**
2
6
  * CSS 变量预处理库 - 增强版
3
7
  * 支持 CSS 变量预处理、嵌套选择器和嵌套变量
@@ -7,1470 +11,1537 @@
7
11
  * 增强 math() 函数,支持复杂数学计算
8
12
  */
9
13
  (function(root, factory) {
10
- if (typeof define === 'function' && define.amd) {
11
- // AMD 支持 (RequireJS)
12
- define([], factory);
13
- } else if (typeof module === 'object' && module.exports) {
14
- // CommonJS 支持 (Node.js, Browserify, Webpack)
15
- module.exports = factory();
16
- } else {
17
- // 浏览器全局变量
18
- root.styimat = factory();
19
- }
14
+ if (typeof define === 'function' && define.amd) {
15
+ // AMD 支持 (RequireJS)
16
+ define([], factory);
17
+ } else if (typeof module === 'object' && module.exports) {
18
+ // CommonJS 支持 (Node.js, Browserify, Webpack)
19
+ module.exports = factory();
20
+ } else {
21
+ // 浏览器全局变量
22
+ root.styimat = factory();
23
+ }
20
24
  }(typeof self !== 'undefined' ? self : this, function() {
21
- // 默认配置
22
- let defaultConfig = {
23
- rootSelector: ':root',
24
- variablePrefix: '--',
25
- preserveOriginal: false,
26
- indentSize: 2,
27
- enableNesting: true,
28
- autoProcessStyleTags: true,
29
- styleTagAttribute: 'e',
30
- convertLabToRGB: true,
31
- convertLchToRGB: true,
32
- enableP3: true,
33
- enableMath: true, // 启用math()函数增强
34
- mathPrecision: 6, // 数学计算精度
35
- };
25
+ // 默认配置
26
+ let defaultConfig = {
27
+ rootSelector: ':root',
28
+ variablePrefix: '--',
29
+ preserveOriginal: false,
30
+ indentSize: 2,
31
+ enableNesting: true,
32
+ autoProcessStyleTags: true,
33
+ styleTagAttribute: 'e',
34
+ convertLabToRGB: true,
35
+ convertLchToRGB: true,
36
+ enableP3: true,
37
+ enableMath: true, // 启用math()函数增强
38
+ mathPrecision: 6, // 数学计算精度
39
+ };
36
40
 
37
- // 全局P3支持检测结果
38
- let p3Supported = null;
41
+ // 全局P3支持检测结果
42
+ let p3Supported = null;
39
43
 
40
- /**
41
- * 检测浏览器是否支持 Display P3
42
- * @returns {boolean} 是否支持P3
43
- */
44
- function detectP3Support() {
45
- if (p3Supported !== null) return p3Supported;
46
-
47
- if (typeof window === 'undefined' || !window.CSS) {
48
- p3Supported = false;
49
- return false;
50
- }
51
-
52
- try {
53
- p3Supported = CSS.supports('color', 'color(display-p3 1 0 0)');
54
- } catch (error) {
55
- console.warn('P3支持检测失败:', error);
56
- p3Supported = false;
44
+ /**
45
+ * 检测浏览器是否支持 Display P3
46
+ * @returns {boolean} 是否支持P3
47
+ */
48
+ function detectP3Support() {
49
+ if (p3Supported !== null) return p3Supported;
50
+
51
+ if (typeof window === 'undefined' || !window.CSS) {
52
+ p3Supported = false;
53
+ return false;
54
+ }
55
+
56
+ try {
57
+ p3Supported = CSS.supports('color', 'color(display-p3 1 0 0)');
58
+ } catch (error) {
59
+ console.warn('P3支持检测失败:', error);
60
+ p3Supported = false;
61
+ }
62
+
63
+ return p3Supported;
57
64
  }
58
-
59
- return p3Supported;
60
- }
61
65
 
62
- /**
63
- * 解析一行CSS(可能多个语句在同一行,可能最后一个语句没有分号)
64
- * 返回格式:[{属性名:值}, {属性名:值}, ...]
65
- * @param {string} cssString - 要解析的CSS字符串
66
- * @returns {Array} 包含属性名值对象的数组
67
- */
68
- function parseSingleLineCSS(cssString) {
69
- // 移除首尾空白字符
70
- let str = cssString.trim();
71
-
72
- // 如果以分号结尾,移除最后一个分号
73
- if (str.endsWith(';')) {
74
- str = str.slice(0, -1);
75
- }
76
-
77
- // 如果字符串为空,返回空数组
78
- if (!str) {
79
- return [];
80
- }
81
-
82
- // 分割多个CSS声明(以分号分隔)
83
- const declarations = [];
84
- let currentDeclaration = '';
85
- let inParens = 0; // 记录括号嵌套层数,用于处理函数内的分号
86
- let inQuotes = false; // 是否在引号内
87
- let quoteChar = ''; // 当前引号字符
88
-
89
- for (let i = 0; i < str.length; i++) {
90
- const char = str[i];
91
-
92
- // 处理引号
93
- if ((char === '"' || char === "'") && !inQuotes) {
94
- inQuotes = true;
95
- quoteChar = char;
96
- } else if (char === quoteChar && inQuotes) {
97
- inQuotes = false;
98
- quoteChar = '';
99
- }
100
-
101
- // 处理括号(不在引号内时)
102
- if (!inQuotes) {
103
- if (char === '(') {
104
- inParens++;
105
- } else if (char === ')') {
106
- inParens--;
66
+ /**
67
+ * 解析一行CSS(可能多个语句在同一行,可能最后一个语句没有分号)
68
+ * 返回格式:[{属性名:值}, {属性名:值}, ...]
69
+ * @param {string} cssString - 要解析的CSS字符串
70
+ * @returns {Array} 包含属性名值对象的数组
71
+ */
72
+ function parseSingleLineCSS(cssString) {
73
+ // 移除首尾空白字符
74
+ let str = cssString.trim();
75
+
76
+ // 如果以分号结尾,移除最后一个分号
77
+ if (str.endsWith(';')) {
78
+ str = str.slice(0, -1);
79
+ }
80
+
81
+ // 如果字符串为空,返回空数组
82
+ if (!str) {
83
+ return [];
84
+ }
85
+
86
+ // 分割多个CSS声明(以分号分隔)
87
+ const declarations = [];
88
+ let currentDeclaration = '';
89
+ let inParens = 0; // 记录括号嵌套层数,用于处理函数内的分号
90
+ let inQuotes = false; // 是否在引号内
91
+ let quoteChar = ''; // 当前引号字符
92
+
93
+ for (let i = 0; i < str.length; i++) {
94
+ const char = str[i];
95
+
96
+ // 处理引号
97
+ if ((char === '"' || char === "'") && !inQuotes) {
98
+ inQuotes = true;
99
+ quoteChar = char;
100
+ } else if (char === quoteChar && inQuotes) {
101
+ inQuotes = false;
102
+ quoteChar = '';
103
+ }
104
+
105
+ // 处理括号(不在引号内时)
106
+ if (!inQuotes) {
107
+ if (char === '(') {
108
+ inParens++;
109
+ } else if (char === ')') {
110
+ inParens--;
111
+ }
112
+ }
113
+
114
+ // 如果遇到分号且不在括号和引号内,则分割声明
115
+ if (char === ';' && !inQuotes && inParens === 0) {
116
+ if (currentDeclaration.trim()) {
117
+ declarations.push(currentDeclaration.trim());
118
+ currentDeclaration = '';
119
+ }
120
+ } else {
121
+ currentDeclaration += char;
122
+ }
107
123
  }
108
- }
109
-
110
- // 如果遇到分号且不在括号和引号内,则分割声明
111
- if (char === ';' && !inQuotes && inParens === 0) {
124
+
125
+ // 添加最后一个声明(如果没有分号结尾)
112
126
  if (currentDeclaration.trim()) {
113
- declarations.push(currentDeclaration.trim());
114
- currentDeclaration = '';
127
+ declarations.push(currentDeclaration.trim());
115
128
  }
116
- } else {
117
- currentDeclaration += char;
118
- }
119
- }
120
-
121
- // 添加最后一个声明(如果没有分号结尾)
122
- if (currentDeclaration.trim()) {
123
- declarations.push(currentDeclaration.trim());
124
- }
125
-
126
- // 解析每个声明为对象
127
- const result = [];
128
-
129
- for (const declaration of declarations) {
130
- // 跳过空声明
131
- if (!declaration.trim()) continue;
132
-
133
- // 查找第一个冒号的位置
134
- const colonIndex = findFirstColonOutsideQuotes(declaration);
135
-
136
- if (colonIndex === -1) {
137
- console.warn(`无效的CSS声明: "${declaration}"`);
138
- continue;
139
- }
140
-
141
- // 分割属性名和值
142
- const property = declaration.substring(0, colonIndex).trim();
143
- let value = declaration.substring(colonIndex + 1).trim();
144
-
145
- // 如果值以分号结尾,移除它(理论上不应该有,但处理一下)
146
- if (value.endsWith(';')) {
147
- value = value.slice(0, -1).trim();
148
- }
149
-
150
- // 添加到结果数组
151
- result.push({ [property]: value });
129
+
130
+ // 解析每个声明为对象
131
+ const result = [];
132
+
133
+ for (const declaration of declarations) {
134
+ // 跳过空声明
135
+ if (!declaration.trim()) continue;
136
+
137
+ // 查找第一个冒号的位置
138
+ const colonIndex = findFirstColonOutsideQuotes(declaration);
139
+
140
+ if (colonIndex === -1) {
141
+ console.warn(`无效的CSS声明: "${declaration}"`);
142
+ continue;
143
+ }
144
+
145
+ // 分割属性名和值
146
+ const property = declaration.substring(0, colonIndex).trim();
147
+ let value = declaration.substring(colonIndex + 1).trim();
148
+
149
+ // 如果值以分号结尾,移除它(理论上不应该有,但处理一下)
150
+ if (value.endsWith(';')) {
151
+ value = value.slice(0, -1).trim();
152
+ }
153
+
154
+ // 添加到结果数组
155
+ result.push({ [property]: value });
156
+ }
157
+
158
+ return result;
152
159
  }
153
-
154
- return result;
155
- }
156
160
 
157
- /**
158
- * 查找不在引号内的第一个冒号位置
159
- * @param {string} str
160
- * @returns {number}
161
- */
162
- function findFirstColonOutsideQuotes(str) {
163
- let inQuotes = false;
164
- let quoteChar = '';
165
-
166
- for (let i = 0; i < str.length; i++) {
167
- const char = str[i];
168
-
169
- // 处理引号
170
- if ((char === '"' || char === "'") && !inQuotes) {
171
- inQuotes = true;
172
- quoteChar = char;
173
- } else if (char === quoteChar && inQuotes) {
174
- inQuotes = false;
175
- quoteChar = '';
176
- }
177
-
178
- // 如果找到冒号且不在引号内,返回位置
179
- if (char === ':' && !inQuotes) {
180
- return i;
181
- }
161
+ /**
162
+ * 查找不在引号内的第一个冒号位置
163
+ * @param {string} str
164
+ * @returns {number}
165
+ */
166
+ function findFirstColonOutsideQuotes(str) {
167
+ let inQuotes = false;
168
+ let quoteChar = '';
169
+
170
+ for (let i = 0; i < str.length; i++) {
171
+ const char = str[i];
172
+
173
+ // 处理引号
174
+ if ((char === '"' || char === "'") && !inQuotes) {
175
+ inQuotes = true;
176
+ quoteChar = char;
177
+ } else if (char === quoteChar && inQuotes) {
178
+ inQuotes = false;
179
+ quoteChar = '';
180
+ }
181
+
182
+ // 如果找到冒号且不在引号内,返回位置
183
+ if (char === ':' && !inQuotes) {
184
+ return i;
185
+ }
186
+ }
187
+
188
+ return -1;
182
189
  }
183
-
184
- return -1;
185
- }
186
190
 
187
- /**
188
- * 增强的数学表达式解析和计算
189
- * @param {string} expression - 数学表达式
190
- * @param {Object} config - 配置对象
191
- * @returns {string} 计算结果
192
- */
193
- function evaluateMathExpression(expression, config) {
194
- // 如果禁用math()增强,则返回原始表达式
195
- if (!config.enableMath) {
196
- return `math(${expression})`;
197
- }
198
-
199
- try {
200
- // 清理表达式:移除空白字符
201
- let cleanExpr = expression.replace(/\s+/g, '');
202
-
203
- // 解析数学表达式
204
- const result = parseMathExpression(cleanExpr, config);
205
-
206
- // 如果结果包含单位,保留原始格式
207
- if (hasUnits(cleanExpr)) {
208
- // 处理带单位的表达式
209
- return processUnitExpression(cleanExpr, result);
210
- } else {
211
- // 纯数字表达式,直接计算结果
212
- return roundNumber(result, config.mathPrecision);
213
- }
214
- } catch (error) {
215
- console.warn('math()表达式解析失败:', expression, error);
216
- return `math(${expression})`;
191
+ /**
192
+ * 增强的数学表达式解析和计算
193
+ * @param {string} expression - 数学表达式
194
+ * @param {Object} config - 配置对象
195
+ * @returns {string} 计算结果
196
+ */
197
+ function evaluateMathExpression(expression, config) {
198
+ // 如果禁用math()增强,则返回原始表达式
199
+ if (!config.enableMath) {
200
+ return `math(${expression})`;
201
+ }
202
+
203
+ try {
204
+ // 清理表达式:移除空白字符
205
+ let cleanExpr = expression.replace(/\s+/g, '');
206
+
207
+ // 解析数学表达式
208
+ const result = parseMathExpression(cleanExpr, config);
209
+
210
+ // 如果结果包含单位,保留原始格式
211
+ if (hasUnits(cleanExpr)) {
212
+ // 处理带单位的表达式
213
+ return processUnitExpression(cleanExpr, result);
214
+ } else {
215
+ // 纯数字表达式,直接计算结果
216
+ return roundNumber(result, config.mathPrecision);
217
+ }
218
+ } catch (error) {
219
+ console.warn('math()表达式解析失败:', expression, error);
220
+ return `math(${expression})`;
221
+ }
217
222
  }
218
- }
219
223
 
220
- /**
221
- * 解析数学表达式
222
- * @param {string} expr - 清理后的表达式
223
- * @param {Object} config - 配置对象
224
- * @returns {number} 计算结果
225
- */
226
- function parseMathExpression(expr, config) {
227
- // 处理括号
228
- while (expr.includes('(') && expr.includes(')')) {
229
- const start = expr.lastIndexOf('(');
230
- const end = expr.indexOf(')', start);
231
-
232
- if (end === -1) break;
233
-
234
- const inner = expr.substring(start + 1, end);
235
- const innerResult = parseMathExpression(inner, config);
236
-
237
- expr = expr.substring(0, start) + innerResult + expr.substring(end + 1);
224
+ /**
225
+ * 解析数学表达式
226
+ * @param {string} expr - 清理后的表达式
227
+ * @param {Object} config - 配置对象
228
+ * @returns {number} 计算结果
229
+ */
230
+ function parseMathExpression(expr, config) {
231
+ // 处理括号
232
+ while (expr.includes('(') && expr.includes(')')) {
233
+ const start = expr.lastIndexOf('(');
234
+ const end = expr.indexOf(')', start);
235
+
236
+ if (end === -1) break;
237
+
238
+ const inner = expr.substring(start + 1, end);
239
+ const innerResult = parseMathExpression(inner, config);
240
+
241
+ expr = expr.substring(0, start) + innerResult + expr.substring(end + 1);
242
+ }
243
+
244
+ // 现在expr应该没有括号了
245
+ return evaluateSimpleExpression(expr, config);
238
246
  }
239
-
240
- // 现在expr应该没有括号了
241
- return evaluateSimpleExpression(expr, config);
242
- }
243
247
 
244
- /**
245
- * 评估简单表达式(无括号)
246
- * @param {string} expr - 简单表达式
247
- * @param {Object} config - 配置对象
248
- * @returns {number} 计算结果
249
- */
250
- function evaluateSimpleExpression(expr, config) {
251
- // 处理操作符优先级:乘除优先于加减
252
- const operators = [
253
- { regex: /([\d.]+(?:[a-zA-Z%]+)?)\*([\d.]+(?:[a-zA-Z%]+)?)/, handler: multiply },
254
- { regex: /([\d.]+(?:[a-zA-Z%]+)?)\/([\d.]+(?:[a-zA-Z%]+)?)/, handler: divide },
255
- { regex: /([\d.]+(?:[a-zA-Z%]+)?)\+([\d.]+(?:[a-zA-Z%]+)?)/, handler: add },
256
- { regex: /([\d.]+(?:[a-zA-Z%]+)?)-([\d.]+(?:[a-zA-Z%]+)?)/, handler: subtract }
257
- ];
258
-
259
- let lastExpr = expr;
260
-
261
- // 按照优先级处理操作符
262
- for (const op of operators) {
263
- let match;
264
- while ((match = expr.match(op.regex)) !== null) {
265
- const left = parseValueWithUnit(match[1]);
266
- const right = parseValueWithUnit(match[2]);
267
- const result = op.handler(left, right);
268
-
269
- // 替换匹配的部分
270
- expr = expr.substring(0, match.index) +
271
- result.value +
272
- expr.substring(match.index + match[0].length);
273
- }
274
- }
275
-
276
- // 如果表达式无法进一步简化,尝试解析为数字
277
- if (expr !== lastExpr) {
278
- return evaluateSimpleExpression(expr, config);
248
+ /**
249
+ * 评估简单表达式(无括号)
250
+ * @param {string} expr - 简单表达式
251
+ * @param {Object} config - 配置对象
252
+ * @returns {number} 计算结果
253
+ */
254
+ function evaluateSimpleExpression(expr, config) {
255
+ // 处理操作符优先级:乘除优先于加减
256
+ const operators = [
257
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\*([\d.]+(?:[a-zA-Z%]+)?)/, handler: multiply },
258
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\/([\d.]+(?:[a-zA-Z%]+)?)/, handler: divide },
259
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\+([\d.]+(?:[a-zA-Z%]+)?)/, handler: add },
260
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)-([\d.]+(?:[a-zA-Z%]+)?)/, handler: subtract }
261
+ ];
262
+
263
+ let lastExpr = expr;
264
+
265
+ // 按照优先级处理操作符
266
+ for (const op of operators) {
267
+ let match;
268
+ while ((match = expr.match(op.regex)) !== null) {
269
+ const left = parseValueWithUnit(match[1]);
270
+ const right = parseValueWithUnit(match[2]);
271
+ const result = op.handler(left, right);
272
+
273
+ // 替换匹配的部分
274
+ expr = expr.substring(0, match.index) +
275
+ result.value +
276
+ expr.substring(match.index + match[0].length);
277
+ }
278
+ }
279
+
280
+ // 如果表达式无法进一步简化,尝试解析为数字
281
+ if (expr !== lastExpr) {
282
+ return evaluateSimpleExpression(expr, config);
283
+ }
284
+
285
+ // 解析最终结果
286
+ const parsed = parseValueWithUnit(expr);
287
+ return parsed.value;
279
288
  }
280
-
281
- // 解析最终结果
282
- const parsed = parseValueWithUnit(expr);
283
- return parsed.value;
284
- }
285
289
 
286
- /**
287
- * 解析带单位的数值
288
- * @param {string} str - 字符串值
289
- * @returns {Object} {value: number, unit: string}
290
- */
291
- function parseValueWithUnit(str) {
292
- // 匹配数值和单位
293
- const match = str.match(/^([\d.]+)([a-zA-Z%]*)$/);
294
-
295
- if (!match) {
296
- throw new Error(`无法解析值: ${str}`);
290
+ /**
291
+ * 解析带单位的数值
292
+ * @param {string} str - 字符串值
293
+ * @returns {Object} {value: number, unit: string}
294
+ */
295
+ function parseValueWithUnit(str) {
296
+ // 匹配数值和单位
297
+ const match = str.match(/^([\d.]+)([a-zA-Z%]*)$/);
298
+
299
+ if (!match) {
300
+ throw new Error(`无法解析值: ${str}`);
301
+ }
302
+
303
+ const value = parseFloat(match[1]);
304
+ const unit = match[2] || '';
305
+
306
+ return { value, unit };
297
307
  }
298
-
299
- const value = parseFloat(match[1]);
300
- const unit = match[2] || '';
301
-
302
- return { value, unit };
303
- }
304
308
 
305
- /**
306
- * 检查表达式是否包含单位
307
- * @param {string} expr - 表达式
308
- * @returns {boolean} 是否包含单位
309
- */
310
- function hasUnits(expr) {
311
- return /[a-zA-Z%]/.test(expr);
312
- }
313
-
314
- /**
315
- * 处理带单位的表达式
316
- * @param {string} originalExpr - 原始表达式
317
- * @param {number} result - 计算结果
318
- * @returns {string} 处理后的表达式
319
- */
320
- function processUnitExpression(originalExpr, result) {
321
- // 提取原始表达式中的单位
322
- const unitMatch = originalExpr.match(/([a-zA-Z%]+)(?!.*[a-zA-Z%])/);
323
-
324
- if (unitMatch) {
325
- return result + unitMatch[1];
309
+ /**
310
+ * 检查表达式是否包含单位
311
+ * @param {string} expr - 表达式
312
+ * @returns {boolean} 是否包含单位
313
+ */
314
+ function hasUnits(expr) {
315
+ return /[a-zA-Z%]/.test(expr);
326
316
  }
327
-
328
- // 如果没有明确单位,检查是否为百分比
329
- if (originalExpr.includes('%')) {
330
- return result + '%';
331
- }
332
-
333
- // 默认为像素
334
- return result + 'px';
335
- }
336
317
 
337
- // 数学运算函数
338
- function multiply(a, b) {
339
- if (a.unit === b.unit || (!a.unit && !b.unit)) {
340
- return { value: a.value * b.value, unit: a.unit || b.unit };
318
+ /**
319
+ * 处理带单位的表达式
320
+ * @param {string} originalExpr - 原始表达式
321
+ * @param {number} result - 计算结果
322
+ * @returns {string} 处理后的表达式
323
+ */
324
+ function processUnitExpression(originalExpr, result) {
325
+ // 提取原始表达式中的单位
326
+ const unitMatch = originalExpr.match(/([a-zA-Z%]+)(?!.*[a-zA-Z%])/);
327
+
328
+ if (unitMatch) {
329
+ return result + unitMatch[1];
330
+ }
331
+
332
+ // 如果没有明确单位,检查是否为百分比
333
+ if (originalExpr.includes('%')) {
334
+ return result + '%';
335
+ }
336
+
337
+ // 默认为像素
338
+ return result + 'px';
341
339
  }
342
-
343
- // 单位不匹配,返回原始表达式
344
- return { value: `${a.value}${a.unit}*${b.value}${b.unit}`, unit: '' };
345
- }
346
340
 
347
- function divide(a, b) {
348
- if (b.value === 0) {
349
- throw new Error('除以零');
350
- }
351
-
352
- if (a.unit && !b.unit) {
353
- // 如 100px / 2
354
- return { value: a.value / b.value, unit: a.unit };
355
- } else if (a.unit === b.unit) {
356
- // 如 100px / 2px
357
- return { value: a.value / b.value, unit: '' };
341
+ // 数学运算函数
342
+ function multiply(a, b) {
343
+ if (a.unit === b.unit || (!a.unit && !b.unit)) {
344
+ return { value: a.value * b.value, unit: a.unit || b.unit };
345
+ }
346
+
347
+ // 单位不匹配,返回原始表达式
348
+ return { value: `${a.value}${a.unit}*${b.value}${b.unit}`, unit: '' };
358
349
  }
359
-
360
- // 单位不匹配,返回原始表达式
361
- return { value: `${a.value}${a.unit}/${b.value}${b.unit}`, unit: '' };
362
- }
363
350
 
364
- function add(a, b) {
365
- if (a.unit === b.unit) {
366
- return { value: a.value + b.value, unit: a.unit };
351
+ function divide(a, b) {
352
+ if (b.value === 0) {
353
+ throw new Error('除以零');
354
+ }
355
+
356
+ if (a.unit && !b.unit) {
357
+ // 如 100px / 2
358
+ return { value: a.value / b.value, unit: a.unit };
359
+ } else if (a.unit === b.unit) {
360
+ // 如 100px / 2px
361
+ return { value: a.value / b.value, unit: '' };
362
+ }
363
+
364
+ // 单位不匹配,返回原始表达式
365
+ return { value: `${a.value}${a.unit}/${b.value}${b.unit}`, unit: '' };
367
366
  }
368
-
369
- // 单位不匹配,返回原始表达式
370
- return { value: `${a.value}${a.unit}+${b.value}${b.unit}`, unit: '' };
371
- }
372
367
 
373
- function subtract(a, b) {
374
- if (a.unit === b.unit) {
375
- return { value: a.value - b.value, unit: a.unit };
368
+ function add(a, b) {
369
+ if (a.unit === b.unit) {
370
+ return { value: a.value + b.value, unit: a.unit };
371
+ }
372
+
373
+ // 单位不匹配,返回原始表达式
374
+ return { value: `${a.value}${a.unit}+${b.value}${b.unit}`, unit: '' };
376
375
  }
377
-
378
- // 单位不匹配,返回原始表达式
379
- return { value: `${a.value}${a.unit}-${b.value}${b.unit}`, unit: '' };
380
- }
381
376
 
382
- /**
383
- * 四舍五入到指定精度
384
- * @param {number} num - 要四舍五入的数字
385
- * @param {number} precision - 精度
386
- * @returns {string} 四舍五入后的数字字符串
387
- */
388
- function roundNumber(num, precision = 6) {
389
- const factor = Math.pow(10, precision);
390
- const rounded = Math.round(num * factor) / factor;
391
-
392
- // 移除不必要的尾随零
393
- const str = rounded.toString();
394
- if (str.includes('.')) {
395
- return str.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
377
+ function subtract(a, b) {
378
+ if (a.unit === b.unit) {
379
+ return { value: a.value - b.value, unit: a.unit };
380
+ }
381
+
382
+ // 单位不匹配,返回原始表达式
383
+ return { value: `${a.value}${a.unit}-${b.value}${b.unit}`, unit: '' };
396
384
  }
397
- return str;
398
- }
399
385
 
400
- /**
401
- * 处理所有的math()函数
402
- * @param {string} cssValue - CSS属性值
403
- * @param {Object} config - 配置对象
404
- * @returns {string} 转换后的CSS值
405
- */
406
- function processMathFunctions(cssValue, config) {
407
- if (!config.enableMath) {
408
- return cssValue;
386
+ /**
387
+ * 四舍五入到指定精度
388
+ * @param {number} num - 要四舍五入的数字
389
+ * @param {number} precision - 精度
390
+ * @returns {string} 四舍五入后的数字字符串
391
+ */
392
+ function roundNumber(num, precision = 6) {
393
+ const factor = Math.pow(10, precision);
394
+ const rounded = Math.round(num * factor) / factor;
395
+
396
+ // 移除不必要的尾随零
397
+ const str = rounded.toString();
398
+ if (str.includes('.')) {
399
+ return str.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
400
+ }
401
+ return str;
409
402
  }
410
-
411
- let result = cssValue;
412
-
413
- // 匹配math()函数
414
- const mathRegex = /math\(([^)]+)\)/gi;
415
-
416
- // 递归处理嵌套的math()函数
417
- const processMath = (str) => {
418
- return str.replace(mathRegex, (match, expression) => {
419
- return evaluateMathExpression(expression, config);
420
- });
421
- };
422
-
423
- // 递归处理,直到没有更多math()函数
424
- let lastResult;
425
- do {
426
- lastResult = result;
427
- result = processMath(result);
428
- } while (result !== lastResult && result.includes('math('));
429
-
430
- return result;
431
- }
432
403
 
433
- /**
434
- * 解析并转换所有的 Lab 和 LCH 颜色(全局一次性处理)
435
- * @param {string} cssValue - CSS属性值
436
- * @param {Object} config - 配置对象
437
- * @returns {string} 转换后的CSS值
438
- */
439
- function convertAllLabLchColors(cssValue, config) {
440
- if (!config.convertLabToRGB && !config.convertLchToRGB) {
441
- return cssValue;
442
- }
443
-
444
- let result = cssValue;
445
-
446
- // 首先处理特殊的十六进制格式
447
- result = parseHexColorFormats(result, config);
448
-
449
- // 一次性处理所有的 lab() 和 lch() 函数
450
- const colorFunctionRegex = /(lab|lch)\([^)]+\)/gi;
451
-
452
- // 存储已经处理过的颜色字符串,避免重复处理
453
- const processedColors = new Map();
454
-
455
- // 使用一个函数来递归处理嵌套的颜色函数
456
- const processColorFunctions = (str) => {
457
- return str.replace(colorFunctionRegex, (match) => {
458
- // 如果这个颜色已经处理过,直接返回缓存结果
459
- if (processedColors.has(match)) {
460
- return processedColors.get(match);
404
+ /**
405
+ * 处理所有的math()函数
406
+ * @param {string} cssValue - CSS属性值
407
+ * @param {Object} config - 配置对象
408
+ * @returns {string} 转换后的CSS值
409
+ */
410
+ function processMathFunctions(cssValue, config) {
411
+ if (!config.enableMath) {
412
+ return cssValue;
461
413
  }
462
414
 
463
- let converted = match;
464
-
465
- // 根据函数类型处理
466
- if (match.toLowerCase().startsWith('lab(')) {
467
- if (config.convertLabToRGB) {
468
- converted = convertSingleLabColor(match, config);
469
- }
470
- } else if (match.toLowerCase().startsWith('lch(')) {
471
- if (config.convertLchToRGB) {
472
- converted = convertSingleLchColor(match, config);
473
- }
474
- }
415
+ let result = cssValue;
475
416
 
476
- // 缓存结果
477
- processedColors.set(match, converted);
478
- return converted;
479
- });
480
- };
481
-
482
- // 递归处理,直到没有更多颜色函数
483
- let lastResult;
484
- do {
485
- lastResult = result;
486
- result = processColorFunctions(result);
487
- } while (result !== lastResult);
488
-
489
- return result;
490
- }
491
-
492
- /**
493
- * 解析十六进制颜色格式:lab#L16A16B16 或 lch#L16C16H
494
- * @param {string} value - 颜色值
495
- * @param {Object} config - 配置对象
496
- * @returns {string} 转换后的CSS颜色
497
- */
498
- function parseHexColorFormats(value, config) {
499
- let result = value;
500
-
501
- // 使用正则表达式一次性匹配所有 lab# 和 lch# 格式
502
- const hexLabRegex = /lab#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/gi;
503
- const hexLchRegex = /lch#([0-9a-f]{2})([0-9a-f]{2})(\d{1,3})/gi;
504
-
505
- // 存储处理过的十六进制颜色,避免重复处理
506
- const processedHexColors = new Map();
507
-
508
- // 处理 lab# 格式
509
- result = result.replace(hexLabRegex, (match, L_hex, A_hex, B_hex) => {
510
- if (processedHexColors.has(match)) {
511
- return processedHexColors.get(match);
512
- }
513
-
514
- try {
515
- // 将十六进制转换为十进制,并映射到Lab范围
516
- const L = parseInt(L_hex, 16) / 255 * 100; // 0-100
517
- const A = (parseInt(A_hex, 16) - 128) * 1.5; // 大约 -192 到 192
518
- const B = (parseInt(B_hex, 16) - 128) * 1.5; // 大约 -192 到 192
519
-
520
- const converted = generateColorString(L, A, B, config);
521
- processedHexColors.set(match, converted);
522
- return converted;
523
- } catch (error) {
524
- console.warn(`无法解析lab#十六进制颜色: ${match}`, error);
525
- return match;
526
- }
527
- });
528
-
529
- // 处理 lch# 格式
530
- result = result.replace(hexLchRegex, (match, L_hex, C_hex, H_dec) => {
531
- if (processedHexColors.has(match)) {
532
- return processedHexColors.get(match);
533
- }
534
-
535
- try {
536
- // 将十六进制转换为十进制,并映射到LCH范围
537
- const L = parseInt(L_hex, 16) / 255 * 100; // 0-100
538
- const C = parseInt(C_hex, 16) / 255 * 150; // 0-150
539
- const H = parseInt(H_dec) / 100 * 360; // 0-360
540
-
541
- const lab = lchToLab(L, C, H);
542
- const converted = generateColorString(lab.L, lab.a, lab.b, config);
543
- processedHexColors.set(match, converted);
544
- return converted;
545
- } catch (error) {
546
- console.warn(`无法解析lch#十六进制颜色: ${match}`, error);
547
- return match;
548
- }
549
- });
550
-
551
- return result;
552
- }
553
-
554
- /**
555
- * 转换单个 lab() 颜色函数
556
- * @param {string} labString - lab() 颜色字符串
557
- * @param {Object} config - 配置对象
558
- * @returns {string} 转换后的颜色字符串
559
- */
560
- function convertSingleLabColor(labString, config) {
561
- // 使用更精确的正则匹配 lab() 函数
562
- const labRegex = /lab\(\s*([\d.]+)(%?)\s+([\d.-]+)\s+([\d.-]+)(?:\s*\/\s*([\d.%]+))?\s*\)/i;
563
- const match = labString.match(labRegex);
564
-
565
- if (!match) {
566
- return labString;
567
- }
568
-
569
- try {
570
- // 转换为数字
571
- let L = parseFloat(match[1]);
572
-
573
- // 如果L有百分号,转换为0-100范围的值
574
- if (match[2] === '%') {
575
- L = L; // 百分比值直接使用 (0-100)
576
- } else {
577
- // 没有百分号,确保在0-100范围内
578
- if (L < 0) L = 0;
579
- if (L > 100) L = 100;
580
- }
581
-
582
- const A = parseFloat(match[3]);
583
- const B = parseFloat(match[4]);
584
-
585
- // 处理 alpha 通道(如果有)
586
- const alphaValue = match[5] !== undefined
587
- ? match[5].includes('%')
588
- ? parseFloat(match[5]) / 100
589
- : parseFloat(match[5])
590
- : null;
591
-
592
- return generateColorString(L, A, B, config, alphaValue);
593
- } catch (error) {
594
- console.warn(`无法转换LAB颜色: ${labString}`, error);
595
- return labString;
417
+ // 匹配math()函数
418
+ const mathRegex = /math\(([^)]+)\)/gi;
419
+
420
+ // 递归处理嵌套的math()函数
421
+ const processMath = (str) => {
422
+ return str.replace(mathRegex, (match, expression) => {
423
+ return evaluateMathExpression(expression, config);
424
+ });
425
+ };
426
+
427
+ // 递归处理,直到没有更多math()函数
428
+ let lastResult;
429
+ do {
430
+ lastResult = result;
431
+ result = processMath(result);
432
+ } while (result !== lastResult && result.includes('math('));
433
+
434
+ return result;
596
435
  }
597
- }
598
436
 
599
- /**
600
- * 转换单个 lch() 颜色函数
601
- * @param {string} lchString - lch() 颜色字符串
602
- * @param {Object} config - 配置对象
603
- * @returns {string} 转换后的颜色字符串
604
- */
605
- function convertSingleLchColor(lchString, config) {
606
- // 使用更精确的正则匹配 lch() 函数
607
- const lchRegex = /lch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(deg)?(?:\s*\/\s*([\d.%]+))?\s*\)/i;
608
- const match = lchString.match(lchRegex);
609
-
610
- if (!match) {
611
- return lchString;
612
- }
613
-
614
- try {
615
- // 转换为数字
616
- let L = parseFloat(match[1]);
617
-
618
- // 如果L有百分号,转换为0-100范围的值
619
- if (match[2] === '%') {
620
- L = L; // 百分比值直接使用 (0-100)
621
- } else {
622
- // 没有百分号,确保在0-100范围内
623
- if (L < 0) L = 0;
624
- if (L > 100) L = 100;
625
- }
626
-
627
- const C = parseFloat(match[3]);
628
- let H = parseFloat(match[4]);
629
-
630
- // 验证C范围:>= 0
631
- if (C < 0) {
632
- console.warn(`LCH中的C值不能为负: ${C}`);
633
- }
634
-
635
- // 验证H范围:0-360,有deg单位或无单位都是0-360
636
- H = ((H % 360) + 360) % 360; // 标准化到0-360
637
-
638
- // LCH -> Lab
639
- const lab = lchToLab(L, C, H);
640
-
641
- // 处理 alpha 通道(如果有)
642
- const alphaValue = match[6] !== undefined
643
- ? match[6].includes('%')
644
- ? parseFloat(match[6]) / 100
645
- : parseFloat(match[6])
646
- : null;
647
-
648
- return generateColorString(lab.L, lab.a, lab.b, config, alphaValue);
649
- } catch (error) {
650
- console.warn(`无法转换LCH颜色: ${lchString}`, error);
651
- return lchString;
437
+ /**
438
+ * 解析并转换所有的 Lab 和 LCH 颜色(全局一次性处理)
439
+ * @param {string} cssValue - CSS属性值
440
+ * @param {Object} config - 配置对象
441
+ * @returns {string} 转换后的CSS值
442
+ */
443
+ function convertAllLabLchColors(cssValue, config) {
444
+ if (!config.convertLabToRGB && !config.convertLchToRGB) {
445
+ return cssValue;
446
+ }
447
+
448
+ let result = cssValue;
449
+
450
+ // 首先处理特殊的十六进制格式
451
+ result = parseHexColorFormats(result, config);
452
+
453
+ // 一次性处理所有的 lab() 和 lch() 函数
454
+ const colorFunctionRegex = /(lab|lch)\([^)]+\)/gi;
455
+
456
+ // 存储已经处理过的颜色字符串,避免重复处理
457
+ const processedColors = new Map();
458
+
459
+ // 使用一个函数来递归处理嵌套的颜色函数
460
+ const processColorFunctions = (str) => {
461
+ return str.replace(colorFunctionRegex, (match) => {
462
+ // 如果这个颜色已经处理过,直接返回缓存结果
463
+ if (processedColors.has(match)) {
464
+ return processedColors.get(match);
465
+ }
466
+
467
+ let converted = match;
468
+
469
+ // 根据函数类型处理
470
+ if (match.toLowerCase().startsWith('lab(')) {
471
+ if (config.convertLabToRGB) {
472
+ converted = convertSingleLabColor(match, config);
473
+ }
474
+ } else if (match.toLowerCase().startsWith('lch(')) {
475
+ if (config.convertLchToRGB) {
476
+ converted = convertSingleLchColor(match, config);
477
+ }
478
+ }
479
+
480
+ // 缓存结果
481
+ processedColors.set(match, converted);
482
+ return converted;
483
+ });
484
+ };
485
+
486
+ // 递归处理,直到没有更多颜色函数
487
+ let lastResult;
488
+ do {
489
+ lastResult = result;
490
+ result = processColorFunctions(result);
491
+ } while (result !== lastResult);
492
+
493
+ return result;
652
494
  }
653
- }
654
495
 
655
- /**
656
- * LCH 转换为 Lab (CSS标准)
657
- * @param {number} L - 明度 0-100 或 0%-100%
658
- * @param {number} C - 色度 >=0
659
- * @param {number} H - 色相角 0-360 度
660
- * @returns {Object} Lab 值 {L, a, b}
661
- */
662
- function lchToLab(L, C, H) {
663
- // 角度转换为弧度
664
- const H_rad = H * Math.PI / 180;
665
-
666
- // LCH -> Lab 转换公式
667
- const a = C * Math.cos(H_rad);
668
- const b = C * Math.sin(H_rad);
669
-
670
- return { L, a, b };
671
- }
496
+ /**
497
+ * 解析十六进制颜色格式:lab#L16A16B16 lch#L16C16H
498
+ * @param {string} value - 颜色值
499
+ * @param {Object} config - 配置对象
500
+ * @returns {string} 转换后的CSS颜色
501
+ */
502
+ function parseHexColorFormats(value, config) {
503
+ let result = value;
504
+
505
+ // 使用正则表达式一次性匹配所有 lab# lch# 格式
506
+ const hexLabRegex = /lab#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/gi;
507
+ const hexLchRegex = /lch#([0-9a-f]{2})([0-9a-f]{2})(\d{1,3})/gi;
508
+
509
+ // 存储处理过的十六进制颜色,避免重复处理
510
+ const processedHexColors = new Map();
511
+
512
+ // 处理 lab# 格式
513
+ result = result.replace(hexLabRegex, (match, L_hex, A_hex, B_hex) => {
514
+ if (processedHexColors.has(match)) {
515
+ return processedHexColors.get(match);
516
+ }
517
+
518
+ try {
519
+ // 将十六进制转换为十进制,并映射到Lab范围
520
+ const L = parseInt(L_hex, 16) / 255 * 100; // 0-100
521
+ const A = (parseInt(A_hex, 16) - 128) * 1.5; // 大约 -192 到 192
522
+ const B = (parseInt(B_hex, 16) - 128) * 1.5; // 大约 -192 到 192
523
+
524
+ const converted = generateColorString(L, A, B, config);
525
+ processedHexColors.set(match, converted);
526
+ return converted;
527
+ } catch (error) {
528
+ console.warn(`无法解析lab#十六进制颜色: ${match}`, error);
529
+ return match;
530
+ }
531
+ });
532
+
533
+ // 处理 lch# 格式
534
+ result = result.replace(hexLchRegex, (match, L_hex, C_hex, H_dec) => {
535
+ if (processedHexColors.has(match)) {
536
+ return processedHexColors.get(match);
537
+ }
538
+
539
+ try {
540
+ // 将十六进制转换为十进制,并映射到LCH范围
541
+ const L = parseInt(L_hex, 16) / 255 * 100; // 0-100
542
+ const C = parseInt(C_hex, 16) / 255 * 150; // 0-150
543
+ const H = parseInt(H_dec) / 100 * 360; // 0-360
544
+
545
+ const lab = lchToLab(L, C, H);
546
+ const converted = generateColorString(lab.L, lab.a, lab.b, config);
547
+ processedHexColors.set(match, converted);
548
+ return converted;
549
+ } catch (error) {
550
+ console.warn(`无法解析lch#十六进制颜色: ${match}`, error);
551
+ return match;
552
+ }
553
+ });
554
+
555
+ return result;
556
+ }
672
557
 
673
- // 精确的 Lab -> sRGB 转换函数
674
- function preciseLabToRGB(L, a, b) {
675
- // 1. Lab -> XYZ (D65白点)
676
- const labToXyz = (L, a, b) => {
677
- // D65 白点参考值
678
- const refX = 0.95047;
679
- const refY = 1.00000;
680
- const refZ = 1.08883;
681
-
682
- const epsilon = 0.008856;
683
- const kappa = 903.3;
684
-
685
- const fy = (L + 16) / 116;
686
- const fx = a / 500 + fy;
687
- const fz = fy - b / 200;
688
-
689
- const xr = fx ** 3 > epsilon ? fx ** 3 : (116 * fx - 16) / kappa;
690
- const yr = L > kappa * epsilon ? ((L + 16) / 116) ** 3 : L / kappa;
691
- const zr = fz ** 3 > epsilon ? fz ** 3 : (116 * fz - 16) / kappa;
692
-
693
- return [
694
- xr * refX,
695
- yr * refY,
696
- zr * refZ
697
- ];
698
- };
699
-
700
- // 2. XYZ -> Linear RGB
701
- const xyzToLinearRgb = (x, y, z) => {
702
- // sRGB 转换矩阵 (D65)
703
- const M = [
704
- [ 3.2404542, -1.5371385, -0.4985314],
705
- [-0.9692660, 1.8760108, 0.0415560],
706
- [ 0.0556434, -0.2040259, 1.0572252]
707
- ];
708
-
709
- const r = M[0][0] * x + M[0][1] * y + M[0][2] * z;
710
- const g = M[1][0] * x + M[1][1] * y + M[1][2] * z;
711
- const b = M[2][0] * x + M[2][1] * y + M[2][2] * z;
712
-
713
- return [r, g, b];
714
- };
715
-
716
- // 3. sRGB Gamma 校正
717
- const applyGamma = (c) => {
718
- const sign = c < 0 ? -1 : 1;
719
- const absC = Math.abs(c);
720
-
721
- if (absC <= 0.0031308) {
722
- return sign * 12.92 * absC;
723
- } else {
724
- return sign * (1.055 * Math.pow(absC, 1/2.4) - 0.055);
725
- }
726
- };
727
-
728
- // 4. 钳制到 [0, 255]
729
- const clamp = (value) => {
730
- return Math.max(0, Math.min(255, Math.round(value * 255)));
731
- };
732
-
733
- // 执行转换
734
- const [X, Y, Z] = labToXyz(L, a, b);
735
- const [linearR, linearG, linearB] = xyzToLinearRgb(X, Y, Z);
736
-
737
- const r = applyGamma(linearR);
738
- const g = applyGamma(linearG);
739
- const bOut = applyGamma(linearB);
740
-
741
- // 返回钳制后的 RGB 值
742
- return {
743
- r: clamp(r),
744
- g: clamp(g),
745
- b: clamp(bOut)
746
- };
747
- }
558
+ /**
559
+ * 转换单个 lab() 颜色函数
560
+ * @param {string} labString - lab() 颜色字符串
561
+ * @param {Object} config - 配置对象
562
+ * @returns {string} 转换后的颜色字符串
563
+ */
564
+ function convertSingleLabColor(labString, config) {
565
+ // 使用更精确的正则匹配 lab() 函数
566
+ const labRegex = /lab\(\s*([\d.]+)(%?)\s+([\d.-]+)\s+([\d.-]+)(?:\s*\/\s*([\d.%]+))?\s*\)/i;
567
+ const match = labString.match(labRegex);
568
+
569
+ if (!match) {
570
+ return labString;
571
+ }
572
+
573
+ try {
574
+ // 转换为数字
575
+ let L = parseFloat(match[1]);
576
+
577
+ // 如果L有百分号,转换为0-100范围的值
578
+ if (match[2] === '%') {
579
+ L = L; // 百分比值直接使用 (0-100)
580
+ } else {
581
+ // 没有百分号,确保在0-100范围内
582
+ if (L < 0) L = 0;
583
+ if (L > 100) L = 100;
584
+ }
585
+
586
+ const A = parseFloat(match[3]);
587
+ const B = parseFloat(match[4]);
588
+
589
+ // 处理 alpha 通道(如果有)
590
+ const alphaValue = match[5] !== undefined
591
+ ? match[5].includes('%')
592
+ ? parseFloat(match[5]) / 100
593
+ : parseFloat(match[5])
594
+ : null;
595
+
596
+ return generateColorString(L, A, B, config, alphaValue);
597
+ } catch (error) {
598
+ console.warn(`无法转换LAB颜色: ${labString}`, error);
599
+ return labString;
600
+ }
601
+ }
748
602
 
749
- /**
750
- * Lab -> Display P3 转换
751
- * @param {number} L - 明度 0-100
752
- * @param {number} a - a分量
753
- * @param {number} b - b分量
754
- * @returns {Object} P3颜色 {r, g, b}
755
- */
756
- function labToP3(L, a, b) {
757
- // 1. Lab -> XYZ (使用与RGB相同的转换)
758
- const labToXyz = (L, a, b) => {
759
- const refX = 0.95047;
760
- const refY = 1.00000;
761
- const refZ = 1.08883;
762
-
763
- const epsilon = 0.008856;
764
- const kappa = 903.3;
765
-
766
- const fy = (L + 16) / 116;
767
- const fx = a / 500 + fy;
768
- const fz = fy - b / 200;
769
-
770
- const xr = fx ** 3 > epsilon ? fx ** 3 : (116 * fx - 16) / kappa;
771
- const yr = L > kappa * epsilon ? ((L + 16) / 116) ** 3 : L / kappa;
772
- const zr = fz ** 3 > epsilon ? fz ** 3 : (116 * fz - 16) / kappa;
773
-
774
- return [
775
- xr * refX,
776
- yr * refY,
777
- zr * refZ
778
- ];
779
- };
780
-
781
- // 2. XYZ -> Linear P3
782
- const xyzToLinearP3 = (x, y, z) => {
783
- // Display P3 转换矩阵 (D65)
784
- const M = [
785
- [ 2.493496911941425, -0.9313836179191239, -0.40271078445071684],
786
- [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
787
- [ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
788
- ];
789
-
790
- const r = M[0][0] * x + M[0][1] * y + M[0][2] * z;
791
- const g = M[1][0] * x + M[1][1] * y + M[1][2] * z;
792
- const b = M[2][0] * x + M[2][1] * y + M[2][2] * z;
793
-
794
- return [r, g, b];
795
- };
796
-
797
- // 3. P3 Gamma 校正(与sRGB相同)
798
- const applyGamma = (c) => {
799
- const sign = c < 0 ? -1 : 1;
800
- const absC = Math.abs(c);
801
-
802
- if (absC <= 0.0031308) {
803
- return sign * 12.92 * absC;
804
- } else {
805
- return sign * (1.055 * Math.pow(absC, 1/2.4) - 0.055);
806
- }
807
- };
808
-
809
- // 4. 钳制到 [0, 255],然后转换为 [0, 1] 范围
810
- const clamp = (value) => {
811
- return Math.max(0, Math.min(255, Math.round(value * 255)));
812
- };
813
-
814
- // 执行转换
815
- try {
816
- const [X, Y, Z] = labToXyz(L, a, b);
817
- const [linearR, linearG, linearB] = xyzToLinearP3(X, Y, Z);
818
-
819
- const r = applyGamma(linearR);
820
- const g = applyGamma(linearG);
821
- const bOut = applyGamma(linearB);
822
-
823
- // 转换为0-1范围的浮点数用于P3颜色格式
824
- return {
825
- r: Math.max(0, Math.min(1, r)),
826
- g: Math.max(0, Math.min(1, g)),
827
- b: Math.max(0, Math.min(1, bOut))
828
- };
829
- } catch (error) {
830
- console.warn('P3转换失败:', error);
831
- const rgb = preciseLabToRGB(L, a, b);
832
- return {
833
- r: rgb.r / 255,
834
- g: rgb.g / 255,
835
- b: rgb.b / 255
836
- };
603
+ /**
604
+ * 转换单个 lch() 颜色函数
605
+ * @param {string} lchString - lch() 颜色字符串
606
+ * @param {Object} config - 配置对象
607
+ * @returns {string} 转换后的颜色字符串
608
+ */
609
+ function convertSingleLchColor(lchString, config) {
610
+ // 使用更精确的正则匹配 lch() 函数
611
+ const lchRegex = /lch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(deg)?(?:\s*\/\s*([\d.%]+))?\s*\)/i;
612
+ const match = lchString.match(lchRegex);
613
+
614
+ if (!match) {
615
+ return lchString;
616
+ }
617
+
618
+ try {
619
+ // 转换为数字
620
+ let L = parseFloat(match[1]);
621
+
622
+ // 如果L有百分号,转换为0-100范围的值
623
+ if (match[2] === '%') {
624
+ L = L; // 百分比值直接使用 (0-100)
625
+ } else {
626
+ // 没有百分号,确保在0-100范围内
627
+ if (L < 0) L = 0;
628
+ if (L > 100) L = 100;
629
+ }
630
+
631
+ const C = parseFloat(match[3]);
632
+ let H = parseFloat(match[4]);
633
+
634
+ // 验证C范围:>= 0
635
+ if (C < 0) {
636
+ console.warn(`LCH中的C值不能为负: ${C}`);
637
+ }
638
+
639
+ // 验证H范围:0-360,有deg单位或无单位都是0-360
640
+ H = ((H % 360) + 360) % 360; // 标准化到0-360
641
+
642
+ // LCH -> Lab
643
+ const lab = lchToLab(L, C, H);
644
+
645
+ // 处理 alpha 通道(如果有)
646
+ const alphaValue = match[6] !== undefined
647
+ ? match[6].includes('%')
648
+ ? parseFloat(match[6]) / 100
649
+ : parseFloat(match[6])
650
+ : null;
651
+
652
+ return generateColorString(lab.L, lab.a, lab.b, config, alphaValue);
653
+ } catch (error) {
654
+ console.warn(`无法转换LCH颜色: ${lchString}`, error);
655
+ return lchString;
656
+ }
837
657
  }
838
- }
839
658
 
840
- /**
841
- * 生成颜色字符串(根据P3支持情况输出P3或RGB)
842
- * @param {number} L - Lab L值
843
- * @param {number} a - Lab a值
844
- * @param {number} b - Lab b值
845
- * @param {Object} config - 配置对象
846
- * @param {number|null} alpha - 透明度值
847
- * @returns {string} CSS颜色字符串
848
- */
849
- function generateColorString(L, a, b, config, alpha = null) {
850
- if (!config.enableP3 || !detectP3Support()) {
851
- // 使用RGB
852
- const rgb = preciseLabToRGB(L, a, b);
853
- if (alpha !== null) {
854
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
855
- }
856
- return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
857
- } else {
858
- // 使用P3
859
- const p3 = labToP3(L, a, b);
860
- if (alpha !== null) {
861
- return `color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)} / ${alpha})`;
862
- }
863
- return `color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)})`;
659
+ /**
660
+ * LCH 转换为 Lab (CSS标准)
661
+ * @param {number} L - 明度 0-100 或 0%-100%
662
+ * @param {number} C - 色度 >=0
663
+ * @param {number} H - 色相角 0-360 度
664
+ * @returns {Object} Lab {L, a, b}
665
+ */
666
+ function lchToLab(L, C, H) {
667
+ // 角度转换为弧度
668
+ const H_rad = H * Math.PI / 180;
669
+
670
+ // LCH -> Lab 转换公式
671
+ const a = C * Math.cos(H_rad);
672
+ const b = C * Math.sin(H_rad);
673
+
674
+ return { L, a, b };
864
675
  }
865
- }
866
676
 
867
- /**
868
- * 处理CSS值,根据配置转换LAB、LCH颜色和math()函数
869
- * @param {string} value - CSS属性值
870
- * @param {Object} config - 配置对象
871
- * @returns {string} 处理后的值
872
- */
873
- function processCSSValue(value, config) {
874
- let result = value;
875
-
876
- // 1. 首先处理math()函数
877
- if (config.enableMath) {
878
- result = processMathFunctions(result, config);
677
+ // 精确的 Lab -> sRGB 转换函数
678
+ function preciseLabToRGB(L, a, b) {
679
+ // 1. Lab -> XYZ (D65白点)
680
+ const labToXyz = (L, a, b) => {
681
+ // D65 白点参考值
682
+ const refX = 0.95047;
683
+ const refY = 1.00000;
684
+ const refZ = 1.08883;
685
+
686
+ const epsilon = 0.008856;
687
+ const kappa = 903.3;
688
+
689
+ const fy = (L + 16) / 116;
690
+ const fx = a / 500 + fy;
691
+ const fz = fy - b / 200;
692
+
693
+ const xr = fx ** 3 > epsilon ? fx ** 3 : (116 * fx - 16) / kappa;
694
+ const yr = L > kappa * epsilon ? ((L + 16) / 116) ** 3 : L / kappa;
695
+ const zr = fz ** 3 > epsilon ? fz ** 3 : (116 * fz - 16) / kappa;
696
+
697
+ return [
698
+ xr * refX,
699
+ yr * refY,
700
+ zr * refZ
701
+ ];
702
+ };
703
+
704
+ // 2. XYZ -> Linear RGB
705
+ const xyzToLinearRgb = (x, y, z) => {
706
+ // sRGB 转换矩阵 (D65)
707
+ const M = [
708
+ [ 3.2404542, -1.5371385, -0.4985314],
709
+ [-0.9692660, 1.8760108, 0.0415560],
710
+ [ 0.0556434, -0.2040259, 1.0572252]
711
+ ];
712
+
713
+ const r = M[0][0] * x + M[0][1] * y + M[0][2] * z;
714
+ const g = M[1][0] * x + M[1][1] * y + M[1][2] * z;
715
+ const b = M[2][0] * x + M[2][1] * y + M[2][2] * z;
716
+
717
+ return [r, g, b];
718
+ };
719
+
720
+ // 3. sRGB Gamma 校正
721
+ const applyGamma = (c) => {
722
+ const sign = c < 0 ? -1 : 1;
723
+ const absC = Math.abs(c);
724
+
725
+ if (absC <= 0.0031308) {
726
+ return sign * 12.92 * absC;
727
+ } else {
728
+ return sign * (1.055 * Math.pow(absC, 1/2.4) - 0.055);
729
+ }
730
+ };
731
+
732
+ // 4. 钳制到 [0, 255]
733
+ const clamp = (value) => {
734
+ return Math.max(0, Math.min(255, Math.round(value * 255)));
735
+ };
736
+
737
+ // 执行转换
738
+ const [X, Y, Z] = labToXyz(L, a, b);
739
+ const [linearR, linearG, linearB] = xyzToLinearRgb(X, Y, Z);
740
+
741
+ const r = applyGamma(linearR);
742
+ const g = applyGamma(linearG);
743
+ const bOut = applyGamma(linearB);
744
+
745
+ // 返回钳制后的 RGB 值
746
+ return {
747
+ r: clamp(r),
748
+ g: clamp(g),
749
+ b: clamp(bOut)
750
+ };
879
751
  }
880
-
881
- // 2. 然后处理LAB和LCH颜色
882
- if (config.convertLabToRGB || config.convertLchToRGB) {
883
- result = convertAllLabLchColors(result, config);
752
+
753
+ /**
754
+ * Lab -> Display P3 转换
755
+ * @param {number} L - 明度 0-100
756
+ * @param {number} a - a分量
757
+ * @param {number} b - b分量
758
+ * @returns {Object} P3颜色 {r, g, b}
759
+ */
760
+ function labToP3(L, a, b) {
761
+ // 1. Lab -> XYZ (使用与RGB相同的转换)
762
+ const labToXyz = (L, a, b) => {
763
+ const refX = 0.95047;
764
+ const refY = 1.00000;
765
+ const refZ = 1.08883;
766
+
767
+ const epsilon = 0.008856;
768
+ const kappa = 903.3;
769
+
770
+ const fy = (L + 16) / 116;
771
+ const fx = a / 500 + fy;
772
+ const fz = fy - b / 200;
773
+
774
+ const xr = fx ** 3 > epsilon ? fx ** 3 : (116 * fx - 16) / kappa;
775
+ const yr = L > kappa * epsilon ? ((L + 16) / 116) ** 3 : L / kappa;
776
+ const zr = fz ** 3 > epsilon ? fz ** 3 : (116 * fz - 16) / kappa;
777
+
778
+ return [
779
+ xr * refX,
780
+ yr * refY,
781
+ zr * refZ
782
+ ];
783
+ };
784
+
785
+ // 2. XYZ -> Linear P3
786
+ const xyzToLinearP3 = (x, y, z) => {
787
+ // Display P3 转换矩阵 (D65)
788
+ const M = [
789
+ [ 2.493496911941425, -0.9313836179191239, -0.40271078445071684],
790
+ [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
791
+ [ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
792
+ ];
793
+
794
+ const r = M[0][0] * x + M[0][1] * y + M[0][2] * z;
795
+ const g = M[1][0] * x + M[1][1] * y + M[1][2] * z;
796
+ const b = M[2][0] * x + M[2][1] * y + M[2][2] * z;
797
+
798
+ return [r, g, b];
799
+ };
800
+
801
+ // 3. P3 Gamma 校正(与sRGB相同)
802
+ const applyGamma = (c) => {
803
+ const sign = c < 0 ? -1 : 1;
804
+ const absC = Math.abs(c);
805
+
806
+ if (absC <= 0.0031308) {
807
+ return sign * 12.92 * absC;
808
+ } else {
809
+ return sign * (1.055 * Math.pow(absC, 1/2.4) - 0.055);
810
+ }
811
+ };
812
+
813
+ // 4. 钳制到 [0, 255],然后转换为 [0, 1] 范围
814
+ const clamp = (value) => {
815
+ return Math.max(0, Math.min(255, Math.round(value * 255)));
816
+ };
817
+
818
+ // 执行转换
819
+ try {
820
+ const [X, Y, Z] = labToXyz(L, a, b);
821
+ const [linearR, linearG, linearB] = xyzToLinearP3(X, Y, Z);
822
+
823
+ const r = applyGamma(linearR);
824
+ const g = applyGamma(linearG);
825
+ const bOut = applyGamma(linearB);
826
+
827
+ // 转换为0-1范围的浮点数用于P3颜色格式
828
+ return {
829
+ r: Math.max(0, Math.min(1, r)),
830
+ g: Math.max(0, Math.min(1, g)),
831
+ b: Math.max(0, Math.min(1, bOut))
832
+ };
833
+ } catch (error) {
834
+ console.warn('P3转换失败:', error);
835
+ const rgb = preciseLabToRGB(L, a, b);
836
+ return {
837
+ r: rgb.r / 255,
838
+ g: rgb.g / 255,
839
+ b: rgb.b / 255
840
+ };
841
+ }
884
842
  }
885
-
886
- return result;
887
- }
888
843
 
889
- // 私有方法:提取变量定义并移除(支持嵌套作用域)
890
- function extractVariablesAndCSS(cssText, config) {
891
- const lines = cssText.split('\n');
892
- const globalVariables = {};
893
- const selectorVariables = new Map();
894
- let cssWithoutVars = '';
895
- let currentSelector = null;
896
- let inSelectorBlock = false;
897
- let currentIndent = 0;
898
-
899
- for (let line of lines) {
900
- const trimmed = line.trim();
901
-
902
- // 检查是否是变量定义
903
- const varMatch = trimmed.match(/^\$([a-zA-Z0-9_-]+)\s*:\s*(.+?);?$/);
904
-
905
- if (varMatch) {
906
- const [, varName, varValue] = varMatch;
907
- // 处理变量值中的数学表达式和颜色转换
908
- const processedValue = processCSSValue(
909
- replaceVariableUsesInValue(varValue.trim(), {
910
- ...globalVariables,
911
- ...(currentSelector ? selectorVariables.get(currentSelector) || {} : {})
912
- }),
913
- config
914
- );
915
-
916
- if (currentSelector) {
917
- // 选择器内部的变量
918
- if (!selectorVariables.has(currentSelector)) {
919
- selectorVariables.set(currentSelector, {});
920
- }
921
- selectorVariables.get(currentSelector)[varName] = processedValue;
844
+ /**
845
+ * 生成颜色字符串(根据P3支持情况输出P3或RGB)
846
+ * @param {number} L - Lab L值
847
+ * @param {number} a - Lab a值
848
+ * @param {number} b - Lab b值
849
+ * @param {Object} config - 配置对象
850
+ * @param {number|null} alpha - 透明度值
851
+ * @returns {string} CSS颜色字符串
852
+ */
853
+ function generateColorString(L, a, b, config, alpha = null) {
854
+ if (!config.enableP3 || !detectP3Support()) {
855
+ // 使用RGB
856
+ const rgb = preciseLabToRGB(L, a, b);
857
+ if (alpha !== null) {
858
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
859
+ }
860
+ return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
922
861
  } else {
923
- // 全局变量(:root 中)
924
- globalVariables[varName] = processedValue;
925
- }
926
- continue;
927
- }
928
-
929
- // 检查是否是选择器开始
930
- if (trimmed.endsWith('{')) {
931
- currentSelector = trimmed.slice(0, -1).trim();
932
- inSelectorBlock = true;
933
- cssWithoutVars += line + '\n';
934
- continue;
935
- }
936
-
937
- // 检查是否是选择器结束
938
- if (trimmed === '}') {
939
- if (inSelectorBlock) {
940
- // 在选择器结束前插入变量声明
941
- if (currentSelector && selectorVariables.has(currentSelector)) {
942
- const vars = selectorVariables.get(currentSelector);
943
- const indent = ' '.repeat(currentIndent);
944
- for (const [varName, varValue] of Object.entries(vars)) {
945
- cssWithoutVars += `${indent} --${varName}: ${varValue};\n`;
862
+ // 使用P3
863
+ const p3 = labToP3(L, a, b);
864
+ if (alpha !== null) {
865
+ return `color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)} / ${alpha})`;
946
866
  }
947
- }
867
+ return `color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)})`;
948
868
  }
949
- inSelectorBlock = false;
950
- currentSelector = null;
951
- }
952
-
953
- cssWithoutVars += line + '\n';
954
-
955
- // 更新缩进级别
956
- if (line.includes('{')) {
957
- currentIndent += 2;
958
- }
959
- if (line.includes('}')) {
960
- currentIndent = Math.max(0, currentIndent - 2);
961
- }
962
869
  }
963
-
964
- return {
965
- globalVariables,
966
- selectorVariables,
967
- cssWithoutVars: cssWithoutVars.trim()
968
- };
969
- }
970
870
 
971
- // 修复的嵌套解析函数
972
- function parseNestedRules(cssText, config) {
973
- const lines = cssText.split('\n');
974
- const stack = []; // 存储规则信息:{selector, properties, children}
975
- const rootRules = [];
976
- let currentIndent = 0;
977
-
978
- for (let i = 0; i < lines.length; i++) {
979
- const line = lines[i];
980
- const trimmed = line.trim();
981
-
982
- if (!trimmed) continue;
983
-
984
- // 计算缩进级别(假设使用空格缩进)
985
- const indent = line.match(/^(\s*)/)[0].length;
986
- const indentLevel = Math.floor(indent / config.indentSize);
987
-
988
- // 处理规则块结束
989
- if (trimmed === '}') {
990
- if (stack.length > 0) {
991
- const rule = stack.pop();
992
-
993
- // 如果这个规则有父规则,添加到父规则的children中
994
- if (stack.length > 0) {
995
- const parent = stack[stack.length - 1];
996
- if (!parent.children) parent.children = [];
997
- parent.children.push(rule);
998
- } else {
999
- // 否则是根级规则
1000
- rootRules.push(rule);
1001
- }
871
+ /**
872
+ * 处理CSS值,根据配置转换LAB、LCH颜色和math()函数
873
+ * @param {string} value - CSS属性值
874
+ * @param {Object} config - 配置对象
875
+ * @returns {string} 处理后的值
876
+ */
877
+ function processCSSValue(value, config) {
878
+ let result = value;
879
+
880
+ // 1. 首先处理math()函数
881
+ if (config.enableMath) {
882
+ result = processMathFunctions(result, config);
1002
883
  }
1003
- continue;
1004
- }
1005
-
1006
- // 处理规则开始
1007
- if (trimmed.endsWith('{')) {
1008
- const selector = trimmed.slice(0, -1).trim();
1009
-
1010
- // 创建新规则
1011
- const rule = {
1012
- selector: selector,
1013
- properties: [],
1014
- children: [],
1015
- indentLevel: indentLevel
1016
- };
1017
884
 
1018
- // 推入堆栈
1019
- stack.push(rule);
1020
- continue;
1021
- }
1022
-
1023
- // 处理属性行(不包含 { 或 } 的行)
1024
- if (!trimmed.includes('{') && !trimmed.includes('}') && trimmed.includes(':')) {
1025
- if (stack.length > 0) {
1026
- const currentRule = stack[stack.length - 1];
1027
- // 处理属性值中的math()函数和颜色转换
1028
- const parsed = parseSingleLineCSS(trimmed);
1029
- parsed.forEach(obj => {
1030
- const key = Object.keys(obj)[0];
1031
- const value = processCSSValue(obj[key], config);
1032
- currentRule.properties.push(`${key}: ${value}`);
1033
- });
885
+ // 2. 然后处理LAB和LCH颜色
886
+ if (config.convertLabToRGB || config.convertLchToRGB) {
887
+ result = convertAllLabLchColors(result, config);
1034
888
  }
1035
- continue;
1036
- }
889
+
890
+ return result;
1037
891
  }
1038
-
1039
- // 处理栈中剩余规则
1040
- while (stack.length > 0) {
1041
- const rule = stack.pop();
1042
- if (stack.length === 0) {
1043
- rootRules.push(rule);
1044
- } else {
1045
- const parent = stack[stack.length - 1];
1046
- if (!parent.children) parent.children = [];
1047
- parent.children.push(rule);
1048
- }
892
+
893
+ // 私有方法:提取变量定义并移除(支持嵌套作用域)
894
+ function extractVariablesAndCSS(cssText, config) {
895
+ const lines = cssText.split('\n');
896
+ const globalVariables = {};
897
+ const selectorVariables = new Map();
898
+ let cssWithoutVars = '';
899
+ let currentSelector = null;
900
+ let inSelectorBlock = false;
901
+ let currentIndent = 0;
902
+
903
+ for (let line of lines) {
904
+ const trimmed = line.trim();
905
+
906
+ // 检查是否是变量定义
907
+ const varMatch = trimmed.match(/^\$([a-zA-Z0-9_-]+)\s*:\s*(.+?);?$/);
908
+
909
+ if (varMatch) {
910
+ const [, varName, varValue] = varMatch;
911
+ // 处理变量值中的数学表达式和颜色转换
912
+ const processedValue = processCSSValue(
913
+ replaceVariableUsesInValue(varValue.trim(), {
914
+ ...globalVariables,
915
+ ...(currentSelector ? selectorVariables.get(currentSelector) || {} : {})
916
+ }),
917
+ config
918
+ );
919
+
920
+ if (currentSelector) {
921
+ // 选择器内部的变量
922
+ if (!selectorVariables.has(currentSelector)) {
923
+ selectorVariables.set(currentSelector, {});
924
+ }
925
+ selectorVariables.get(currentSelector)[varName] = processedValue;
926
+ } else {
927
+ // 全局变量(:root 中)
928
+ globalVariables[varName] = processedValue;
929
+ }
930
+ continue;
931
+ }
932
+
933
+ // 检查是否是选择器开始
934
+ if (trimmed.endsWith('{')) {
935
+ currentSelector = trimmed.slice(0, -1).trim();
936
+ inSelectorBlock = true;
937
+ cssWithoutVars += line + '\n';
938
+ continue;
939
+ }
940
+
941
+ // 检查是否是选择器结束
942
+ if (trimmed === '}') {
943
+ if (inSelectorBlock) {
944
+ // 在选择器结束前插入变量声明
945
+ if (currentSelector && selectorVariables.has(currentSelector)) {
946
+ const vars = selectorVariables.get(currentSelector);
947
+ const indent = ' '.repeat(currentIndent);
948
+ for (const [varName, varValue] of Object.entries(vars)) {
949
+ cssWithoutVars += `${indent} --${varName}: ${varValue};\n`;
950
+ }
951
+ }
952
+ }
953
+ inSelectorBlock = false;
954
+ currentSelector = null;
955
+ }
956
+
957
+ cssWithoutVars += line + '\n';
958
+
959
+ // 更新缩进级别
960
+ if (line.includes('{')) {
961
+ currentIndent += 2;
962
+ }
963
+ if (line.includes('}')) {
964
+ currentIndent = Math.max(0, currentIndent - 2);
965
+ }
966
+ }
967
+
968
+ return {
969
+ globalVariables,
970
+ selectorVariables,
971
+ cssWithoutVars: cssWithoutVars.trim()
972
+ };
1049
973
  }
1050
-
1051
- // 将规则树转换为CSS字符串
1052
- return convertRulesToCSS(rootRules, config);
1053
- }
1054
-
1055
- // 将规则树转换为CSS字符串
1056
- function convertRulesToCSS(rules, config, parentSelector = '') {
1057
- let result = '';
1058
- const isParentAt = parentSelector.startsWith("@");
1059
-
1060
- for (const rule of rules) {
1061
- // 构建完整选择器
1062
- const isAt = rule.selector.startsWith("@");
1063
- let fullSelector = (isParentAt ? ' ' : '') + rule.selector;
1064
-
1065
- if (isAt) {
1066
- fullSelector = rule.selector + " {\n";
1067
- }
1068
-
1069
- if (parentSelector) {
1070
- // 处理 & 选择器
1071
- if (fullSelector.includes('&')) {
1072
- fullSelector = fullSelector.replace(/&/g, parentSelector);
1073
- } else if (fullSelector.trim().startsWith(':')) {
1074
- fullSelector = parentSelector + fullSelector;
1075
- } else {
1076
- fullSelector = parentSelector + (isParentAt ? '' : ' ') + fullSelector;
974
+
975
+ // 修复的嵌套解析函数
976
+ function parseNestedRules(cssText, config) {
977
+ const lines = cssText.split('\n');
978
+ const stack = []; // 存储规则信息:{selector, properties, children}
979
+ const rootRules = [];
980
+ let currentIndent = 0;
981
+
982
+ for (let i = 0; i < lines.length; i++) {
983
+ const line = lines[i];
984
+ const trimmed = line.trim();
985
+
986
+ if (!trimmed) continue;
987
+
988
+ // 计算缩进级别(假设使用空格缩进)
989
+ const indent = line.match(/^(\s*)/)[0].length;
990
+ const indentLevel = Math.floor(indent / config.indentSize);
991
+
992
+ // 处理规则块结束
993
+ if (trimmed === '}') {
994
+ if (stack.length > 0) {
995
+ const rule = stack.pop();
996
+
997
+ // 如果这个规则有父规则,添加到父规则的children中
998
+ if (stack.length > 0) {
999
+ const parent = stack[stack.length - 1];
1000
+ if (!parent.children) parent.children = [];
1001
+ parent.children.push(rule);
1002
+ } else {
1003
+ // 否则是根级规则
1004
+ rootRules.push(rule);
1005
+ }
1006
+ }
1007
+ continue;
1008
+ }
1009
+
1010
+ // 处理规则开始
1011
+ if (trimmed.endsWith('{')) {
1012
+ const selector = trimmed.slice(0, -1).trim();
1013
+
1014
+ // 创建新规则
1015
+ const rule = {
1016
+ selector: selector,
1017
+ properties: [],
1018
+ children: [],
1019
+ indentLevel: indentLevel
1020
+ };
1021
+
1022
+ // 推入堆栈
1023
+ stack.push(rule);
1024
+ continue;
1025
+ }
1026
+
1027
+ // 处理属性行(不包含 { 或 } 的行)
1028
+ if (!trimmed.includes('{') && !trimmed.includes('}') && trimmed.includes(':')) {
1029
+ if (stack.length > 0) {
1030
+ const currentRule = stack[stack.length - 1];
1031
+ // 处理属性值中的math()函数和颜色转换
1032
+ const parsed = parseSingleLineCSS(trimmed);
1033
+ parsed.forEach(obj => {
1034
+ const key = Object.keys(obj)[0];
1035
+ const value = processCSSValue(obj[key], config);
1036
+ currentRule.properties.push(`${key}: ${value}`);
1037
+ });
1038
+ }
1039
+ continue;
1040
+ }
1077
1041
  }
1078
- }
1079
-
1080
- // 如果有属性,生成规则块
1081
- if (rule.properties.length > 0) {
1082
- result += (isAt ? "" : fullSelector) + ' {\n';
1083
- for (const prop of rule.properties) {
1084
- // 再次处理属性值(确保math()函数和颜色被转换)
1085
- const parsed = parseSingleLineCSS(prop);
1086
- parsed.forEach(obj => {
1087
- const key = Object.keys(obj)[0];
1088
- const value = processCSSValue(obj[key], config);
1089
- result += (isParentAt ? ' ' : '') + ` ${key}: ${value};\n`;
1090
- });
1042
+
1043
+ // 处理栈中剩余规则
1044
+ while (stack.length > 0) {
1045
+ const rule = stack.pop();
1046
+ if (stack.length === 0) {
1047
+ rootRules.push(rule);
1048
+ } else {
1049
+ const parent = stack[stack.length - 1];
1050
+ if (!parent.children) parent.children = [];
1051
+ parent.children.push(rule);
1052
+ }
1091
1053
  }
1092
- result += isParentAt ? ' }\n' : '}\n\n';
1093
- }
1094
-
1095
- // 递归处理子规则
1096
- if (rule.children && rule.children.length > 0) {
1097
- result += convertRulesToCSS(rule.children, config, fullSelector);
1098
- }
1099
-
1100
- if (isParentAt){
1101
- result += "}\n\n";
1102
- }
1054
+
1055
+ // 将规则树转换为CSS字符串
1056
+ return convertRulesToCSS(rootRules, config);
1103
1057
  }
1104
1058
 
1105
- return result.trim() + (isParentAt ? "\n\n" : "");
1106
- }
1107
-
1108
- // 替换变量值中的变量引用
1109
- function replaceVariableUsesInValue(value, variables) {
1110
- return value.replace(/\$([a-zA-Z0-9_-]+)/g, (match, varName) => {
1111
- if (variables[varName]) {
1112
- return `var(--${varName})`;
1113
- }
1114
- return match;
1115
- });
1116
- }
1117
-
1118
- // 替换变量使用
1119
- function replaceVariableUses(cssText, globalVariables, selectorVariables, config) {
1120
- let result = cssText;
1121
-
1122
- // 先替换全局变量
1123
- for (const [varName, varValue] of Object.entries(globalVariables)) {
1124
- const varRegex = new RegExp(`\\$${varName}(?![a-zA-Z0-9_-])`, 'g');
1125
- result = result.replace(varRegex, `var(--${varName})`);
1059
+ // 将规则树转换为CSS字符串
1060
+ function convertRulesToCSS(rules, config, parentSelector = '') {
1061
+ let result = '';
1062
+ const isParentAt = parentSelector.startsWith("@");
1063
+
1064
+ for (const rule of rules) {
1065
+ // 构建完整选择器
1066
+ const isAt = rule.selector.startsWith("@");
1067
+ let fullSelector = (isParentAt ? ' ' : '') + rule.selector;
1068
+
1069
+ if (isAt) {
1070
+ fullSelector = rule.selector + " {\n";
1071
+ }
1072
+
1073
+ if (parentSelector) {
1074
+ // 处理 & 选择器
1075
+ if (fullSelector.includes('&')) {
1076
+ fullSelector = fullSelector.replace(/&/g, parentSelector);
1077
+ } else if (fullSelector.trim().startsWith(':')) {
1078
+ fullSelector = parentSelector + fullSelector;
1079
+ } else {
1080
+ fullSelector = parentSelector + (isParentAt ? '' : ' ') + fullSelector;
1081
+ }
1082
+ }
1083
+
1084
+ // 如果有属性,生成规则块
1085
+ if (rule.properties.length > 0) {
1086
+ result += (isAt ? "" : fullSelector) + ' {\n';
1087
+ for (const prop of rule.properties) {
1088
+ // 再次处理属性值(确保math()函数和颜色被转换)
1089
+ const parsed = parseSingleLineCSS(prop);
1090
+ parsed.forEach(obj => {
1091
+ const key = Object.keys(obj)[0];
1092
+ const value = processCSSValue(obj[key], config);
1093
+ result += (isParentAt ? ' ' : '') + ` ${key}: ${value};\n`;
1094
+ });
1095
+ }
1096
+ result += isParentAt ? ' }\n' : '}\n\n';
1097
+ }
1098
+
1099
+ // 递归处理子规则
1100
+ if (rule.children && rule.children.length > 0) {
1101
+ result += convertRulesToCSS(rule.children, config, fullSelector);
1102
+ }
1103
+
1104
+ if (isParentAt){
1105
+ result += "}\n\n";
1106
+ }
1107
+ }
1108
+
1109
+ return result.trim() + (isParentAt ? "\n\n" : "");
1126
1110
  }
1127
-
1128
- // 然后处理选择器特定的变量
1129
- result = result.replace(/\$([a-zA-Z0-9_-]+)/g, (match, varName) => {
1130
- return `var(--${varName})`;
1131
- });
1132
-
1133
- // 最后处理所有的LAB、LCH颜色和math()函数
1134
- result = processCSSValue(result, config);
1135
-
1136
- return result;
1137
- }
1138
1111
 
1139
- // 生成根规则
1140
- function generateRootRule(variables, config) {
1141
- if (Object.keys(variables).length === 0) {
1142
- return '';
1112
+ // 替换变量值中的变量引用
1113
+ function replaceVariableUsesInValue(value, variables) {
1114
+ return value.replace(/\$([a-zA-Z0-9_-]+)/g, (match, varName) => {
1115
+ if (variables[varName]) {
1116
+ return `var(--${varName})`;
1117
+ }
1118
+ return match;
1119
+ });
1143
1120
  }
1144
-
1145
- const declarations = Object.entries(variables)
1146
- .map(([name, value]) => {
1147
- const processedValue = processCSSValue(
1148
- replaceVariableUsesInValue(value, variables),
1149
- config
1150
- );
1151
- return ` --${name}: ${processedValue};`;
1152
- })
1153
- .join('\n');
1154
-
1155
- return `${config.rootSelector} {\n${declarations}\n}\n\n`;
1156
- }
1157
1121
 
1158
- // 处理选择器内部的变量
1159
- function injectSelectorVariables(cssText, selectorVariables, config) {
1160
- let result = '';
1161
- const lines = cssText.split('\n');
1162
- let currentSelector = null;
1163
-
1164
- for (let line of lines) {
1165
- const trimmed = line.trim();
1166
-
1167
- if (trimmed.endsWith('{')) {
1168
- currentSelector = trimmed.slice(0, -1).trim();
1169
- }
1170
-
1171
- if (trimmed === '}' && currentSelector) {
1172
- // 在选择器结束前插入变量声明
1173
- if (selectorVariables.has(currentSelector)) {
1174
- const vars = selectorVariables.get(currentSelector);
1175
- const indent = line.match(/^(\s*)/)[0];
1176
- for (const [varName, varValue] of Object.entries(vars)) {
1177
- result += indent + ' --' + varName + ': ' + varValue + ';\n';
1178
- }
1122
+ // 替换变量使用
1123
+ function replaceVariableUses(cssText, globalVariables, selectorVariables, config) {
1124
+ let result = cssText;
1125
+
1126
+ // 先替换全局变量
1127
+ for (const [varName, varValue] of Object.entries(globalVariables)) {
1128
+ const varRegex = new RegExp(`\\$${varName}(?![a-zA-Z0-9_-])`, 'g');
1129
+ result = result.replace(varRegex, `var(--${varName})`);
1179
1130
  }
1180
- currentSelector = null;
1181
- }
1182
-
1183
- // 使用全局处理函数处理所有math()和颜色
1184
- result += processCSSValue(line, config) + '\n';
1131
+
1132
+ // 然后处理选择器特定的变量
1133
+ result = result.replace(/\$([a-zA-Z0-9_-]+)/g, (match, varName) => {
1134
+ return `var(--${varName})`;
1135
+ });
1136
+
1137
+ // 最后处理所有的LAB、LCH颜色和math()函数
1138
+ result = processCSSValue(result, config);
1139
+
1140
+ return result;
1185
1141
  }
1186
-
1187
- return result.trim();
1188
- }
1189
1142
 
1190
- // 主转换函数
1191
- function convert(cssText, customConfig = {}) {
1192
- const config = { ...defaultConfig, ...customConfig };
1193
-
1194
- // 1. 提取变量定义(区分全局和选择器局部)
1195
- const { globalVariables, selectorVariables, cssWithoutVars } =
1196
- extractVariablesAndCSS(cssText, config);
1197
-
1198
- // 2. 解析嵌套规则(如果启用)
1199
- let processedCSS = cssWithoutVars.trim();
1200
- if (config.enableNesting && cssWithoutVars.includes('{')) {
1201
- try {
1202
- processedCSS = parseNestedRules(cssWithoutVars, config);
1203
- } catch (error) {
1204
- console.warn('嵌套解析失败,使用原始CSS:', error);
1205
- }
1206
- }
1207
-
1208
- // 3. 生成根规则(全局变量)
1209
- const rootRule = generateRootRule(globalVariables, config);
1210
-
1211
- // 4. 注入选择器局部变量
1212
- let finalCSS = processedCSS;
1213
- if (selectorVariables.size > 0) {
1214
- finalCSS = injectSelectorVariables(processedCSS, selectorVariables, config);
1143
+ // 生成根规则
1144
+ function generateRootRule(variables, config) {
1145
+ if (Object.keys(variables).length === 0) {
1146
+ return '';
1147
+ }
1148
+
1149
+ const declarations = Object.entries(variables)
1150
+ .map(([name, value]) => {
1151
+ const processedValue = processCSSValue(
1152
+ replaceVariableUsesInValue(value, variables),
1153
+ config
1154
+ );
1155
+ return ` --${name}: ${processedValue};`;
1156
+ })
1157
+ .join('\n');
1158
+
1159
+ return `${config.rootSelector} {\n${declarations}\n}\n\n`;
1215
1160
  }
1216
-
1217
- // 5. 替换变量使用
1218
- finalCSS = replaceVariableUses(finalCSS, globalVariables, selectorVariables, config);
1219
-
1220
- // 6. 组合结果
1221
- return rootRule + finalCSS;
1222
- }
1223
1161
 
1224
- // 自动处理带有 e 属性的 style 标签
1225
- function autoProcessStyleTags(customConfig = {}) {
1226
- const config = { ...defaultConfig, ...customConfig };
1227
- const styleTags = document.querySelectorAll(`style[${config.styleTagAttribute || 'e'}]`);
1228
-
1229
- styleTags.forEach(styleTag => {
1230
- const originalCSS = styleTag.textContent;
1231
- const convertedCSS = convert(originalCSS, config);
1232
-
1233
- // 创建新的 style 标签
1234
- const newStyleTag = document.createElement('style');
1235
- newStyleTag.textContent = convertedCSS;
1236
-
1237
- // 插入到原标签后面
1238
- styleTag.parentNode.insertBefore(newStyleTag, styleTag.nextSibling);
1239
-
1240
- // 可选:移除原标签
1241
- if (!config.preserveOriginal) {
1242
- styleTag.remove();
1243
- } else {
1244
- styleTag.style.display = 'none';
1245
- }
1246
- });
1247
-
1248
- return styleTags.length;
1249
- }
1162
+ // 处理选择器内部的变量
1163
+ function injectSelectorVariables(cssText, selectorVariables, config) {
1164
+ let result = '';
1165
+ const lines = cssText.split('\n');
1166
+ let currentSelector = null;
1167
+
1168
+ for (let line of lines) {
1169
+ const trimmed = line.trim();
1170
+
1171
+ if (trimmed.endsWith('{')) {
1172
+ currentSelector = trimmed.slice(0, -1).trim();
1173
+ }
1174
+
1175
+ if (trimmed === '}' && currentSelector) {
1176
+ // 在选择器结束前插入变量声明
1177
+ if (selectorVariables.has(currentSelector)) {
1178
+ const vars = selectorVariables.get(currentSelector);
1179
+ const indent = line.match(/^(\s*)/)[0];
1180
+ for (const [varName, varValue] of Object.entries(vars)) {
1181
+ result += indent + ' --' + varName + ': ' + varValue + ';\n';
1182
+ }
1183
+ }
1184
+ currentSelector = null;
1185
+ }
1186
+
1187
+ // 使用全局处理函数处理所有math()和颜色
1188
+ result += processCSSValue(line, config) + '\n';
1189
+ }
1190
+
1191
+ return result.trim();
1192
+ }
1250
1193
 
1251
- function config(config = {}) {
1252
- defaultConfig = { ...defaultConfig, ...config };
1253
- }
1194
+ // 主转换函数
1195
+ function convert(cssText, customConfig = {}) {
1196
+ const config = { ...defaultConfig, ...customConfig };
1197
+
1198
+ // 1. 提取变量定义(区分全局和选择器局部)
1199
+ const { globalVariables, selectorVariables, cssWithoutVars } =
1200
+ extractVariablesAndCSS(cssText, config);
1201
+
1202
+ // 2. 解析嵌套规则(如果启用)
1203
+ let processedCSS = cssWithoutVars.trim();
1204
+ if (config.enableNesting && cssWithoutVars.includes('{')) {
1205
+ try {
1206
+ processedCSS = parseNestedRules(cssWithoutVars, config);
1207
+ } catch (error) {
1208
+ console.warn('嵌套解析失败,使用原始CSS:', error);
1209
+ }
1210
+ }
1211
+
1212
+ // 3. 生成根规则(全局变量)
1213
+ const rootRule = generateRootRule(globalVariables, config);
1214
+
1215
+ // 4. 注入选择器局部变量
1216
+ let finalCSS = processedCSS;
1217
+ if (selectorVariables.size > 0) {
1218
+ finalCSS = injectSelectorVariables(processedCSS, selectorVariables, config);
1219
+ }
1220
+
1221
+ // 5. 替换变量使用
1222
+ finalCSS = replaceVariableUses(finalCSS, globalVariables, selectorVariables, config);
1223
+
1224
+ // 6. 组合结果
1225
+ return rootRule + finalCSS;
1226
+ }
1254
1227
 
1255
- // 初始化自动处理
1256
- function apply(cssText, customConfig = {}) {
1257
- const config = { ...defaultConfig, ...customConfig };
1258
-
1259
- if (cssText) {
1260
- const converted = convert(cssText, config);
1261
- const styleEl = document.createElement('style');
1262
- styleEl.textContent = converted;
1263
- document.head.appendChild(styleEl);
1264
- return styleEl;
1265
- } else {
1266
- if (document.readyState === 'loading') {
1267
- document.addEventListener('DOMContentLoaded', () => {
1268
- autoProcessStyleTags(config);
1228
+ // 自动处理带有 e 属性的 style 标签
1229
+ function autoProcessStyleTags(customConfig = {}) {
1230
+ const config = { ...defaultConfig, ...customConfig };
1231
+ const styleTags = document.querySelectorAll(`style[${config.styleTagAttribute || 'e'}]`);
1232
+
1233
+ styleTags.forEach(styleTag => {
1234
+ const originalCSS = styleTag.textContent;
1235
+ const convertedCSS = convert(originalCSS, config);
1236
+
1237
+ // 创建新的 style 标签
1238
+ const newStyleTag = document.createElement('style');
1239
+ newStyleTag.textContent = convertedCSS;
1240
+
1241
+ // 插入到原标签后面
1242
+ styleTag.parentNode.insertBefore(newStyleTag, styleTag.nextSibling);
1243
+
1244
+ // 可选:移除原标签
1245
+ if (!config.preserveOriginal) {
1246
+ styleTag.remove();
1247
+ } else {
1248
+ styleTag.style.display = 'none';
1249
+ }
1269
1250
  });
1270
- } else {
1271
- autoProcessStyleTags(config);
1272
- }
1251
+
1252
+ return styleTags.length;
1273
1253
  }
1274
- }
1275
1254
 
1276
- // 返回公共 API
1277
- const api = {
1278
- convert,
1279
- apply,
1280
- config,
1281
- version: '1.9.0',
1282
-
1283
- // 检测P3支持
1284
- supportsP3: detectP3Support(),
1285
-
1286
- // 重新检测P3支持(如果配置变化)
1287
- detectP3Support: detectP3Support,
1288
-
1289
- // 数学计算工具方法
1290
- math: {
1291
- /**
1292
- * 计算数学表达式
1293
- * @param {string} expression - 数学表达式
1294
- * @returns {string} 计算结果
1295
- */
1296
- evaluate: function(expression) {
1297
- return evaluateMathExpression(expression, defaultConfig);
1298
- },
1299
-
1300
- /**
1301
- * 解析带单位的数值
1302
- * @param {string} value - 带单位的字符串
1303
- * @returns {Object} {value: number, unit: string}
1304
- */
1305
- parseUnit: parseValueWithUnit,
1306
-
1307
- /**
1308
- * 四舍五入数字
1309
- * @param {number} num - 要四舍五入的数字
1310
- * @param {number} precision - 精度
1311
- * @returns {string} 四舍五入后的数字字符串
1312
- */
1313
- round: roundNumber,
1314
-
1315
- /**
1316
- * 测试数学表达式
1317
- * @param {string} expression - 要测试的表达式
1318
- * @param {Object} testConfig - 测试配置
1319
- * @returns {Object} 测试结果
1320
- */
1321
- test: function(expression, testConfig = {}) {
1322
- const config = { ...defaultConfig, ...testConfig };
1323
- try {
1324
- const result = evaluateMathExpression(expression, config);
1325
- return {
1326
- success: true,
1327
- expression,
1328
- result,
1329
- parsed: parseValueWithUnit(result.replace(/^calc\(|\)$/g, ''))
1330
- };
1331
- } catch (error) {
1332
- return {
1333
- success: false,
1334
- expression,
1335
- error: error.message
1336
- };
1255
+ function config(config = {}) {
1256
+ defaultConfig = { ...defaultConfig, ...config };
1257
+ }
1258
+
1259
+ // 初始化自动处理
1260
+ function apply(cssText, customConfig = {}) {
1261
+ const config = { ...defaultConfig, ...customConfig };
1262
+
1263
+ if (cssText) {
1264
+ const converted = convert(cssText, config);
1265
+ const styleEl = document.createElement('style');
1266
+ styleEl.textContent = converted;
1267
+ document.head.appendChild(styleEl);
1268
+ return styleEl;
1269
+ } else {
1270
+ if (document.readyState === 'loading') {
1271
+ document.addEventListener('DOMContentLoaded', () => {
1272
+ autoProcessStyleTags(config);
1273
+ });
1274
+ } else {
1275
+ autoProcessStyleTags(config);
1276
+ }
1337
1277
  }
1338
- }
1339
- },
1340
-
1341
- // 颜色转换工具方法
1342
- colorUtils: {
1343
- labToRGB: preciseLabToRGB,
1344
- lchToLab: lchToLab,
1345
- lchToRGB: function(L, C, H) {
1346
- const lab = lchToLab(L, C, H);
1347
- return preciseLabToRGB(lab.L, lab.a, lab.b);
1348
- },
1349
- labToP3: labToP3,
1350
- lchToP3: function(L, C, H) {
1351
- const lab = lchToLab(L, C, H);
1352
- return labToP3(lab.L, lab.a, lab.b);
1353
- },
1354
- parseHexLab: function(hexString) {
1355
- const match = hexString.match(/lab#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i);
1356
- if (!match) return null;
1357
-
1358
- const L_hex = match[1];
1359
- const A_hex = match[2];
1360
- const B_hex = match[3];
1361
-
1362
- const L = parseInt(L_hex, 16) / 255 * 100;
1363
- const A = (parseInt(A_hex, 16) - 128) * 1.5;
1364
- const B = (parseInt(B_hex, 16) - 128) * 1.5;
1365
-
1366
- return preciseLabToRGB(L, A, B);
1367
- },
1368
- parseHexLch: function(hexString) {
1369
- const match = hexString.match(/lch#([0-9a-f]{2})([0-9a-f]{2})(\d{1,3})/i);
1370
- if (!match) return null;
1371
-
1372
- const L_hex = match[1];
1373
- const C_hex = match[2];
1374
- const H_dec = match[3];
1375
-
1376
- const L = parseInt(L_hex, 16) / 255 * 100;
1377
- const C = parseInt(C_hex, 16) / 255 * 150;
1378
- const H = parseInt(H_dec) / 100 * 360;
1379
-
1380
- const lab = lchToLab(L, C, H);
1381
- return preciseLabToRGB(lab.L, lab.a, lab.b);
1382
- },
1383
- /**
1384
- * 生成颜色字符串(根据P3支持情况)
1385
- * @param {number} L - Lab L值
1386
- * @param {number} a - Lab a值
1387
- * @param {number} b - Lab b值
1388
- * @param {number|null} alpha - 透明度
1389
- * @param {boolean} useP3 - 是否使用P3(自动检测)
1390
- * @returns {string} CSS颜色字符串
1391
- */
1392
- generateColor: function(L, a, b, alpha = null, useP3 = true) {
1393
- return generateColorString(L, a, b, { enableP3: useP3 }, alpha);
1394
- },
1395
- /**
1396
- * 解析CSS颜色字符串
1397
- * @param {string} colorString - CSS颜色字符串
1398
- * @returns {Object} 包含原始Lab值和颜色字符串的对象
1399
- */
1400
- parseColor: function(colorString) {
1401
- try {
1402
- // 尝试解析lab()函数格式
1403
- const labMatch = colorString.match(/lab\(\s*([\d.]+)(%?)\s+([\d.-]+)\s+([\d.-]+)(?:\s*\/\s*([\d.%]+))?\s*\)/i);
1404
- if (labMatch) {
1405
- let L = parseFloat(labMatch[1]);
1406
- const A = parseFloat(labMatch[3]);
1407
- const B = parseFloat(labMatch[4]);
1408
- const alpha = labMatch[5] ?
1409
- (labMatch[5].includes('%') ? parseFloat(labMatch[5]) / 100 : parseFloat(labMatch[5])) :
1410
- null;
1411
-
1412
- const colorStr = generateColorString(L, A, B, defaultConfig, alpha);
1278
+ }
1279
+
1280
+ // 返回公共 API
1281
+ const api = {
1282
+ convert,
1283
+ apply,
1284
+ config,
1285
+ version: '1.9.0',
1286
+
1287
+ // 检测P3支持
1288
+ supportsP3: detectP3Support(),
1289
+
1290
+ // 重新检测P3支持(如果配置变化)
1291
+ detectP3Support: detectP3Support,
1292
+
1293
+ // 数学计算工具方法
1294
+ math: {
1295
+ /**
1296
+ * 计算数学表达式
1297
+ * @param {string} expression - 数学表达式
1298
+ * @returns {string} 计算结果
1299
+ */
1300
+ evaluate: function(expression) {
1301
+ return evaluateMathExpression(expression, defaultConfig);
1302
+ },
1413
1303
 
1414
- return {
1415
- L, A, B, alpha,
1416
- rgb: preciseLabToRGB(L, A, B),
1417
- p3: labToP3(L, A, B),
1418
- colorString: colorStr
1419
- };
1420
- }
1421
-
1422
- // 尝试解析lch()函数格式
1423
- const lchMatch = colorString.match(/lch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(deg)?(?:\s*\/\s*([\d.%]+))?\s*\)/i);
1424
- if (lchMatch) {
1425
- let L = parseFloat(lchMatch[1]);
1426
- const C = parseFloat(lchMatch[3]);
1427
- let H = parseFloat(lchMatch[4]);
1428
- const alpha = lchMatch[6] ?
1429
- (lchMatch[6].includes('%') ? parseFloat(lchMatch[6]) / 100 : parseFloat(lchMatch[6])) :
1430
- null;
1304
+ /**
1305
+ * 解析带单位的数值
1306
+ * @param {string} value - 带单位的字符串
1307
+ * @returns {Object} {value: number, unit: string}
1308
+ */
1309
+ parseUnit: parseValueWithUnit,
1431
1310
 
1432
- const lab = lchToLab(L, C, H);
1433
- const colorStr = generateColorString(lab.L, lab.a, lab.b, defaultConfig, alpha);
1311
+ /**
1312
+ * 四舍五入数字
1313
+ * @param {number} num - 要四舍五入的数字
1314
+ * @param {number} precision - 精度
1315
+ * @returns {string} 四舍五入后的数字字符串
1316
+ */
1317
+ round: roundNumber,
1434
1318
 
1435
- return {
1436
- L, C, H, alpha,
1437
- lab: lab,
1438
- rgb: preciseLabToRGB(lab.L, lab.a, lab.b),
1439
- p3: labToP3(lab.L, lab.a, lab.b),
1440
- colorString: colorStr
1441
- };
1442
- }
1443
-
1444
- return null;
1445
- } catch (error) {
1446
- console.warn('无法解析颜色:', colorString, error);
1447
- return null;
1319
+ /**
1320
+ * 测试数学表达式
1321
+ * @param {string} expression - 要测试的表达式
1322
+ * @param {Object} testConfig - 测试配置
1323
+ * @returns {Object} 测试结果
1324
+ */
1325
+ test: function(expression, testConfig = {}) {
1326
+ const config = { ...defaultConfig, ...testConfig };
1327
+ try {
1328
+ const result = evaluateMathExpression(expression, config);
1329
+ return {
1330
+ success: true,
1331
+ expression,
1332
+ result,
1333
+ parsed: parseValueWithUnit(result.replace(/^calc\(|\)$/g, ''))
1334
+ };
1335
+ } catch (error) {
1336
+ return {
1337
+ success: false,
1338
+ expression,
1339
+ error: error.message
1340
+ };
1341
+ }
1342
+ }
1343
+ },
1344
+
1345
+ // 颜色转换工具方法
1346
+ colorUtils: {
1347
+ labToRGB: preciseLabToRGB,
1348
+ lchToLab: lchToLab,
1349
+ lchToRGB: function(L, C, H) {
1350
+ const lab = lchToLab(L, C, H);
1351
+ return preciseLabToRGB(lab.L, lab.a, lab.b);
1352
+ },
1353
+ labToP3: labToP3,
1354
+ lchToP3: function(L, C, H) {
1355
+ const lab = lchToLab(L, C, H);
1356
+ return labToP3(lab.L, lab.a, lab.b);
1357
+ },
1358
+ parseHexLab: function(hexString) {
1359
+ const match = hexString.match(/lab#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i);
1360
+ if (!match) return null;
1361
+
1362
+ const L_hex = match[1];
1363
+ const A_hex = match[2];
1364
+ const B_hex = match[3];
1365
+
1366
+ const L = parseInt(L_hex, 16) / 255 * 100;
1367
+ const A = (parseInt(A_hex, 16) - 128) * 1.5;
1368
+ const B = (parseInt(B_hex, 16) - 128) * 1.5;
1369
+
1370
+ return preciseLabToRGB(L, A, B);
1371
+ },
1372
+ parseHexLch: function(hexString) {
1373
+ const match = hexString.match(/lch#([0-9a-f]{2})([0-9a-f]{2})(\d{1,3})/i);
1374
+ if (!match) return null;
1375
+
1376
+ const L_hex = match[1];
1377
+ const C_hex = match[2];
1378
+ const H_dec = match[3];
1379
+
1380
+ const L = parseInt(L_hex, 16) / 255 * 100;
1381
+ const C = parseInt(C_hex, 16) / 255 * 150;
1382
+ const H = parseInt(H_dec) / 100 * 360;
1383
+
1384
+ const lab = lchToLab(L, C, H);
1385
+ return preciseLabToRGB(lab.L, lab.a, lab.b);
1386
+ },
1387
+ /**
1388
+ * 生成颜色字符串(根据P3支持情况)
1389
+ * @param {number} L - Lab L值
1390
+ * @param {number} a - Lab a值
1391
+ * @param {number} b - Lab b值
1392
+ * @param {number|null} alpha - 透明度
1393
+ * @param {boolean} useP3 - 是否使用P3(自动检测)
1394
+ * @returns {string} CSS颜色字符串
1395
+ */
1396
+ generateColor: function(L, a, b, alpha = null, useP3 = true) {
1397
+ return generateColorString(L, a, b, { enableP3: useP3 }, alpha);
1398
+ },
1399
+ /**
1400
+ * 解析CSS颜色字符串
1401
+ * @param {string} colorString - CSS颜色字符串
1402
+ * @returns {Object} 包含原始Lab值和颜色字符串的对象
1403
+ */
1404
+ parseColor: function(colorString) {
1405
+ try {
1406
+ // 尝试解析lab()函数格式
1407
+ const labMatch = colorString.match(/lab\(\s*([\d.]+)(%?)\s+([\d.-]+)\s+([\d.-]+)(?:\s*\/\s*([\d.%]+))?\s*\)/i);
1408
+ if (labMatch) {
1409
+ let L = parseFloat(labMatch[1]);
1410
+ const A = parseFloat(labMatch[3]);
1411
+ const B = parseFloat(labMatch[4]);
1412
+ const alpha = labMatch[5] ?
1413
+ (labMatch[5].includes('%') ? parseFloat(labMatch[5]) / 100 : parseFloat(labMatch[5])) :
1414
+ null;
1415
+
1416
+ const colorStr = generateColorString(L, A, B, defaultConfig, alpha);
1417
+
1418
+ return {
1419
+ L, A, B, alpha,
1420
+ rgb: preciseLabToRGB(L, A, B),
1421
+ p3: labToP3(L, A, B),
1422
+ colorString: colorStr
1423
+ };
1424
+ }
1425
+
1426
+ // 尝试解析lch()函数格式
1427
+ const lchMatch = colorString.match(/lch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(deg)?(?:\s*\/\s*([\d.%]+))?\s*\)/i);
1428
+ if (lchMatch) {
1429
+ let L = parseFloat(lchMatch[1]);
1430
+ const C = parseFloat(lchMatch[3]);
1431
+ let H = parseFloat(lchMatch[4]);
1432
+ const alpha = lchMatch[6] ?
1433
+ (lchMatch[6].includes('%') ? parseFloat(lchMatch[6]) / 100 : parseFloat(lchMatch[6])) :
1434
+ null;
1435
+
1436
+ const lab = lchToLab(L, C, H);
1437
+ const colorStr = generateColorString(lab.L, lab.a, lab.b, defaultConfig, alpha);
1438
+
1439
+ return {
1440
+ L, C, H, alpha,
1441
+ lab: lab,
1442
+ rgb: preciseLabToRGB(lab.L, lab.a, lab.b),
1443
+ p3: labToP3(lab.L, lab.a, lab.b),
1444
+ colorString: colorStr
1445
+ };
1446
+ }
1447
+
1448
+ return null;
1449
+ } catch (error) {
1450
+ console.warn('无法解析颜色:', colorString, error);
1451
+ return null;
1452
+ }
1453
+ }
1454
+ }
1455
+ };
1456
+
1457
+ // 创建一个可调用的主函数
1458
+ const styimat = function(...args) {
1459
+ // 检查是否是模板字符串调用(标签函数)
1460
+ if (args.length > 1 || (args[0] && args[0].raw)) {
1461
+ // 处理模板字符串
1462
+ return handleTemplateTag(...args);
1463
+ }
1464
+
1465
+ // 获取第一个参数
1466
+ const firstArg = args[0];
1467
+
1468
+ // 如果传入CSS文本,则编译并返回结果
1469
+ if (typeof firstArg === 'string') {
1470
+ return convert(firstArg, { ...defaultConfig, ...args[1] });
1471
+ }
1472
+
1473
+ // 如果传入配置对象,则应用配置
1474
+ if (typeof firstArg === 'object' && firstArg !== null) {
1475
+ defaultConfig = { ...defaultConfig, ...firstArg };
1476
+ return styimat;
1477
+ }
1478
+
1479
+ // 如果没有参数,执行自动处理
1480
+ if (args.length === 0) {
1481
+ return apply();
1448
1482
  }
1449
- }
1483
+
1484
+ return styimat;
1485
+ };
1486
+
1487
+ // 处理模板字符串的函数
1488
+ function handleTemplateTag(strings, ...values) {
1489
+ // 拼接模板字符串
1490
+ let cssText = strings[0];
1491
+
1492
+ for (let i = 0; i < values.length; i++) {
1493
+ // 处理插值(支持字符串、数字、函数等)
1494
+ const value = values[i];
1495
+ let result = '';
1496
+
1497
+ if (typeof value === 'function') {
1498
+ result = value();
1499
+ } else if (Array.isArray(value)) {
1500
+ result = value.join(' ');
1501
+ } else {
1502
+ result = String(value != null ? value : '');
1503
+ }
1504
+
1505
+ cssText += result + strings[i + 1];
1506
+ }
1507
+
1508
+ // 使用convert函数处理
1509
+ return convert(cssText, defaultConfig);
1450
1510
  }
1451
- };
1452
1511
 
1453
- // 自动初始化
1454
- if (typeof window !== 'undefined') {
1455
- apply();
1456
- Object.defineProperty(window.HTMLElement.prototype, 'cssVar', {
1457
- get() {
1458
- const element = this;
1459
- return new Proxy({}, {
1460
- get(target, prop) {
1461
- const varName = prop.startsWith('--') ? prop : `--${prop}`;
1462
- return element.style.getPropertyValue(varName);
1463
- },
1464
-
1465
- set(target, prop, value) {
1466
- const varName = prop.startsWith('--') ? prop : `--${prop}`;
1467
- element.style.setProperty(varName, value);
1468
- return true;
1469
- }
1512
+ // 将API的所有方法复制到主函数上
1513
+ Object.assign(styimat, api);
1514
+ Object.setPrototypeOf(styimat, Function.prototype);
1515
+
1516
+ // 自动初始化
1517
+ if (typeof window !== 'undefined') {
1518
+ apply();
1519
+ Object.defineProperty(window.HTMLElement.prototype, 'cssVar', {
1520
+ get() {
1521
+ const element = this;
1522
+ return new Proxy(() => {}, {
1523
+ get(target, prop) {
1524
+ const varName = prop.startsWith('--') ? prop : `--${prop}`;
1525
+ return element.style.getPropertyValue(varName);
1526
+ },
1527
+
1528
+ set(target, prop, value) {
1529
+ const varName = prop.startsWith('--') ? prop : `--${prop}`;
1530
+ element.style.setProperty(varName, value);
1531
+ return true;
1532
+ },
1533
+
1534
+ apply(target, thisArg, argumentsList) {
1535
+ const prop = argumentsList[0];
1536
+ const value = argumentsList[1];
1537
+ const varName = prop.startsWith('--') ? prop : `--${prop}`;
1538
+ if (value === undefined) return element.style.getPropertyValue(varName);
1539
+ element.style.setProperty(varName, value);
1540
+ }
1541
+ });
1542
+ }
1470
1543
  });
1471
- }
1472
- });
1473
- }
1474
-
1475
- return api;
1544
+ }
1545
+
1546
+ return styimat;
1476
1547
  }));