@atomixstudio/mcp 1.0.9 → 1.0.10
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 +1292 -40
- package/dist/index.js.map +1 -1
- package/package.json +47 -1
package/dist/index.js
CHANGED
|
@@ -11,29 +11,1281 @@ import {
|
|
|
11
11
|
ListPromptsRequestSchema,
|
|
12
12
|
GetPromptRequestSchema
|
|
13
13
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
getTokenByPath,
|
|
17
|
-
flattenTokens,
|
|
18
|
-
searchTokens,
|
|
19
|
-
getTokenStats,
|
|
20
|
-
compareDesignSystems,
|
|
21
|
-
generateCSSOutput,
|
|
22
|
-
generateSCSSOutput,
|
|
23
|
-
generateLessOutput,
|
|
24
|
-
generateJSONOutput,
|
|
25
|
-
generateTSOutput,
|
|
26
|
-
generateJSOutput,
|
|
27
|
-
generateSwiftOutput,
|
|
28
|
-
generateKotlinOutput,
|
|
29
|
-
generateDartOutput,
|
|
30
|
-
diffTokens,
|
|
31
|
-
formatSyncResponse,
|
|
32
|
-
detectGovernanceChangesByFoundation,
|
|
33
|
-
syncRulesFiles
|
|
34
|
-
} from "@atomixstudio/sync-core";
|
|
35
|
-
import * as path from "path";
|
|
14
|
+
|
|
15
|
+
// node_modules/.pnpm/@atomixstudio+sync-core@file+..+atomix-sync-core/node_modules/@atomixstudio/sync-core/dist/index.js
|
|
36
16
|
import * as fs from "fs";
|
|
17
|
+
import * as path from "path";
|
|
18
|
+
import * as path3 from "path";
|
|
19
|
+
function generateETag(meta) {
|
|
20
|
+
const ts = meta.updatedAt ?? meta.exportedAt ?? "";
|
|
21
|
+
const hash = `${meta.version}-${ts}`;
|
|
22
|
+
return `"v${hash}"`;
|
|
23
|
+
}
|
|
24
|
+
function compareDesignSystems(cached, fresh) {
|
|
25
|
+
const changes = {
|
|
26
|
+
hasChanges: false,
|
|
27
|
+
versionChanged: false,
|
|
28
|
+
timestampChanged: false,
|
|
29
|
+
addedTokens: [],
|
|
30
|
+
removedTokens: [],
|
|
31
|
+
modifiedTokens: [],
|
|
32
|
+
governanceChanged: false,
|
|
33
|
+
summary: ""
|
|
34
|
+
};
|
|
35
|
+
if (cached.meta.version !== fresh.meta.version) {
|
|
36
|
+
changes.versionChanged = true;
|
|
37
|
+
changes.hasChanges = true;
|
|
38
|
+
}
|
|
39
|
+
if (cached.meta.exportedAt !== fresh.meta.exportedAt) {
|
|
40
|
+
changes.timestampChanged = true;
|
|
41
|
+
changes.hasChanges = true;
|
|
42
|
+
}
|
|
43
|
+
const cachedVars = Object.keys(cached.cssVariables);
|
|
44
|
+
const freshVars = Object.keys(fresh.cssVariables);
|
|
45
|
+
for (const varName of freshVars) {
|
|
46
|
+
if (!(varName in cached.cssVariables)) {
|
|
47
|
+
changes.addedTokens.push(varName);
|
|
48
|
+
changes.hasChanges = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const varName of cachedVars) {
|
|
52
|
+
if (!(varName in fresh.cssVariables)) {
|
|
53
|
+
changes.removedTokens.push(varName);
|
|
54
|
+
changes.hasChanges = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const varName of cachedVars) {
|
|
58
|
+
if (varName in fresh.cssVariables) {
|
|
59
|
+
const oldValue = cached.cssVariables[varName];
|
|
60
|
+
const newValue = fresh.cssVariables[varName];
|
|
61
|
+
if (oldValue !== newValue) {
|
|
62
|
+
changes.modifiedTokens.push({
|
|
63
|
+
token: varName,
|
|
64
|
+
oldValue,
|
|
65
|
+
newValue
|
|
66
|
+
});
|
|
67
|
+
changes.hasChanges = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const cachedDark = cached.tokens?.colors?.modes;
|
|
72
|
+
const freshDark = fresh.tokens?.colors?.modes;
|
|
73
|
+
const cachedDarkColors = cachedDark?.dark || {};
|
|
74
|
+
const freshDarkColors = freshDark?.dark || {};
|
|
75
|
+
const cachedDarkKeys = Object.keys(cachedDarkColors);
|
|
76
|
+
const freshDarkKeys = Object.keys(freshDarkColors);
|
|
77
|
+
const firstColorVar = cachedVars.find((v) => v.includes("-color-")) || freshVars.find((v) => v.includes("-color-"));
|
|
78
|
+
const prefixMatch = firstColorVar?.match(/^(--[a-z]+-color-)/);
|
|
79
|
+
const colorPrefix = prefixMatch ? prefixMatch[1] : "--atmx-color-";
|
|
80
|
+
for (const key of freshDarkKeys) {
|
|
81
|
+
if (!(key in cachedDarkColors)) {
|
|
82
|
+
const cssVarName = `${colorPrefix}${key}`;
|
|
83
|
+
if (!changes.addedTokens.includes(cssVarName)) {
|
|
84
|
+
changes.addedTokens.push(cssVarName);
|
|
85
|
+
changes.hasChanges = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const key of cachedDarkKeys) {
|
|
90
|
+
if (!(key in freshDarkColors)) {
|
|
91
|
+
const cssVarName = `${colorPrefix}${key}`;
|
|
92
|
+
if (!changes.removedTokens.includes(cssVarName)) {
|
|
93
|
+
changes.removedTokens.push(cssVarName);
|
|
94
|
+
changes.hasChanges = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const key of cachedDarkKeys) {
|
|
99
|
+
if (key in freshDarkColors) {
|
|
100
|
+
const oldValue = cachedDarkColors[key];
|
|
101
|
+
const newValue = freshDarkColors[key];
|
|
102
|
+
if (oldValue !== newValue) {
|
|
103
|
+
const cssVarName = `${colorPrefix}${key}`;
|
|
104
|
+
const existingIndex = changes.modifiedTokens.findIndex((m) => m.token === cssVarName);
|
|
105
|
+
if (existingIndex >= 0) {
|
|
106
|
+
changes.modifiedTokens[existingIndex].oldValue += ` | dark: ${oldValue}`;
|
|
107
|
+
changes.modifiedTokens[existingIndex].newValue += ` | dark: ${newValue}`;
|
|
108
|
+
} else {
|
|
109
|
+
changes.modifiedTokens.push({
|
|
110
|
+
token: `${cssVarName} (dark mode)`,
|
|
111
|
+
oldValue,
|
|
112
|
+
newValue
|
|
113
|
+
});
|
|
114
|
+
changes.hasChanges = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const cachedGovernance = cached.governance || { rules: [], categories: {} };
|
|
120
|
+
const freshGovernance = fresh.governance || { rules: [], categories: {} };
|
|
121
|
+
const cachedRules = cachedGovernance.rules || [];
|
|
122
|
+
const freshRules = freshGovernance.rules || [];
|
|
123
|
+
const cachedCategories = cachedGovernance.categories || {};
|
|
124
|
+
const freshCategories = freshGovernance.categories || {};
|
|
125
|
+
const cachedRulesStr = JSON.stringify(cachedRules.sort());
|
|
126
|
+
const freshRulesStr = JSON.stringify(freshRules.sort());
|
|
127
|
+
const cachedCategoriesStr = JSON.stringify(cachedCategories, Object.keys(cachedCategories).sort());
|
|
128
|
+
const freshCategoriesStr = JSON.stringify(freshCategories, Object.keys(freshCategories).sort());
|
|
129
|
+
if (cachedRulesStr !== freshRulesStr || cachedCategoriesStr !== freshCategoriesStr) {
|
|
130
|
+
changes.governanceChanged = true;
|
|
131
|
+
changes.hasChanges = true;
|
|
132
|
+
}
|
|
133
|
+
const summaryLines = [];
|
|
134
|
+
if (!changes.hasChanges) {
|
|
135
|
+
summaryLines.push("\u2713 No changes detected - design system is up to date");
|
|
136
|
+
} else {
|
|
137
|
+
summaryLines.push("\u{1F4E6} Design System Changes Detected:");
|
|
138
|
+
summaryLines.push("");
|
|
139
|
+
if (changes.versionChanged) {
|
|
140
|
+
summaryLines.push(` Version: ${cached.meta.version} \u2192 ${fresh.meta.version}`);
|
|
141
|
+
}
|
|
142
|
+
if (changes.timestampChanged) {
|
|
143
|
+
const cachedDate = new Date(cached.meta.exportedAt).toLocaleString();
|
|
144
|
+
const freshDate = new Date(fresh.meta.exportedAt).toLocaleString();
|
|
145
|
+
summaryLines.push(` Published: ${cachedDate} \u2192 ${freshDate}`);
|
|
146
|
+
}
|
|
147
|
+
if (changes.addedTokens.length > 0) {
|
|
148
|
+
summaryLines.push(` \u2795 Added: ${changes.addedTokens.length} token(s)`);
|
|
149
|
+
if (changes.addedTokens.length <= 10) {
|
|
150
|
+
changes.addedTokens.forEach((token) => {
|
|
151
|
+
summaryLines.push(` - ${token}`);
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
changes.addedTokens.slice(0, 10).forEach((token) => {
|
|
155
|
+
summaryLines.push(` - ${token}`);
|
|
156
|
+
});
|
|
157
|
+
summaryLines.push(` ... and ${changes.addedTokens.length - 10} more`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (changes.removedTokens.length > 0) {
|
|
161
|
+
summaryLines.push(` \u2796 Removed: ${changes.removedTokens.length} token(s)`);
|
|
162
|
+
if (changes.removedTokens.length <= 10) {
|
|
163
|
+
changes.removedTokens.forEach((token) => {
|
|
164
|
+
summaryLines.push(` - ${token}`);
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
changes.removedTokens.slice(0, 10).forEach((token) => {
|
|
168
|
+
summaryLines.push(` - ${token}`);
|
|
169
|
+
});
|
|
170
|
+
summaryLines.push(` ... and ${changes.removedTokens.length - 10} more`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (changes.modifiedTokens.length > 0) {
|
|
174
|
+
summaryLines.push(` \u{1F504} Modified: ${changes.modifiedTokens.length} token(s)`);
|
|
175
|
+
if (changes.modifiedTokens.length <= 10) {
|
|
176
|
+
changes.modifiedTokens.forEach(({ token, oldValue, newValue }) => {
|
|
177
|
+
summaryLines.push(` ${token}:`);
|
|
178
|
+
summaryLines.push(` - ${oldValue}`);
|
|
179
|
+
summaryLines.push(` + ${newValue}`);
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
changes.modifiedTokens.slice(0, 5).forEach(({ token, oldValue, newValue }) => {
|
|
183
|
+
summaryLines.push(` ${token}:`);
|
|
184
|
+
summaryLines.push(` - ${oldValue}`);
|
|
185
|
+
summaryLines.push(` + ${newValue}`);
|
|
186
|
+
});
|
|
187
|
+
summaryLines.push(` ... and ${changes.modifiedTokens.length - 5} more`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (changes.governanceChanged) {
|
|
191
|
+
summaryLines.push(` \u{1F4DD} AI guide updated`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
changes.summary = summaryLines.join("\n");
|
|
195
|
+
return changes;
|
|
196
|
+
}
|
|
197
|
+
function detectGovernanceChangesByFoundation(cached, fresh) {
|
|
198
|
+
const changes = [];
|
|
199
|
+
const cachedGov = cached.governance || { rules: [], categories: {} };
|
|
200
|
+
const freshGov = fresh.governance || { rules: [], categories: {} };
|
|
201
|
+
const cachedRulesStr = JSON.stringify((cachedGov.rules || []).sort());
|
|
202
|
+
const freshRulesStr = JSON.stringify((freshGov.rules || []).sort());
|
|
203
|
+
if (cachedRulesStr !== freshRulesStr) {
|
|
204
|
+
changes.push("general");
|
|
205
|
+
}
|
|
206
|
+
const cachedCategories = cachedGov.categories || {};
|
|
207
|
+
const freshCategories = freshGov.categories || {};
|
|
208
|
+
const allFoundationKeys = /* @__PURE__ */ new Set([
|
|
209
|
+
...Object.keys(cachedCategories),
|
|
210
|
+
...Object.keys(freshCategories)
|
|
211
|
+
]);
|
|
212
|
+
for (const foundation of allFoundationKeys) {
|
|
213
|
+
const cachedRules = cachedCategories[foundation] || [];
|
|
214
|
+
const freshRules = freshCategories[foundation] || [];
|
|
215
|
+
const cachedRulesStr2 = JSON.stringify(cachedRules.sort());
|
|
216
|
+
const freshRulesStr2 = JSON.stringify(freshRules.sort());
|
|
217
|
+
if (cachedRulesStr2 !== freshRulesStr2) {
|
|
218
|
+
changes.push(foundation);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return changes;
|
|
222
|
+
}
|
|
223
|
+
async function fetchDesignSystem(options) {
|
|
224
|
+
const { dsId: dsId2, apiKey: apiKey2, apiBase: apiBase2 = "https://atomixstudio.eu", etag, forceRefresh = false } = options;
|
|
225
|
+
if (!dsId2) {
|
|
226
|
+
throw new Error("Missing dsId. Usage: fetchDesignSystem({ dsId: '...' })");
|
|
227
|
+
}
|
|
228
|
+
const url = `${apiBase2}/api/ds/${dsId2}/tokens?format=export`;
|
|
229
|
+
const headers = {
|
|
230
|
+
"Content-Type": "application/json"
|
|
231
|
+
};
|
|
232
|
+
if (apiKey2) {
|
|
233
|
+
headers["x-api-key"] = apiKey2;
|
|
234
|
+
}
|
|
235
|
+
if (etag) {
|
|
236
|
+
headers["if-none-match"] = etag;
|
|
237
|
+
}
|
|
238
|
+
const response = await fetch(url, { headers });
|
|
239
|
+
if (response.status === 304) {
|
|
240
|
+
return {
|
|
241
|
+
data: null,
|
|
242
|
+
etag,
|
|
243
|
+
status: 304
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
const text = await response.text();
|
|
248
|
+
throw new Error(`Failed to fetch design system: ${response.status} ${text}`);
|
|
249
|
+
}
|
|
250
|
+
const data = await response.json();
|
|
251
|
+
const responseETag = response.headers.get("etag");
|
|
252
|
+
const finalETag = responseETag || generateETag(data.meta);
|
|
253
|
+
const designSystemData = {
|
|
254
|
+
tokens: data.tokens,
|
|
255
|
+
cssVariables: data.cssVariables,
|
|
256
|
+
governance: data.governance,
|
|
257
|
+
meta: data.meta
|
|
258
|
+
};
|
|
259
|
+
return {
|
|
260
|
+
data: designSystemData,
|
|
261
|
+
etag: finalETag,
|
|
262
|
+
status: 200
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function diffTokens(oldContent, newCssVars, format, newDarkVars) {
|
|
266
|
+
const added = [];
|
|
267
|
+
const modified = [];
|
|
268
|
+
const removed = [];
|
|
269
|
+
const addedDark = [];
|
|
270
|
+
const modifiedDark = [];
|
|
271
|
+
const removedDark = [];
|
|
272
|
+
if (format === "css" || format === "scss" || format === "less") {
|
|
273
|
+
const rootMatch = oldContent.match(/:root\s*\{([^}]*)\}/);
|
|
274
|
+
const darkMatch = oldContent.match(/\.dark[^{]*\{([^}]*)\}/);
|
|
275
|
+
const oldLightVars = {};
|
|
276
|
+
if (rootMatch) {
|
|
277
|
+
const rootContent = rootMatch[1];
|
|
278
|
+
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
279
|
+
let match;
|
|
280
|
+
while ((match = varRegex.exec(rootContent)) !== null) {
|
|
281
|
+
oldLightVars[match[1]] = match[2].trim();
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
285
|
+
let match;
|
|
286
|
+
while ((match = varRegex.exec(oldContent)) !== null) {
|
|
287
|
+
if (!(match[1] in oldLightVars)) {
|
|
288
|
+
oldLightVars[match[1]] = match[2].trim();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const oldDarkVars = {};
|
|
293
|
+
if (darkMatch) {
|
|
294
|
+
const darkContent = darkMatch[1];
|
|
295
|
+
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
296
|
+
let match;
|
|
297
|
+
while ((match = varRegex.exec(darkContent)) !== null) {
|
|
298
|
+
oldDarkVars[match[1]] = match[2].trim();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (const [key, value] of Object.entries(newCssVars)) {
|
|
302
|
+
if (!(key in oldLightVars)) {
|
|
303
|
+
added.push(key);
|
|
304
|
+
} else if (oldLightVars[key] !== value) {
|
|
305
|
+
modified.push({ key, old: oldLightVars[key], new: value });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
for (const key of Object.keys(oldLightVars)) {
|
|
309
|
+
if (!(key in newCssVars)) {
|
|
310
|
+
removed.push(key);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (newDarkVars && Object.keys(newDarkVars).length > 0) {
|
|
314
|
+
const firstKey = Object.keys(newCssVars)[0] || "--atmx-";
|
|
315
|
+
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
316
|
+
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
317
|
+
for (const [key, value] of Object.entries(newDarkVars)) {
|
|
318
|
+
const cssVarName = `${prefix}color-${key}`;
|
|
319
|
+
if (!(cssVarName in oldDarkVars)) {
|
|
320
|
+
addedDark.push(cssVarName);
|
|
321
|
+
} else if (oldDarkVars[cssVarName] !== value) {
|
|
322
|
+
modifiedDark.push({ key: cssVarName, old: oldDarkVars[cssVarName], new: value });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (const key of Object.keys(oldDarkVars)) {
|
|
326
|
+
const shortKey = key.replace(new RegExp(`^${prefix}color-`), "");
|
|
327
|
+
if (!(shortKey in newDarkVars)) {
|
|
328
|
+
removedDark.push(key);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return { added, modified, removed, addedDark, modifiedDark, removedDark };
|
|
334
|
+
}
|
|
335
|
+
async function syncRulesFiles(options) {
|
|
336
|
+
const { dsId: dsId2, apiKey: apiKey2, apiBase: apiBase2 = "https://atomixstudio.eu", rulesDir = process.cwd() } = options;
|
|
337
|
+
const rulesDirResolved = path.resolve(process.cwd(), rulesDir);
|
|
338
|
+
const toolsToSync = [
|
|
339
|
+
{ tool: "cursor", filename: ".cursorrules" },
|
|
340
|
+
{ tool: "windsurf", filename: ".windsurfrules" },
|
|
341
|
+
{ tool: "cline", filename: ".clinerules" },
|
|
342
|
+
{ tool: "continue", filename: ".continuerules" },
|
|
343
|
+
{ tool: "copilot", filename: "copilot-instructions.md", dir: ".github" },
|
|
344
|
+
{ tool: "generic", filename: "AI_GUIDELINES.md" }
|
|
345
|
+
];
|
|
346
|
+
const existingTools = toolsToSync.filter((t) => {
|
|
347
|
+
const filePath = t.dir ? path.join(rulesDirResolved, t.dir, t.filename) : path.join(rulesDirResolved, t.filename);
|
|
348
|
+
return fs.existsSync(filePath);
|
|
349
|
+
});
|
|
350
|
+
const toolsToWrite = existingTools.length > 0 ? existingTools : [{ tool: "cursor", filename: ".cursorrules" }];
|
|
351
|
+
const results = [];
|
|
352
|
+
for (const { tool, filename, dir } of toolsToWrite) {
|
|
353
|
+
try {
|
|
354
|
+
const rulesUrl = `${apiBase2}/api/ds/${dsId2}/rules?format=${tool}`;
|
|
355
|
+
const headers = { "Content-Type": "application/json" };
|
|
356
|
+
if (apiKey2) headers["x-api-key"] = apiKey2;
|
|
357
|
+
const response = await fetch(rulesUrl, { headers });
|
|
358
|
+
if (!response.ok) {
|
|
359
|
+
results.push({
|
|
360
|
+
tool,
|
|
361
|
+
filename,
|
|
362
|
+
path: dir ? `${dir}/${filename}` : filename,
|
|
363
|
+
success: false,
|
|
364
|
+
error: `Failed to fetch ${tool} rules: ${response.status}`
|
|
365
|
+
});
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const rulesData = await response.json();
|
|
369
|
+
if (!rulesData.content) {
|
|
370
|
+
results.push({
|
|
371
|
+
tool,
|
|
372
|
+
filename,
|
|
373
|
+
path: dir ? `${dir}/${filename}` : filename,
|
|
374
|
+
success: false,
|
|
375
|
+
error: `No content for ${tool} rules`
|
|
376
|
+
});
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const targetDir = dir ? path.join(rulesDirResolved, dir) : rulesDirResolved;
|
|
380
|
+
if (!fs.existsSync(targetDir)) {
|
|
381
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
382
|
+
}
|
|
383
|
+
const filePath = path.join(targetDir, filename);
|
|
384
|
+
fs.writeFileSync(filePath, rulesData.content);
|
|
385
|
+
results.push({
|
|
386
|
+
tool,
|
|
387
|
+
filename,
|
|
388
|
+
path: dir ? `${dir}/${filename}` : filename,
|
|
389
|
+
success: true
|
|
390
|
+
});
|
|
391
|
+
} catch (error) {
|
|
392
|
+
results.push({
|
|
393
|
+
tool,
|
|
394
|
+
filename,
|
|
395
|
+
path: dir ? `${dir}/${filename}` : filename,
|
|
396
|
+
success: false,
|
|
397
|
+
error: error instanceof Error ? error.message : String(error)
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return results;
|
|
402
|
+
}
|
|
403
|
+
function generateCSSOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
404
|
+
const lines = [
|
|
405
|
+
"/* Atomix Design System Tokens",
|
|
406
|
+
" * Auto-generated - do not edit manually",
|
|
407
|
+
` * Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
408
|
+
" *",
|
|
409
|
+
" * WARNING: This file is completely rewritten on each sync.",
|
|
410
|
+
" * Only CSS custom properties (variables) are preserved.",
|
|
411
|
+
" * Custom CSS rules (selectors, classes, etc.) will be lost.",
|
|
412
|
+
" * Keep custom CSS in a separate file.",
|
|
413
|
+
" */",
|
|
414
|
+
"",
|
|
415
|
+
"/* Light mode (default) */",
|
|
416
|
+
":root {"
|
|
417
|
+
];
|
|
418
|
+
const allVars = /* @__PURE__ */ new Map();
|
|
419
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
420
|
+
allVars.set(key, { value, deprecated: false });
|
|
421
|
+
}
|
|
422
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
423
|
+
allVars.set(key, { value, deprecated: true });
|
|
424
|
+
}
|
|
425
|
+
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
426
|
+
for (const key of sortedKeys) {
|
|
427
|
+
const { value, deprecated } = allVars.get(key);
|
|
428
|
+
if (deprecated) {
|
|
429
|
+
lines.push(` /* DEPRECATED */ ${key}: ${value};`);
|
|
430
|
+
} else {
|
|
431
|
+
lines.push(` ${key}: ${value};`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
lines.push("}");
|
|
435
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
436
|
+
lines.push("");
|
|
437
|
+
lines.push("/* Dark mode */");
|
|
438
|
+
lines.push(".dark,");
|
|
439
|
+
lines.push('[data-theme="dark"] {');
|
|
440
|
+
const firstKey = sortedKeys[0] || "--atmx-";
|
|
441
|
+
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
442
|
+
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
443
|
+
const sortedDarkKeys = Object.keys(darkModeColors).sort();
|
|
444
|
+
for (const key of sortedDarkKeys) {
|
|
445
|
+
lines.push(` ${prefix}color-${key}: ${darkModeColors[key]};`);
|
|
446
|
+
}
|
|
447
|
+
lines.push("}");
|
|
448
|
+
}
|
|
449
|
+
lines.push("");
|
|
450
|
+
return lines.join("\n");
|
|
451
|
+
}
|
|
452
|
+
function generateSCSSOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
453
|
+
const lines = [
|
|
454
|
+
"// Atomix Design System Tokens",
|
|
455
|
+
"// Auto-generated - do not edit manually",
|
|
456
|
+
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
457
|
+
"",
|
|
458
|
+
"// CSS Custom Properties (for use in CSS/HTML)",
|
|
459
|
+
"// Light mode (default)",
|
|
460
|
+
":root {"
|
|
461
|
+
];
|
|
462
|
+
const allVars = /* @__PURE__ */ new Map();
|
|
463
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
464
|
+
allVars.set(key, { value, deprecated: false });
|
|
465
|
+
}
|
|
466
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
467
|
+
allVars.set(key, { value, deprecated: true });
|
|
468
|
+
}
|
|
469
|
+
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
470
|
+
for (const key of sortedKeys) {
|
|
471
|
+
const { value, deprecated } = allVars.get(key);
|
|
472
|
+
if (deprecated) {
|
|
473
|
+
lines.push(` // DEPRECATED ${key}: ${value};`);
|
|
474
|
+
} else {
|
|
475
|
+
lines.push(` ${key}: ${value};`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
lines.push("}");
|
|
479
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
480
|
+
lines.push("");
|
|
481
|
+
lines.push("// Dark mode");
|
|
482
|
+
lines.push(".dark,");
|
|
483
|
+
lines.push('[data-theme="dark"] {');
|
|
484
|
+
const firstKey = sortedKeys[0] || "--atmx-";
|
|
485
|
+
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
486
|
+
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
487
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
488
|
+
lines.push(` ${prefix}color-${key}: ${value};`);
|
|
489
|
+
}
|
|
490
|
+
lines.push("}");
|
|
491
|
+
}
|
|
492
|
+
lines.push("");
|
|
493
|
+
lines.push("// SCSS Variables (light mode values)");
|
|
494
|
+
for (const key of sortedKeys) {
|
|
495
|
+
const { value, deprecated } = allVars.get(key);
|
|
496
|
+
const scssVar = "$" + key.replace(/^--/, "");
|
|
497
|
+
if (deprecated) {
|
|
498
|
+
lines.push(`// DEPRECATED ${scssVar}: ${value};`);
|
|
499
|
+
} else {
|
|
500
|
+
lines.push(`${scssVar}: ${value};`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
504
|
+
lines.push("");
|
|
505
|
+
lines.push("// SCSS Variables (dark mode values)");
|
|
506
|
+
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
507
|
+
const prefixMatch = firstKey.match(/^--([a-z]+)-/);
|
|
508
|
+
const prefix = prefixMatch ? prefixMatch[1] : "atmx";
|
|
509
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
510
|
+
const scssVar = `$${prefix}-color-${key}-dark`;
|
|
511
|
+
lines.push(`${scssVar}: ${value};`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
lines.push("");
|
|
515
|
+
return lines.join("\n");
|
|
516
|
+
}
|
|
517
|
+
function generateLessOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
518
|
+
const lines = [
|
|
519
|
+
"// Atomix Design System Tokens",
|
|
520
|
+
"// Auto-generated - do not edit manually",
|
|
521
|
+
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
522
|
+
"",
|
|
523
|
+
"// CSS Custom Properties (for use in CSS/HTML)",
|
|
524
|
+
"// Light mode (default)",
|
|
525
|
+
":root {"
|
|
526
|
+
];
|
|
527
|
+
const allVars = /* @__PURE__ */ new Map();
|
|
528
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
529
|
+
allVars.set(key, { value, deprecated: false });
|
|
530
|
+
}
|
|
531
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
532
|
+
allVars.set(key, { value, deprecated: true });
|
|
533
|
+
}
|
|
534
|
+
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
535
|
+
for (const key of sortedKeys) {
|
|
536
|
+
const { value, deprecated } = allVars.get(key);
|
|
537
|
+
if (deprecated) {
|
|
538
|
+
lines.push(` // DEPRECATED ${key}: ${value};`);
|
|
539
|
+
} else {
|
|
540
|
+
lines.push(` ${key}: ${value};`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
lines.push("}");
|
|
544
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
545
|
+
lines.push("");
|
|
546
|
+
lines.push("// Dark mode");
|
|
547
|
+
lines.push(".dark,");
|
|
548
|
+
lines.push('[data-theme="dark"] {');
|
|
549
|
+
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
550
|
+
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
551
|
+
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
552
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
553
|
+
lines.push(` ${prefix}color-${key}: ${value};`);
|
|
554
|
+
}
|
|
555
|
+
lines.push("}");
|
|
556
|
+
}
|
|
557
|
+
lines.push("");
|
|
558
|
+
lines.push("// Less Variables (light mode values)");
|
|
559
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
560
|
+
const lessVar = "@" + key.replace(/^--/, "");
|
|
561
|
+
lines.push(`${lessVar}: ${value};`);
|
|
562
|
+
}
|
|
563
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
564
|
+
lines.push("");
|
|
565
|
+
lines.push("// Less Variables (dark mode values)");
|
|
566
|
+
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
567
|
+
const prefixMatch = firstKey.match(/^--([a-z]+)-/);
|
|
568
|
+
const prefix = prefixMatch ? prefixMatch[1] : "atmx";
|
|
569
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
570
|
+
const lessVar = `@${prefix}-color-${key}-dark`;
|
|
571
|
+
lines.push(`${lessVar}: ${value};`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
lines.push("");
|
|
575
|
+
return lines.join("\n");
|
|
576
|
+
}
|
|
577
|
+
function generateJSONOutput(tokens) {
|
|
578
|
+
return JSON.stringify(tokens, null, 2);
|
|
579
|
+
}
|
|
580
|
+
function generateTSOutput(tokens) {
|
|
581
|
+
return `// Atomix Design System Tokens
|
|
582
|
+
// Auto-generated - do not edit manually
|
|
583
|
+
// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
584
|
+
|
|
585
|
+
export const tokens = ${JSON.stringify(tokens, null, 2)} as const;
|
|
586
|
+
|
|
587
|
+
export type Tokens = typeof tokens;
|
|
588
|
+
`;
|
|
589
|
+
}
|
|
590
|
+
function generateJSOutput(tokens) {
|
|
591
|
+
return `// Atomix Design System Tokens
|
|
592
|
+
// Auto-generated - do not edit manually
|
|
593
|
+
// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
594
|
+
|
|
595
|
+
export const tokens = ${JSON.stringify(tokens, null, 2)};
|
|
596
|
+
`;
|
|
597
|
+
}
|
|
598
|
+
function toSwiftName(cssVar) {
|
|
599
|
+
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
600
|
+
}
|
|
601
|
+
function isColorValue(value) {
|
|
602
|
+
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
603
|
+
}
|
|
604
|
+
function hexToSwiftColor(hex) {
|
|
605
|
+
const clean = hex.replace("#", "");
|
|
606
|
+
if (clean.length === 3) {
|
|
607
|
+
const r = clean[0], g = clean[1], b = clean[2];
|
|
608
|
+
return `Color(hex: 0x${r}${r}${g}${g}${b}${b})`;
|
|
609
|
+
}
|
|
610
|
+
if (clean.length === 6) {
|
|
611
|
+
return `Color(hex: 0x${clean})`;
|
|
612
|
+
}
|
|
613
|
+
if (clean.length === 8) {
|
|
614
|
+
const rgb = clean.substring(0, 6);
|
|
615
|
+
const alpha = parseInt(clean.substring(6, 8), 16) / 255;
|
|
616
|
+
return `Color(hex: 0x${rgb}).opacity(${alpha.toFixed(2)})`;
|
|
617
|
+
}
|
|
618
|
+
return `Color.clear // Could not parse: ${hex}`;
|
|
619
|
+
}
|
|
620
|
+
function generateSwiftOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
621
|
+
const lines = [
|
|
622
|
+
"// Atomix Design System Tokens",
|
|
623
|
+
"// Auto-generated - do not edit manually",
|
|
624
|
+
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
625
|
+
"",
|
|
626
|
+
"import SwiftUI",
|
|
627
|
+
"",
|
|
628
|
+
"// MARK: - Color Extension for Hex",
|
|
629
|
+
"extension Color {",
|
|
630
|
+
" init(hex: UInt, alpha: Double = 1.0) {",
|
|
631
|
+
" self.init(",
|
|
632
|
+
" .sRGB,",
|
|
633
|
+
" red: Double((hex >> 16) & 0xFF) / 255.0,",
|
|
634
|
+
" green: Double((hex >> 8) & 0xFF) / 255.0,",
|
|
635
|
+
" blue: Double(hex & 0xFF) / 255.0,",
|
|
636
|
+
" opacity: alpha",
|
|
637
|
+
" )",
|
|
638
|
+
" }",
|
|
639
|
+
"}",
|
|
640
|
+
"",
|
|
641
|
+
"// MARK: - Design Tokens",
|
|
642
|
+
"enum DesignTokens {",
|
|
643
|
+
"",
|
|
644
|
+
" // MARK: Colors (Light Mode)",
|
|
645
|
+
" enum Colors {"
|
|
646
|
+
];
|
|
647
|
+
const colors = [];
|
|
648
|
+
const spacing = [];
|
|
649
|
+
const typography = [];
|
|
650
|
+
const other = [];
|
|
651
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
652
|
+
const isDeprecated = false;
|
|
653
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
654
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
655
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
656
|
+
else other.push([key, value, isDeprecated]);
|
|
657
|
+
}
|
|
658
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
659
|
+
const isDeprecated = true;
|
|
660
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
661
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
662
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
663
|
+
else other.push([key, value, isDeprecated]);
|
|
664
|
+
}
|
|
665
|
+
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
666
|
+
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
667
|
+
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
668
|
+
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
669
|
+
for (const [key, value, isDeprecated] of colors) {
|
|
670
|
+
const name = toSwiftName(key);
|
|
671
|
+
if (isColorValue(value)) {
|
|
672
|
+
if (isDeprecated) {
|
|
673
|
+
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
674
|
+
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
675
|
+
} else {
|
|
676
|
+
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
lines.push(" }");
|
|
681
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
682
|
+
lines.push("");
|
|
683
|
+
lines.push(" // MARK: Colors (Dark Mode)");
|
|
684
|
+
lines.push(" enum ColorsDark {");
|
|
685
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
686
|
+
const name = `color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}`;
|
|
687
|
+
if (isColorValue(value)) {
|
|
688
|
+
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
lines.push(" }");
|
|
692
|
+
}
|
|
693
|
+
lines.push("");
|
|
694
|
+
lines.push(" // MARK: Spacing");
|
|
695
|
+
lines.push(" enum Spacing {");
|
|
696
|
+
for (const [key, value, isDeprecated] of spacing) {
|
|
697
|
+
const name = toSwiftName(key);
|
|
698
|
+
const numValue = parseFloat(value);
|
|
699
|
+
if (!isNaN(numValue)) {
|
|
700
|
+
if (isDeprecated) {
|
|
701
|
+
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
702
|
+
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
703
|
+
} else {
|
|
704
|
+
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
lines.push(" }");
|
|
709
|
+
lines.push("");
|
|
710
|
+
lines.push(" // MARK: Typography");
|
|
711
|
+
lines.push(" enum Typography {");
|
|
712
|
+
for (const [key, value, isDeprecated] of typography) {
|
|
713
|
+
const name = toSwiftName(key);
|
|
714
|
+
if (isDeprecated) {
|
|
715
|
+
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
716
|
+
}
|
|
717
|
+
if (key.includes("size")) {
|
|
718
|
+
const numValue = parseFloat(value);
|
|
719
|
+
if (!isNaN(numValue)) {
|
|
720
|
+
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
721
|
+
}
|
|
722
|
+
} else if (key.includes("weight")) {
|
|
723
|
+
lines.push(` static let ${name} = "${value}"`);
|
|
724
|
+
} else if (key.includes("family")) {
|
|
725
|
+
lines.push(` static let ${name} = "${value}"`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
lines.push(" }");
|
|
729
|
+
lines.push("");
|
|
730
|
+
lines.push(" // MARK: Other");
|
|
731
|
+
lines.push(" enum Other {");
|
|
732
|
+
for (const [key, value, isDeprecated] of other) {
|
|
733
|
+
const name = toSwiftName(key);
|
|
734
|
+
if (isDeprecated) {
|
|
735
|
+
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
736
|
+
}
|
|
737
|
+
if (isColorValue(value)) {
|
|
738
|
+
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
739
|
+
} else {
|
|
740
|
+
const numValue = parseFloat(value);
|
|
741
|
+
if (!isNaN(numValue)) {
|
|
742
|
+
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
743
|
+
} else {
|
|
744
|
+
lines.push(` static let ${name} = "${value}"`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
lines.push(" }");
|
|
749
|
+
lines.push("}");
|
|
750
|
+
lines.push("");
|
|
751
|
+
return lines.join("\n");
|
|
752
|
+
}
|
|
753
|
+
function toSwiftName2(cssVar) {
|
|
754
|
+
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
755
|
+
}
|
|
756
|
+
function toKotlinName(cssVar) {
|
|
757
|
+
const camel = toSwiftName2(cssVar);
|
|
758
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
759
|
+
}
|
|
760
|
+
function isColorValue2(value) {
|
|
761
|
+
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
762
|
+
}
|
|
763
|
+
function hexToKotlinColor(hex) {
|
|
764
|
+
const clean = hex.replace("#", "").toUpperCase();
|
|
765
|
+
if (clean.length === 3) {
|
|
766
|
+
const r = clean[0], g = clean[1], b = clean[2];
|
|
767
|
+
return `Color(0xFF${r}${r}${g}${g}${b}${b})`;
|
|
768
|
+
}
|
|
769
|
+
if (clean.length === 6) {
|
|
770
|
+
return `Color(0xFF${clean})`;
|
|
771
|
+
}
|
|
772
|
+
if (clean.length === 8) {
|
|
773
|
+
const rgb = clean.substring(0, 6);
|
|
774
|
+
const alpha = clean.substring(6, 8);
|
|
775
|
+
return `Color(0x${alpha}${rgb})`;
|
|
776
|
+
}
|
|
777
|
+
return `Color.Transparent // Could not parse: ${hex}`;
|
|
778
|
+
}
|
|
779
|
+
function generateKotlinOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
780
|
+
const lines = [
|
|
781
|
+
"// Atomix Design System Tokens",
|
|
782
|
+
"// Auto-generated - do not edit manually",
|
|
783
|
+
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
784
|
+
"",
|
|
785
|
+
"package com.atomix.design",
|
|
786
|
+
"",
|
|
787
|
+
"import androidx.compose.ui.graphics.Color",
|
|
788
|
+
"import androidx.compose.ui.unit.dp",
|
|
789
|
+
"import androidx.compose.ui.unit.sp",
|
|
790
|
+
"",
|
|
791
|
+
"object DesignTokens {",
|
|
792
|
+
"",
|
|
793
|
+
" // Light mode colors",
|
|
794
|
+
" object Colors {"
|
|
795
|
+
];
|
|
796
|
+
const colors = [];
|
|
797
|
+
const spacing = [];
|
|
798
|
+
const typography = [];
|
|
799
|
+
const other = [];
|
|
800
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
801
|
+
const isDeprecated = false;
|
|
802
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
803
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
804
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
805
|
+
else other.push([key, value, isDeprecated]);
|
|
806
|
+
}
|
|
807
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
808
|
+
const isDeprecated = true;
|
|
809
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
810
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
811
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
812
|
+
else other.push([key, value, isDeprecated]);
|
|
813
|
+
}
|
|
814
|
+
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
815
|
+
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
816
|
+
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
817
|
+
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
818
|
+
for (const [key, value, isDeprecated] of colors) {
|
|
819
|
+
const name = toKotlinName(key);
|
|
820
|
+
if (isColorValue2(value)) {
|
|
821
|
+
if (isDeprecated) {
|
|
822
|
+
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
823
|
+
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
824
|
+
} else {
|
|
825
|
+
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
lines.push(" }");
|
|
830
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
831
|
+
lines.push("");
|
|
832
|
+
lines.push(" // Dark mode colors");
|
|
833
|
+
lines.push(" object ColorsDark {");
|
|
834
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
835
|
+
const name = `Color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}`;
|
|
836
|
+
if (isColorValue2(value)) {
|
|
837
|
+
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
lines.push(" }");
|
|
841
|
+
}
|
|
842
|
+
lines.push("");
|
|
843
|
+
lines.push(" object Spacing {");
|
|
844
|
+
for (const [key, value, isDeprecated] of spacing) {
|
|
845
|
+
const name = toKotlinName(key);
|
|
846
|
+
const numValue = parseFloat(value);
|
|
847
|
+
if (!isNaN(numValue)) {
|
|
848
|
+
if (isDeprecated) {
|
|
849
|
+
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
850
|
+
lines.push(` val ${name} = ${numValue}.dp`);
|
|
851
|
+
} else {
|
|
852
|
+
lines.push(` val ${name} = ${numValue}.dp`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
lines.push(" }");
|
|
857
|
+
lines.push("");
|
|
858
|
+
lines.push(" object Typography {");
|
|
859
|
+
for (const [key, value, isDeprecated] of typography) {
|
|
860
|
+
const name = toKotlinName(key);
|
|
861
|
+
if (isDeprecated) {
|
|
862
|
+
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
863
|
+
}
|
|
864
|
+
if (key.includes("size")) {
|
|
865
|
+
const numValue = parseFloat(value);
|
|
866
|
+
if (!isNaN(numValue)) {
|
|
867
|
+
lines.push(` val ${name} = ${numValue}.sp`);
|
|
868
|
+
}
|
|
869
|
+
} else {
|
|
870
|
+
lines.push(` const val ${name} = "${value}"`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
lines.push(" }");
|
|
874
|
+
lines.push("");
|
|
875
|
+
lines.push(" object Other {");
|
|
876
|
+
for (const [key, value, isDeprecated] of other) {
|
|
877
|
+
const name = toKotlinName(key);
|
|
878
|
+
if (isDeprecated) {
|
|
879
|
+
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
880
|
+
}
|
|
881
|
+
if (isColorValue2(value)) {
|
|
882
|
+
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
883
|
+
} else {
|
|
884
|
+
const numValue = parseFloat(value);
|
|
885
|
+
if (!isNaN(numValue)) {
|
|
886
|
+
lines.push(` val ${name} = ${numValue}.dp`);
|
|
887
|
+
} else {
|
|
888
|
+
lines.push(` const val ${name} = "${value}"`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
lines.push(" }");
|
|
893
|
+
lines.push("}");
|
|
894
|
+
lines.push("");
|
|
895
|
+
return lines.join("\n");
|
|
896
|
+
}
|
|
897
|
+
function toSwiftName3(cssVar) {
|
|
898
|
+
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
899
|
+
}
|
|
900
|
+
function toDartName(cssVar) {
|
|
901
|
+
return toSwiftName3(cssVar);
|
|
902
|
+
}
|
|
903
|
+
function isColorValue3(value) {
|
|
904
|
+
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
905
|
+
}
|
|
906
|
+
function hexToDartColor(hex) {
|
|
907
|
+
const clean = hex.replace("#", "").toUpperCase();
|
|
908
|
+
if (clean.length === 3) {
|
|
909
|
+
const r = clean[0], g = clean[1], b = clean[2];
|
|
910
|
+
return `Color(0xFF${r}${r}${g}${g}${b}${b})`;
|
|
911
|
+
}
|
|
912
|
+
if (clean.length === 6) {
|
|
913
|
+
return `Color(0xFF${clean})`;
|
|
914
|
+
}
|
|
915
|
+
if (clean.length === 8) {
|
|
916
|
+
const rgb = clean.substring(0, 6);
|
|
917
|
+
const alpha = clean.substring(6, 8);
|
|
918
|
+
return `Color(0x${alpha}${rgb})`;
|
|
919
|
+
}
|
|
920
|
+
return `Colors.transparent // Could not parse: ${hex}`;
|
|
921
|
+
}
|
|
922
|
+
function generateDartOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
923
|
+
const lines = [
|
|
924
|
+
"// Atomix Design System Tokens",
|
|
925
|
+
"// Auto-generated - do not edit manually",
|
|
926
|
+
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
927
|
+
"",
|
|
928
|
+
"import 'package:flutter/material.dart';",
|
|
929
|
+
"",
|
|
930
|
+
"class DesignTokens {",
|
|
931
|
+
" DesignTokens._();",
|
|
932
|
+
"",
|
|
933
|
+
" // Colors (Light Mode)"
|
|
934
|
+
];
|
|
935
|
+
const colors = [];
|
|
936
|
+
const spacing = [];
|
|
937
|
+
const typography = [];
|
|
938
|
+
const other = [];
|
|
939
|
+
for (const [key, value] of Object.entries(cssVariables)) {
|
|
940
|
+
const isDeprecated = false;
|
|
941
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
942
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
943
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
944
|
+
else other.push([key, value, isDeprecated]);
|
|
945
|
+
}
|
|
946
|
+
for (const [key, value] of deprecatedTokens.entries()) {
|
|
947
|
+
const isDeprecated = true;
|
|
948
|
+
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
949
|
+
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
950
|
+
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
951
|
+
else other.push([key, value, isDeprecated]);
|
|
952
|
+
}
|
|
953
|
+
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
954
|
+
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
955
|
+
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
956
|
+
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
957
|
+
for (const [key, value, isDeprecated] of colors) {
|
|
958
|
+
const name = toDartName(key);
|
|
959
|
+
if (isColorValue3(value)) {
|
|
960
|
+
if (isDeprecated) {
|
|
961
|
+
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
962
|
+
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
963
|
+
} else {
|
|
964
|
+
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
969
|
+
lines.push("");
|
|
970
|
+
lines.push(" // Colors (Dark Mode)");
|
|
971
|
+
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
972
|
+
const name = `color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}Dark`;
|
|
973
|
+
if (isColorValue3(value)) {
|
|
974
|
+
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
lines.push("");
|
|
979
|
+
lines.push(" // Spacing");
|
|
980
|
+
for (const [key, value, isDeprecated] of spacing) {
|
|
981
|
+
const name = toDartName(key);
|
|
982
|
+
const numValue = parseFloat(value);
|
|
983
|
+
if (!isNaN(numValue)) {
|
|
984
|
+
if (isDeprecated) {
|
|
985
|
+
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
986
|
+
lines.push(` static const double ${name} = ${numValue};`);
|
|
987
|
+
} else {
|
|
988
|
+
lines.push(` static const double ${name} = ${numValue};`);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
lines.push("");
|
|
993
|
+
lines.push(" // Typography");
|
|
994
|
+
for (const [key, value, isDeprecated] of typography) {
|
|
995
|
+
const name = toDartName(key);
|
|
996
|
+
if (isDeprecated) {
|
|
997
|
+
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
998
|
+
}
|
|
999
|
+
if (key.includes("size")) {
|
|
1000
|
+
const numValue = parseFloat(value);
|
|
1001
|
+
if (!isNaN(numValue)) {
|
|
1002
|
+
lines.push(` static const double ${name} = ${numValue};`);
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
lines.push(` static const String ${name} = '${value}';`);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
lines.push("");
|
|
1009
|
+
lines.push(" // Other");
|
|
1010
|
+
for (const [key, value, isDeprecated] of other) {
|
|
1011
|
+
const name = toDartName(key);
|
|
1012
|
+
if (isDeprecated) {
|
|
1013
|
+
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
1014
|
+
}
|
|
1015
|
+
if (isColorValue3(value)) {
|
|
1016
|
+
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
1017
|
+
} else {
|
|
1018
|
+
const numValue = parseFloat(value);
|
|
1019
|
+
if (!isNaN(numValue)) {
|
|
1020
|
+
lines.push(` static const double ${name} = ${numValue};`);
|
|
1021
|
+
} else {
|
|
1022
|
+
lines.push(` static const String ${name} = '${value}';`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
lines.push("}");
|
|
1027
|
+
lines.push("");
|
|
1028
|
+
return lines.join("\n");
|
|
1029
|
+
}
|
|
1030
|
+
function formatSyncResponse(options) {
|
|
1031
|
+
const {
|
|
1032
|
+
data,
|
|
1033
|
+
output,
|
|
1034
|
+
format,
|
|
1035
|
+
dsTokenCount,
|
|
1036
|
+
deprecatedCount,
|
|
1037
|
+
deprecatedTokens,
|
|
1038
|
+
diff,
|
|
1039
|
+
changes = [],
|
|
1040
|
+
fileExists,
|
|
1041
|
+
rulesResults = [],
|
|
1042
|
+
governanceChanges = [],
|
|
1043
|
+
changeSummary,
|
|
1044
|
+
hasRefactorRecommendation = false,
|
|
1045
|
+
deprecatedTokenCount = 0
|
|
1046
|
+
} = options;
|
|
1047
|
+
const lastUpdated = data.meta.exportedAt ? new Date(data.meta.exportedAt).toLocaleString() : "N/A";
|
|
1048
|
+
let response = `\u2713 Synced ${dsTokenCount} tokens to ${output}
|
|
1049
|
+
`;
|
|
1050
|
+
response += `Format: ${format}
|
|
1051
|
+
`;
|
|
1052
|
+
response += `Design System: ${data.meta.name} (v${data.meta.version})
|
|
1053
|
+
`;
|
|
1054
|
+
if (deprecatedCount > 0) {
|
|
1055
|
+
response += `Tokens: ${dsTokenCount} active, ${deprecatedCount} deprecated (run /refactor to migrate)
|
|
1056
|
+
`;
|
|
1057
|
+
} else {
|
|
1058
|
+
response += `Tokens: ${dsTokenCount}
|
|
1059
|
+
`;
|
|
1060
|
+
}
|
|
1061
|
+
response += `Version: ${data.meta.version}
|
|
1062
|
+
`;
|
|
1063
|
+
response += `Last updated: ${lastUpdated}
|
|
1064
|
+
`;
|
|
1065
|
+
if (deprecatedCount > 0) {
|
|
1066
|
+
response += `
|
|
1067
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1068
|
+
`;
|
|
1069
|
+
response += `\u{1F4CB} DEPRECATED TOKENS (${deprecatedCount})
|
|
1070
|
+
`;
|
|
1071
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1072
|
+
|
|
1073
|
+
`;
|
|
1074
|
+
response += `These tokens are no longer in the design system but are preserved in your file for backward compatibility.
|
|
1075
|
+
`;
|
|
1076
|
+
response += `Run \`/refactor\` to find and migrate usage in your codebase.
|
|
1077
|
+
|
|
1078
|
+
`;
|
|
1079
|
+
const deprecatedTokenList = Array.from(deprecatedTokens.keys()).sort();
|
|
1080
|
+
deprecatedTokenList.forEach((token, index) => {
|
|
1081
|
+
const value = deprecatedTokens.get(token);
|
|
1082
|
+
response += ` ${index + 1}. ${token}`;
|
|
1083
|
+
if (value) {
|
|
1084
|
+
response += ` = ${value}`;
|
|
1085
|
+
}
|
|
1086
|
+
response += `
|
|
1087
|
+
`;
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
const newTokenList = [];
|
|
1091
|
+
if (diff) {
|
|
1092
|
+
newTokenList.push(...diff.added, ...diff.addedDark);
|
|
1093
|
+
}
|
|
1094
|
+
if (changeSummary && changeSummary.includes("\u2795 Added:")) {
|
|
1095
|
+
const addedSection = changeSummary.match(/➕ Added: \d+ token\(s\)([\s\S]*?)(?=➖|🔄|📝|$)/);
|
|
1096
|
+
if (addedSection) {
|
|
1097
|
+
const addedLines = addedSection[0].match(/ - (--[^\n]+)/g) || [];
|
|
1098
|
+
addedLines.forEach((line) => {
|
|
1099
|
+
const token = line.replace(/ - /, "").trim();
|
|
1100
|
+
if (token && !newTokenList.includes(token)) {
|
|
1101
|
+
newTokenList.push(token);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (newTokenList.length > 0) {
|
|
1107
|
+
response += `
|
|
1108
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1109
|
+
`;
|
|
1110
|
+
response += `\u2728 NEW TOKENS (${newTokenList.length})
|
|
1111
|
+
`;
|
|
1112
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1113
|
+
|
|
1114
|
+
`;
|
|
1115
|
+
newTokenList.sort().forEach((token, index) => {
|
|
1116
|
+
response += ` ${index + 1}. ${token}
|
|
1117
|
+
`;
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
const modifiedFiles = [output];
|
|
1121
|
+
rulesResults.forEach((result) => {
|
|
1122
|
+
if (result.success) {
|
|
1123
|
+
const fullPath = path3.resolve(process.cwd(), result.path);
|
|
1124
|
+
if (!modifiedFiles.includes(fullPath)) {
|
|
1125
|
+
modifiedFiles.push(fullPath);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
response += `
|
|
1130
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1131
|
+
`;
|
|
1132
|
+
response += `\u{1F4C1} MODIFIED FILES (${modifiedFiles.length})
|
|
1133
|
+
`;
|
|
1134
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1135
|
+
|
|
1136
|
+
`;
|
|
1137
|
+
modifiedFiles.forEach((file, index) => {
|
|
1138
|
+
const relativePath = path3.relative(process.cwd(), file);
|
|
1139
|
+
response += ` ${index + 1}. ${relativePath}
|
|
1140
|
+
`;
|
|
1141
|
+
});
|
|
1142
|
+
if (governanceChanges.length > 0) {
|
|
1143
|
+
response += `
|
|
1144
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1145
|
+
`;
|
|
1146
|
+
response += `\u{1F4DD} AI RULES CHANGES
|
|
1147
|
+
`;
|
|
1148
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1149
|
+
|
|
1150
|
+
`;
|
|
1151
|
+
response += `The following AI guidance sections have been updated:
|
|
1152
|
+
|
|
1153
|
+
`;
|
|
1154
|
+
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
1155
|
+
governanceChanges.forEach((foundation, index) => {
|
|
1156
|
+
const displayName = foundation === "general" ? "General Rules" : capitalize(foundation);
|
|
1157
|
+
response += ` ${index + 1}. ${displayName}
|
|
1158
|
+
`;
|
|
1159
|
+
});
|
|
1160
|
+
response += `
|
|
1161
|
+
These changes are reflected in your AI tool rules files (e.g., .cursorrules).
|
|
1162
|
+
`;
|
|
1163
|
+
response += `Review the updated rules files to see the new guidance.
|
|
1164
|
+
`;
|
|
1165
|
+
}
|
|
1166
|
+
if (diff) {
|
|
1167
|
+
const lightChanges = diff.added.length + diff.modified.length;
|
|
1168
|
+
const darkChanges = diff.addedDark.length + diff.modifiedDark.length;
|
|
1169
|
+
if (lightChanges > 0 || darkChanges > 0 || deprecatedCount > 0) {
|
|
1170
|
+
response += `
|
|
1171
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1172
|
+
`;
|
|
1173
|
+
response += `\u{1F4CA} DETAILED CHANGES
|
|
1174
|
+
`;
|
|
1175
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1176
|
+
|
|
1177
|
+
`;
|
|
1178
|
+
const lightSummary = lightChanges > 0 ? `Light: ${diff.modified.length} modified, ${diff.added.length} added` : "";
|
|
1179
|
+
const darkSummary = darkChanges > 0 ? `Dark: ${diff.modifiedDark.length} modified, ${diff.addedDark.length} added` : "";
|
|
1180
|
+
const deprecatedSummary = deprecatedCount > 0 ? `${deprecatedCount} deprecated (use /refactor)` : "";
|
|
1181
|
+
const diffSummary = [lightSummary, darkSummary, deprecatedSummary].filter(Boolean).join(" | ");
|
|
1182
|
+
response += `${diffSummary}
|
|
1183
|
+
`;
|
|
1184
|
+
if (changes.length > 0 && changes.length <= 10) {
|
|
1185
|
+
response += "\nModified tokens:\n";
|
|
1186
|
+
for (const { key, old: oldVal, new: newVal } of changes) {
|
|
1187
|
+
response += ` ${key}: ${oldVal} \u2192 ${newVal}
|
|
1188
|
+
`;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
} else if (!fileExists) {
|
|
1193
|
+
response += `
|
|
1194
|
+
(New file created)
|
|
1195
|
+
`;
|
|
1196
|
+
}
|
|
1197
|
+
if (hasRefactorRecommendation && deprecatedTokenCount > 0) {
|
|
1198
|
+
response += `
|
|
1199
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1200
|
+
`;
|
|
1201
|
+
response += `\u{1F4A1} NEXT STEP
|
|
1202
|
+
`;
|
|
1203
|
+
response += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
1204
|
+
|
|
1205
|
+
`;
|
|
1206
|
+
response += `${deprecatedTokenCount} token(s) have been deprecated.
|
|
1207
|
+
`;
|
|
1208
|
+
response += `Your codebase may still reference these deprecated tokens.
|
|
1209
|
+
|
|
1210
|
+
`;
|
|
1211
|
+
response += `Run \`/refactor\` to:
|
|
1212
|
+
`;
|
|
1213
|
+
response += ` \u2022 Scan your codebase for deprecated token usage
|
|
1214
|
+
`;
|
|
1215
|
+
response += ` \u2022 See which files need updates
|
|
1216
|
+
`;
|
|
1217
|
+
response += ` \u2022 Review and apply changes with your confirmation
|
|
1218
|
+
`;
|
|
1219
|
+
}
|
|
1220
|
+
return response;
|
|
1221
|
+
}
|
|
1222
|
+
function getTokenByPath(tokens, path5) {
|
|
1223
|
+
const parts = path5.split(".");
|
|
1224
|
+
let current = tokens;
|
|
1225
|
+
for (const part of parts) {
|
|
1226
|
+
if (current && typeof current === "object" && part in current) {
|
|
1227
|
+
current = current[part];
|
|
1228
|
+
} else {
|
|
1229
|
+
return void 0;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return current;
|
|
1233
|
+
}
|
|
1234
|
+
function flattenTokens(obj, prefix = "") {
|
|
1235
|
+
const results = [];
|
|
1236
|
+
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
|
|
1237
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1238
|
+
const newPath = prefix ? `${prefix}.${key}` : key;
|
|
1239
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1240
|
+
results.push(...flattenTokens(value, newPath));
|
|
1241
|
+
} else {
|
|
1242
|
+
results.push({ path: newPath, value });
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return results;
|
|
1247
|
+
}
|
|
1248
|
+
function searchTokens(tokens, query) {
|
|
1249
|
+
const flat = flattenTokens(tokens);
|
|
1250
|
+
const lowerQuery = query.toLowerCase();
|
|
1251
|
+
return flat.filter(({ path: path5, value }) => {
|
|
1252
|
+
const pathMatch = path5.toLowerCase().includes(lowerQuery);
|
|
1253
|
+
const valueMatch = String(value).toLowerCase().includes(lowerQuery);
|
|
1254
|
+
return pathMatch || valueMatch;
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
function countTokens(tokens, prefix = "") {
|
|
1258
|
+
let count = 0;
|
|
1259
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
1260
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1261
|
+
count += countTokens(value, `${prefix}${key}.`);
|
|
1262
|
+
} else if (value !== void 0 && value !== null) {
|
|
1263
|
+
count++;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return count;
|
|
1267
|
+
}
|
|
1268
|
+
function getTokenStats(data) {
|
|
1269
|
+
const byCategory = {};
|
|
1270
|
+
let total = 0;
|
|
1271
|
+
for (const [category, value] of Object.entries(data.tokens)) {
|
|
1272
|
+
if (value && typeof value === "object") {
|
|
1273
|
+
const count = countTokens(value);
|
|
1274
|
+
byCategory[category] = count;
|
|
1275
|
+
total += count;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return {
|
|
1279
|
+
total,
|
|
1280
|
+
byCategory,
|
|
1281
|
+
cssVariables: Object.keys(data.cssVariables).length,
|
|
1282
|
+
governanceRules: data.governance.rules.length
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// src/index.ts
|
|
1287
|
+
import * as path2 from "path";
|
|
1288
|
+
import * as fs2 from "fs";
|
|
37
1289
|
function parseArgs() {
|
|
38
1290
|
const args = process.argv.slice(2);
|
|
39
1291
|
let dsId2 = null;
|
|
@@ -93,7 +1345,7 @@ var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows",
|
|
|
93
1345
|
var server = new Server(
|
|
94
1346
|
{
|
|
95
1347
|
name: "atomix-mcp-user",
|
|
96
|
-
version: "1.0.
|
|
1348
|
+
version: "1.0.10"
|
|
97
1349
|
},
|
|
98
1350
|
{
|
|
99
1351
|
capabilities: {
|
|
@@ -265,27 +1517,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
265
1517
|
const data = await fetchDesignSystemForMCP(shouldForceRefresh);
|
|
266
1518
|
switch (name) {
|
|
267
1519
|
case "getToken": {
|
|
268
|
-
const
|
|
269
|
-
const value = getTokenByPath(data.tokens,
|
|
1520
|
+
const path4 = args?.path;
|
|
1521
|
+
const value = getTokenByPath(data.tokens, path4);
|
|
270
1522
|
if (value === void 0) {
|
|
271
1523
|
return {
|
|
272
1524
|
content: [{
|
|
273
1525
|
type: "text",
|
|
274
1526
|
text: JSON.stringify({
|
|
275
|
-
error: `Token not found: ${
|
|
1527
|
+
error: `Token not found: ${path4}`,
|
|
276
1528
|
suggestion: "Use listTokens or searchTokens to find available tokens.",
|
|
277
1529
|
availableCategories: TOKEN_CATEGORIES
|
|
278
1530
|
}, null, 2)
|
|
279
1531
|
}]
|
|
280
1532
|
};
|
|
281
1533
|
}
|
|
282
|
-
const cssVarKey = `--atmx-${
|
|
1534
|
+
const cssVarKey = `--atmx-${path4.replace(/\./g, "-")}`;
|
|
283
1535
|
const cssVar = data.cssVariables[cssVarKey];
|
|
284
1536
|
return {
|
|
285
1537
|
content: [{
|
|
286
1538
|
type: "text",
|
|
287
1539
|
text: JSON.stringify({
|
|
288
|
-
path:
|
|
1540
|
+
path: path4,
|
|
289
1541
|
value,
|
|
290
1542
|
cssVariable: cssVar || `var(${cssVarKey})`,
|
|
291
1543
|
usage: `style={{ property: "var(${cssVarKey})" }}`
|
|
@@ -312,13 +1564,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
312
1564
|
};
|
|
313
1565
|
}
|
|
314
1566
|
const flat = flattenTokens(tokensToList);
|
|
315
|
-
const tokensWithCssVars = flat.map(({ path:
|
|
316
|
-
const fullPath = subcategory ? `${category}.${subcategory}.${
|
|
1567
|
+
const tokensWithCssVars = flat.map(({ path: path4, value }) => {
|
|
1568
|
+
const fullPath = subcategory ? `${category}.${subcategory}.${path4}` : `${category}.${path4}`;
|
|
317
1569
|
let cssVar;
|
|
318
1570
|
if (category === "colors" && subcategory === "static.brand") {
|
|
319
|
-
cssVar = data.cssVariables[`--atmx-color-brand-${
|
|
1571
|
+
cssVar = data.cssVariables[`--atmx-color-brand-${path4}`];
|
|
320
1572
|
} else if (category === "colors" && subcategory?.startsWith("modes.")) {
|
|
321
|
-
cssVar = data.cssVariables[`--atmx-color-${
|
|
1573
|
+
cssVar = data.cssVariables[`--atmx-color-${path4}`];
|
|
322
1574
|
} else {
|
|
323
1575
|
const cssVarKey = `--atmx-${fullPath.replace(/\./g, "-")}`;
|
|
324
1576
|
cssVar = data.cssVariables[cssVarKey];
|
|
@@ -638,12 +1890,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
638
1890
|
}]
|
|
639
1891
|
};
|
|
640
1892
|
}
|
|
641
|
-
const outputPath =
|
|
642
|
-
const fileExists =
|
|
1893
|
+
const outputPath = path2.resolve(process.cwd(), output);
|
|
1894
|
+
const fileExists = fs2.existsSync(outputPath);
|
|
643
1895
|
const deprecatedTokens = /* @__PURE__ */ new Map();
|
|
644
1896
|
const existingTokens = /* @__PURE__ */ new Map();
|
|
645
1897
|
if (fileExists && ["css", "scss", "less"].includes(format)) {
|
|
646
|
-
const oldContent =
|
|
1898
|
+
const oldContent = fs2.readFileSync(outputPath, "utf-8");
|
|
647
1899
|
const oldVarPattern = /(?:^|\n)\s*(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/\s*)?(--[a-zA-Z0-9-]+):\s*([^;]+);/gm;
|
|
648
1900
|
let match;
|
|
649
1901
|
while ((match = oldVarPattern.exec(oldContent)) !== null) {
|
|
@@ -696,7 +1948,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
696
1948
|
let changes = [];
|
|
697
1949
|
let diff;
|
|
698
1950
|
if (fileExists && ["css", "scss", "less"].includes(format)) {
|
|
699
|
-
const oldContent =
|
|
1951
|
+
const oldContent = fs2.readFileSync(outputPath, "utf-8");
|
|
700
1952
|
diff = diffTokens(oldContent, mergedCssVariables, format, darkModeColors?.dark);
|
|
701
1953
|
const lightChanges = diff.added.length + diff.modified.length;
|
|
702
1954
|
const darkChanges = diff.addedDark.length + diff.modifiedDark.length;
|
|
@@ -736,11 +1988,11 @@ Last updated: ${lastUpdated}`
|
|
|
736
1988
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
737
1989
|
};
|
|
738
1990
|
}
|
|
739
|
-
const outputDir =
|
|
740
|
-
if (!
|
|
741
|
-
|
|
1991
|
+
const outputDir = path2.dirname(outputPath);
|
|
1992
|
+
if (!fs2.existsSync(outputDir)) {
|
|
1993
|
+
fs2.mkdirSync(outputDir, { recursive: true });
|
|
742
1994
|
}
|
|
743
|
-
|
|
1995
|
+
fs2.writeFileSync(outputPath, newContent);
|
|
744
1996
|
let rulesResults = [];
|
|
745
1997
|
try {
|
|
746
1998
|
rulesResults = await syncRulesFiles({
|