@shohojdhara/atomix 0.3.5 → 0.3.7

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 (182) hide show
  1. package/README.md +101 -199
  2. package/atomix.config.ts +241 -0
  3. package/dist/atomix.css +260 -179
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +250 -179
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/charts.js +69 -166
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.js +184 -263
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js +55 -131
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +184 -263
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +1831 -1657
  16. package/dist/index.esm.js +4497 -4318
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +4510 -4328
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/dist/theme.d.ts +1431 -1472
  23. package/dist/theme.js +4175 -4138
  24. package/dist/theme.js.map +1 -1
  25. package/package.json +6 -20
  26. package/src/components/Accordion/Accordion.stories.tsx +50 -17
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +128 -322
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +12 -5
  29. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
  31. package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
  32. package/src/components/Avatar/Avatar.stories.tsx +7 -0
  33. package/src/components/Badge/Badge.stories.tsx +91 -13
  34. package/src/components/Block/Block.stories.tsx +7 -23
  35. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
  36. package/src/components/Button/Button.stories.tsx +141 -22
  37. package/src/components/Button/Button.tsx +85 -167
  38. package/src/components/Button/ButtonGroup.stories.tsx +315 -0
  39. package/src/components/Button/ButtonGroup.tsx +67 -0
  40. package/src/components/Button/index.ts +2 -0
  41. package/src/components/Callout/Callout.stories.tsx +8 -6
  42. package/src/components/Card/Card.stories.tsx +82 -28
  43. package/src/components/Chart/AnimatedChart.tsx +0 -1
  44. package/src/components/Chart/AreaChart.tsx +0 -1
  45. package/src/components/Chart/BarChart.tsx +0 -1
  46. package/src/components/Chart/BubbleChart.tsx +0 -1
  47. package/src/components/Chart/CandlestickChart.tsx +0 -1
  48. package/src/components/Chart/Chart.stories.tsx +5 -7
  49. package/src/components/Chart/Chart.tsx +0 -16
  50. package/src/components/Chart/ChartRenderer.tsx +1 -1
  51. package/src/components/Chart/DonutChart.tsx +0 -1
  52. package/src/components/Chart/FunnelChart.tsx +0 -1
  53. package/src/components/Chart/GaugeChart.tsx +0 -1
  54. package/src/components/Chart/HeatmapChart.tsx +0 -1
  55. package/src/components/Chart/LineChart.tsx +0 -1
  56. package/src/components/Chart/MultiAxisChart.tsx +0 -1
  57. package/src/components/Chart/PieChart.tsx +0 -1
  58. package/src/components/Chart/RadarChart.tsx +0 -1
  59. package/src/components/Chart/ScatterChart.tsx +0 -1
  60. package/src/components/Chart/WaterfallChart.tsx +0 -1
  61. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
  62. package/src/components/DataTable/DataTable.stories.tsx +23 -16
  63. package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
  64. package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
  65. package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
  66. package/src/components/Footer/Footer.stories.tsx +8 -6
  67. package/src/components/Footer/FooterLink.tsx +9 -2
  68. package/src/components/Form/Checkbox.stories.tsx +7 -0
  69. package/src/components/Form/Form.stories.tsx +7 -0
  70. package/src/components/Form/FormGroup.stories.tsx +9 -1
  71. package/src/components/Form/Input.stories.tsx +69 -16
  72. package/src/components/Form/Radio.stories.tsx +9 -1
  73. package/src/components/Form/Select.stories.tsx +9 -1
  74. package/src/components/Form/Textarea.stories.tsx +10 -2
  75. package/src/components/Hero/Hero.stories.tsx +7 -0
  76. package/src/components/List/List.stories.tsx +7 -0
  77. package/src/components/Messages/Messages.stories.tsx +8 -7
  78. package/src/components/Modal/Modal.stories.tsx +17 -6
  79. package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
  80. package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
  81. package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
  82. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
  83. package/src/components/Pagination/Pagination.stories.tsx +188 -111
  84. package/src/components/Pagination/Pagination.tsx +83 -3
  85. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
  86. package/src/components/Popover/Popover.stories.tsx +191 -115
  87. package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
  88. package/src/components/Progress/Progress.stories.tsx +79 -49
  89. package/src/components/Rating/Rating.stories.tsx +109 -84
  90. package/src/components/River/River.stories.tsx +194 -114
  91. package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
  92. package/src/components/Slider/Slider.stories.tsx +7 -0
  93. package/src/components/Spinner/Spinner.stories.tsx +15 -11
  94. package/src/components/Steps/Steps.stories.tsx +132 -98
  95. package/src/components/Tabs/Tabs.stories.tsx +163 -112
  96. package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
  97. package/src/components/Todo/Todo.stories.tsx +38 -12
  98. package/src/components/Toggle/Toggle.stories.tsx +61 -28
  99. package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
  100. package/src/components/Upload/Upload.stories.tsx +122 -84
  101. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
  102. package/src/components/index.ts +1 -0
  103. package/src/lib/composables/useAtomixGlass.ts +9 -10
  104. package/src/lib/composables/useNavbar.ts +0 -10
  105. package/src/lib/config/loader.ts +4 -4
  106. package/src/lib/constants/components.ts +17 -0
  107. package/src/lib/hooks/useComponentCustomization.ts +1 -1
  108. package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
  109. package/src/lib/hooks/useThemeTokens.ts +105 -0
  110. package/src/lib/theme/README.md +174 -0
  111. package/src/lib/theme/adapters/index.ts +31 -0
  112. package/src/lib/theme/adapters/themeAdapter.ts +287 -0
  113. package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
  114. package/src/lib/theme/config/configLoader.ts +95 -0
  115. package/src/lib/theme/config/loader.ts +37 -54
  116. package/src/lib/theme/config/types.ts +2 -2
  117. package/src/lib/theme/config/validator.ts +15 -91
  118. package/src/lib/theme/{constants.ts → constants/constants.ts} +1 -19
  119. package/src/lib/theme/constants/index.ts +8 -0
  120. package/src/lib/theme/core/ThemeRegistry.ts +75 -266
  121. package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
  122. package/src/lib/theme/core/composeTheme.ts +105 -0
  123. package/src/lib/theme/core/createTheme.ts +108 -0
  124. package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +12 -8
  125. package/src/lib/theme/core/index.ts +19 -19
  126. package/src/lib/theme/devtools/Comparator.tsx +346 -22
  127. package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
  128. package/src/lib/theme/devtools/Inspector.tsx +335 -51
  129. package/src/lib/theme/devtools/LiveEditor.tsx +478 -107
  130. package/src/lib/theme/devtools/Preview.tsx +471 -221
  131. package/src/lib/theme/{core → devtools}/ThemeValidator.ts +1 -1
  132. package/src/lib/theme/devtools/index.ts +14 -4
  133. package/src/lib/theme/devtools/useHistory.ts +130 -0
  134. package/src/lib/theme/{errors.ts → errors/errors.ts} +1 -1
  135. package/src/lib/theme/errors/index.ts +12 -0
  136. package/src/lib/theme/generators/cssFile.ts +79 -0
  137. package/src/lib/theme/generators/generateCSS.ts +89 -0
  138. package/src/lib/theme/generators/generateCSSNested.ts +130 -0
  139. package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +3 -13
  140. package/src/lib/theme/generators/index.ts +25 -0
  141. package/src/lib/theme/i18n/rtl.ts +5 -6
  142. package/src/lib/theme/index.ts +149 -19
  143. package/src/lib/theme/runtime/ThemeApplicator.ts +53 -112
  144. package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
  145. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +5 -5
  146. package/src/lib/theme/runtime/ThemeProvider.tsx +266 -282
  147. package/src/lib/theme/runtime/index.ts +2 -2
  148. package/src/lib/theme/runtime/useTheme.ts +1 -2
  149. package/src/lib/theme/runtime/useThemeTokens.ts +131 -0
  150. package/src/lib/theme/test/testTheme.ts +385 -0
  151. package/src/lib/theme/tokens/index.ts +12 -0
  152. package/src/lib/theme/tokens/tokens.ts +721 -0
  153. package/src/lib/theme/types.ts +6 -42
  154. package/src/lib/theme/utils/componentTheming.ts +132 -0
  155. package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
  156. package/src/lib/theme/utils/index.ts +11 -0
  157. package/src/lib/theme/utils/injectCSS.ts +90 -0
  158. package/src/lib/theme/utils/naming.ts +100 -0
  159. package/src/lib/theme/utils/themeHelpers.ts +78 -0
  160. package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +7 -7
  161. package/src/lib/theme-tools.ts +7 -8
  162. package/src/lib/types/components.ts +40 -130
  163. package/src/lib/utils/componentUtils.ts +2 -2
  164. package/src/lib/utils/memoryMonitor.ts +3 -3
  165. package/src/lib/utils/themeNaming.ts +135 -0
  166. package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
  167. package/src/styles/02-tools/_tools.button.scss +66 -79
  168. package/src/styles/06-components/_components.atomix-glass.scss +13 -3
  169. package/src/styles/06-components/_components.pagination.scss +88 -0
  170. package/scripts/sync-theme-config.js +0 -309
  171. package/src/lib/theme/composeTheme.ts +0 -370
  172. package/src/lib/theme/core/ThemeCache.ts +0 -283
  173. package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
  174. package/src/lib/theme/core/ThemeEngine.ts +0 -665
  175. package/src/lib/theme/createThemeFromConfig.ts +0 -132
  176. package/src/lib/theme/devtools/CLI.ts +0 -364
  177. package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
  178. package/src/lib/theme/runtime/ThemeManager.ts +0 -446
  179. package/src/styles/03-generic/_generated-root.css +0 -26
  180. package/src/themes/README.md +0 -442
  181. package/src/themes/themes.config.js +0 -68
  182. /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
