@atomixstudio/mcp 1.0.29 → 1.0.30
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/dist/index.js +443 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1288,10 +1288,447 @@ function getTokenStats(data) {
|
|
|
1288
1288
|
};
|
|
1289
1289
|
}
|
|
1290
1290
|
|
|
1291
|
+
// ../figma-tools/dist/index.js
|
|
1292
|
+
function figmaColorNameWithGroup(key) {
|
|
1293
|
+
if (key.includes("/")) {
|
|
1294
|
+
const [group2, ...rest] = key.split("/");
|
|
1295
|
+
const name2 = rest.join("/").trim();
|
|
1296
|
+
if (!name2) return key;
|
|
1297
|
+
const groupDisplay2 = group2.charAt(0).toUpperCase() + group2.slice(1).toLowerCase();
|
|
1298
|
+
return `${groupDisplay2} / ${name2}`;
|
|
1299
|
+
}
|
|
1300
|
+
const firstDash = key.indexOf("-");
|
|
1301
|
+
if (firstDash <= 0) return key;
|
|
1302
|
+
const group = key.slice(0, firstDash);
|
|
1303
|
+
const name = key.slice(firstDash + 1);
|
|
1304
|
+
const groupDisplay = group.charAt(0).toUpperCase() + group.slice(1).toLowerCase();
|
|
1305
|
+
return `${groupDisplay} / ${name}`;
|
|
1306
|
+
}
|
|
1307
|
+
var FIGMA_SHADOW_ORDER = {
|
|
1308
|
+
none: 0,
|
|
1309
|
+
xs: 1,
|
|
1310
|
+
sm: 2,
|
|
1311
|
+
md: 3,
|
|
1312
|
+
lg: 4,
|
|
1313
|
+
xl: 5,
|
|
1314
|
+
"2xl": 6,
|
|
1315
|
+
focus: 7
|
|
1316
|
+
};
|
|
1317
|
+
function tokenValueToNumber(s) {
|
|
1318
|
+
if (typeof s !== "string" || !s.trim()) return 0;
|
|
1319
|
+
const t = s.trim();
|
|
1320
|
+
if (t.endsWith("rem")) {
|
|
1321
|
+
const n2 = parseFloat(t.replace(/rem$/, ""));
|
|
1322
|
+
return Number.isFinite(n2) ? Math.round(n2 * 16) : 0;
|
|
1323
|
+
}
|
|
1324
|
+
if (t.endsWith("px")) {
|
|
1325
|
+
const n2 = parseFloat(t.replace(/px$/, ""));
|
|
1326
|
+
return Number.isFinite(n2) ? Math.round(n2) : 0;
|
|
1327
|
+
}
|
|
1328
|
+
const n = parseFloat(t);
|
|
1329
|
+
return Number.isFinite(n) ? n : 0;
|
|
1330
|
+
}
|
|
1331
|
+
function parseBoxShadowToFigmaEffect(shadowStr) {
|
|
1332
|
+
const s = shadowStr.trim();
|
|
1333
|
+
if (!s || s.toLowerCase() === "none") return null;
|
|
1334
|
+
const parsePx = (x) => typeof x === "string" ? parseFloat(x.replace(/px$/i, "")) : NaN;
|
|
1335
|
+
const colorMatch = s.match(/(rgba?\s*\([^)]+\)|#[0-9A-Fa-f]{3,8})\s*$/i);
|
|
1336
|
+
const colorStr = colorMatch ? colorMatch[1].trim() : void 0;
|
|
1337
|
+
const rest = (colorMatch ? s.slice(0, colorMatch.index) : s).trim();
|
|
1338
|
+
const parts = rest ? rest.split(/\s+/) : [];
|
|
1339
|
+
if (parts.length < 3) return null;
|
|
1340
|
+
const offsetX = parsePx(parts[0]);
|
|
1341
|
+
const offsetY = parsePx(parts[1]);
|
|
1342
|
+
const blur = parsePx(parts[2]);
|
|
1343
|
+
let spread = 0;
|
|
1344
|
+
if (parts.length >= 4) spread = parsePx(parts[3]);
|
|
1345
|
+
let r = 0, g = 0, b = 0, a = 0.1;
|
|
1346
|
+
if (colorStr) {
|
|
1347
|
+
const rgbaMatch = colorStr.match(/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)/i);
|
|
1348
|
+
if (rgbaMatch) {
|
|
1349
|
+
r = Number(rgbaMatch[1]) / 255;
|
|
1350
|
+
g = Number(rgbaMatch[2]) / 255;
|
|
1351
|
+
b = Number(rgbaMatch[3]) / 255;
|
|
1352
|
+
a = rgbaMatch[4] != null ? parseFloat(rgbaMatch[4]) : 1;
|
|
1353
|
+
} else {
|
|
1354
|
+
const hexMatch = colorStr.match(/^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/);
|
|
1355
|
+
if (hexMatch) {
|
|
1356
|
+
r = parseInt(hexMatch[1].slice(0, 2), 16) / 255;
|
|
1357
|
+
g = parseInt(hexMatch[1].slice(2, 4), 16) / 255;
|
|
1358
|
+
b = parseInt(hexMatch[1].slice(4, 6), 16) / 255;
|
|
1359
|
+
a = hexMatch[2] ? parseInt(hexMatch[2], 16) / 255 : 0.1;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (!Number.isFinite(offsetX) || !Number.isFinite(offsetY) || !Number.isFinite(blur)) return null;
|
|
1364
|
+
return {
|
|
1365
|
+
type: "DROP_SHADOW",
|
|
1366
|
+
offset: { x: offsetX, y: offsetY },
|
|
1367
|
+
radius: Math.max(0, blur),
|
|
1368
|
+
spread: Number.isFinite(spread) ? spread : 0,
|
|
1369
|
+
color: { r, g, b, a },
|
|
1370
|
+
visible: true,
|
|
1371
|
+
blendMode: "NORMAL"
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
function parseBoxShadowToFigmaEffects(shadowStr) {
|
|
1375
|
+
const s = (shadowStr || "").trim();
|
|
1376
|
+
if (!s || s.toLowerCase() === "none") return [];
|
|
1377
|
+
const out = [];
|
|
1378
|
+
const segments = s.split(/\s*,\s*/);
|
|
1379
|
+
for (const seg of segments) {
|
|
1380
|
+
const effect = parseBoxShadowToFigmaEffect(seg.trim());
|
|
1381
|
+
if (effect) out.push(effect);
|
|
1382
|
+
}
|
|
1383
|
+
return out;
|
|
1384
|
+
}
|
|
1385
|
+
function colorTo8DigitHex(color) {
|
|
1386
|
+
if (!color || typeof color !== "string") return null;
|
|
1387
|
+
const s = color.trim();
|
|
1388
|
+
const rgbaMatch = s.match(/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)/i);
|
|
1389
|
+
if (rgbaMatch) {
|
|
1390
|
+
const r = Math.max(0, Math.min(255, parseInt(rgbaMatch[1], 10)));
|
|
1391
|
+
const g = Math.max(0, Math.min(255, parseInt(rgbaMatch[2], 10)));
|
|
1392
|
+
const b = Math.max(0, Math.min(255, parseInt(rgbaMatch[3], 10)));
|
|
1393
|
+
const a = rgbaMatch[4] != null ? Math.max(0, Math.min(1, parseFloat(rgbaMatch[4]))) : 1;
|
|
1394
|
+
const aByte = Math.round(a * 255);
|
|
1395
|
+
const hex = "#" + [r, g, b, aByte].map((n) => n.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
1396
|
+
return hex;
|
|
1397
|
+
}
|
|
1398
|
+
const hexMatch = s.match(/^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/i);
|
|
1399
|
+
if (hexMatch) {
|
|
1400
|
+
const rgb = hexMatch[1];
|
|
1401
|
+
const aa = hexMatch[2] ?? "FF";
|
|
1402
|
+
return `#${rgb}${aa}`.toUpperCase();
|
|
1403
|
+
}
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
function typesetKeyToFontFamilyRole(key) {
|
|
1407
|
+
const prefix = (key.split("-")[0] ?? key).toLowerCase();
|
|
1408
|
+
if (prefix === "display" || prefix.startsWith("display")) return "display";
|
|
1409
|
+
if (prefix === "heading" || prefix.startsWith("heading")) return "heading";
|
|
1410
|
+
if (prefix === "mono" || prefix.startsWith("mono")) return "mono";
|
|
1411
|
+
if (prefix.startsWith("body")) return "body";
|
|
1412
|
+
return "body";
|
|
1413
|
+
}
|
|
1414
|
+
function buildFigmaPayloadsFromDS(data) {
|
|
1415
|
+
const tokens = data.tokens;
|
|
1416
|
+
const colors = tokens?.colors;
|
|
1417
|
+
const typography = tokens?.typography;
|
|
1418
|
+
const modes = [];
|
|
1419
|
+
const variables = [];
|
|
1420
|
+
const paintStyles = [];
|
|
1421
|
+
const hexRe = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
|
|
1422
|
+
const addedNames = /* @__PURE__ */ new Set();
|
|
1423
|
+
if (colors?.modes) {
|
|
1424
|
+
const light = colors.modes.light ?? {};
|
|
1425
|
+
const dark = colors.modes.dark ?? {};
|
|
1426
|
+
if (Object.keys(light).length > 0) modes.push("Light");
|
|
1427
|
+
if (Object.keys(dark).length > 0 && !modes.includes("Dark")) modes.push("Dark");
|
|
1428
|
+
if (modes.length === 0) modes.push("Light");
|
|
1429
|
+
const orderedKeys = [...Object.keys(light)];
|
|
1430
|
+
for (const k of Object.keys(dark)) {
|
|
1431
|
+
if (!orderedKeys.includes(k)) orderedKeys.push(k);
|
|
1432
|
+
}
|
|
1433
|
+
for (const key of orderedKeys) {
|
|
1434
|
+
const lightVal = light[key];
|
|
1435
|
+
const darkVal = dark[key];
|
|
1436
|
+
const lightHex = typeof lightVal === "string" ? colorTo8DigitHex(lightVal) ?? (hexRe.test(lightVal) ? lightVal : null) : null;
|
|
1437
|
+
if (lightHex && hexRe.test(lightHex)) {
|
|
1438
|
+
const figmaName = figmaColorNameWithGroup(key);
|
|
1439
|
+
if (addedNames.has(figmaName)) continue;
|
|
1440
|
+
addedNames.add(figmaName);
|
|
1441
|
+
const darkHex = typeof darkVal === "string" ? colorTo8DigitHex(darkVal) ?? (hexRe.test(darkVal) ? darkVal : null) : null;
|
|
1442
|
+
const values = {};
|
|
1443
|
+
if (modes.includes("Light")) values.Light = lightHex;
|
|
1444
|
+
if (modes.includes("Dark")) values.Dark = darkHex && hexRe.test(darkHex) ? darkHex : lightHex;
|
|
1445
|
+
variables.push({ name: figmaName, values });
|
|
1446
|
+
paintStyles.push({ name: figmaName, color: lightHex });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
if (colors?.static?.brand && typeof colors.static.brand === "object") {
|
|
1451
|
+
for (const [key, hex] of Object.entries(colors.static.brand)) {
|
|
1452
|
+
if (typeof hex !== "string" || !hexRe.test(hex)) continue;
|
|
1453
|
+
const figmaName = figmaColorNameWithGroup(`brand/${key}`);
|
|
1454
|
+
if (addedNames.has(figmaName)) continue;
|
|
1455
|
+
addedNames.add(figmaName);
|
|
1456
|
+
paintStyles.push({ name: figmaName, color: hex });
|
|
1457
|
+
if (modes.length === 0) modes.push("Light");
|
|
1458
|
+
const values = {};
|
|
1459
|
+
for (const m of modes) values[m] = hex;
|
|
1460
|
+
variables.push({ name: figmaName, values });
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
const alphaScales = colors?.alphaScales;
|
|
1464
|
+
if (alphaScales?.whiteAlpha && typeof alphaScales.whiteAlpha === "object") {
|
|
1465
|
+
if (modes.length === 0) modes.push("Light");
|
|
1466
|
+
for (const [step, value] of Object.entries(alphaScales.whiteAlpha)) {
|
|
1467
|
+
if (typeof value !== "string") continue;
|
|
1468
|
+
const hex = colorTo8DigitHex(value);
|
|
1469
|
+
if (!hex || !hexRe.test(hex)) continue;
|
|
1470
|
+
const figmaName = `WhiteAlpha / ${step}`;
|
|
1471
|
+
if (addedNames.has(figmaName)) continue;
|
|
1472
|
+
addedNames.add(figmaName);
|
|
1473
|
+
const values = {};
|
|
1474
|
+
for (const m of modes) values[m] = hex;
|
|
1475
|
+
variables.push({ name: figmaName, values });
|
|
1476
|
+
paintStyles.push({ name: figmaName, color: hex });
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (alphaScales?.blackAlpha && typeof alphaScales.blackAlpha === "object") {
|
|
1480
|
+
if (modes.length === 0) modes.push("Light");
|
|
1481
|
+
for (const [step, value] of Object.entries(alphaScales.blackAlpha)) {
|
|
1482
|
+
if (typeof value !== "string") continue;
|
|
1483
|
+
const hex = colorTo8DigitHex(value);
|
|
1484
|
+
if (!hex || !hexRe.test(hex)) continue;
|
|
1485
|
+
const figmaName = `BlackAlpha / ${step}`;
|
|
1486
|
+
if (addedNames.has(figmaName)) continue;
|
|
1487
|
+
addedNames.add(figmaName);
|
|
1488
|
+
const values = {};
|
|
1489
|
+
for (const m of modes) values[m] = hex;
|
|
1490
|
+
variables.push({ name: figmaName, values });
|
|
1491
|
+
paintStyles.push({ name: figmaName, color: hex });
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (variables.length === 0 && modes.length === 0) modes.push("Light");
|
|
1495
|
+
const dsName = data.meta?.name;
|
|
1496
|
+
const collectionPrefix = dsName ? `${dsName} ` : "";
|
|
1497
|
+
const colorCollectionName = `${collectionPrefix}Colors`;
|
|
1498
|
+
const textStyles = [];
|
|
1499
|
+
const sizeToPx = (val, basePx = 16) => {
|
|
1500
|
+
if (typeof val === "number") return Math.round(val);
|
|
1501
|
+
const s = String(val).trim();
|
|
1502
|
+
const pxMatch = s.match(/^([\d.]+)\s*px$/i);
|
|
1503
|
+
if (pxMatch) return Math.round(parseFloat(pxMatch[1]));
|
|
1504
|
+
const remMatch = s.match(/^([\d.]+)\s*rem$/i);
|
|
1505
|
+
if (remMatch) return Math.round(parseFloat(remMatch[1]) * basePx);
|
|
1506
|
+
const n = parseFloat(s);
|
|
1507
|
+
if (Number.isFinite(n)) return n <= 0 ? basePx : n < 50 ? Math.round(n * basePx) : Math.round(n);
|
|
1508
|
+
return basePx;
|
|
1509
|
+
};
|
|
1510
|
+
const letterSpacingToPx = (val, fontSizePx) => {
|
|
1511
|
+
if (val === void 0 || val === null) return void 0;
|
|
1512
|
+
if (typeof val === "number") return Math.round(val);
|
|
1513
|
+
const s = String(val).trim();
|
|
1514
|
+
const pxMatch = s.match(/^([-\d.]+)\s*px$/i);
|
|
1515
|
+
if (pxMatch) return Math.round(parseFloat(pxMatch[1]));
|
|
1516
|
+
const emMatch = s.match(/^([-\d.]+)\s*em$/i);
|
|
1517
|
+
if (emMatch) return Math.round(parseFloat(emMatch[1]) * fontSizePx);
|
|
1518
|
+
const n = parseFloat(s);
|
|
1519
|
+
return Number.isFinite(n) ? Math.round(n) : void 0;
|
|
1520
|
+
};
|
|
1521
|
+
const firstFont = (obj) => {
|
|
1522
|
+
if (typeof obj === "string") {
|
|
1523
|
+
const primary = obj.split(",")[0].trim().replace(/^['"]|['"]$/g, "");
|
|
1524
|
+
return primary || "Inter";
|
|
1525
|
+
}
|
|
1526
|
+
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
|
|
1527
|
+
const v = obj.body ?? obj.heading ?? obj.display ?? Object.values(obj)[0];
|
|
1528
|
+
return firstFont(v);
|
|
1529
|
+
}
|
|
1530
|
+
return "Inter";
|
|
1531
|
+
};
|
|
1532
|
+
const toFontFamilyString = (val) => {
|
|
1533
|
+
if (typeof val === "string") {
|
|
1534
|
+
const s = val.trim().replace(/^["']|["']$/g, "");
|
|
1535
|
+
return s || "Inter";
|
|
1536
|
+
}
|
|
1537
|
+
return firstFont(val);
|
|
1538
|
+
};
|
|
1539
|
+
const fontFamilyMap = typography?.fontFamily ?? {};
|
|
1540
|
+
const defaultFontFamily = typography ? firstFont(typography.fontFamily ?? "Inter") : "Inter";
|
|
1541
|
+
const fontSizeMap = typography?.fontSize;
|
|
1542
|
+
const fontWeightMap = typography?.fontWeight;
|
|
1543
|
+
const lineHeightMap = typography?.lineHeight;
|
|
1544
|
+
const letterSpacingMap = typography?.letterSpacing;
|
|
1545
|
+
const textTransformMap = typography?.textTransform;
|
|
1546
|
+
const textDecorationMap = typography?.textDecoration;
|
|
1547
|
+
if (fontSizeMap && typeof fontSizeMap === "object" && Object.keys(fontSizeMap).length > 0) {
|
|
1548
|
+
for (const [key, sizeVal] of Object.entries(fontSizeMap)) {
|
|
1549
|
+
const fontSize = sizeToPx(sizeVal);
|
|
1550
|
+
if (fontSize <= 0) continue;
|
|
1551
|
+
const role = typesetKeyToFontFamilyRole(key);
|
|
1552
|
+
const fontFamily = toFontFamilyString(
|
|
1553
|
+
fontFamilyMap[role] ?? fontFamilyMap.body ?? fontFamilyMap.heading ?? fontFamilyMap.display ?? defaultFontFamily
|
|
1554
|
+
);
|
|
1555
|
+
const lh = lineHeightMap && typeof lineHeightMap === "object" ? lineHeightMap[key] : void 0;
|
|
1556
|
+
const weight = fontWeightMap && typeof fontWeightMap === "object" ? fontWeightMap[key] : void 0;
|
|
1557
|
+
const fontWeight = weight != null ? String(weight) : "400";
|
|
1558
|
+
const letterSpacingPx = letterSpacingToPx(
|
|
1559
|
+
letterSpacingMap && typeof letterSpacingMap === "object" ? letterSpacingMap[key] : void 0,
|
|
1560
|
+
fontSize
|
|
1561
|
+
);
|
|
1562
|
+
const textTransform = textTransformMap && typeof textTransformMap === "object" ? textTransformMap[key] : void 0;
|
|
1563
|
+
const textDecoration = textDecorationMap && typeof textDecorationMap === "object" ? textDecorationMap[key] : void 0;
|
|
1564
|
+
const namePart = key.replace(/-/g, " / ");
|
|
1565
|
+
const style = {
|
|
1566
|
+
name: namePart.startsWith("Typography") ? namePart : `Typography / ${namePart}`,
|
|
1567
|
+
fontFamily,
|
|
1568
|
+
fontWeight,
|
|
1569
|
+
fontSize,
|
|
1570
|
+
lineHeightUnit: "PERCENT",
|
|
1571
|
+
letterSpacingUnit: "PIXELS",
|
|
1572
|
+
...letterSpacingPx !== void 0 && letterSpacingPx !== 0 ? { letterSpacingValue: letterSpacingPx } : {}
|
|
1573
|
+
};
|
|
1574
|
+
if (lh != null && typeof lh === "number" && lh > 0) {
|
|
1575
|
+
style.lineHeightValue = lh >= 10 ? Math.round(lh / fontSize * 100) : Math.round(lh * 100);
|
|
1576
|
+
} else {
|
|
1577
|
+
style.lineHeightValue = 150;
|
|
1578
|
+
}
|
|
1579
|
+
if (textTransform === "uppercase") style.textCase = "UPPER";
|
|
1580
|
+
else if (textTransform === "lowercase") style.textCase = "LOWER";
|
|
1581
|
+
else if (textTransform === "capitalize") style.textCase = "TITLE";
|
|
1582
|
+
else style.textCase = "ORIGINAL";
|
|
1583
|
+
if (textDecoration === "underline") style.textDecoration = "UNDERLINE";
|
|
1584
|
+
else style.textDecoration = "NONE";
|
|
1585
|
+
textStyles.push(style);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
const textStylesMap = typography?.textStyles;
|
|
1589
|
+
if (textStyles.length === 0 && textStylesMap && typeof textStylesMap === "object") {
|
|
1590
|
+
for (const [styleName, style] of Object.entries(textStylesMap)) {
|
|
1591
|
+
if (!style || typeof style !== "object") continue;
|
|
1592
|
+
const fontSize = sizeToPx(style.fontSize ?? "1rem");
|
|
1593
|
+
const lhStr = style.lineHeight;
|
|
1594
|
+
const lineHeightUnitless = lhStr != null ? lhStr.endsWith("%") ? parseFloat(lhStr) / 100 : sizeToPx(lhStr) / fontSize : 1.5;
|
|
1595
|
+
const payload = {
|
|
1596
|
+
name: styleName.startsWith("Typography") ? styleName : `Typography / ${styleName.replace(/\//g, " / ")}`,
|
|
1597
|
+
fontFamily: defaultFontFamily,
|
|
1598
|
+
fontWeight: String(style.fontWeight ?? "400"),
|
|
1599
|
+
fontSize,
|
|
1600
|
+
lineHeightUnit: "PERCENT",
|
|
1601
|
+
lineHeightValue: Math.round((Number.isFinite(lineHeightUnitless) ? lineHeightUnitless : 1.5) * 100),
|
|
1602
|
+
letterSpacingUnit: "PIXELS",
|
|
1603
|
+
textCase: "ORIGINAL",
|
|
1604
|
+
textDecoration: "NONE"
|
|
1605
|
+
};
|
|
1606
|
+
textStyles.push(payload);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
textStyles.sort((a, b) => {
|
|
1610
|
+
if (a.fontSize !== b.fontSize) return a.fontSize - b.fontSize;
|
|
1611
|
+
return (a.name || "").localeCompare(b.name || "");
|
|
1612
|
+
});
|
|
1613
|
+
const numberVariableCollections = [];
|
|
1614
|
+
const spacing = tokens?.spacing;
|
|
1615
|
+
if (spacing?.scale && typeof spacing.scale === "object") {
|
|
1616
|
+
const vars = [];
|
|
1617
|
+
for (const [key, val] of Object.entries(spacing.scale)) {
|
|
1618
|
+
const n = tokenValueToNumber(val);
|
|
1619
|
+
if (n >= 0) vars.push({ name: `Spacing / ${key}`, value: n });
|
|
1620
|
+
}
|
|
1621
|
+
vars.sort((a, b) => a.value - b.value);
|
|
1622
|
+
if (vars.length > 0)
|
|
1623
|
+
numberVariableCollections.push({
|
|
1624
|
+
collectionName: `${collectionPrefix}Spacing`,
|
|
1625
|
+
categoryKey: "Spacing",
|
|
1626
|
+
variables: vars,
|
|
1627
|
+
scopes: ["GAP"]
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
const radius = tokens?.radius;
|
|
1631
|
+
if (radius?.scale && typeof radius.scale === "object") {
|
|
1632
|
+
const vars = [];
|
|
1633
|
+
for (const [key, val] of Object.entries(radius.scale)) {
|
|
1634
|
+
const n = tokenValueToNumber(val);
|
|
1635
|
+
if (n >= 0) vars.push({ name: `Radius / ${key}`, value: n });
|
|
1636
|
+
}
|
|
1637
|
+
vars.sort((a, b) => a.value - b.value);
|
|
1638
|
+
if (vars.length > 0)
|
|
1639
|
+
numberVariableCollections.push({
|
|
1640
|
+
collectionName: `${collectionPrefix}Radius`,
|
|
1641
|
+
categoryKey: "Radius",
|
|
1642
|
+
variables: vars,
|
|
1643
|
+
scopes: ["CORNER_RADIUS"]
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
const borders = tokens?.borders;
|
|
1647
|
+
if (borders?.width && typeof borders.width === "object") {
|
|
1648
|
+
const vars = [];
|
|
1649
|
+
for (const [key, val] of Object.entries(borders.width)) {
|
|
1650
|
+
const n = tokenValueToNumber(val);
|
|
1651
|
+
if (n >= 0) vars.push({ name: `Borders / ${key}`, value: n });
|
|
1652
|
+
}
|
|
1653
|
+
vars.sort((a, b) => a.value - b.value);
|
|
1654
|
+
if (vars.length > 0)
|
|
1655
|
+
numberVariableCollections.push({
|
|
1656
|
+
collectionName: `${collectionPrefix}Borders`,
|
|
1657
|
+
categoryKey: "Borders",
|
|
1658
|
+
variables: vars,
|
|
1659
|
+
scopes: ["STROKE_FLOAT"]
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
const sizing = tokens?.sizing;
|
|
1663
|
+
const sizingVariables = [];
|
|
1664
|
+
if (sizing?.height && typeof sizing.height === "object") {
|
|
1665
|
+
for (const [key, val] of Object.entries(sizing.height)) {
|
|
1666
|
+
const n = tokenValueToNumber(val);
|
|
1667
|
+
if (n >= 0) sizingVariables.push({ name: `Height / ${key}`, value: n });
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (sizing?.icon && typeof sizing.icon === "object") {
|
|
1671
|
+
for (const [key, val] of Object.entries(sizing.icon)) {
|
|
1672
|
+
const n = tokenValueToNumber(val);
|
|
1673
|
+
if (n >= 0) sizingVariables.push({ name: `Icon / ${key}`, value: n });
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
sizingVariables.sort((a, b) => a.value - b.value);
|
|
1677
|
+
if (sizingVariables.length > 0) {
|
|
1678
|
+
numberVariableCollections.push({
|
|
1679
|
+
collectionName: `${collectionPrefix}Sizing`,
|
|
1680
|
+
categoryKey: "Sizing",
|
|
1681
|
+
variables: sizingVariables,
|
|
1682
|
+
scopes: ["WIDTH_HEIGHT"]
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
const layout = tokens?.layout;
|
|
1686
|
+
if (layout?.breakpoint && typeof layout.breakpoint === "object") {
|
|
1687
|
+
const vars = [];
|
|
1688
|
+
for (const [key, val] of Object.entries(layout.breakpoint)) {
|
|
1689
|
+
const n = tokenValueToNumber(val);
|
|
1690
|
+
if (n >= 0) vars.push({ name: `Breakpoint / ${key}`, value: n });
|
|
1691
|
+
}
|
|
1692
|
+
vars.sort((a, b) => a.value - b.value);
|
|
1693
|
+
if (vars.length > 0)
|
|
1694
|
+
numberVariableCollections.push({
|
|
1695
|
+
collectionName: `${collectionPrefix}Layout`,
|
|
1696
|
+
categoryKey: "Layout",
|
|
1697
|
+
variables: vars,
|
|
1698
|
+
scopes: ["WIDTH_HEIGHT"]
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
const effectStyles = [];
|
|
1702
|
+
const shadows = tokens?.shadows;
|
|
1703
|
+
if (shadows?.elevation && typeof shadows.elevation === "object") {
|
|
1704
|
+
for (const [key, val] of Object.entries(shadows.elevation)) {
|
|
1705
|
+
if (typeof val !== "string") continue;
|
|
1706
|
+
const effects = parseBoxShadowToFigmaEffects(val);
|
|
1707
|
+
if (effects.length > 0) effectStyles.push({ name: `Shadow / ${key}`, effects });
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
if (shadows?.focus && typeof shadows.focus === "string") {
|
|
1711
|
+
const effects = parseBoxShadowToFigmaEffects(shadows.focus);
|
|
1712
|
+
if (effects.length > 0) effectStyles.push({ name: "Shadow / focus", effects });
|
|
1713
|
+
}
|
|
1714
|
+
effectStyles.sort((a, b) => {
|
|
1715
|
+
const nameA = a.name.startsWith("Shadow / ") ? a.name.slice(9) : a.name;
|
|
1716
|
+
const nameB = b.name.startsWith("Shadow / ") ? b.name.slice(9) : b.name;
|
|
1717
|
+
const orderA = FIGMA_SHADOW_ORDER[nameA] ?? 100;
|
|
1718
|
+
const orderB = FIGMA_SHADOW_ORDER[nameB] ?? 100;
|
|
1719
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
1720
|
+
return nameA.localeCompare(nameB);
|
|
1721
|
+
});
|
|
1722
|
+
return {
|
|
1723
|
+
colorVariables: { collectionName: colorCollectionName, modes, variables },
|
|
1724
|
+
paintStyles,
|
|
1725
|
+
textStyles,
|
|
1726
|
+
numberVariableCollections,
|
|
1727
|
+
effectStyles
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1291
1731
|
// src/index.ts
|
|
1292
|
-
import {
|
|
1293
|
-
buildFigmaPayloadsFromDS
|
|
1294
|
-
} from "@atomix/figma_tools";
|
|
1295
1732
|
import * as path2 from "path";
|
|
1296
1733
|
import * as fs2 from "fs";
|
|
1297
1734
|
import { execSync } from "child_process";
|
|
@@ -1573,7 +2010,7 @@ async function fetchDesignSystemForMCP(forceRefresh = false) {
|
|
|
1573
2010
|
return result.data;
|
|
1574
2011
|
}
|
|
1575
2012
|
var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows", "radius", "borders", "motion", "zIndex"];
|
|
1576
|
-
function
|
|
2013
|
+
function typesetKeyToFontFamilyRole2(key) {
|
|
1577
2014
|
const prefix = (key.split("-")[0] ?? key).toLowerCase();
|
|
1578
2015
|
if (prefix === "display" || prefix.startsWith("display")) return "display";
|
|
1579
2016
|
if (prefix === "heading" || prefix.startsWith("heading")) return "heading";
|
|
@@ -1593,7 +2030,7 @@ function buildTypesetsList(typography, cssPrefix = "atmx") {
|
|
|
1593
2030
|
const p = cssPrefix ? `${cssPrefix}-` : "";
|
|
1594
2031
|
const typesets = [];
|
|
1595
2032
|
for (const key of Object.keys(fontSize)) {
|
|
1596
|
-
const role =
|
|
2033
|
+
const role = typesetKeyToFontFamilyRole2(key);
|
|
1597
2034
|
const familyName = fontFamily[role] ?? fontFamily.body;
|
|
1598
2035
|
const fontFamilyVarName = familyName ? `--${p}typography-font-family-${role}` : void 0;
|
|
1599
2036
|
const fontFamilyVar = familyName ? `var(${fontFamilyVarName})` : "";
|
|
@@ -1619,7 +2056,7 @@ function buildTypesetsList(typography, cssPrefix = "atmx") {
|
|
|
1619
2056
|
var server = new Server(
|
|
1620
2057
|
{
|
|
1621
2058
|
name: "atomix-mcp-user",
|
|
1622
|
-
version: "1.0.
|
|
2059
|
+
version: "1.0.29"
|
|
1623
2060
|
},
|
|
1624
2061
|
{
|
|
1625
2062
|
capabilities: {
|