@atomixstudio/mcp 1.0.9 → 1.0.11
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 +1325 -99
- 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.11"
|
|
97
1349
|
},
|
|
98
1350
|
{
|
|
99
1351
|
capabilities: {
|
|
@@ -238,7 +1490,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
238
1490
|
},
|
|
239
1491
|
{
|
|
240
1492
|
name: "getDependencies",
|
|
241
|
-
description: "Get suggested dependencies for this design system (icon package, fonts, SKILL.md,
|
|
1493
|
+
description: "Get suggested dependencies for this design system (icon package, fonts, SKILL.md, token files). Use with atomix/setup. Optional platform and stack for tailored suggestions.",
|
|
242
1494
|
inputSchema: {
|
|
243
1495
|
type: "object",
|
|
244
1496
|
properties: {
|
|
@@ -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({
|
|
@@ -797,18 +2049,6 @@ Last updated: ${lastUpdated}`
|
|
|
797
2049
|
};
|
|
798
2050
|
const lib = icons?.library || "lucide";
|
|
799
2051
|
const iconPkgs = ICON_PACKAGES[lib] || ICON_PACKAGES.lucide;
|
|
800
|
-
const serverName = data.meta.name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
801
|
-
const npxArgs = ["@atomixstudio/mcp@latest"];
|
|
802
|
-
if (dsId) npxArgs.push("--ds-id", dsId);
|
|
803
|
-
if (apiKey) npxArgs.push("--api-key", apiKey);
|
|
804
|
-
const mcpConfig = {
|
|
805
|
-
path: ".cursor/mcp.json",
|
|
806
|
-
content: JSON.stringify({
|
|
807
|
-
mcpServers: {
|
|
808
|
-
[serverName]: { command: "npx", args: npxArgs }
|
|
809
|
-
}
|
|
810
|
-
}, null, 2)
|
|
811
|
-
};
|
|
812
2052
|
const payload = {
|
|
813
2053
|
iconLibrary: {
|
|
814
2054
|
package: iconPkgs.web,
|
|
@@ -823,10 +2063,9 @@ Last updated: ${lastUpdated}`
|
|
|
823
2063
|
path: ".cursor/skills/atomix-ds/SKILL.md",
|
|
824
2064
|
content: GENERIC_SKILL_MD
|
|
825
2065
|
},
|
|
826
|
-
mcpConfig,
|
|
827
2066
|
tokenFiles: {
|
|
828
2067
|
files: ["tokens.css", "tokens.json"],
|
|
829
|
-
copyInstructions: "
|
|
2068
|
+
copyInstructions: "Call the syncTokens MCP tool to create the token file; do not only suggest the user run sync later."
|
|
830
2069
|
},
|
|
831
2070
|
meta: { dsName: data.meta.name, platform: platform ?? void 0, stack: stack ?? void 0 }
|
|
832
2071
|
};
|
|
@@ -1071,8 +2310,8 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
1071
2310
|
description: "Migrate deprecated tokens in codebase. Scans for deprecated tokens marked by /sync and suggests replacements. Run after /sync to update code."
|
|
1072
2311
|
},
|
|
1073
2312
|
{
|
|
1074
|
-
name: "
|
|
1075
|
-
description: "Suggest dependencies for this design system (icons, fonts, SKILL.md,
|
|
2313
|
+
name: "setup",
|
|
2314
|
+
description: "Suggest dependencies for this design system (icons, fonts, SKILL.md, tokens). Three phases: scan, report and ask, then create only after approval.",
|
|
1076
2315
|
arguments: [
|
|
1077
2316
|
{ name: "platform", description: "Target platform (web, ios, android). Optional; ask user if unknown.", required: false },
|
|
1078
2317
|
{ name: "stack", description: "Stack or framework (e.g. react, vue, next, swift, kotlin). Optional.", required: false }
|
|
@@ -1196,13 +2435,6 @@ Configure the MCP server in your Cursor settings, then restart Cursor.`
|
|
|
1196
2435
|
messages: [
|
|
1197
2436
|
{
|
|
1198
2437
|
role: "user",
|
|
1199
|
-
content: {
|
|
1200
|
-
type: "text",
|
|
1201
|
-
text: "Show me the design system overview and available tools."
|
|
1202
|
-
}
|
|
1203
|
-
},
|
|
1204
|
-
{
|
|
1205
|
-
role: "assistant",
|
|
1206
2438
|
content: {
|
|
1207
2439
|
type: "text",
|
|
1208
2440
|
text: welcome
|
|
@@ -1511,54 +2743,48 @@ Use \`/color\`, \`/spacing\`, \`/radius\`, \`/typography\`, \`/shadow\`, \`/bord
|
|
|
1511
2743
|
]
|
|
1512
2744
|
};
|
|
1513
2745
|
}
|
|
1514
|
-
case "
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
## Phase 1 \u2013 Resolve platform and stack
|
|
1518
|
-
|
|
1519
|
-
- If the project clearly indicates platform (e.g. package.json + web deps, or build.gradle / Android, or Xcode / iOS), infer \`platform\` (e.g. web, ios, android) and optionally \`stack\` (e.g. react, next, vue, swift, kotlin).
|
|
1520
|
-
- If the project gives **no hint** (blank repo, empty folder, or ambiguous): **Ask the user:** "Which platform are you building for? (e.g. web, Android, iOS)" and, if relevant, "Which stack or framework? (e.g. React, Vue, Next, Swift, Kotlin)." Do **not** assume web or any default.
|
|
1521
|
-
- Proceed only once platform (and optionally stack) is known or confirmed.
|
|
1522
|
-
|
|
1523
|
-
## Phase 2 \u2013 Get suggested dependencies
|
|
1524
|
-
|
|
1525
|
-
- Call MCP tool **getDependencies** with the resolved \`platform\` (and optional \`stack\`). Use the parameter names the tool expects (e.g. platform, stack).
|
|
1526
|
-
- If the call fails (e.g. MCP not connected or no ds-id), tell the user: "Atomix MCP is not connected or design system ID is missing. Configure MCP with --ds-id and try again."
|
|
1527
|
-
|
|
1528
|
-
## Phase 3 \u2013 Scan codebase and build suggestion list
|
|
1529
|
-
|
|
1530
|
-
- Scan the repo for: package.json (or equivalent), existing skill path (e.g. .cursor/skills), presence of tokens.css or project token file, font imports/config, icon package usage.
|
|
1531
|
-
- Build two lists: **Suggested dependencies** (from getDependencies, minus what is already present) and **Already present**. Include: icon package, fonts, SKILL path, MCP config, token files.
|
|
1532
|
-
- Do **not** install, copy, or write any file in this phase.
|
|
1533
|
-
|
|
1534
|
-
## Phase 4 \u2013 Present list and ask before install
|
|
1535
|
-
|
|
1536
|
-
- Reply with: "Suggested dependencies: \u2026" and "Already present: \u2026" and state: "Do not install or copy anything until you confirm. Would you like me to install or add these?"
|
|
1537
|
-
- If the user says yes, perform only the steps they approved. If no or only part, perform only that.
|
|
2746
|
+
case "setup": {
|
|
2747
|
+
const setupInstructions = `You are running **atomix/setup**. Three phases only. Do not create, write, or modify any file until Phase 3 and only after the user has approved.
|
|
1538
2748
|
|
|
1539
|
-
## Phase
|
|
2749
|
+
## Phase 1 \u2013 Scan
|
|
1540
2750
|
|
|
1541
|
-
-
|
|
2751
|
+
- Resolve platform/stack: infer from the project (e.g. package.json, build.gradle, Xcode) or ask the user once: "Which platform? (e.g. web, Android, iOS)" and if relevant "Which stack? (e.g. React, Vue, Next, Swift, Kotlin)." Do not assume a default.
|
|
2752
|
+
- Call MCP tool **getDependencies** with \`platform\` and optional \`stack\`. If it fails, tell the user: "Atomix MCP is not connected or design system ID is missing. Configure MCP with --ds-id and try again."
|
|
2753
|
+
- Scan the repo for what already exists: .cursor/skills/atomix-ds/SKILL.md, a tokens file (e.g. tokens.css or src/tokens.css), icon package from getDependencies, font setup. Do **not** include or check for MCP config; MCP is already configured for this session.
|
|
2754
|
+
- **Web:** Scan for any existing CSS (e.g. globals.css, main.css, App.css, index.css, Tailwind entry, framework styles). Note whether at least one CSS file exists.
|
|
2755
|
+
- **Native (iOS/Android):** Scan for existing theme or style files (e.g. SwiftUI theme/asset catalog, Android themes.xml/styles.xml, Compose theme). Note whether the project already has a theme or style setup.
|
|
2756
|
+
- Build two lists: **Suggested** (from getDependencies minus what exists) and **Already present**. Only include: icon package, fonts, skill (.cursor/skills/atomix-ds/SKILL.md), token files. No MCP.
|
|
2757
|
+
- Do not write, create, or add anything in Phase 1.
|
|
1542
2758
|
|
|
1543
|
-
|
|
2759
|
+
## Phase 2 \u2013 Report and ask for permission
|
|
1544
2760
|
|
|
1545
|
-
-
|
|
2761
|
+
- Reply with: **Suggested:** [list] and **Already present:** [list].
|
|
2762
|
+
- Ask exactly once: "Do you want me to add the suggested items? (Yes for all, or say which ones.)" (Integration with existing CSS or theme is recommended after Phase 3; new global CSS/theme is only suggested when none exists.)
|
|
2763
|
+
- **Stop.** Do not run Phase 3 in this response. Wait for the user to reply.
|
|
1546
2764
|
|
|
1547
|
-
## Phase
|
|
2765
|
+
## Phase 3 \u2013 Create or add (only after user approval)
|
|
1548
2766
|
|
|
1549
|
-
-
|
|
2767
|
+
- Run only when the user has said yes (all or specific items).
|
|
2768
|
+
- For each approved item:
|
|
2769
|
+
- **Skill:** Write the skill content from getDependencies to .cursor/skills/atomix-ds/SKILL.md.
|
|
2770
|
+
- **Token file:** Call the **syncTokens** MCP tool with \`output\` set to the path (e.g. "./src/tokens.css" or "./tokens.css"). You must call syncTokens; do not only suggest the user run sync later.
|
|
2771
|
+
- **Icon package / fonts:** Add per getDependencies (e.g. install package, add font config). Do not overwrite existing.
|
|
2772
|
+
- Report only what you actually created or updated (e.g. "Added: .cursor/skills/atomix-ds/SKILL.md. Synced: src/tokens.css."). Do not claim the token file was added if you did not call syncTokens.
|
|
2773
|
+
- **After reporting \u2013 styles/theme (per platform):**
|
|
2774
|
+
- **Web:** If the project **already has** at least one CSS file: recommend how to integrate Atomix (e.g. import the synced tokens file, use \`var(--atomix-*)\` or semantic classes). Do **not** suggest creating a new global CSS. Only if the project has **no** CSS file at all, ask once: "There are no CSS files yet. Do you want a minimal global CSS (typography + semantic utilities from the design system)?" and add it only if the user says yes.
|
|
2775
|
+
- **iOS/Android:** If the project **already has** theme or style files: recommend how to integrate Atomix tokens (e.g. use synced DesignTokens.swift / Kotlin tokens in existing theme). Do **not** suggest creating a new global theme file. Only if the project has **no** theme/style setup at all, ask once: "There's no theme/style setup yet. Do you want a minimal token-based theme (colors, typography, spacing from the design system)?" and add it only if the user says yes.
|
|
1550
2776
|
|
|
1551
2777
|
---
|
|
1552
2778
|
|
|
1553
|
-
Execute Phase 1 now (resolve
|
|
2779
|
+
Execute Phase 1 now (resolve platform/stack, call getDependencies, scan, build lists). Then Phase 2 (report lists and ask). Do not perform Phase 3 until the user replies.`;
|
|
1554
2780
|
return {
|
|
1555
|
-
description: "
|
|
2781
|
+
description: "Setup design system in project (atomix/setup). Phase 1 scan, Phase 2 report and ask, Phase 3 create only after user approval.",
|
|
1556
2782
|
messages: [
|
|
1557
2783
|
{
|
|
1558
2784
|
role: "user",
|
|
1559
2785
|
content: {
|
|
1560
2786
|
type: "text",
|
|
1561
|
-
text:
|
|
2787
|
+
text: setupInstructions
|
|
1562
2788
|
}
|
|
1563
2789
|
}
|
|
1564
2790
|
]
|
|
@@ -1622,7 +2848,7 @@ ${tokenSummary}
|
|
|
1622
2848
|
|
|
1623
2849
|
| Command | What to expect |
|
|
1624
2850
|
|---------|----------------|
|
|
1625
|
-
| **
|
|
2851
|
+
| **setup** | Scans your repo to set up global styles (if none), icons and fonts. Safe: Won't add anything until you confirm. |
|
|
1626
2852
|
| **sync** | Syncs tokens to a local file. Adds new, updates existing, marks deprecated. Safe: No changes until you confirm. |
|
|
1627
2853
|
| **refactor** | Scans codebase for deprecated tokens (after sync), suggests replacements. Run after sync to migrate code. |
|
|
1628
2854
|
|
|
@@ -1640,7 +2866,7 @@ ${tokenSummary}
|
|
|
1640
2866
|
| **sizing** | Table of height and icon size tokens. |
|
|
1641
2867
|
| **motion** | Table of duration and easing tokens. |
|
|
1642
2868
|
|
|
1643
|
-
**Suggested next step:** Run **
|
|
2869
|
+
**Suggested next step:** Run **setup** to set up global styles, icons, fonts, and token files; the AI will list options and ask before adding anything.
|
|
1644
2870
|
|
|
1645
2871
|
---
|
|
1646
2872
|
|