@shohojdhara/atomix 0.5.2 → 0.5.5

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 (61) hide show
  1. package/atomix.config.ts +33 -33
  2. package/dist/atomix.css +3213 -159
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +5 -5
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/config.d.ts +187 -112
  7. package/dist/config.js +2 -47
  8. package/dist/config.js.map +1 -1
  9. package/dist/index.d.ts +1958 -900
  10. package/dist/index.esm.js +2279 -382
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/index.js +2332 -413
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.min.js +1 -1
  15. package/dist/index.min.js.map +1 -1
  16. package/dist/theme.d.ts +1390 -276
  17. package/dist/theme.js +2125 -621
  18. package/dist/theme.js.map +1 -1
  19. package/package.json +1 -1
  20. package/scripts/cli/internal/config-loader.js +30 -20
  21. package/src/lib/config/index.ts +38 -362
  22. package/src/lib/config/loader.ts +422 -0
  23. package/src/lib/config/public-api.ts +43 -0
  24. package/src/lib/config/types.ts +389 -0
  25. package/src/lib/config/validator.ts +305 -0
  26. package/src/lib/theme/adapters/index.ts +1 -1
  27. package/src/lib/theme/adapters/themeAdapter.ts +358 -229
  28. package/src/lib/theme/components/ThemeToggle.tsx +276 -0
  29. package/src/lib/theme/config/configLoader.ts +351 -0
  30. package/src/lib/theme/config/loader.ts +221 -0
  31. package/src/lib/theme/core/createTheme.ts +126 -50
  32. package/src/lib/theme/core/createThemeObject.ts +7 -4
  33. package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
  34. package/src/lib/theme/index.ts +322 -38
  35. package/src/lib/theme/runtime/ThemeProvider.tsx +44 -10
  36. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
  37. package/src/lib/theme/runtime/useTheme.ts +1 -0
  38. package/src/lib/theme/tokens/tokens.ts +101 -1
  39. package/src/lib/theme/types.ts +91 -0
  40. package/src/lib/theme/utils/performanceMonitor.ts +315 -0
  41. package/src/lib/theme/utils/responsive.ts +280 -0
  42. package/src/lib/theme/utils/themeUtils.ts +531 -117
  43. package/src/styles/01-settings/_settings.background.scss +34 -5
  44. package/src/styles/02-tools/_tools.background.scss +330 -52
  45. package/src/styles/05-objects/_objects.masonry-grid.scss +3 -3
  46. package/src/styles/06-components/_components.accordion.scss +2 -2
  47. package/src/styles/06-components/_components.badge.scss +1 -1
  48. package/src/styles/06-components/_components.button.scss +2 -2
  49. package/src/styles/06-components/_components.callout.scss +2 -2
  50. package/src/styles/06-components/_components.card.scss +1 -1
  51. package/src/styles/06-components/_components.dropdown.scss +1 -1
  52. package/src/styles/06-components/_components.dynamic-background.scss +69 -0
  53. package/src/styles/06-components/_components.edge-panel.scss +2 -2
  54. package/src/styles/06-components/_components.input.scss +3 -3
  55. package/src/styles/06-components/_components.messages.scss +6 -6
  56. package/src/styles/06-components/_components.modal.scss +1 -1
  57. package/src/styles/06-components/_components.navbar.scss +1 -1
  58. package/src/styles/06-components/_components.popover.scss +1 -1
  59. package/src/styles/06-components/_components.toggle.scss +1 -1
  60. package/src/styles/06-components/_components.tooltip.scss +3 -3
  61. package/src/styles/06-components/_index.scss +1 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Atomix Config Loader
