@atomixstudio/mcp 0.1.1 → 1.0.1
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/README.md +72 -288
- package/dist/index.d.ts +1 -201
- package/dist/index.js +1288 -2740
- package/dist/index.js.map +1 -1
- package/package.json +31 -32
- package/data/component-tokens-snapshot.json +0 -659
- package/data/tenants/default.json +0 -73
- package/scripts/sync-component-tokens.cjs +0 -974
- package/scripts/sync-component-tokens.js +0 -678
- package/src/ai-rules-generator.ts +0 -1144
- package/src/component-tokens.ts +0 -702
- package/src/index.ts +0 -1155
- package/src/tenant-store.ts +0 -436
- package/src/tokens.ts +0 -208
- package/src/user-tokens.ts +0 -268
- package/src/utils.ts +0 -465
- package/tests/stress-test.cjs +0 -907
- package/tsconfig.json +0 -21
- package/tsup.config.ts +0 -16
package/src/utils.ts
DELETED
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for token traversal and manipulation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { TOKEN_CATEGORIES, type TokenCategory } from "./tokens.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get a token value by its dot-notation path
|
|
9
|
-
* @example getTokenByPath(primitives, "colors.static.brand.primary")
|
|
10
|
-
*/
|
|
11
|
-
export function getTokenByPath(obj: Record<string, unknown>, path: string): unknown {
|
|
12
|
-
const parts = path.split(".");
|
|
13
|
-
let current: unknown = obj;
|
|
14
|
-
|
|
15
|
-
for (const part of parts) {
|
|
16
|
-
if (current === null || current === undefined) return undefined;
|
|
17
|
-
if (typeof current !== "object") return undefined;
|
|
18
|
-
current = (current as Record<string, unknown>)[part];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return current;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* List all tokens in a category, optionally filtered by subcategory
|
|
26
|
-
*/
|
|
27
|
-
export function listTokensInCategory(
|
|
28
|
-
primitives: Record<string, unknown>,
|
|
29
|
-
category: string,
|
|
30
|
-
subcategory?: string
|
|
31
|
-
): Record<string, unknown> {
|
|
32
|
-
let base = primitives[category];
|
|
33
|
-
|
|
34
|
-
if (!base) return {};
|
|
35
|
-
|
|
36
|
-
if (subcategory) {
|
|
37
|
-
const subParts = subcategory.split(".");
|
|
38
|
-
for (const part of subParts) {
|
|
39
|
-
if (typeof base !== "object" || base === null) return {};
|
|
40
|
-
base = (base as Record<string, unknown>)[part];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (typeof base !== "object" || base === null) {
|
|
45
|
-
return { [subcategory || category]: base };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return flattenTokens(base as Record<string, unknown>);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Flatten a nested token object into dot-notation paths
|
|
53
|
-
*/
|
|
54
|
-
export function flattenTokens(
|
|
55
|
-
obj: Record<string, unknown>,
|
|
56
|
-
prefix = ""
|
|
57
|
-
): Record<string, unknown> {
|
|
58
|
-
const result: Record<string, unknown> = {};
|
|
59
|
-
|
|
60
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
61
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
62
|
-
|
|
63
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
64
|
-
Object.assign(result, flattenTokens(value as Record<string, unknown>, path));
|
|
65
|
-
} else {
|
|
66
|
-
result[path] = value;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Get all available token categories
|
|
75
|
-
*/
|
|
76
|
-
export function getTokenCategories(): TokenCategory[] {
|
|
77
|
-
return [...TOKEN_CATEGORIES];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Convert a token path to a CSS variable name
|
|
82
|
-
* @example getCssVariableName("colors.static.brand.primary") → "--atomix-colors-static-brand-primary"
|
|
83
|
-
*/
|
|
84
|
-
export function getCssVariableName(path: string): string {
|
|
85
|
-
// Convert dot notation to dash notation
|
|
86
|
-
const dashPath = path
|
|
87
|
-
.replace(/\./g, "-")
|
|
88
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2") // camelCase to kebab-case
|
|
89
|
-
.toLowerCase();
|
|
90
|
-
|
|
91
|
-
return `--atomix-${dashPath}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Parse a CSS variable name back to a token path
|
|
96
|
-
* @example parseVariableName("--atomix-colors-static-brand-primary") → "colors.static.brand.primary"
|
|
97
|
-
*/
|
|
98
|
-
export function parseVariableName(varName: string): string | null {
|
|
99
|
-
if (!varName.startsWith("--atomix-")) return null;
|
|
100
|
-
|
|
101
|
-
return varName
|
|
102
|
-
.replace("--atomix-", "")
|
|
103
|
-
.replace(/-/g, ".");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Check if a value looks like a hardcoded token (hex, rgb, px, etc.)
|
|
108
|
-
*/
|
|
109
|
-
export function isHardcodedValue(value: string): {
|
|
110
|
-
isHardcoded: boolean;
|
|
111
|
-
type?: "color" | "spacing" | "unknown";
|
|
112
|
-
details?: string;
|
|
113
|
-
} {
|
|
114
|
-
// Hex colors
|
|
115
|
-
if (/^#[0-9A-Fa-f]{3,8}$/.test(value)) {
|
|
116
|
-
return { isHardcoded: true, type: "color", details: "Hex color" };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// RGB/RGBA/HSL
|
|
120
|
-
if (/^(rgb|rgba|hsl|hsla)\(/.test(value)) {
|
|
121
|
-
return { isHardcoded: true, type: "color", details: "Color function" };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Pixel values
|
|
125
|
-
if (/^\d+px$/.test(value)) {
|
|
126
|
-
return { isHardcoded: true, type: "spacing", details: "Pixel value" };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Rem values (outside of tokens)
|
|
130
|
-
if (/^\d+(\.\d+)?rem$/.test(value)) {
|
|
131
|
-
return { isHardcoded: true, type: "spacing", details: "Rem value" };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Tailwind arbitrary values
|
|
135
|
-
if (/\[.+\]/.test(value)) {
|
|
136
|
-
return { isHardcoded: true, type: "unknown", details: "Tailwind arbitrary value" };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return { isHardcoded: false };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Find tokens matching a hex color value
|
|
144
|
-
*/
|
|
145
|
-
export function findTokensByColor(
|
|
146
|
-
primitives: Record<string, unknown>,
|
|
147
|
-
hexColor: string
|
|
148
|
-
): Array<{ path: string; value: string }> {
|
|
149
|
-
const allTokens = flattenTokens(primitives);
|
|
150
|
-
const normalizedHex = hexColor.toLowerCase();
|
|
151
|
-
|
|
152
|
-
return Object.entries(allTokens)
|
|
153
|
-
.filter(([_, value]) => {
|
|
154
|
-
if (typeof value !== "string") return false;
|
|
155
|
-
return value.toLowerCase() === normalizedHex;
|
|
156
|
-
})
|
|
157
|
-
.map(([path, value]) => ({ path, value: value as string }));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Get semantic alias for a token path (for shorter CSS variable names)
|
|
162
|
-
*/
|
|
163
|
-
export function getSemanticAlias(path: string): string | null {
|
|
164
|
-
// Common semantic aliases
|
|
165
|
-
const aliases: Record<string, string> = {
|
|
166
|
-
"colors.static.brand.primary": "--atomix-brand",
|
|
167
|
-
"colors.modes.light.bgPage": "--atomix-bg-page",
|
|
168
|
-
"colors.modes.light.bgSurface": "--atomix-bg-surface",
|
|
169
|
-
"colors.modes.light.textPrimary": "--atomix-text-primary",
|
|
170
|
-
"colors.modes.light.borderPrimary": "--atomix-border-primary",
|
|
171
|
-
// Icon colors
|
|
172
|
-
"colors.modes.light.iconBrand": "--atomix-icon-brand",
|
|
173
|
-
"colors.modes.light.iconStrong": "--atomix-icon-strong",
|
|
174
|
-
"colors.modes.light.iconSubtle": "--atomix-icon-subtle",
|
|
175
|
-
"colors.modes.light.iconDisabled": "--atomix-icon-disabled",
|
|
176
|
-
"typography.fontFamily.sans": "--atomix-font-sans",
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
return aliases[path] || null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ============================================
|
|
183
|
-
// TOKEN TIER CLASSIFICATION
|
|
184
|
-
// ============================================
|
|
185
|
-
|
|
186
|
-
export type TokenTier = "primitive" | "semantic" | "component";
|
|
187
|
-
|
|
188
|
-
export interface TokenMetadata {
|
|
189
|
-
/** Token tier: primitive (raw values), semantic (purpose-driven), component (usage-specific) */
|
|
190
|
-
tier: TokenTier;
|
|
191
|
-
/** Whether this token can be modified by external tools */
|
|
192
|
-
mutable: boolean;
|
|
193
|
-
/** How to edit this token if mutable */
|
|
194
|
-
editVia?: "designer" | "code" | "api";
|
|
195
|
-
/** Usage guidance for AI tools */
|
|
196
|
-
guidance: string;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Determine the tier and metadata for a token path.
|
|
201
|
-
*
|
|
202
|
-
* TIER DEFINITIONS:
|
|
203
|
-
* - Primitive: Raw foundational values (scales, raw colors, px/rem values)
|
|
204
|
-
* → Read-only reference for validation/a11y/docs
|
|
205
|
-
*
|
|
206
|
-
* - Semantic: Purpose-driven mappings that reference primitives
|
|
207
|
-
* → The primary API for styling, editable via Designer
|
|
208
|
-
*
|
|
209
|
-
* - Component: Token assignments for specific components
|
|
210
|
-
* → Editable via Designer, specific to component variants/sizes
|
|
211
|
-
*/
|
|
212
|
-
export function getTokenMetadata(path: string): TokenMetadata {
|
|
213
|
-
// ============================================
|
|
214
|
-
// PRIMITIVE TIER (Read-Only Reference)
|
|
215
|
-
// Raw values that should not be modified by AI
|
|
216
|
-
// ============================================
|
|
217
|
-
|
|
218
|
-
// Color scales (raw palette)
|
|
219
|
-
if (path.match(/^colors\.scales\./)) {
|
|
220
|
-
return {
|
|
221
|
-
tier: "primitive",
|
|
222
|
-
mutable: false,
|
|
223
|
-
guidance: "Raw color palette. Use semantic colors (colors.modes.*) in components instead.",
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Alpha scales
|
|
228
|
-
if (path.match(/^colors\.scales\.(black|white|green)Alpha\./)) {
|
|
229
|
-
return {
|
|
230
|
-
tier: "primitive",
|
|
231
|
-
mutable: false,
|
|
232
|
-
guidance: "Alpha transparency scale. Reference only for overlays/effects.",
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Spacing scale
|
|
237
|
-
if (path.match(/^spacing\.scale\./)) {
|
|
238
|
-
return {
|
|
239
|
-
tier: "primitive",
|
|
240
|
-
mutable: false,
|
|
241
|
-
guidance: "Spacing scale values. Use semantic spacing keys (xs, sm, md, lg) in components.",
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Spacing inset/stack/inline (derived from scale)
|
|
246
|
-
if (path.match(/^spacing\.(inset|stack|inline|gap)\./)) {
|
|
247
|
-
return {
|
|
248
|
-
tier: "primitive",
|
|
249
|
-
mutable: false,
|
|
250
|
-
guidance: "Spacing presets derived from scale. Reference only.",
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Typography raw values (fontSize, fontWeight, lineHeight)
|
|
255
|
-
if (path.match(/^typography\.(fontSize|fontWeight|lineHeight|lineHeightPx|letterSpacing|paragraphSpacing)\./)) {
|
|
256
|
-
return {
|
|
257
|
-
tier: "primitive",
|
|
258
|
-
mutable: false,
|
|
259
|
-
guidance: "Raw typography values. Use typeSets (title-lg, text-normal-regular) in components.",
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Radius scale
|
|
264
|
-
if (path.match(/^radius\.scale\./)) {
|
|
265
|
-
return {
|
|
266
|
-
tier: "primitive",
|
|
267
|
-
mutable: false,
|
|
268
|
-
guidance: "Border radius scale. Use semantic radius (radius.semantic.*) or scale keys in components.",
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Border width scale
|
|
273
|
-
if (path.match(/^borders\.width\./)) {
|
|
274
|
-
return {
|
|
275
|
-
tier: "primitive",
|
|
276
|
-
mutable: false,
|
|
277
|
-
guidance: "Border width scale. Use semantic border keys in components.",
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Shadow elevation (raw shadows)
|
|
282
|
-
if (path.match(/^shadows\.elevation\./)) {
|
|
283
|
-
return {
|
|
284
|
-
tier: "primitive",
|
|
285
|
-
mutable: false,
|
|
286
|
-
guidance: "Elevation shadow values. Use elevation keys (sm, md, lg) in components.",
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Motion duration/easing (raw values)
|
|
291
|
-
if (path.match(/^motion\.(duration|easing)\./)) {
|
|
292
|
-
return {
|
|
293
|
-
tier: "primitive",
|
|
294
|
-
mutable: false,
|
|
295
|
-
guidance: "Motion primitives. Use semantic motion presets (motion.semantic.*) when available.",
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Z-index scale
|
|
300
|
-
if (path.match(/^zIndex\.scale\./)) {
|
|
301
|
-
return {
|
|
302
|
-
tier: "primitive",
|
|
303
|
-
mutable: false,
|
|
304
|
-
guidance: "Z-index scale. Use semantic z-index (zIndex.semantic.*) in components.",
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Sizing raw values
|
|
309
|
-
if (path.match(/^sizing\./)) {
|
|
310
|
-
return {
|
|
311
|
-
tier: "primitive",
|
|
312
|
-
mutable: false,
|
|
313
|
-
guidance: "Component sizing values. Reference only for layout calculations.",
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ============================================
|
|
318
|
-
// SEMANTIC TIER (Primary API)
|
|
319
|
-
// Purpose-driven tokens, editable via Designer
|
|
320
|
-
// ============================================
|
|
321
|
-
|
|
322
|
-
// Mode colors (light/dark semantic colors)
|
|
323
|
-
if (path.match(/^colors\.modes\.(light|dark)\./)) {
|
|
324
|
-
return {
|
|
325
|
-
tier: "semantic",
|
|
326
|
-
mutable: true,
|
|
327
|
-
editVia: "designer",
|
|
328
|
-
guidance: "Semantic color for theming. Use this in components. Editable via Atomix Designer.",
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Adaptive colors (feedback colors)
|
|
333
|
-
if (path.match(/^colors\.adaptive\./)) {
|
|
334
|
-
return {
|
|
335
|
-
tier: "semantic",
|
|
336
|
-
mutable: true,
|
|
337
|
-
editVia: "designer",
|
|
338
|
-
guidance: "Adaptive feedback color (error, warning, success, info). Use for validation states.",
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Static brand colors
|
|
343
|
-
if (path.match(/^colors\.static\.brand/)) {
|
|
344
|
-
return {
|
|
345
|
-
tier: "semantic",
|
|
346
|
-
mutable: false, // Brand colors are locked
|
|
347
|
-
guidance: "Brand identity color. Use for primary brand elements. Protected from modification.",
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Static utility colors (white, black, transparent)
|
|
352
|
-
if (path.match(/^colors\.static\.(white|black|transparent)/)) {
|
|
353
|
-
return {
|
|
354
|
-
tier: "primitive",
|
|
355
|
-
mutable: false,
|
|
356
|
-
guidance: "Static utility color. Use for absolute white/black when needed.",
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Semantic radius
|
|
361
|
-
if (path.match(/^radius\.semantic\./)) {
|
|
362
|
-
return {
|
|
363
|
-
tier: "semantic",
|
|
364
|
-
mutable: true,
|
|
365
|
-
editVia: "designer",
|
|
366
|
-
guidance: "Semantic border radius for component types. Use this in component styling.",
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Semantic borders
|
|
371
|
-
if (path.match(/^borders\.semantic\./)) {
|
|
372
|
-
return {
|
|
373
|
-
tier: "semantic",
|
|
374
|
-
mutable: true,
|
|
375
|
-
editVia: "designer",
|
|
376
|
-
guidance: "Semantic border width for component types. Use this in component styling.",
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Semantic motion
|
|
381
|
-
if (path.match(/^motion\.semantic\./)) {
|
|
382
|
-
return {
|
|
383
|
-
tier: "semantic",
|
|
384
|
-
mutable: true,
|
|
385
|
-
editVia: "designer",
|
|
386
|
-
guidance: "Semantic animation preset. Use for consistent motion patterns.",
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Semantic z-index
|
|
391
|
-
if (path.match(/^zIndex\.semantic\./)) {
|
|
392
|
-
return {
|
|
393
|
-
tier: "semantic",
|
|
394
|
-
mutable: true,
|
|
395
|
-
editVia: "designer",
|
|
396
|
-
guidance: "Semantic z-index for UI layers. Use for proper stacking order.",
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Focus shadows
|
|
401
|
-
if (path.match(/^shadows\.focus\./)) {
|
|
402
|
-
return {
|
|
403
|
-
tier: "semantic",
|
|
404
|
-
mutable: true,
|
|
405
|
-
editVia: "designer",
|
|
406
|
-
guidance: "Focus ring styles for accessibility. Use for keyboard focus indicators.",
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Typography typeSets
|
|
411
|
-
if (path.match(/^typography\.typeSets\./)) {
|
|
412
|
-
return {
|
|
413
|
-
tier: "semantic",
|
|
414
|
-
mutable: true,
|
|
415
|
-
editVia: "designer",
|
|
416
|
-
guidance: "Composed typography style. Use these for consistent text styling.",
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Typography font families
|
|
421
|
-
if (path.match(/^typography\.fontFamily\./)) {
|
|
422
|
-
return {
|
|
423
|
-
tier: "semantic",
|
|
424
|
-
mutable: true,
|
|
425
|
-
editVia: "designer",
|
|
426
|
-
guidance: "Font family selection. Use 'sans', 'mono', or 'display' keys.",
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Typography text styles
|
|
431
|
-
if (path.match(/^typography\.textStyles\./)) {
|
|
432
|
-
return {
|
|
433
|
-
tier: "semantic",
|
|
434
|
-
mutable: true,
|
|
435
|
-
editVia: "designer",
|
|
436
|
-
guidance: "Complete text style definition. Use for consistent typography.",
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// ============================================
|
|
441
|
-
// DEFAULT: Assume primitive if unknown
|
|
442
|
-
// ============================================
|
|
443
|
-
return {
|
|
444
|
-
tier: "primitive",
|
|
445
|
-
mutable: false,
|
|
446
|
-
guidance: "Token classification unknown. Treat as read-only reference.",
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Check if a token path is a semantic token (the primary API for AI tools)
|
|
452
|
-
*/
|
|
453
|
-
export function isSemanticToken(path: string): boolean {
|
|
454
|
-
const metadata = getTokenMetadata(path);
|
|
455
|
-
return metadata.tier === "semantic";
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Check if a token path is a primitive (read-only reference)
|
|
460
|
-
*/
|
|
461
|
-
export function isPrimitiveToken(path: string): boolean {
|
|
462
|
-
const metadata = getTokenMetadata(path);
|
|
463
|
-
return metadata.tier === "primitive";
|
|
464
|
-
}
|
|
465
|
-
|