@atomixstudio/mcp 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,678 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Sync Component Tokens from Atomix Lab
4
- *
5
- * This script reads component-defaults.ts from atomix-lab and generates
6
- * component-tokens.ts for the MCP server.
7
- *
8
- * Usage:
9
- * node scripts/sync-component-tokens.js
10
- *
11
- * Called automatically by:
12
- * - npm run sync (in atomix-mcp)
13
- * - npm run build (in atomix package, after token build)
14
- */
15
-
16
- const fs = require("fs");
17
- const path = require("path");
18
-
19
- // Paths
20
- const LAB_DEFAULTS_PATH = path.join(__dirname, "../../../apps/atomix-lab/src/config/component-defaults.ts");
21
- const MCP_TOKENS_PATH = path.join(__dirname, "../src/component-tokens.ts");
22
-
23
- // ============================================
24
- // PARSE COMPONENT DEFAULTS
25
- // ============================================
26
-
27
- function parseDefaults(content) {
28
- const defaults = {};
29
-
30
- // Extract BUTTON_DEFAULTS
31
- const buttonMatch = content.match(/export const BUTTON_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
32
- if (buttonMatch) {
33
- defaults.button = parseButtonDefaults(buttonMatch[1]);
34
- }
35
-
36
- // Extract CARD_DEFAULTS
37
- const cardMatch = content.match(/export const CARD_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
38
- if (cardMatch) {
39
- defaults.card = parseCardDefaults(cardMatch[1]);
40
- }
41
-
42
- // Extract DIALOG_DEFAULTS
43
- const dialogMatch = content.match(/export const DIALOG_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
44
- if (dialogMatch) {
45
- defaults.dialog = parseDialogDefaults(dialogMatch[1]);
46
- }
47
-
48
- // Extract INPUT_DEFAULTS
49
- const inputMatch = content.match(/export const INPUT_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
50
- if (inputMatch) {
51
- defaults.input = parseInputDefaults(inputMatch[1]);
52
- }
53
-
54
- // Extract TEXTFIELD_DEFAULTS (becomes select in MCP)
55
- const textfieldMatch = content.match(/export const TEXTFIELD_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
56
- if (textfieldMatch) {
57
- defaults.select = parseSelectDefaults(textfieldMatch[1]);
58
- }
59
-
60
- // Extract HEADING_DEFAULTS
61
- const headingMatch = content.match(/export const HEADING_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
62
- if (headingMatch) {
63
- defaults.heading = parseHeadingDefaults(headingMatch[1]);
64
- }
65
-
66
- // Extract SELECTION_CONTROLS_DEFAULTS
67
- const selectionMatch = content.match(/export const SELECTION_CONTROLS_DEFAULTS[^{]*(\{[\s\S]*?\n\});/);
68
- if (selectionMatch) {
69
- defaults.selectionControls = parseSelectionControlsDefaults(selectionMatch[1]);
70
- }
71
-
72
- return defaults;
73
- }
74
-
75
- // ============================================
76
- // INDIVIDUAL PARSERS (extract key info from TS)
77
- // ============================================
78
-
79
- function parseButtonDefaults(tsBlock) {
80
- // Extract variant bgColors
81
- const variants = {};
82
- const variantMatches = tsBlock.matchAll(/(\w+):\s*\{\s*bgColor:\s*"([^"]+)"/g);
83
- for (const match of variantMatches) {
84
- variants[match[1]] = { bgColor: match[2] };
85
- }
86
-
87
- // Extract sizes
88
- const sizes = {};
89
- const sizeMatches = tsBlock.matchAll(/(sm|md|lg):\s*\{[^}]*borderRadius:\s*"([^"]+)"[^}]*typeSet:\s*"([^"]+)"/g);
90
- for (const match of sizeMatches) {
91
- sizes[match[1]] = { borderRadius: match[2], typeSet: match[3] };
92
- }
93
-
94
- return { variants, sizes };
95
- }
96
-
97
- function parseCardDefaults(tsBlock) {
98
- const variants = {};
99
- const variantMatches = tsBlock.matchAll(/(default|elevated|outlined|ghost):\s*\{[^}]*background:\s*"([^"]+)"[^}]*shadow:\s*"([^"]+)"/gs);
100
- for (const match of variantMatches) {
101
- variants[match[1]] = { background: match[2], shadow: match[3] };
102
- }
103
- return { variants };
104
- }
105
-
106
- function parseDialogDefaults(tsBlock) {
107
- // Extract global props
108
- const borderRadius = tsBlock.match(/borderRadius:\s*"([^"]+)"/)?.[1] || "2xl";
109
- const overlayBackground = tsBlock.match(/overlayBackground:\s*"([^"]+)"/)?.[1] || "blackAlpha-600";
110
- return { borderRadius, overlayBackground };
111
- }
112
-
113
- function parseInputDefaults(tsBlock) {
114
- const textfieldColor = tsBlock.match(/textfieldColor:\s*"([^"]+)"/)?.[1] || "text-primary";
115
- const placeholderColor = tsBlock.match(/placeholderColor:\s*"([^"]+)"/)?.[1] || "text-muted";
116
- return { textfieldColor, placeholderColor };
117
- }
118
-
119
- function parseSelectDefaults(tsBlock) {
120
- const dropdownBg = tsBlock.match(/background:\s*"([^"]+)"/)?.[1] || "bg-surface";
121
- return { dropdownBackground: dropdownBg };
122
- }
123
-
124
- function parseHeadingDefaults(tsBlock) {
125
- const fontFamily = tsBlock.match(/fontFamily:\s*"([^"]+)"/)?.[1] || "sans";
126
- return { fontFamily };
127
- }
128
-
129
- function parseSelectionControlsDefaults(tsBlock) {
130
- const variants = {};
131
- const variantMatches = tsBlock.matchAll(/(checkbox|radio|toggle):\s*\{[^}]*backgroundChecked:\s*"([^"]+)"/gs);
132
- for (const match of variantMatches) {
133
- variants[match[1]] = { backgroundChecked: match[2] };
134
- }
135
- return { variants };
136
- }
137
-
138
- // ============================================
139
- // GENERATE MCP TOKENS
140
- // ============================================
141
-
142
- function generateMCPTokens(defaults) {
143
- const components = {
144
- button: {
145
- description: "Interactive button component with multiple variants and sizes",
146
- variants: {
147
- primary: {
148
- description: "Primary action button with brand color",
149
- tokens: {
150
- background: "colors.modes.{mode}.actionPrimary",
151
- backgroundHover: "colors.modes.{mode}.actionPrimaryHover",
152
- text: "colors.static.brand.primaryForeground",
153
- border: "transparent",
154
- },
155
- },
156
- secondary: {
157
- description: "Secondary action button with subtle styling",
158
- tokens: {
159
- background: "colors.modes.{mode}.actionSecondary",
160
- backgroundHover: "colors.modes.{mode}.actionSecondaryHover",
161
- text: "colors.modes.{mode}.textPrimary",
162
- border: "transparent",
163
- },
164
- },
165
- outline: {
166
- description: "Outline button with border and transparent background",
167
- tokens: {
168
- background: "transparent",
169
- backgroundHover: "colors.modes.{mode}.bgMuted",
170
- text: "colors.modes.{mode}.textPrimary",
171
- border: "colors.modes.{mode}.borderPrimary",
172
- },
173
- },
174
- ghost: {
175
- description: "Ghost button with no background or border",
176
- tokens: {
177
- background: "transparent",
178
- backgroundHover: "colors.modes.{mode}.bgMuted",
179
- text: "colors.modes.{mode}.textPrimary",
180
- border: "transparent",
181
- },
182
- },
183
- destructive: {
184
- description: "Destructive action button for delete/remove operations",
185
- tokens: {
186
- background: "colors.modes.{mode}.actionDestructive",
187
- backgroundHover: "colors.modes.{mode}.actionDestructiveHover",
188
- text: "colors.static.white",
189
- border: "transparent",
190
- },
191
- },
192
- },
193
- sizes: {
194
- sm: {
195
- tokens: {
196
- height: "sizing.button.sm.height",
197
- padding: "spacing.inset.sm",
198
- fontSize: "typography.fontSize.xs",
199
- borderRadius: defaults.button?.sizes?.sm?.borderRadius
200
- ? `radius.scale.${defaults.button.sizes.sm.borderRadius}`
201
- : "radius.scale.md",
202
- },
203
- },
204
- md: {
205
- tokens: {
206
- height: "sizing.button.md.height",
207
- padding: "spacing.inset.md",
208
- fontSize: "typography.fontSize.sm",
209
- borderRadius: defaults.button?.sizes?.md?.borderRadius
210
- ? `radius.scale.${defaults.button.sizes.md.borderRadius}`
211
- : "radius.scale.md",
212
- },
213
- },
214
- lg: {
215
- tokens: {
216
- height: "sizing.button.lg.height",
217
- padding: "spacing.inset.lg",
218
- fontSize: "typography.fontSize.md",
219
- borderRadius: defaults.button?.sizes?.lg?.borderRadius
220
- ? `radius.scale.${defaults.button.sizes.lg.borderRadius}`
221
- : "radius.scale.lg",
222
- },
223
- },
224
- },
225
- shared: {
226
- fontFamily: "typography.fontFamily.sans",
227
- fontWeight: "typography.fontWeight.medium",
228
- transitionDuration: "motion.duration.fast",
229
- transitionEasing: "motion.easing.ease",
230
- focusRing: "shadows.focus.ring",
231
- },
232
- cssVariables: [
233
- "--atomix-button-bg",
234
- "--atomix-button-text",
235
- "--atomix-button-border",
236
- "--atomix-button-radius",
237
- ],
238
- },
239
-
240
- card: {
241
- description: "Container component for grouping related content",
242
- variants: {
243
- default: {
244
- description: "Standard card with subtle background",
245
- tokens: {
246
- background: "colors.modes.{mode}.bgSurface",
247
- border: "colors.modes.{mode}.borderPrimary",
248
- shadow: "shadows.elevation.sm",
249
- },
250
- },
251
- outlined: {
252
- description: "Card with visible border, no shadow",
253
- tokens: {
254
- background: "colors.modes.{mode}.bgSurface",
255
- border: "colors.modes.{mode}.borderSecondary",
256
- shadow: "none",
257
- },
258
- },
259
- elevated: {
260
- description: "Card with prominent elevation shadow",
261
- tokens: {
262
- background: "colors.modes.{mode}.bgSurface",
263
- border: "transparent",
264
- shadow: "shadows.elevation.md",
265
- },
266
- },
267
- filled: {
268
- description: "Card with muted background fill",
269
- tokens: {
270
- background: "colors.modes.{mode}.bgMuted",
271
- border: "transparent",
272
- shadow: "none",
273
- },
274
- },
275
- },
276
- sizes: {
277
- sm: {
278
- tokens: {
279
- padding: "spacing.inset.md",
280
- borderRadius: "radius.scale.lg",
281
- gap: "spacing.gap.sm",
282
- },
283
- },
284
- md: {
285
- tokens: {
286
- padding: "spacing.inset.lg",
287
- borderRadius: "radius.scale.xl",
288
- gap: "spacing.gap.md",
289
- },
290
- },
291
- lg: {
292
- tokens: {
293
- padding: "spacing.inset.xl",
294
- borderRadius: "radius.scale.2xl",
295
- gap: "spacing.gap.lg",
296
- },
297
- },
298
- },
299
- shared: {
300
- titleColor: "colors.modes.{mode}.textPrimary",
301
- descriptionColor: "colors.modes.{mode}.textSecondary",
302
- transitionDuration: "motion.duration.normal",
303
- },
304
- },
305
-
306
- dialog: {
307
- description: "Modal dialog for focused interactions",
308
- variants: {
309
- default: {
310
- tokens: {
311
- background: "colors.modes.{mode}.bgSurface",
312
- border: "colors.modes.{mode}.borderPrimary",
313
- shadow: "shadows.elevation.xl",
314
- overlayBackground: defaults.dialog?.overlayBackground || "colors.scales.blackAlpha.600",
315
- },
316
- },
317
- },
318
- sizes: {
319
- sm: {
320
- tokens: {
321
- width: "sizing.modal.sm",
322
- padding: "spacing.inset.lg",
323
- borderRadius: `radius.scale.${defaults.dialog?.borderRadius || "xl"}`,
324
- },
325
- },
326
- md: {
327
- tokens: {
328
- width: "sizing.modal.md",
329
- padding: "spacing.inset.xl",
330
- borderRadius: `radius.scale.${defaults.dialog?.borderRadius || "2xl"}`,
331
- },
332
- },
333
- lg: {
334
- tokens: {
335
- width: "sizing.modal.lg",
336
- padding: "spacing.inset.2xl",
337
- borderRadius: `radius.scale.${defaults.dialog?.borderRadius || "2xl"}`,
338
- },
339
- },
340
- },
341
- shared: {
342
- titleColor: "colors.modes.{mode}.textPrimary",
343
- descriptionColor: "colors.modes.{mode}.textSecondary",
344
- animationDuration: "motion.duration.normal",
345
- animationEasing: "motion.easing.expressiveEntrance",
346
- },
347
- },
348
-
349
- input: {
350
- description: "Text input field component",
351
- variants: {
352
- default: {
353
- tokens: {
354
- background: "colors.modes.{mode}.bgSurface",
355
- border: "colors.modes.{mode}.borderPrimary",
356
- borderFocus: "colors.static.brand.primary",
357
- text: `colors.modes.{mode}.${defaults.input?.textfieldColor || "textPrimary"}`,
358
- placeholder: `colors.modes.{mode}.${defaults.input?.placeholderColor || "textMuted"}`,
359
- },
360
- },
361
- filled: {
362
- tokens: {
363
- background: "colors.modes.{mode}.bgMuted",
364
- border: "transparent",
365
- borderFocus: "colors.static.brand.primary",
366
- text: "colors.modes.{mode}.textPrimary",
367
- placeholder: "colors.modes.{mode}.textMuted",
368
- },
369
- },
370
- error: {
371
- tokens: {
372
- background: "colors.modes.{mode}.bgSurface",
373
- border: "colors.adaptive.error.{mode}.border",
374
- text: "colors.modes.{mode}.textPrimary",
375
- placeholder: "colors.modes.{mode}.textMuted",
376
- },
377
- },
378
- },
379
- sizes: {
380
- sm: {
381
- tokens: {
382
- height: "sizing.input.sm.height",
383
- padding: "spacing.inset.sm",
384
- fontSize: "typography.fontSize.sm",
385
- borderRadius: "radius.scale.md",
386
- },
387
- },
388
- md: {
389
- tokens: {
390
- height: "sizing.input.md.height",
391
- padding: "spacing.inset.md",
392
- fontSize: "typography.fontSize.sm",
393
- borderRadius: "radius.scale.md",
394
- },
395
- },
396
- lg: {
397
- tokens: {
398
- height: "sizing.input.lg.height",
399
- padding: "spacing.inset.lg",
400
- fontSize: "typography.fontSize.md",
401
- borderRadius: "radius.scale.lg",
402
- },
403
- },
404
- },
405
- shared: {
406
- fontFamily: "typography.fontFamily.sans",
407
- transitionDuration: "motion.duration.fast",
408
- focusRing: "shadows.focus.ring",
409
- },
410
- },
411
-
412
- select: {
413
- description: "Dropdown select component",
414
- variants: {
415
- default: {
416
- tokens: {
417
- triggerBackground: "colors.modes.{mode}.bgSurface",
418
- triggerBorder: "colors.modes.{mode}.borderPrimary",
419
- contentBackground: "colors.modes.{mode}.bgSurface",
420
- contentBorder: "colors.modes.{mode}.borderPrimary",
421
- contentShadow: "shadows.elevation.lg",
422
- itemHover: "colors.modes.{mode}.bgMuted",
423
- },
424
- },
425
- },
426
- sizes: {
427
- sm: {
428
- tokens: {
429
- height: "sizing.input.sm.height",
430
- fontSize: "typography.fontSize.sm",
431
- borderRadius: "radius.scale.md",
432
- },
433
- },
434
- md: {
435
- tokens: {
436
- height: "sizing.input.md.height",
437
- fontSize: "typography.fontSize.sm",
438
- borderRadius: "radius.scale.md",
439
- },
440
- },
441
- },
442
- shared: {
443
- fontFamily: "typography.fontFamily.sans",
444
- transitionDuration: "motion.duration.fast",
445
- },
446
- },
447
-
448
- heading: {
449
- description: "Typography component for titles, descriptions, and eyebrows",
450
- variants: {
451
- "title-only": {
452
- tokens: {
453
- titleColor: "colors.modes.{mode}.textPrimary",
454
- },
455
- },
456
- "with-description": {
457
- tokens: {
458
- titleColor: "colors.modes.{mode}.textPrimary",
459
- descriptionColor: "colors.modes.{mode}.textSecondary",
460
- },
461
- },
462
- full: {
463
- tokens: {
464
- eyebrowColor: "colors.static.brand.primary",
465
- titleColor: "colors.modes.{mode}.textPrimary",
466
- descriptionColor: "colors.modes.{mode}.textSecondary",
467
- },
468
- },
469
- },
470
- sizes: {
471
- sm: {
472
- tokens: {
473
- titleTypeSet: "typography.typeSets.title-sm",
474
- descriptionTypeSet: "typography.typeSets.text-small-regular",
475
- gap: "spacing.scale.xs",
476
- },
477
- },
478
- md: {
479
- tokens: {
480
- titleTypeSet: "typography.typeSets.title-md",
481
- descriptionTypeSet: "typography.typeSets.text-normal-regular",
482
- gap: "spacing.scale.sm",
483
- },
484
- },
485
- lg: {
486
- tokens: {
487
- titleTypeSet: "typography.typeSets.title-lg",
488
- descriptionTypeSet: "typography.typeSets.text-large-regular",
489
- gap: "spacing.scale.sm",
490
- },
491
- },
492
- },
493
- shared: {
494
- fontFamily: `typography.fontFamily.${defaults.heading?.fontFamily || "sans"}`,
495
- },
496
- },
497
-
498
- checkbox: {
499
- description: "Checkbox selection control",
500
- variants: {
501
- default: {
502
- tokens: {
503
- background: "colors.modes.{mode}.bgSurface",
504
- backgroundChecked: "colors.modes.{mode}.actionPrimary",
505
- border: "colors.modes.{mode}.borderPrimary",
506
- borderChecked: "colors.modes.{mode}.actionPrimary",
507
- indicator: "colors.static.white",
508
- labelColor: "colors.modes.{mode}.textPrimary",
509
- },
510
- },
511
- },
512
- sizes: {
513
- sm: {
514
- tokens: {
515
- size: "16px",
516
- borderRadius: "radius.scale.sm",
517
- labelFontSize: "typography.fontSize.sm",
518
- },
519
- },
520
- md: {
521
- tokens: {
522
- size: "20px",
523
- borderRadius: "radius.scale.sm",
524
- labelFontSize: "typography.fontSize.sm",
525
- },
526
- },
527
- },
528
- shared: {
529
- transitionDuration: "motion.duration.fast",
530
- focusRing: "shadows.focus.ring",
531
- },
532
- },
533
-
534
- radio: {
535
- description: "Radio button selection control",
536
- variants: {
537
- default: {
538
- tokens: {
539
- background: "colors.modes.{mode}.bgSurface",
540
- backgroundChecked: "colors.modes.{mode}.actionPrimary",
541
- border: "colors.modes.{mode}.borderPrimary",
542
- borderChecked: "colors.modes.{mode}.actionPrimary",
543
- indicator: "colors.static.white",
544
- labelColor: "colors.modes.{mode}.textPrimary",
545
- },
546
- },
547
- },
548
- sizes: {
549
- sm: {
550
- tokens: {
551
- size: "16px",
552
- indicatorSize: "6px",
553
- labelFontSize: "typography.fontSize.sm",
554
- },
555
- },
556
- md: {
557
- tokens: {
558
- size: "20px",
559
- indicatorSize: "8px",
560
- labelFontSize: "typography.fontSize.sm",
561
- },
562
- },
563
- },
564
- shared: {
565
- transitionDuration: "motion.duration.fast",
566
- focusRing: "shadows.focus.ring",
567
- },
568
- },
569
-
570
- toggle: {
571
- description: "Toggle switch control",
572
- variants: {
573
- default: {
574
- tokens: {
575
- trackBackground: "colors.modes.{mode}.bgMuted",
576
- trackBackgroundChecked: "colors.modes.{mode}.actionPrimary",
577
- thumbBackground: "colors.static.white",
578
- labelColor: "colors.modes.{mode}.textPrimary",
579
- },
580
- },
581
- },
582
- sizes: {
583
- sm: {
584
- tokens: {
585
- trackWidth: "36px",
586
- trackHeight: "20px",
587
- thumbSize: "16px",
588
- labelFontSize: "typography.fontSize.sm",
589
- },
590
- },
591
- md: {
592
- tokens: {
593
- trackWidth: "44px",
594
- trackHeight: "24px",
595
- thumbSize: "20px",
596
- labelFontSize: "typography.fontSize.sm",
597
- },
598
- },
599
- },
600
- shared: {
601
- transitionDuration: "motion.duration.fast",
602
- transitionEasing: "motion.easing.spring",
603
- focusRing: "shadows.focus.ring",
604
- },
605
- },
606
- };
607
-
608
- return components;
609
- }
610
-
611
- // ============================================
612
- // MAIN
613
- // ============================================
614
-
615
- function main() {
616
- console.log("Syncing component tokens from Atomix Lab...");
617
-
618
- // Check if source file exists
619
- if (!fs.existsSync(LAB_DEFAULTS_PATH)) {
620
- console.error(`Error: Could not find ${LAB_DEFAULTS_PATH}`);
621
- process.exit(1);
622
- }
623
-
624
- // Read and parse defaults
625
- const content = fs.readFileSync(LAB_DEFAULTS_PATH, "utf-8");
626
- const defaults = parseDefaults(content);
627
-
628
- console.log(` Parsed ${Object.keys(defaults).length} component defaults`);
629
-
630
- // Generate MCP tokens
631
- const components = generateMCPTokens(defaults);
632
-
633
- // Generate output file
634
- const output = `/**
635
- * Component Token Mappings
636
- *
637
- * AUTO-GENERATED from apps/atomix-lab/src/config/component-defaults.ts
638
- * DO NOT EDIT MANUALLY - run \`npm run sync\` to regenerate.
639
- *
640
- * Maps each component to its design tokens.
641
- * This allows AI tools to understand which tokens apply to which components.
642
- *
643
- * Last synced: ${new Date().toISOString()}
644
- */
645
-
646
- export interface ComponentTokenConfig {
647
- description: string;
648
- variants?: Record<string, VariantTokens>;
649
- sizes?: Record<string, SizeTokens>;
650
- shared?: Record<string, string>;
651
- cssVariables?: string[];
652
- tailwindClasses?: string[];
653
- }
654
-
655
- export interface VariantTokens {
656
- description?: string;
657
- tokens: Record<string, string>;
658
- }
659
-
660
- export interface SizeTokens {
661
- description?: string;
662
- tokens: Record<string, string>;
663
- }
664
-
665
- export const COMPONENT_TOKENS: Record<string, ComponentTokenConfig> = ${JSON.stringify(components, null, 2)};
666
- `;
667
-
668
- // Write output
669
- fs.writeFileSync(MCP_TOKENS_PATH, output, "utf-8");
670
- console.log(` ✓ Written to ${MCP_TOKENS_PATH}`);
671
-
672
- // Also rebuild MCP
673
- console.log("\nTo apply changes, rebuild the MCP server:");
674
- console.log(" cd packages/atomix-mcp && npm run build");
675
- }
676
-
677
- main();
678
-