@@ -1,132 +0,0 @@
1
- /**
2
- * Create Theme from Atomix Config
3
- *
4
- * Helper function to create a theme from atomix.config.ts,
5
- * similar to how Tailwind processes its config.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { createThemeFromConfig } from '@shohojdhara/atomix/theme';
10
- * import config from './atomix.config';
11
- *
12
- * const theme = createThemeFromConfig(config);
13
- * ```
14
- */
15
-
16
- import type { AtomixConfig } from '../config';
17
- import { createTheme } from './createTheme';
18
- import type { ThemeOptions } from './types';
19
-
20
- /**
21
- * Convert config tokens to theme options
22
- */
23
- function configToThemeOptions(config: AtomixConfig): ThemeOptions {
24
- const tokens = config.theme?.tokens || config.theme?.extend || {};
25
- const options: ThemeOptions = {};
26
-
27
- // Convert colors
28
- if (tokens.colors) {
29
- options.palette = {};
30
-
31
- // Handle primary color
32
- if (tokens.colors.primary) {
33
- const primary = tokens.colors.primary;
34
- if (typeof primary === 'string') {
35
- options.palette.primary = { main: primary };
36
- } else if (typeof primary === 'object' && 'main' in primary) {
37
- options.palette.primary = primary as any;
38
- } else if (typeof primary === 'object' && '6' in primary) {
39
- // Color scale format
40
- options.palette.primary = { main: (primary as any)[6] || (primary as any).main };
41
- }
42
- }
43
-
44
- // Handle other colors
45
- if (tokens.colors) {
46
- const colorKeys = ['secondary', 'error', 'warning', 'info', 'success'] as const;
47
- colorKeys.forEach((key) => {
48
- if (tokens.colors![key]) {
49
- const color = tokens.colors![key];
50
- if (typeof color === 'string') {
51
- options.palette![key] = { main: color };
52
- } else if (typeof color === 'object' && 'main' in color) {
53
- options.palette![key] = color as any;
54
- } else if (typeof color === 'object' && '6' in color) {
55
- options.palette![key] = { main: (color as any)[6] || (color as any).main };
56
- }
57
- }
58
- });
59
- }
60
- }
61
-
62
- // Convert typography
63
- if (tokens.typography) {
64
- options.typography = {};
65
-
66
- if (tokens.typography.fontFamilies?.sans) {
67
- options.typography.fontFamily = Array.isArray(tokens.typography.fontFamilies.sans)
68
- ? tokens.typography.fontFamilies.sans.join(', ')
69
- : tokens.typography.fontFamilies.sans;
70
- }
71
-
72
- if (tokens.typography.fontSizes) {
73
- // Map font sizes to theme typography
74
- const baseSize = tokens.typography.fontSizes.base || tokens.typography.fontSizes.md;
75
- if (baseSize) {
76
- const numericSize = parseFloat(String(baseSize).replace(/[^\d.]/g, ''));
77
- options.typography.fontSize = numericSize || 16;
78
- }
79
- }
80
- }
81
-
82
- // Convert spacing (if needed for theme spacing function)
83
- if (tokens.spacing) {
84
- // Spacing is handled via CSS variables, but we can set a base multiplier
85
- options.spacing = 4; // Default 4px base unit
86
- }
87
-
88
- // Convert border radius
89
- if (tokens.borderRadius) {
90
- options.borderRadius = {};
91
- Object.entries(tokens.borderRadius).forEach(([key, value]) => {
92
- options.borderRadius![key as keyof typeof options.borderRadius] = String(value);
93
- });
94
- }
95
-
96
- return options;
97
- }
98
-
99
- /**
100
- * Create a theme from Atomix configuration
101
- *
102
- * This function converts atomix.config.ts format to a theme object
103
- * that can be used with ThemeProvider.
104
- *
105
- * @param config - Atomix configuration object
106
- * @returns Theme object ready for use
107
- *
108
- * @example
109
- * ```typescript
110
- * import { createThemeFromConfig } from '@shohojdhara/atomix/theme';
111
- * import config from './atomix.config';
112
- *
113
- * const theme = createThemeFromConfig(config);
114
- * ```
115
- */
116
- export function createThemeFromConfig(config: AtomixConfig) {
117
- const themeOptions = configToThemeOptions(config);
118
-
119
- // Create theme with options
120
- const theme = createTheme(themeOptions);
121
-
122
- // Apply prefix if specified
123
- if (config.prefix && config.prefix !== 'atomix') {
124
- // Note: Prefix is applied when generating CSS variables
125
- // The theme object itself doesn't store prefix
126
- }
127
-
128
- return theme;
129
- }
130
-
131
- export default createThemeFromConfig;
132
-
@@ -1,364 +0,0 @@
1
- /**
2
- * Theme CLI
3
- *
4
- * Command-line interface for theme management
5
- */
6
-
7
- import { loadThemeConfig } from '../config/loader';
8
- import { validateConfig } from '../config/validator';
9
-
10
- /**
11
- * CLI command interface
12
- */
13
- export interface CLICommand {
14
- name: string;
15
- description: string;
16
- options?: Record<string, string>;
17
- handler: (args: string[], options: Record<string, any>) => Promise<void> | void;
18
- }
19
-
20
- /**
21
- * Theme CLI
22
- *
23
- * Command-line interface for theme operations
24
- */
25
- export class ThemeCLI {
26
- private commands: Map<string, CLICommand> = new Map();
27
-
28
- constructor() {
29
- this.registerDefaultCommands();
30
- }
31
-
32
- /**
33
- * Register default commands
34
- */
35
- private registerDefaultCommands(): void {
36
- this.register({
37
- name: 'validate',
38
- description: 'Validate theme configuration',
39
- options: {
40
- '--config': 'Path to config file',
41
- '--strict': 'Enable strict validation',
42
- },
43
- handler: this.handleValidate.bind(this),
44
- });
45
-
46
- this.register({
47
- name: 'list',
48
- description: 'List all available themes',
49
- handler: this.handleList.bind(this),
50
- });
51
-
52
- this.register({
53
- name: 'inspect',
54
- description: 'Inspect a specific theme',
55
- options: {
56
- '--theme': 'Theme name to inspect',
57
- '--json': 'Output as JSON',
58
- },
59
- handler: this.handleInspect.bind(this),
60
- });
61
-
62
- this.register({
63
- name: 'compare',
64
- description: 'Compare two themes',
65
- options: {
66
- '--theme1': 'First theme name',
67
- '--theme2': 'Second theme name',
68
- },
69
- handler: this.handleCompare.bind(this),
70
- });
71
-
72
- this.register({
73
- name: 'export',
74
- description: 'Export theme to JSON',
75
- options: {
76
- '--theme': 'Theme name to export',
77
- '--output': 'Output file path',
78
- },
79
- handler: this.handleExport.bind(this),
80
- });
81
-
82
- this.register({
83
- name: 'help',
84
- description: 'Show help information',
85
- handler: this.handleHelp.bind(this),
86
- });
87
- }
88
-
89
- /**
90
- * Register a command
91
- */
92
- register(command: CLICommand): void {
93
- this.commands.set(command.name, command);
94
- }
95
-
96
- /**
97
- * Run CLI with arguments
98
- */
99
- async run(args: string[]): Promise<void> {
100
- const [commandName, ...commandArgs] = args;
101
-
102
- if (!commandName) {
103
- this.handleHelp([], {});
104
- return;
105
- }
106
-
107
- const command = this.commands.get(commandName);
108
- if (!command) {
109
- console.error(`Unknown command: ${commandName}`);
110
- console.error('Run "atomix-theme help" for available commands');
111
- process.exit(1);
112
- }
113
-
114
- try {
115
- const { args: parsedArgs, options } = this.parseArgs(commandArgs);
116
- await command.handler(parsedArgs, options);
117
- } catch (error) {
118
- console.error(`Error running command "${commandName}":`, error);
119
- process.exit(1);
120
- }
121
- }
122
-
123
- /**
124
- * Parse command arguments
125
- */
126
- private parseArgs(args: string[]): { args: string[]; options: Record<string, any> } {
127
- const parsedArgs: string[] = [];
128
- const options: Record<string, any> = {};
129
-
130
- for (let i = 0; i < args.length; i++) {
131
- const arg = args[i];
132
- if (arg?.startsWith('--')) {
133
- const key = arg.slice(2);
134
- const nextArg = args[i + 1];
135
- if (nextArg && !nextArg.startsWith('--')) {
136
- options[key] = nextArg;
137
- i++; // Skip next argument
138
- } else {
139
- options[key] = true;
140
- }
141
- } else if (arg) {
142
- parsedArgs.push(arg);
143
- }
144
- }
145
-
146
- return { args: parsedArgs, options };
147
- }
148
-
149
- /**
150
- * Handle validate command
151
- */
152
- private handleValidate(args: string[], options: Record<string, any>): void {
153
- try {
154
- const config = loadThemeConfig();
155
- const result = validateConfig(config);
156
-
157
- if (result.valid) {
158
- console.log('✅ Theme configuration is valid');
159
- if (result.warnings.length > 0) {
160
- console.log('\n⚠️ Warnings:');
161
- result.warnings.forEach(warning => console.log(` - ${warning}`));
162
- }
163
- } else {
164
- console.log('❌ Theme configuration is invalid');
165
- console.log('\nErrors:');
166
- result.errors.forEach(error => console.log(` - ${error}`));
167
-
168
- if (result.warnings.length > 0) {
169
- console.log('\nWarnings:');
170
- result.warnings.forEach(warning => console.log(` - ${warning}`));
171
- }
172
- process.exit(1);
173
- }
174
- } catch (error) {
175
- console.error('Failed to load theme configuration:', error);
176
- process.exit(1);
177
- }
178
- }
179
-
180
- /**
181
- * Handle list command
182
- */
183
- private handleList(args: string[], options: Record<string, any>): void {
184
- try {
185
- const config = loadThemeConfig();
186
- const themes = config.themes || {};
187
-
188
- console.log('Available Themes:\n');
189
-
190
- if (Object.keys(themes).length === 0) {
191
- console.log('No themes found in configuration.');
192
- return;
193
- }
194
-
195
- for (const [id, theme] of Object.entries(themes)) {
196
- console.log(` ${id}`);
197
- console.log(` Name: ${theme.name}`);
198
- if (theme.description) {
199
- console.log(` Description: ${theme.description}`);
200
- }
201
- if (theme.version) {
202
- console.log(` Version: ${theme.version}`);
203
- }
204
- if (theme.status) {
205
- console.log(` Status: ${theme.status}`);
206
- }
207
- console.log();
208
- }
209
- } catch (error) {
210
- console.error('Failed to list themes:', error);
211
- process.exit(1);
212
- }
213
- }
214
-
215
- /**
216
- * Handle inspect command
217
- */
218
- private handleInspect(args: string[], options: Record<string, any>): void {
219
- const themeName = options.theme || args[0];
220
-
221
- if (!themeName) {
222
- console.error('Error: Theme name is required');
223
- console.error('Usage: atomix-theme inspect --theme <theme-name>');
224
- process.exit(1);
225
- }
226
-
227
- try {
228
- const config = loadThemeConfig();
229
- const theme = config.themes?.[themeName];
230
-
231
- if (!theme) {
232
- console.error(`Error: Theme "${themeName}" not found`);
233
- process.exit(1);
234
- }
235
-
236
- if (options.json) {
237
- console.log(JSON.stringify(theme, null, 2));
238
- } else {
239
- console.log(`Theme: ${themeName}\n`);
240
- console.log(JSON.stringify(theme, null, 2));
241
- }
242
- } catch (error) {
243
- console.error('Failed to inspect theme:', error);
244
- process.exit(1);
245
- }
246
- }
247
-
248
- /**
249
- * Handle compare command
250
- */
251
- private handleCompare(args: string[], options: Record<string, any>): void {
252
- const theme1 = options.theme1 || args[0];
253
- const theme2 = options.theme2 || args[1];
254
-
255
- if (!theme1 || !theme2) {
256
- console.error('Error: Two theme names are required');
257
- console.error('Usage: atomix-theme compare --theme1 <name1> --theme2 <name2>');
258
- process.exit(1);
259
- }
260
-
261
- try {
262
- const config = loadThemeConfig();
263
- const themeA = config.themes?.[theme1];
264
- const themeB = config.themes?.[theme2];
265
-
266
- if (!themeA) {
267
- console.error(`Error: Theme "${theme1}" not found`);
268
- process.exit(1);
269
- }
270
-
271
- if (!themeB) {
272
- console.error(`Error: Theme "${theme2}" not found`);
273
- process.exit(1);
274
- }
275
-
276
- console.log(`Comparing: ${theme1} vs ${theme2}\n`);
277
- console.log('Differences:');
278
-
279
- // Simple comparison (could be enhanced)
280
- const keys = new Set([...Object.keys(themeA), ...Object.keys(themeB)]);
281
-
282
- for (const key of keys) {
283
- const valueA = (themeA as any)[key];
284
- const valueB = (themeB as any)[key];
285
-
286
- if (JSON.stringify(valueA) !== JSON.stringify(valueB)) {
287
- console.log(`\n ${key}:`);
288
- console.log(` ${theme1}: ${JSON.stringify(valueA)}`);
289
- console.log(` ${theme2}: ${JSON.stringify(valueB)}`);
290
- }
291
- }
292
- } catch (error) {
293
- console.error('Failed to compare themes:', error);
294
- process.exit(1);
295
- }
296
- }
297
-
298
- /**
299
- * Handle export command
300
- */
301
- private handleExport(args: string[], options: Record<string, any>): void {
302
- const themeName = options.theme || args[0];
303
- const outputPath = options.output || `${themeName}.json`;
304
-
305
- if (!themeName) {
306
- console.error('Error: Theme name is required');
307
- console.error('Usage: atomix-theme export --theme <theme-name> [--output <path>]');
308
- process.exit(1);
309
- }
310
-
311
- try {
312
- const config = loadThemeConfig();
313
- const theme = config.themes?.[themeName];
314
-
315
- if (!theme) {
316
- console.error(`Error: Theme "${themeName}" not found`);
317
- process.exit(1);
318
- }
319
-
320
- const fs = require('fs');
321
- fs.writeFileSync(outputPath, JSON.stringify(theme, null, 2));
322
- console.log(`✅ Theme exported to: ${outputPath}`);
323
- } catch (error) {
324
- console.error('Failed to export theme:', error);
325
- process.exit(1);
326
- }
327
- }
328
-
329
- /**
330
- * Handle help command
331
- */
332
- private handleHelp(args: string[], options: Record<string, any>): void {
333
- console.log('Atomix Theme CLI\n');
334
- console.log('Usage: atomix-theme <command> [options]\n');
335
- console.log('Commands:');
336
-
337
- for (const [name, command] of this.commands.entries()) {
338
- console.log(` ${name.padEnd(12)} ${command.description}`);
339
-
340
- if (command.options) {
341
- for (const [option, description] of Object.entries(command.options)) {
342
- console.log(` ${option.padEnd(16)} ${description}`);
343
- }
344
- }
345
- console.log();
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * Create CLI instance
352
- */
353
- export function createCLI(): ThemeCLI {
354
- return new ThemeCLI();
355
- }
356
-
357
- /**
358
- * Run CLI with process arguments
359
- */
360
- export function runCLI(): void {
361
- const cli = createCLI();
362
- const args = process.argv.slice(2);
363
- cli.run(args);
364
- }
@@ -1,192 +0,0 @@
1
- /**
2
- * ThemeManager Tests
3
- *
4
- * Tests for the ThemeManager class
5
- */
6
-
7
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
- import { ThemeManager } from './ThemeManager';
9
- import { ThemeError, ThemeErrorCode } from '../errors';
10
- import type { ThemeMetadata } from '../types';
11
-
12
- // Mock dependencies
13
- vi.mock('../core/ThemeEngine', () => {
14
- return {
15
- ThemeEngine: class {
16
- initialize = vi.fn().mockResolvedValue(undefined);
17
- on = vi.fn();
18
- getRegistry = vi.fn().mockReturnValue({
19
- has: vi.fn().mockReturnValue(false),
20
- register: vi.fn(),
21
- getAllMetadata: vi.fn().mockReturnValue([]),
22
- });
23
- setTheme = vi.fn().mockResolvedValue(undefined);
24
- getActiveTheme = vi.fn().mockReturnValue(null);
25
- isThemeLoaded = vi.fn().mockReturnValue(false);
26
- preloadTheme = vi.fn().mockResolvedValue(undefined);
27
- },
28
- };
29
- });
30
- vi.mock('../config/loader');
31
- vi.mock('../utils', () => ({
32
- isBrowser: () => true,
33
- isServer: () => false,
34
- createLocalStorageAdapter: () => ({
35
- getItem: vi.fn(),
36
- setItem: vi.fn(),
37
- removeItem: vi.fn(),
38
- isAvailable: () => true,
39
- }),
40
- }));
41
-
42
- describe('ThemeManager', () => {
43
- let themeManager: ThemeManager;
44
- const mockThemes: Record<string, ThemeMetadata> = {
45
- 'test-theme': {
46
- name: 'Test Theme',
47
- class: 'test-theme',
48
- description: 'A test theme',
49
- },
50
- };
51
-
52
- beforeEach(() => {
53
- vi.clearAllMocks();
54
- });
55
-
56
- afterEach(() => {
57
- if (themeManager) {
58
- themeManager.destroy();
59
- }
60
- });
61
-
62
- describe('constructor', () => {
63
- it('should create ThemeManager with default config', () => {
64
- themeManager = new ThemeManager({ themes: {} });
65
- expect(themeManager).toBeInstanceOf(ThemeManager);
66
- });
67
-
68
- it('should create ThemeManager with custom config', () => {
69
- themeManager = new ThemeManager({
70
- themes: mockThemes,
71
- basePath: '/custom-themes',
72
- storageKey: 'custom-key',
73
- });
74
- expect(themeManager).toBeInstanceOf(ThemeManager);
75
- });
76
-
77
- it('should initialize with themes from config', () => {
78
- themeManager = new ThemeManager({ themes: mockThemes });
79
- const available = themeManager.getAvailableThemes();
80
- expect(available.length).toBeGreaterThanOrEqual(0);
81
- });
82
- });
83
-
84
- describe('getTheme', () => {
85
- it('should return current theme', () => {
86
- themeManager = new ThemeManager({ themes: mockThemes });
87
- const theme = themeManager.getTheme();
88
- expect(typeof theme).toBe('string');
89
- });
90
-
91
- it('should return default theme if no theme set', () => {
92
- themeManager = new ThemeManager({
93
- themes: mockThemes,
94
- defaultTheme: 'test-theme',
95
- });
96
- const theme = themeManager.getTheme();
97
- expect(theme).toBe('test-theme');
98
- });
99
- });
100
-
101
- describe('getAvailableThemes', () => {
102
- it('should return available themes', () => {
103
- themeManager = new ThemeManager({ themes: mockThemes });
104
- const themes = themeManager.getAvailableThemes();
105
- expect(Array.isArray(themes)).toBe(true);
106
- });
107
- });
108
-
109
- describe('event listeners', () => {
110
- it('should add theme change listener', () => {
111
- themeManager = new ThemeManager({ themes: mockThemes });
112
- const listener = vi.fn();
113
- themeManager.on('themeChange', listener);
114
- // Note: Actual event emission would require theme switching
115
- expect(() => themeManager.on('themeChange', listener)).not.toThrow();
116
- });
117
-
118
- it('should remove theme change listener', () => {
119
- themeManager = new ThemeManager({ themes: mockThemes });
120
- const listener = vi.fn();
121
- themeManager.on('themeChange', listener);
122
- themeManager.off('themeChange', listener);
123
- expect(() => themeManager.off('themeChange', listener)).not.toThrow();
124
- });
125
-
126
- it('should add theme load listener', () => {
127
- themeManager = new ThemeManager({ themes: mockThemes });
128
- const listener = vi.fn();
129
- themeManager.on('themeLoad', listener);
130
- expect(() => themeManager.on('themeLoad', listener)).not.toThrow();
131
- });
132
-
133
- it('should add theme error listener', () => {
134
- themeManager = new ThemeManager({ themes: mockThemes });
135
- const listener = vi.fn();
136
- themeManager.on('themeError', listener);
137
- expect(() => themeManager.on('themeError', listener)).not.toThrow();
138
- });
139
- });
140
-
141
- describe('isThemeLoaded', () => {
142
- it('should check if theme is loaded', () => {
143
- themeManager = new ThemeManager({ themes: mockThemes });
144
- const isLoaded = themeManager.isThemeLoaded('test-theme');
145
- expect(typeof isLoaded).toBe('boolean');
146
- });
147
- });
148
-
149
- describe('destroy', () => {
150
- it('should destroy theme manager', () => {
151
- themeManager = new ThemeManager({ themes: mockThemes });
152
- expect(() => themeManager.destroy()).not.toThrow();
153
- });
154
-
155
- it('should clear event listeners on destroy', () => {
156
- themeManager = new ThemeManager({ themes: mockThemes });
157
- const listener = vi.fn();
158
- themeManager.on('themeChange', listener);
159
- themeManager.destroy();
160
- // After destroy, listeners should be cleared
161
- expect(() => themeManager.destroy()).not.toThrow();
162
- });
163
- });
164
-
165
- describe('RTL support', () => {
166
- it('should get RTL manager when configured', () => {
167
- themeManager = new ThemeManager({
168
- themes: mockThemes,
169
- rtl: { enabled: true, direction: 'ltr' },
170
- });
171
- const rtlManager = themeManager.getRTLManager();
172
- expect(rtlManager).toBeDefined();
173
- });
174
-
175
- it('should set direction', () => {
176
- themeManager = new ThemeManager({
177
- themes: mockThemes,
178
- rtl: { enabled: true, direction: 'ltr' },
179
- });
180
- expect(() => themeManager.setDirection('rtl')).not.toThrow();
181
- });
182
-
183
- it('should get direction', () => {
184
- themeManager = new ThemeManager({
185
- themes: mockThemes,
186
- rtl: { enabled: true, direction: 'ltr' },
187
- });
188
- const direction = themeManager.getDirection();
189
- expect(['ltr', 'rtl']).toContain(direction);
190
- });
191
- });
192
- });