@shohojdhara/atomix 0.5.0 โ†’ 0.5.2

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.
Files changed (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -17,9 +17,15 @@ export class AIEngine {
17
17
 
18
18
  /**
19
19
  * Generate component code based on prompt
20
+ * @param {string} name - Component name
21
+ * @param {string} prompt - User prompt for AI
22
+ * @param {Object} options - Override options (provider, model, temperature, maxTokens)
20
23
  */
21
- async generateComponent(name, prompt) {
22
- const provider = this.config.provider || 'openai';
24
+ async generateComponent(name, prompt, options = {}) {
25
+ const provider = options.provider || this.config.provider || 'openai';
26
+ const model = options.model || this.config.model;
27
+ const temperature = options.temperature ?? this.config.temperature ?? 0.7;
28
+ const maxTokens = options.maxTokens || this.config.maxTokens || 4000;
23
29
  const apiKey = this.config.apiKey || process.env.ATOMIX_AI_API_KEY;
24
30
 
25
31
  if (!apiKey) {
@@ -31,9 +37,9 @@ export class AIEngine {
31
37
  try {
32
38
  let response;
33
39
  if (provider === 'openai') {
34
- response = await this.callOpenAI(name, prompt, apiKey);
40
+ response = await this.callOpenAI(name, prompt, apiKey, model, temperature, maxTokens);
35
41
  } else if (provider === 'anthropic') {
36
- response = await this.callAnthropic(name, prompt, apiKey);
42
+ response = await this.callAnthropic(name, prompt, apiKey, model, temperature, maxTokens);
37
43
  } else {
38
44
  throw new Error(`Unsupported AI provider: ${provider}`);
39
45
  }
@@ -47,9 +53,15 @@ export class AIEngine {
47
53
 
48
54
  /**
49
55
  * Call OpenAI API
56
+ * @param {string} name - Component name
57
+ * @param {string} prompt - User prompt
58
+ * @param {string} apiKey - API key
59
+ * @param {string} [modelOverride] - Model override
60
+ * @param {number} [temperature] - Temperature (0.0-1.0)
61
+ * @param {number} [maxTokens] - Max tokens
50
62
  */
51
- async callOpenAI(name, prompt, apiKey) {
52
- const model = this.config.model || 'gpt-4';
63
+ async callOpenAI(name, prompt, apiKey, modelOverride, temperature, maxTokens) {
64
+ const model = modelOverride || 'gpt-4';
53
65
  const systemPrompt = this.getSystemPrompt(name);
54
66
 
55
67
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
@@ -64,7 +76,8 @@ export class AIEngine {
64
76
  { role: 'system', content: systemPrompt },
65
77
  { role: 'user', content: prompt }
66
78
  ],
67
- temperature: 0.7
79
+ temperature: temperature,
80
+ max_tokens: maxTokens
68
81
  })
69
82
  });
70
83
 
@@ -79,9 +92,15 @@ export class AIEngine {
79
92
 
80
93
  /**
81
94
  * Call Anthropic API
95
+ * @param {string} name - Component name
96
+ * @param {string} prompt - User prompt
97
+ * @param {string} apiKey - API key
98
+ * @param {string} [modelOverride] - Model override
99
+ * @param {number} [temperature] - Temperature (0.0-1.0)
100
+ * @param {number} [maxTokens] - Max tokens
82
101
  */
83
- async callAnthropic(name, prompt, apiKey) {
84
- const model = this.config.model || 'claude-3-sonnet-20240229';
102
+ async callAnthropic(name, prompt, apiKey, modelOverride, temperature, maxTokens) {
103
+ const model = modelOverride || 'claude-3-sonnet-20240229';
85
104
  const systemPrompt = this.getSystemPrompt(name);
86
105
 
87
106
  const response = await fetch('https://api.anthropic.com/v1/messages', {
@@ -93,7 +112,8 @@ export class AIEngine {
93
112
  },
94
113
  body: JSON.stringify({
95
114
  model: model,
96
- max_tokens: 4000,
115
+ max_tokens: maxTokens,
116
+ temperature: temperature,
97
117
  system: systemPrompt,
98
118
  messages: [
99
119
  { role: 'user', content: prompt }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Framework-aware default complexity and normalization for component templates.
3
+ */
4
+
5
+ /** @typedef {'react' | 'next' | 'vanilla'} Framework */
6
+
7
+ /**
8
+ * Default complexity when the user does not pass --complexity (framework-specific).
9
+ * @param {Framework} framework
10
+ * @returns {'simple' | 'medium' | 'complex' | 'client'}
11
+ */
12
+ export function resolveDefaultComplexity(framework) {
13
+ const f = String(framework).toLowerCase();
14
+ if (f === 'next') return 'simple';
15
+ if (f === 'vanilla') return 'medium';
16
+ return 'medium';
17
+ }
18
+
19
+ /**
20
+ * Map requested complexity to a value valid for selectTemplate (Next has no "medium").
21
+ * @param {Framework} framework
22
+ * @param {string} [complexity]
23
+ * @returns {string}
24
+ */
25
+ export function normalizeComplexityForFramework(framework, complexity) {
26
+ const f = String(framework).toLowerCase();
27
+ const c = (complexity || resolveDefaultComplexity(f)).toLowerCase();
28
+
29
+ if (f === 'vanilla') {
30
+ return c;
31
+ }
32
+
33
+ if (f === 'next') {
34
+ const valid = ['simple', 'client', 'complex'];
35
+ if (valid.includes(c)) return c;
36
+ if (c === 'medium') return 'simple';
37
+ return 'simple';
38
+ }
39
+
40
+ if (f === 'react') {
41
+ const valid = ['simple', 'medium', 'complex'];
42
+ if (valid.includes(c)) return c;
43
+ return 'medium';
44
+ }
45
+
46
+ return c;
47
+ }
48
+
49
+ /**
50
+ * Resolve effective complexity: use explicit value when provided, else framework default.
51
+ * @param {Framework} framework
52
+ * @param {string|undefined} explicitComplexity - undefined when CLI did not pass --complexity
53
+ */
54
+ export function resolveEffectiveComplexity(framework, explicitComplexity) {
55
+ const f = String(framework).toLowerCase();
56
+ if (explicitComplexity === undefined || explicitComplexity === '') {
57
+ return normalizeComplexityForFramework(f, resolveDefaultComplexity(f));
58
+ }
59
+ return normalizeComplexityForFramework(f, explicitComplexity);
60
+ }
@@ -9,6 +9,26 @@ import { join } from 'path';
9
9
  import { logger } from '../utils/logger.js';
10
10
  import { AtomixCLIError } from '../utils/error.js';
11
11
 
12
+ /**
13
+ * Next.js server components (no "use client") do not use forwardRef / client hooks.
14
+ * @param {string} content
15
+ */
16
+ function isNextServerOnlyComponent(content) {
17
+ if (content.includes("'use client'") || content.includes('"use client"')) {
18
+ return false;
19
+ }
20
+ if (content.includes('forwardRef')) {
21
+ return false;
22
+ }
23
+ if (/export\s+default\s+async\s+function/.test(content) || /export\s+default\s+function/.test(content)) {
24
+ return true;
25
+ }
26
+ if (/async\s+function\s+\w+/.test(content) && /export\s+default\s+\w+/.test(content)) {
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+
12
32
  /**
13
33
  * Component validation rule severity levels
14
34
  */
@@ -31,7 +51,11 @@ export const COMPONENT_RULES = {
31
51
  severity: COMPONENT_SEVERITY.ERROR,
32
52
  validate: (content) => {
33
53
  const issues = [];
34
-
54
+
55
+ if (isNextServerOnlyComponent(content)) {
56
+ return issues;
57
+ }
58
+
35
59
  if (!content.includes('forwardRef')) {
36
60
  issues.push({
37
61
  rule: 'forward-ref-required',
@@ -40,7 +64,7 @@ export const COMPONENT_RULES = {
40
64
  severity: COMPONENT_SEVERITY.ERROR
41
65
  });
42
66
  }
43
-
67
+
44
68
  return issues;
45
69
  }
46
70
  },
@@ -200,7 +224,11 @@ export const COMPONENT_RULES = {
200
224
  severity: COMPONENT_SEVERITY.INFO,
201
225
  validate: (content) => {
202
226
  const issues = [];
203
-
227
+
228
+ if (isNextServerOnlyComponent(content)) {
229
+ return issues;
230
+ }
231
+
204
232
  if (!content.includes('memo') && !content.includes('React.memo')) {
205
233
  issues.push({
206
234
  rule: 'memo-usage',
@@ -209,7 +237,7 @@ export const COMPONENT_RULES = {
209
237
  severity: COMPONENT_SEVERITY.INFO
210
238
  });
211
239
  }
212
-
240
+
213
241
  return issues;
214
242
  }
215
243
  },
@@ -223,9 +251,13 @@ export const COMPONENT_RULES = {
223
251
  severity: COMPONENT_SEVERITY.INFO,
224
252
  validate: (content, componentName) => {
225
253
  const issues = [];
226
-
254
+
255
+ if (isNextServerOnlyComponent(content)) {
256
+ return issues;
257
+ }
258
+
227
259
  const hookPattern = new RegExp(`use${componentName}`);
228
-
260
+
229
261
  if (!hookPattern.test(content)) {
230
262
  issues.push({
231
263
  rule: 'composable-hook-pattern',
@@ -234,7 +266,7 @@ export const COMPONENT_RULES = {
234
266
  severity: COMPONENT_SEVERITY.INFO
235
267
  });
236
268
  }
237
-
269
+
238
270
  return issues;
239
271
  }
240
272
  },
@@ -248,13 +280,14 @@ export const COMPONENT_RULES = {
248
280
  severity: COMPONENT_SEVERITY.INFO,
249
281
  validate: (content) => {
250
282
  const issues = [];
251
-
252
- // Check for ThemeNaming import or usage
283
+
284
+ if (isNextServerOnlyComponent(content)) {
285
+ return issues;
286
+ }
287
+
253
288
  const hasThemeNaming = /ThemeNaming\./.test(content) || /themeNaming\./.test(content);
254
-
255
- // Check for variantClass, sizeClass, stateClass patterns
256
289
  const hasVariantPattern = /(variant|size|state)Class/.test(content);
257
-
290
+
258
291
  if (!hasThemeNaming && !hasVariantPattern) {
259
292
  issues.push({
260
293
  rule: 'theme-naming-usage',
@@ -263,7 +296,7 @@ export const COMPONENT_RULES = {
263
296
  severity: COMPONENT_SEVERITY.INFO
264
297
  });
265
298
  }
266
-
299
+
267
300
  return issues;
268
301
  }
269
302
  }
@@ -339,10 +372,8 @@ export class ComponentValidator {
339
372
  const issues = rule.validate(content, componentName);
340
373
 
341
374
  if (issues.length > 0) {
342
- results.valid = false;
343
375
  results.issues.push(...issues);
344
-
345
- // Update summary
376
+
346
377
  for (const issue of issues) {
347
378
  if (issue.severity === COMPONENT_SEVERITY.ERROR) {
348
379
  results.summary.errors++;
@@ -363,6 +394,8 @@ export class ComponentValidator {
363
394
  }
364
395
  }
365
396
 
397
+ results.valid = results.summary.errors === 0;
398
+
366
399
  return results;
367
400
  }
368
401
 
@@ -7,6 +7,7 @@ import { readFile } from 'fs/promises';
7
7
  import { existsSync } from 'fs';
8
8
  import { join } from 'path';
9
9
  import { templateEngine, COMPLEXITY_LEVELS } from './template-engine.js';
10
+ import { resolveEffectiveComplexity } from './complexity-utils.js';
10
11
  import { detectFramework } from '../utils/detector.js';
11
12
  import { filesystem } from './filesystem.js';
12
13
  import { aiEngine } from './ai-engine.js';
@@ -21,6 +22,7 @@ import { tokenValidator } from './tokens/token-validator.js';
21
22
  import { componentValidator } from './component-validator.js';
22
23
  import { generateComponentStylesPackage } from './itcss-generator.js';
23
24
  import { generateHookFile } from './hook-generator.js';
25
+ import { configLoader } from './config-loader.js';
24
26
 
25
27
  export { COMPLEXITY_LEVELS };
26
28
 
@@ -33,8 +35,23 @@ export const COMPONENT_FEATURES = {
33
35
  ACCESSIBILITY: { name: 'accessibility', default: true }
34
36
  };
35
37
 
36
- // Global rate limiter for AI operations
37
- const aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute
38
+ // Global rate limiter for AI operations - initialized with defaults, will be updated from config
39
+ let aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute default
40
+
41
+ /**
42
+ * Initialize rate limiter from config
43
+ */
44
+ async function initializeRateLimiter() {
45
+ const config = configLoader.get('ai') || {};
46
+ const rateLimitConfig = config.rateLimit;
47
+
48
+ if (rateLimitConfig) {
49
+ aiRateLimiter = new RateLimiter(
50
+ rateLimitConfig.requests || 5,
51
+ rateLimitConfig.windowMs || 60000
52
+ );
53
+ }
54
+ }
38
55
 
39
56
  export const generator = {
40
57
  /**
@@ -47,9 +64,13 @@ export const generator = {
47
64
  async generateComponent(name, options = {}) {
48
65
  const {
49
66
  outputPath,
50
- complexity = 'medium',
67
+ complexity: complexityInput,
51
68
  features = [],
52
- logger
69
+ logger,
70
+ framework: frameworkOverride,
71
+ prefix: designTokenPrefix = 'atomix',
72
+ storybookCssImport,
73
+ hookOutputDir
53
74
  } = options;
54
75
 
55
76
  // Sanitize and validate component name
@@ -67,10 +88,12 @@ export const generator = {
67
88
  );
68
89
  }
69
90
 
91
+ const projectRoot = process.cwd();
92
+
70
93
  // Detect framework
71
94
  let framework;
72
95
  try {
73
- framework = await detectFramework();
96
+ framework = await detectFramework(projectRoot, { framework: frameworkOverride });
74
97
  if (logger) logger.debug(`Detected framework: ${framework}`);
75
98
  } catch (error) {
76
99
  throw new AtomixCLIError(
@@ -84,6 +107,8 @@ export const generator = {
84
107
  );
85
108
  }
86
109
 
110
+ const complexity = resolveEffectiveComplexity(framework, complexityInput);
111
+
87
112
  // Load design tokens if available
88
113
  let availableTokens = {};
89
114
  try {
@@ -94,7 +119,7 @@ export const generator = {
94
119
  ];
95
120
 
96
121
  for (const tokenPath of tokenPaths) {
97
- if (existsSync(join(process.cwd(), tokenPath))) {
122
+ if (existsSync(join(projectRoot, tokenPath))) {
98
123
  availableTokens = await tokenProvider.loadTokens(tokenPath);
99
124
  if (logger) logger.debug(`Loaded design tokens from ${tokenPath}`);
100
125
  break;
@@ -171,7 +196,9 @@ export const generator = {
171
196
  if (features.includes('storybook')) {
172
197
  try {
173
198
  const storyTemplateFn = templateEngine.selectTemplate(framework, complexity, 'story');
174
- const storyContent = templateEngine.render(storyTemplateFn, sanitizedName);
199
+ const storyContent = templateEngine.render(storyTemplateFn, sanitizedName, {
200
+ storybookCssImport
201
+ });
175
202
  await filesystem.writeFile(join(componentPath, `${sanitizedName}.stories.tsx`), storyContent, 'utf8');
176
203
  if (logger) logger.debug(`Created ${sanitizedName}.stories.tsx`);
177
204
  } catch (error) {
@@ -206,31 +233,12 @@ export const generator = {
206
233
  }
207
234
  }
208
235
 
209
- if (features.includes('hook') && framework !== 'vanilla') {
210
- try {
211
- const hookDir = join(outputPath, '..', 'lib', 'composables');
212
- const hookTemplateFn = templateEngine.selectTemplate(framework, complexity, 'hook');
213
- const hookContent = templateEngine.render(hookTemplateFn, sanitizedName);
214
- await filesystem.writeFile(join(hookDir, `use${sanitizedName}.ts`), hookContent, 'utf8');
215
- if (logger) logger.debug(`Created use${sanitizedName}.ts`);
216
- } catch (error) {
217
- throw new AtomixCLIError(
218
- `Failed to generate composable hook: ${error.message}`,
219
- 'HOOK_GENERATION_FAILED',
220
- [
221
- 'Check hook template exists',
222
- 'Verify hook feature is supported for this framework',
223
- 'Try generating without --hook flag'
224
- ]
225
- );
226
- }
227
- }
228
-
229
236
  // 4. Styles (ITCSS) - Enhanced with auto-generation
230
237
  if (features.includes('styles')) {
231
238
  try {
232
- const stylesResult = await generateComponentStylesPackage(sanitizedName, process.cwd(), {
233
- force: options.force || false
239
+ const stylesResult = await generateComponentStylesPackage(sanitizedName, projectRoot, {
240
+ force: options.force || false,
241
+ prefix: designTokenPrefix
234
242
  });
235
243
 
236
244
  if (logger && stylesResult.created.length > 0) {
@@ -249,11 +257,12 @@ export const generator = {
249
257
  }
250
258
  }
251
259
 
252
- // 5. Composable Hook - Enhanced generation
260
+ // 5. Composable hook (single path: lib/composables + barrel updates)
253
261
  if (features.includes('hook') && framework !== 'vanilla') {
254
262
  try {
255
- const hookResult = await generateHookFile(sanitizedName, process.cwd(), {
256
- force: options.force || false
263
+ const hookResult = await generateHookFile(sanitizedName, projectRoot, {
264
+ force: options.force || false,
265
+ outputDir: hookOutputDir || 'src/lib/composables'
257
266
  });
258
267
 
259
268
  if (logger && hookResult.created.length > 0) {
@@ -279,12 +288,15 @@ export const generator = {
279
288
  * Generates component files using AI based on a prompt
280
289
  * @param {string} name - Component name
281
290
  * @param {string} prompt - AI prompt
282
- * @param {Object} options - Generation options
291
+ * @param {Object} options - Generation options (aiProvider, aiModel, aiTemperature, aiMaxTokens, aiPreview)
283
292
  * @returns {Promise<string>} Path to generated component
284
293
  * @throws {AtomixCLIError} If generation fails
285
294
  */
286
295
  async generateAIComponent(name, prompt, options = {}) {
287
- const { outputPath, logger } = options;
296
+ const { outputPath, logger, aiProvider, aiModel, aiTemperature, aiMaxTokens, aiPreview } = options;
297
+
298
+ // Initialize rate limiter from config
299
+ await initializeRateLimiter();
288
300
 
289
301
  // Apply rate limiting for AI operations
290
302
  const userId = process.env.USER || 'anonymous';
@@ -319,10 +331,15 @@ export const generator = {
319
331
 
320
332
  const componentPath = join(outputPath, sanitizedName);
321
333
 
322
- // Call AI Engine
334
+ // Call AI Engine with override options
323
335
  let generated;
324
336
  try {
325
- generated = await aiEngine.generateComponent(name, prompt);
337
+ generated = await aiEngine.generateComponent(name, prompt, {
338
+ provider: aiProvider,
339
+ model: aiModel,
340
+ temperature: aiTemperature,
341
+ maxTokens: aiMaxTokens
342
+ });
326
343
  } catch (error) {
327
344
  throw new AtomixCLIError(
328
345
  `AI generation failed: ${error.message}`,
@@ -336,6 +353,42 @@ export const generator = {
336
353
  );
337
354
  }
338
355
 
356
+ // Preview mode: show output without writing files
357
+ if (aiPreview) {
358
+ logger.info('\n' + '='.repeat(60));
359
+ logger.info('๐Ÿ“ AI PREVIEW MODE - Generated content will not be saved');
360
+ logger.info('='.repeat(60));
361
+
362
+ logger.info('\n๐Ÿ“„ Component File:');
363
+ logger.info(generated.component || 'No component generated');
364
+
365
+ if (generated.styles) {
366
+ logger.info('\n๐ŸŽจ Styles File:');
367
+ logger.info(generated.styles);
368
+ }
369
+
370
+ if (generated.tests) {
371
+ logger.info('\n๐Ÿงช Test File:');
372
+ logger.info(generated.tests);
373
+ }
374
+
375
+ if (generated.stories) {
376
+ logger.info('\n๐Ÿ“š Stories File:');
377
+ logger.info(generated.stories);
378
+ }
379
+
380
+ if (generated.readme) {
381
+ logger.info('\n๐Ÿ“– README:');
382
+ logger.info(generated.readme);
383
+ }
384
+
385
+ logger.info('\n' + '='.repeat(60));
386
+ logger.info('Preview complete. Run without --ai-preview to save files.');
387
+ logger.info('='.repeat(60));
388
+
389
+ return componentPath; // Return path without writing
390
+ }
391
+
339
392
  // Write component file
340
393
  try {
341
394
  await filesystem.writeFile(join(componentPath, `${sanitizedName}.tsx`), generated.component, 'utf8');
@@ -334,8 +334,11 @@ export async function generateHookFile(componentName, projectRoot, options = {})
334
334
  await filesystem.writeFile(indexFile, updatedContent, 'utf8');
335
335
  }
336
336
  } else {
337
- // Create new index
338
- const indexContent = generateComposablesIndex();
337
+ const indexContent = `/**
338
+ * Composable hooks (Atomix CLI)
339
+ */
340
+ export { ${hookName} } from './${hookName}';
341
+ `;
339
342
  await filesystem.writeFile(indexFile, indexContent, 'utf8');
340
343
  created.push(`${outputDir}/index.ts`);
341
344
  }
@@ -63,10 +63,11 @@ export const ITCSS_LAYERS = {
63
63
  * Generate SCSS settings file for a component
64
64
  */
65
65
  export function generateSettingsFile(componentName, options = {}) {
66
+ const dsPrefix = options.prefix || 'atomix';
66
67
  const {
67
- primaryColor = '--atomix-color-primary',
68
- spacingBase = '--atomix-spacing-4',
69
- radiusBase = '--atomix-radius-md'
68
+ primaryColor = `--${dsPrefix}-color-primary`,
69
+ spacingBase = `--${dsPrefix}-spacing-4`,
70
+ radiusBase = `--${dsPrefix}-radius-md`
70
71
  } = options;
71
72
 
72
73
  const componentPrefix = componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '');
@@ -97,19 +98,19 @@ export function generateSettingsFile(componentName, options = {}) {
97
98
  --${componentPrefix}-radius: var(${radiusBase});
98
99
 
99
100
  // Typography
100
- --${componentPrefix}-font-size: var(--atomix-font-size-base);
101
- --${componentPrefix}-font-weight: var(--atomix-font-weight-medium);
102
- --${componentPrefix}-line-height: var(--atomix-line-height-tight);
101
+ --${componentPrefix}-font-size: var(--${dsPrefix}-font-size-base);
102
+ --${componentPrefix}-font-weight: var(--${dsPrefix}-font-weight-medium);
103
+ --${componentPrefix}-line-height: var(--${dsPrefix}-line-height-tight);
103
104
 
104
105
  // States
105
106
  --${componentPrefix}-disabled-opacity: 0.5;
106
- --${componentPrefix}-focus-ring: 0 0 0 2px var(--atomix-color-primary-200);
107
+ --${componentPrefix}-focus-ring: 0 0 0 2px var(--${dsPrefix}-color-primary-200);
107
108
  }
108
109
 
109
110
  // Dark mode overrides
110
111
  .dark {
111
112
  :root {
112
- --${componentPrefix}-focus-ring: 0 0 0 2px var(--atomix-color-primary-800);
113
+ --${componentPrefix}-focus-ring: 0 0 0 2px var(--${dsPrefix}-color-primary-800);
113
114
  }
114
115
  }
115
116
 
@@ -131,6 +132,7 @@ $${componentPrefix}-config: (
131
132
  * Generate SCSS component styles file
132
133
  */
133
134
  export function generateComponentStyles(componentName, options = {}) {
135
+ const dsPrefix = options.prefix || 'atomix';
134
136
  const {
135
137
  baseClass = `c-${componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '')}`,
136
138
  hasVariants = true,
@@ -141,7 +143,7 @@ export function generateComponentStyles(componentName, options = {}) {
141
143
 
142
144
  const componentPrefix = componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '');
143
145
 
144
- return `// ${componentName} Component Styles
146
+ const scss = `// ${componentName} Component Styles
145
147
  // Generated by Atomix CLI
146
148
  // ITCSS Layer: Components
147
149
  // =============================================================================
@@ -354,6 +356,7 @@ export function generateComponentStyles(componentName, options = {}) {
354
356
  }
355
357
  }
356
358
  `;
359
+ return scss.replaceAll('--atomix-', `--${dsPrefix}-`);
357
360
  }
358
361
 
359
362
  /**
@@ -483,7 +486,8 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
483
486
  const {
484
487
  skipSettings = false,
485
488
  skipComponents = false,
486
- force = false
489
+ force = false,
490
+ prefix = 'atomix'
487
491
  } = options;
488
492
 
489
493
  const created = [];
@@ -498,7 +502,7 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
498
502
  const settingsFile = join(settingsPath, `_settings.${componentPrefix}.scss`);
499
503
 
500
504
  if (force || !existsSync(settingsFile)) {
501
- const settingsContent = generateSettingsFile(componentName);
505
+ const settingsContent = generateSettingsFile(componentName, { prefix });
502
506
  await filesystem.writeFile(settingsFile, settingsContent, 'utf8');
503
507
  created.push(`src/styles/01-settings/_settings.${componentPrefix}.scss`);
504
508
  logger.debug(`Created settings file: ${settingsFile}`);
@@ -513,7 +517,7 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
513
517
  const componentFile = join(componentsPath, `_components.${componentPrefix}.scss`);
514
518
 
515
519
  if (force || !existsSync(componentFile)) {
516
- const componentContent = generateComponentStyles(componentName);
520
+ const componentContent = generateComponentStyles(componentName, { prefix });
517
521
  await filesystem.writeFile(componentFile, componentContent, 'utf8');
518
522
  created.push(`src/styles/06-components/_components.${componentPrefix}.scss`);
519
523
  logger.debug(`Created component styles: ${componentFile}`);