3
+ *
4
+ * Helper functions to load atomix.config.ts from external projects.
5
+ * Now also supports atomix.config.js and atomix.config.json
6
+ */
7
+
8
+ import type { AtomixConfig } from './types';
9
+
10
+ /**
11
+ * Validate Atomix configuration structure
12
+ *
13
+ * Performs basic validation to catch common configuration errors early.
14
+ * Returns warnings for potential issues.
15
+ *
16
+ * @param config - Configuration object to validate
17
+ * @returns Array of validation warnings (empty if valid)
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { loadAtomixConfig, validateConfig } from '@shohojdhara/atomix/config';
22
+ *
23
+ * const config = loadAtomixConfig();
24
+ * const warnings = validateConfig(config);
25
+ * warnings.forEach(w => console.warn(w));
26
+ * ```
27
+ */
28
+ export function validateConfig(config: AtomixConfig): string[] {
29
+ const warnings: string[] = [];
30
+
31
+ // Check prefix format
32
+ if (config.prefix) {
33
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(config.prefix)) {
34
+ warnings.push(
35
+ `Invalid prefix "${config.prefix}". Prefix should start with a letter and contain only letters, numbers, and hyphens.\n` +
36
+ 'Example: "myapp", "brand-ui", "enterprise"'
37
+ );
38
+ }
39
+
40
+ if (config.prefix.length < 2) {
41
+ warnings.push(
42
+ `Prefix "${config.prefix}" is too short. Use at least 2 characters for clarity.\n` +
43
+ 'Example: "app" instead of "a"'
44
+ );
45
+ }
46
+ }
47
+
48
+ // Check theme structure
49
+ if (config.theme) {
50
+ // Warn if both extend and tokens are provided
51
+ if (config.theme.extend && config.theme.tokens) {
52
+ warnings.push(
53
+ 'Both theme.extend and theme.tokens are defined. theme.tokens will take precedence and completely replace the default token system.\n' +
54
+ 'If you want to extend defaults, remove theme.tokens and use only theme.extend.'
55
+ );
56
+ }
57
+
58
+ // Check extend structure
59
+ if (config.theme.extend) {
60
+ const extend = config.theme.extend;
61
+
62
+ // Check for common typos in theme properties
63
+ const validThemeKeys = [
64
+ 'colors', 'typography', 'spacing', 'borderRadius',
65
+ 'shadows', 'zIndex', 'transitions', 'breakpoints'
66
+ ];
67
+
68
+ Object.keys(extend).forEach(key => {
69
+ if (!validThemeKeys.includes(key)) {
70
+ warnings.push(
71
+ `Unknown theme property: "${key}"\n` +
72
+ `Valid properties: ${validThemeKeys.join(', ')}\n` +
73
+ 'Did you mean one of these? Check for typos.'
74
+ );
75
+ }
76
+ });
77
+ }
78
+ }
79
+
80
+ // Validate advanced features
81
+ if (config.interactiveEffects) {
82
+ const ie = config.interactiveEffects;
83
+
84
+ // Validate vortex settings
85
+ if (ie.vortex) {
86
+ if (ie.vortex.strength && (ie.vortex.strength < 0 || ie.vortex.strength > 10)) {
87
+ warnings.push('Vortex strength should be between 0 and 10 for optimal performance');
88
+ }
89
+ if (ie.vortex.radius && ie.vortex.radius < 0) {
90
+ warnings.push('Vortex radius should be a positive number');
91
+ }
92
+ if (ie.vortex.decay && (ie.vortex.decay <= 0 || ie.vortex.decay > 1)) {
93
+ warnings.push('Vortex decay should be between 0 and 1');
94
+ }
95
+ }
96
+
97
+ // Validate chromatic aberration settings
98
+ if (ie.chromaticAberration) {
99
+ if (ie.chromaticAberration.redShift && Math.abs(ie.chromaticAberration.redShift) > 0.1) {
100
+ warnings.push('Chromatic red shift value seems unusually high (>0.1), verify this is intended');
101
+ }
102
+ if (ie.chromaticAberration.blueShift && Math.abs(ie.chromaticAberration.blueShift) > 0.1) {
103
+ warnings.push('Chromatic blue shift value seems unusually high (>0.1), verify this is intended');
104
+ }
105
+ if (ie.chromaticAberration.edgeThreshold && (ie.chromaticAberration.edgeThreshold < 0 || ie.chromaticAberration.edgeThreshold > 1)) {
106
+ warnings.push('Chromatic edge threshold should be between 0 and 1');
107
+ }
108
+ }
109
+
110
+ // Validate mouse interaction settings
111
+ if (ie.mouseInteraction) {
112
+ if (ie.mouseInteraction.sensitivity && ie.mouseInteraction.sensitivity < 0) {
113
+ warnings.push('Mouse sensitivity should be a positive number');
114
+ }
115
+ }
116
+
117
+ // Validate animation speed settings
118
+ if (ie.animationSpeed) {
119
+ if (ie.animationSpeed.base && ie.animationSpeed.base <= 0) {
120
+ warnings.push('Animation base speed should be greater than 0');
121
+ }
122
+ if (ie.animationSpeed.timeMultiplier && ie.animationSpeed.timeMultiplier <= 0) {
123
+ warnings.push('Animation time multiplier should be greater than 0');
124
+ }
125
+ }
126
+ }
127
+
128
+ // Validate optimization settings
129
+ if (config.optimization) {
130
+ const opt = config.optimization;
131
+
132
+ // Validate responsive breakpoints
133
+ if (opt.responsive && opt.responsive.breakpoints) {
134
+ const breakpoints = opt.responsive.breakpoints;
135
+ if (breakpoints.mobile && !isValidCSSLength(breakpoints.mobile)) {
136
+ warnings.push('Mobile breakpoint value is not a valid CSS length');
137
+ }
138
+ if (breakpoints.tablet && !isValidCSSLength(breakpoints.tablet)) {
139
+ warnings.push('Tablet breakpoint value is not a valid CSS length');
140
+ }
141
+ if (breakpoints.desktop && !isValidCSSLength(breakpoints.desktop)) {
142
+ warnings.push('Desktop breakpoint value is not a valid CSS length');
143
+ }
144
+ if (breakpoints.wide && !isValidCSSLength(breakpoints.wide)) {
145
+ warnings.push('Wide breakpoint value is not a valid CSS length');
146
+ }
147
+ }
148
+
149
+ // Validate device scaling
150
+ if (opt.responsive && opt.responsive.deviceScaling) {
151
+ const scaling = opt.responsive.deviceScaling;
152
+ if (scaling.mobile && (scaling.mobile <= 0 || scaling.mobile > 1)) {
153
+ warnings.push('Mobile device scaling should be between 0 and 1');
154
+ }
155
+ if (scaling.tablet && (scaling.tablet <= 0 || scaling.tablet > 1)) {
156
+ warnings.push('Tablet device scaling should be between 0 and 1');
157
+ }
158
+ if (scaling.desktop && (scaling.desktop <= 0 || scaling.desktop > 1)) {
159
+ warnings.push('Desktop device scaling should be between 0 and 1');
160
+ }
161
+ }
162
+
163
+ // Validate performance settings
164
+ if (opt.performance) {
165
+ if (opt.performance.fpsTarget && (opt.performance.fpsTarget <= 0 || opt.performance.fpsTarget > 240)) {
166
+ warnings.push('FPS target should be a reasonable value (typically 30-120)');
167
+ }
168
+ }
169
+
170
+ // Validate auto-scaling thresholds
171
+ if (opt.autoScaling && opt.autoScaling.qualityThresholds) {
172
+ const thresholds = opt.autoScaling.qualityThresholds;
173
+ if (thresholds.lowEnd && (thresholds.lowEnd < 0 || thresholds.lowEnd > 1)) {
174
+ warnings.push('Auto-scaling low-end threshold should be between 0 and 1');
175
+ }
176
+ if (thresholds.midRange && (thresholds.midRange < 0 || thresholds.midRange > 1)) {
177
+ warnings.push('Auto-scaling mid-range threshold should be between 0 and 1');
178
+ }
179
+ if (thresholds.highEnd && (thresholds.highEnd < 0 || thresholds.highEnd > 1)) {
180
+ warnings.push('Auto-scaling high-end threshold should be between 0 and 1');
181
+ }
182
+ }
183
+ }
184
+
185
+ // Validate visual polish settings
186
+ if (config.visualPolish) {
187
+ const vp = config.visualPolish;
188
+
189
+ // Validate content aware blur settings
190
+ if (vp.contentAwareBlur) {
191
+ if (vp.contentAwareBlur.edgePreservation !== undefined && typeof vp.contentAwareBlur.edgePreservation !== 'boolean') {
192
+ warnings.push('Content-aware blur edge preservation should be a boolean value');
193
+ }
194
+ if (vp.contentAwareBlur.depthDetection !== undefined && typeof vp.contentAwareBlur.depthDetection !== 'boolean') {
195
+ warnings.push('Content-aware blur depth detection should be a boolean value');
196
+ }
197
+ }
198
+
199
+ // Validate holographic effects settings
200
+ if (vp.holographicEffects) {
201
+ if (vp.holographicEffects.enabled !== undefined && typeof vp.holographicEffects.enabled !== 'boolean') {
202
+ warnings.push('Holographic effects enabled should be a boolean value');
203
+ }
204
+ if (vp.holographicEffects.rainbowDiffraction !== undefined && typeof vp.holographicEffects.rainbowDiffraction !== 'boolean') {
205
+ warnings.push('Holographic rainbow diffraction should be a boolean value');
206
+ }
207
+ }
208
+ }
209
+
210
+ // Validate AI settings
211
+ if (config.ai) {
212
+ if (config.ai.provider && !['openai', 'anthropic'].includes(config.ai.provider)) {
213
+ warnings.push(`Unknown AI provider: "${config.ai.provider}". Supported: openai, anthropic`);
214
+ }
215
+ if (config.ai.temperature && (config.ai.temperature < 0 || config.ai.temperature > 1)) {
216
+ warnings.push('AI temperature should be between 0 and 1');
217
+ }
218
+ if (config.ai.maxTokens && config.ai.maxTokens < 100) {
219
+ warnings.push('AI maxTokens should typically be 100 or more');
220
+ }
221
+ if (config.ai.rateLimit) {
222
+ if (config.ai.rateLimit.requests <= 0) {
223
+ warnings.push('AI rate limit requests should be greater than 0');
224
+ }
225
+ if (config.ai.rateLimit.windowMs <= 0) {
226
+ warnings.push('AI rate limit window should be greater than 0');
227
+ }
228
+ }
229
+ }
230
+
231
+ // Validate telemetry settings
232
+ if (config.telemetry) {
233
+ if (config.telemetry.path && !config.telemetry.path.endsWith('.json')) {
234
+ warnings.push('Telemetry path should typically end with .json');
235
+ }
236
+ }
237
+
238
+ return warnings;
239
+ }
240
+
241
+ /**
242
+ * Helper function to validate CSS length values
243
+ */
244
+ function isValidCSSLength(value: string): boolean {
245
+ // Basic validation for CSS length values
246
+ const cssLengthRegex = /^(\d+(\.\d+)?)(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ex|ch)?$/;
247
+ return cssLengthRegex.test(value);
248
+ }
249
+
250
+ /**
251
+ * Load Atomix configuration from project root
252
+ *
253
+ * Attempts to load atomix.config.ts, atomix.config.js, or atomix.config.json from the current working directory.
254
+ * Falls back to default config if file doesn't exist.
255
+ *
256
+ * @param options - Loader options
257
+ * @returns Loaded configuration or default
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * import { loadAtomixConfig } from '@shohojdhara/atomix/config';
262
+ * import { createTheme } from '@shohojdhara/atomix/theme';
263
+ *
264
+ * const config = loadAtomixConfig();
265
+ * const theme = createTheme(config.theme?.tokens || {});
266
+ * ```
267
+ */
268
+ export function loadAtomixConfig(
269
+ options: {
270
+ /** Custom config path (default: 'atomix.config.ts') */
271
+ configPath?: string;
272
+ /** Whether to throw error if config not found (default: false) */
273
+ required?: boolean;
274
+ } = {}
275
+ ): AtomixConfig {
276
+ const { configPath, required = false } = options;
277
+
278
+ // Default config
279
+ const defaultConfig: AtomixConfig = {
280
+ prefix: 'atomix',
281
+ theme: {
282
+ extend: {},
283
+ },
284
+ };
285
+
286
+ // In browser environments, config loading is not supported
287
+ if (typeof window !== 'undefined') {
288
+ if (required) {
289
+ throw new Error(
290
+ 'Config loading requires Node.js file system access.\n' +
291
+ '\n' +
292
+ 'Solutions:\n' +
293
+ '1. Provide tokens explicitly to createTheme():\n' +
294
+ ' const css = createTheme({ "--brand-primary": "#6366f1" });\n' +
295
+ '\n' +
296
+ '2. Use SSR framework (Next.js, Remix, Astro)\n' +
297
+ '\n' +
298
+ '3. Load config on server and pass to client\n' +
299
+ '\n' +
300
+ 'See examples/config-examples/browser-only.config.ts'
301
+ );
302
+ }
303
+ return defaultConfig;
304
+ }
305
+
306
+ // If a specific config path is provided, try to load it directly
307
+ if (configPath) {
308
+ return loadConfigAtPath(configPath, required, defaultConfig);
309
+ }
310
+
311
+ // Otherwise, try standard locations in order of preference
312
+ const possiblePaths = [
313
+ 'atomix.config.ts',
314
+ 'atomix.config.js',
315
+ 'atomix.config.json'
316
+ ];
317
+
318
+ for (const path of possiblePaths) {
319
+ const config = loadConfigAtPath(path, false, defaultConfig);
320
+ // If we found a valid config, return it
321
+ if (JSON.stringify(config) !== JSON.stringify(defaultConfig)) {
322
+ return config;
323
+ }
324
+ }
325
+
326
+ // If no config file was found or all contained only defaults, return default config
327
+ if (required) {
328
+ throw new Error(
329
+ `No Atomix configuration file found in project root.\n` +
330
+ '\n' +
331
+ 'Expected one of:\n' +
332
+ ' - atomix.config.ts (recommended)\n' +
333
+ ' - atomix.config.js\n' +
334
+ ' - atomix.config.json\n' +
335
+ '\n' +
336
+ 'Quick Fix:\n' +
337
+ '1. Create a config file in your project root:\n' +
338
+ ' touch atomix.config.ts\n' +
339
+ '\n' +
340
+ '2. Add basic configuration:\n' +
341
+ ' import { defineConfig } from "@shohojdhara/atomix/config";\n' +
342
+ ' export default defineConfig({ prefix: "myapp" });\n' +
343
+ '\n' +
344
+ '3. Or copy an example:\n' +
345
+ ' cp node_modules/@shohojdhara/atomix/examples/config-examples/standard.config.ts ./atomix.config.ts'
346
+ );
347
+ }
348
+
349
+ return defaultConfig;
350
+ }
351
+
352
+ /**
353
+ * Helper function to load config from a specific path
354
+ */
355
+ function loadConfigAtPath(path: string, required: boolean, defaultConfig: AtomixConfig): AtomixConfig {
356
+ try {
357
+ // Use dynamic import for ESM compatibility
358
+ const configModule = require(path);
359
+ const config = configModule.default || configModule;
360
+
361
+ // Validate it's an AtomixConfig
362
+ if (config && typeof config === 'object') {
363
+ return config as AtomixConfig;
364
+ }
365
+
366
+ throw new Error('Invalid config format');
367
+ } catch (error: any) {
368
+ if (required) {
369
+ throw new Error(`Failed to load config from ${path}: ${error.message}`);
370
+ }
371
+
372
+ // Return default config if not required
373
+ return defaultConfig;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Resolve config path
379
+ *
380
+ * Finds atomix.config.ts in the project, checking common locations.
381
+ * Returns null in browser environments where file system access is not available.
382
+ *
383
+ * This function is designed to help tools identify if a config exists without loading it.
384
+ *
385
+ * @param configPath - Optional custom path to check
386
+ * @returns Absolute path to config file or null if not found
387
+ */
388
+ export function resolveConfigPath(configPath?: string): string | null {
389
+ // In browser environments, config resolution is not possible
390
+ if (typeof window !== 'undefined') {
391
+ return null;
392
+ }
393
+
394
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
395
+ const { existsSync } = require('fs') as typeof import('fs');
396
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
397
+ const { join } = require('path') as typeof import('path');
398
+
399
+ // If a specific config path is provided, check if it exists
400
+ if (configPath) {
401
+ const absPath = join(process.cwd(), configPath);
402
+ if (existsSync(absPath)) {
403
+ return absPath;
404
+ }
405
+ return null;
406
+ }
407
+
408
+ // Otherwise, check standard locations
409
+ const possiblePaths = [
410
+ join(process.cwd(), 'atomix.config.ts'),
411
+ join(process.cwd(), 'atomix.config.js'),
412
+ join(process.cwd(), 'atomix.config.json')
413
+ ];
414
+
415
+ for (const path of possiblePaths) {
416
+ if (existsSync(path)) {
417
+ return path;
418
+ }
419
+ }
420
+
421
+ return null;
422
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Public API for loading and managing Atomix configuration
3
+ *
4
+ * This module provides the public-facing API for configuration loading
5
+ * in external projects.
6
+ */
7
+
8
+ import type { AtomixConfig } from './types';
9
+ import { loadAtomixConfig as internalLoadConfig, validateConfig as internalValidateConfig } from './loader';
10
+
11
+ /**
12
+ * Load Atomix configuration from an external project.
13
+ *
14
+ * @param options - Loading options
15
+ * @returns The loaded configuration
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { loadConfig } from '@shohojdhara/atomix/config';
20
+ *
21
+ * const config = loadConfig();
22
+ * console.log(config.prefix); // 'atomix' or user's custom prefix
23
+ * ```
24
+ */
25
+ export function loadConfig(options?: {
26
+ configPath?: string;
27
+ required?: boolean;
28
+ }): AtomixConfig {
29
+ return internalLoadConfig({
30
+ configPath: options?.configPath,
31
+ required: options?.required ?? false,
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Validate Atomix configuration structure.
37
+ *
38
+ * @param config - Configuration object to validate
39
+ * @returns Array of validation warnings (empty if valid)
40
+ */
41
+ export function validateConfig(config: AtomixConfig): string[] {
42
+ return internalValidateConfig(config);
43
+ }