@shohojdhara/atomix 0.3.4 → 0.3.6

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 (237) hide show
  1. package/README.md +101 -199
  2. package/atomix.config.ts +241 -0
  3. package/dist/atomix.css +269 -189
  4. package/dist/atomix.css.map +1 -0
  5. package/dist/atomix.min.css +15179 -11
  6. package/dist/atomix.min.css.map +1 -0
  7. package/dist/charts.d.ts +1929 -0
  8. package/dist/charts.js +6477 -0
  9. package/dist/charts.js.map +1 -0
  10. package/dist/core.d.ts +1289 -0
  11. package/dist/core.js +3373 -0
  12. package/dist/core.js.map +1 -0
  13. package/dist/forms.d.ts +1085 -0
  14. package/dist/forms.js +2466 -0
  15. package/dist/forms.js.map +1 -0
  16. package/dist/heavy.d.ts +636 -0
  17. package/dist/heavy.js +4566 -0
  18. package/dist/heavy.js.map +1 -0
  19. package/dist/index.d.ts +5171 -4792
  20. package/dist/index.esm.js +6098 -4563
  21. package/dist/index.esm.js.map +1 -1
  22. package/dist/index.js +6291 -4747
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.min.js.map +1 -1
  26. package/dist/layout.d.ts +300 -0
  27. package/dist/layout.js +336 -0
  28. package/dist/layout.js.map +1 -0
  29. package/dist/theme.d.ts +2122 -0
  30. package/dist/theme.js +6084 -0
  31. package/dist/theme.js.map +1 -0
  32. package/package.json +59 -27
  33. package/scripts/atomix-cli.js +544 -16
  34. package/scripts/cli/__tests__/cli-commands.test.js +204 -0
  35. package/scripts/cli/__tests__/utils.test.js +201 -0
  36. package/scripts/cli/__tests__/vitest.config.js +26 -0
  37. package/scripts/cli/interactive-init.js +1 -1
  38. package/scripts/cli/token-manager.js +32 -7
  39. package/scripts/cli/utils.js +347 -0
  40. package/src/components/Accordion/Accordion.stories.tsx +50 -17
  41. package/src/components/Accordion/Accordion.tsx +5 -54
  42. package/src/components/Accordion/index.ts +1 -1
  43. package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
  44. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
  45. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
  46. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
  47. package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
  48. package/src/components/Avatar/Avatar.stories.tsx +7 -0
  49. package/src/components/Avatar/Avatar.tsx +3 -3
  50. package/src/components/Badge/Badge.stories.tsx +91 -13
  51. package/src/components/Badge/Badge.tsx +3 -3
  52. package/src/components/Block/Block.stories.tsx +7 -23
  53. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
  54. package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
  55. package/src/components/Button/Button.stories.tsx +141 -22
  56. package/src/components/Button/ButtonGroup.stories.tsx +315 -0
  57. package/src/components/Button/ButtonGroup.tsx +67 -0
  58. package/src/components/Button/index.ts +2 -0
  59. package/src/components/Callout/Callout.stories.tsx +8 -6
  60. package/src/components/Card/Card.stories.tsx +82 -28
  61. package/src/components/Card/ElevationCard.tsx +1 -1
  62. package/src/components/Chart/AnimatedChart.tsx +19 -18
  63. package/src/components/Chart/AreaChart.tsx +5 -2
  64. package/src/components/Chart/BarChart.tsx +1 -1
  65. package/src/components/Chart/BubbleChart.tsx +6 -6
  66. package/src/components/Chart/CandlestickChart.tsx +0 -1
  67. package/src/components/Chart/Chart.stories.tsx +5 -7
  68. package/src/components/Chart/Chart.tsx +0 -16
  69. package/src/components/Chart/ChartRenderer.tsx +1 -1
  70. package/src/components/Chart/ChartToolbar.tsx +1 -0
  71. package/src/components/Chart/DonutChart.tsx +0 -1
  72. package/src/components/Chart/FunnelChart.tsx +1 -2
  73. package/src/components/Chart/GaugeChart.tsx +0 -1
  74. package/src/components/Chart/HeatmapChart.tsx +0 -1
  75. package/src/components/Chart/LineChart.tsx +0 -1
  76. package/src/components/Chart/MultiAxisChart.tsx +0 -1
  77. package/src/components/Chart/PieChart.tsx +0 -1
  78. package/src/components/Chart/RadarChart.tsx +19 -13
  79. package/src/components/Chart/ScatterChart.tsx +3 -4
  80. package/src/components/Chart/TreemapChart.tsx +2 -1
  81. package/src/components/Chart/WaterfallChart.tsx +0 -2
  82. package/src/components/Chart/types.ts +12 -2
  83. package/src/components/Chart/utils.ts +4 -3
  84. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
  85. package/src/components/DataTable/DataTable.stories.tsx +23 -16
  86. package/src/components/DataTable/DataTable.tsx +3 -3
  87. package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
  88. package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
  89. package/src/components/Dropdown/Dropdown.tsx +12 -9
  90. package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
  91. package/src/components/Footer/Footer.stories.tsx +8 -6
  92. package/src/components/Footer/FooterLink.tsx +9 -2
  93. package/src/components/Footer/FooterSection.tsx +3 -3
  94. package/src/components/Form/Checkbox.stories.tsx +7 -0
  95. package/src/components/Form/Checkbox.tsx +3 -3
  96. package/src/components/Form/Form.stories.tsx +7 -0
  97. package/src/components/Form/FormGroup.stories.tsx +9 -1
  98. package/src/components/Form/Input.stories.tsx +69 -16
  99. package/src/components/Form/Input.tsx +4 -2
  100. package/src/components/Form/Radio.stories.tsx +9 -1
  101. package/src/components/Form/Radio.tsx +3 -3
  102. package/src/components/Form/Select.stories.tsx +9 -1
  103. package/src/components/Form/Select.tsx +3 -3
  104. package/src/components/Form/Textarea.stories.tsx +10 -2
  105. package/src/components/Form/Textarea.tsx +4 -2
  106. package/src/components/Hero/Hero.stories.tsx +7 -0
  107. package/src/components/List/List.stories.tsx +10 -3
  108. package/src/components/List/List.tsx +3 -3
  109. package/src/components/List/ListGroup.tsx +3 -1
  110. package/src/components/Messages/Messages.stories.tsx +8 -7
  111. package/src/components/Modal/Modal.stories.tsx +17 -6
  112. package/src/components/Modal/Modal.tsx +3 -3
  113. package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
  114. package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
  115. package/src/components/Navigation/Menu/Menu.tsx +9 -3
  116. package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
  117. package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
  118. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
  119. package/src/components/Pagination/Pagination.stories.tsx +188 -111
  120. package/src/components/Pagination/Pagination.tsx +88 -7
  121. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
  122. package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
  123. package/src/components/Popover/Popover.stories.tsx +191 -115
  124. package/src/components/Popover/Popover.tsx +4 -4
  125. package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
  126. package/src/components/Progress/Progress.stories.tsx +79 -49
  127. package/src/components/Progress/Progress.tsx +6 -2
  128. package/src/components/Rating/Rating.stories.tsx +109 -84
  129. package/src/components/Rating/Rating.tsx +5 -2
  130. package/src/components/River/River.stories.tsx +194 -114
  131. package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
  132. package/src/components/Slider/Slider.stories.tsx +7 -0
  133. package/src/components/Slider/Slider.tsx +10 -9
  134. package/src/components/Spinner/Spinner.stories.tsx +15 -11
  135. package/src/components/Spinner/Spinner.tsx +3 -3
  136. package/src/components/Steps/Steps.stories.tsx +132 -98
  137. package/src/components/Tabs/Tabs.stories.tsx +163 -112
  138. package/src/components/Tabs/Tabs.tsx +3 -3
  139. package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
  140. package/src/components/Todo/Todo.stories.tsx +38 -12
  141. package/src/components/Toggle/Toggle.stories.tsx +61 -28
  142. package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
  143. package/src/components/Tooltip/Tooltip.tsx +3 -3
  144. package/src/components/Upload/Upload.stories.tsx +122 -84
  145. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
  146. package/src/components/index.ts +6 -2
  147. package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
  148. package/src/lib/composables/useAtomixGlass.ts +2 -3
  149. package/src/lib/composables/useChartPerformance.ts +102 -78
  150. package/src/lib/composables/useChartScale.ts +10 -0
  151. package/src/lib/composables/useHero.ts +9 -2
  152. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
  153. package/src/lib/composables/useNavbar.ts +0 -10
  154. package/src/lib/composables/useSideMenu.ts +1 -0
  155. package/src/lib/composables/useVideoPlayer.ts +3 -2
  156. package/src/lib/config/loader.ts +57 -14
  157. package/src/lib/constants/components.ts +10 -0
  158. package/src/lib/hooks/index.ts +0 -1
  159. package/src/lib/hooks/useComponentCustomization.ts +11 -15
  160. package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
  161. package/src/lib/patterns/index.ts +2 -2
  162. package/src/lib/patterns/slots.tsx +2 -2
  163. package/src/lib/theme/README.md +174 -0
  164. package/src/lib/theme/adapters/index.ts +31 -0
  165. package/src/lib/theme/adapters/themeAdapter.ts +287 -0
  166. package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
  167. package/src/lib/theme/config/configLoader.ts +254 -0
  168. package/src/lib/theme/config/loader.ts +37 -48
  169. package/src/lib/theme/config/types.ts +2 -2
  170. package/src/lib/theme/config/validator.ts +15 -91
  171. package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
  172. package/src/lib/theme/constants/index.ts +8 -0
  173. package/src/lib/theme/core/ThemeRegistry.ts +19 -6
  174. package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
  175. package/src/lib/theme/core/composeTheme.ts +155 -0
  176. package/src/lib/theme/core/createTheme.ts +94 -0
  177. package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
  178. package/src/lib/theme/core/index.ts +5 -19
  179. package/src/lib/theme/devtools/Comparator.tsx +346 -22
  180. package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
  181. package/src/lib/theme/devtools/Inspector.tsx +335 -51
  182. package/src/lib/theme/devtools/LiveEditor.tsx +489 -112
  183. package/src/lib/theme/devtools/Preview.tsx +471 -221
  184. package/src/lib/theme/{core → devtools}/ThemeValidator.ts +6 -3
  185. package/src/lib/theme/devtools/index.ts +14 -4
  186. package/src/lib/theme/devtools/useHistory.ts +130 -0
  187. package/src/lib/theme/errors/index.ts +12 -0
  188. package/src/lib/theme/generators/cssFile.ts +79 -0
  189. package/src/lib/theme/generators/generateCSS.ts +89 -0
  190. package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +4 -14
  191. package/src/lib/theme/generators/index.ts +19 -0
  192. package/src/lib/theme/i18n/rtl.ts +7 -7
  193. package/src/lib/theme/index.ts +120 -15
  194. package/src/lib/theme/runtime/ThemeApplicator.ts +53 -95
  195. package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
  196. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
  197. package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
  198. package/src/lib/theme/runtime/index.ts +1 -2
  199. package/src/lib/theme/runtime/useTheme.ts +1 -2
  200. package/src/lib/theme/test/testTheme.ts +385 -0
  201. package/src/lib/theme/tokens/index.ts +12 -0
  202. package/src/lib/theme/tokens/tokens.ts +721 -0
  203. package/src/lib/theme/types.ts +6 -42
  204. package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
  205. package/src/lib/theme/utils/index.ts +11 -0
  206. package/src/lib/theme/utils/injectCSS.ts +90 -0
  207. package/src/lib/theme/utils/themeHelpers.ts +78 -0
  208. package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
  209. package/src/lib/theme-tools.ts +8 -9
  210. package/src/lib/types/components.ts +93 -34
  211. package/src/lib/types/partProps.ts +0 -16
  212. package/src/lib/utils/componentUtils.ts +1 -1
  213. package/src/lib/utils/fontPreloader.ts +148 -0
  214. package/src/lib/utils/index.ts +11 -0
  215. package/src/lib/utils/memoryMonitor.ts +189 -0
  216. package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
  217. package/src/styles/01-settings/_settings.fonts.scss +2 -5
  218. package/src/styles/02-tools/_tools.button.scss +66 -79
  219. package/src/styles/06-components/_components.atomix-glass.scss +13 -3
  220. package/src/styles/06-components/_components.navbar.scss +0 -6
  221. package/src/styles/06-components/_components.pagination.scss +88 -0
  222. package/scripts/build-themes.js +0 -208
  223. package/scripts/sync-theme-config.js +0 -309
  224. package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
  225. package/src/lib/theme/composeTheme.ts +0 -370
  226. package/src/lib/theme/core/ThemeCache.ts +0 -283
  227. package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
  228. package/src/lib/theme/core/ThemeEngine.ts +0 -657
  229. package/src/lib/theme/createThemeFromConfig.ts +0 -132
  230. package/src/lib/theme/devtools/CLI.ts +0 -364
  231. package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
  232. package/src/lib/theme/runtime/ThemeManager.ts +0 -442
  233. package/src/styles/03-generic/_generated-root.css +0 -5
  234. package/src/themes/README.md +0 -442
  235. package/src/themes/themes.config.js +0 -35
  236. /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
  237. /package/src/lib/theme/{errors.ts → errors/errors.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
- });