@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.
- package/README.md +101 -199
- package/atomix.config.ts +241 -0
- package/dist/atomix.css +269 -189
- package/dist/atomix.css.map +1 -0
- package/dist/atomix.min.css +15179 -11
- package/dist/atomix.min.css.map +1 -0
- package/dist/charts.d.ts +1929 -0
- package/dist/charts.js +6477 -0
- package/dist/charts.js.map +1 -0
- package/dist/core.d.ts +1289 -0
- package/dist/core.js +3373 -0
- package/dist/core.js.map +1 -0
- package/dist/forms.d.ts +1085 -0
- package/dist/forms.js +2466 -0
- package/dist/forms.js.map +1 -0
- package/dist/heavy.d.ts +636 -0
- package/dist/heavy.js +4566 -0
- package/dist/heavy.js.map +1 -0
- package/dist/index.d.ts +5171 -4792
- package/dist/index.esm.js +6098 -4563
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +6291 -4747
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.d.ts +300 -0
- package/dist/layout.js +336 -0
- package/dist/layout.js.map +1 -0
- package/dist/theme.d.ts +2122 -0
- package/dist/theme.js +6084 -0
- package/dist/theme.js.map +1 -0
- package/package.json +59 -27
- package/scripts/atomix-cli.js +544 -16
- package/scripts/cli/__tests__/cli-commands.test.js +204 -0
- package/scripts/cli/__tests__/utils.test.js +201 -0
- package/scripts/cli/__tests__/vitest.config.js +26 -0
- package/scripts/cli/interactive-init.js +1 -1
- package/scripts/cli/token-manager.js +32 -7
- package/scripts/cli/utils.js +347 -0
- package/src/components/Accordion/Accordion.stories.tsx +50 -17
- package/src/components/Accordion/Accordion.tsx +5 -54
- package/src/components/Accordion/index.ts +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
- package/src/components/Avatar/Avatar.stories.tsx +7 -0
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Badge/Badge.stories.tsx +91 -13
- package/src/components/Badge/Badge.tsx +3 -3
- package/src/components/Block/Block.stories.tsx +7 -23
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
- package/src/components/Button/Button.stories.tsx +141 -22
- package/src/components/Button/ButtonGroup.stories.tsx +315 -0
- package/src/components/Button/ButtonGroup.tsx +67 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Callout/Callout.stories.tsx +8 -6
- package/src/components/Card/Card.stories.tsx +82 -28
- package/src/components/Card/ElevationCard.tsx +1 -1
- package/src/components/Chart/AnimatedChart.tsx +19 -18
- package/src/components/Chart/AreaChart.tsx +5 -2
- package/src/components/Chart/BarChart.tsx +1 -1
- package/src/components/Chart/BubbleChart.tsx +6 -6
- package/src/components/Chart/CandlestickChart.tsx +0 -1
- package/src/components/Chart/Chart.stories.tsx +5 -7
- package/src/components/Chart/Chart.tsx +0 -16
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/ChartToolbar.tsx +1 -0
- package/src/components/Chart/DonutChart.tsx +0 -1
- package/src/components/Chart/FunnelChart.tsx +1 -2
- package/src/components/Chart/GaugeChart.tsx +0 -1
- package/src/components/Chart/HeatmapChart.tsx +0 -1
- package/src/components/Chart/LineChart.tsx +0 -1
- package/src/components/Chart/MultiAxisChart.tsx +0 -1
- package/src/components/Chart/PieChart.tsx +0 -1
- package/src/components/Chart/RadarChart.tsx +19 -13
- package/src/components/Chart/ScatterChart.tsx +3 -4
- package/src/components/Chart/TreemapChart.tsx +2 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -2
- package/src/components/Chart/types.ts +12 -2
- package/src/components/Chart/utils.ts +4 -3
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
- package/src/components/DataTable/DataTable.stories.tsx +23 -16
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
- package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
- package/src/components/Dropdown/Dropdown.tsx +12 -9
- package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
- package/src/components/Footer/Footer.stories.tsx +8 -6
- package/src/components/Footer/FooterLink.tsx +9 -2
- package/src/components/Footer/FooterSection.tsx +3 -3
- package/src/components/Form/Checkbox.stories.tsx +7 -0
- package/src/components/Form/Checkbox.tsx +3 -3
- package/src/components/Form/Form.stories.tsx +7 -0
- package/src/components/Form/FormGroup.stories.tsx +9 -1
- package/src/components/Form/Input.stories.tsx +69 -16
- package/src/components/Form/Input.tsx +4 -2
- package/src/components/Form/Radio.stories.tsx +9 -1
- package/src/components/Form/Radio.tsx +3 -3
- package/src/components/Form/Select.stories.tsx +9 -1
- package/src/components/Form/Select.tsx +3 -3
- package/src/components/Form/Textarea.stories.tsx +10 -2
- package/src/components/Form/Textarea.tsx +4 -2
- package/src/components/Hero/Hero.stories.tsx +7 -0
- package/src/components/List/List.stories.tsx +10 -3
- package/src/components/List/List.tsx +3 -3
- package/src/components/List/ListGroup.tsx +3 -1
- package/src/components/Messages/Messages.stories.tsx +8 -7
- package/src/components/Modal/Modal.stories.tsx +17 -6
- package/src/components/Modal/Modal.tsx +3 -3
- package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
- package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
- package/src/components/Navigation/Menu/Menu.tsx +9 -3
- package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +188 -111
- package/src/components/Pagination/Pagination.tsx +88 -7
- package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
- package/src/components/Popover/Popover.stories.tsx +191 -115
- package/src/components/Popover/Popover.tsx +4 -4
- package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
- package/src/components/Progress/Progress.stories.tsx +79 -49
- package/src/components/Progress/Progress.tsx +6 -2
- package/src/components/Rating/Rating.stories.tsx +109 -84
- package/src/components/Rating/Rating.tsx +5 -2
- package/src/components/River/River.stories.tsx +194 -114
- package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
- package/src/components/Slider/Slider.stories.tsx +7 -0
- package/src/components/Slider/Slider.tsx +10 -9
- package/src/components/Spinner/Spinner.stories.tsx +15 -11
- package/src/components/Spinner/Spinner.tsx +3 -3
- package/src/components/Steps/Steps.stories.tsx +132 -98
- package/src/components/Tabs/Tabs.stories.tsx +163 -112
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
- package/src/components/Todo/Todo.stories.tsx +38 -12
- package/src/components/Toggle/Toggle.stories.tsx +61 -28
- package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/Upload/Upload.stories.tsx +122 -84
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
- package/src/components/index.ts +6 -2
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
- package/src/lib/composables/useAtomixGlass.ts +2 -3
- package/src/lib/composables/useChartPerformance.ts +102 -78
- package/src/lib/composables/useChartScale.ts +10 -0
- package/src/lib/composables/useHero.ts +9 -2
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
- package/src/lib/composables/useNavbar.ts +0 -10
- package/src/lib/composables/useSideMenu.ts +1 -0
- package/src/lib/composables/useVideoPlayer.ts +3 -2
- package/src/lib/config/loader.ts +57 -14
- package/src/lib/constants/components.ts +10 -0
- package/src/lib/hooks/index.ts +0 -1
- package/src/lib/hooks/useComponentCustomization.ts +11 -15
- package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
- package/src/lib/patterns/index.ts +2 -2
- package/src/lib/patterns/slots.tsx +2 -2
- package/src/lib/theme/README.md +174 -0
- package/src/lib/theme/adapters/index.ts +31 -0
- package/src/lib/theme/adapters/themeAdapter.ts +287 -0
- package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
- package/src/lib/theme/config/configLoader.ts +254 -0
- package/src/lib/theme/config/loader.ts +37 -48
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +15 -91
- package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
- package/src/lib/theme/constants/index.ts +8 -0
- package/src/lib/theme/core/ThemeRegistry.ts +19 -6
- package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
- package/src/lib/theme/core/composeTheme.ts +155 -0
- package/src/lib/theme/core/createTheme.ts +94 -0
- package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
- package/src/lib/theme/core/index.ts +5 -19
- package/src/lib/theme/devtools/Comparator.tsx +346 -22
- package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
- package/src/lib/theme/devtools/Inspector.tsx +335 -51
- package/src/lib/theme/devtools/LiveEditor.tsx +489 -112
- package/src/lib/theme/devtools/Preview.tsx +471 -221
- package/src/lib/theme/{core → devtools}/ThemeValidator.ts +6 -3
- package/src/lib/theme/devtools/index.ts +14 -4
- package/src/lib/theme/devtools/useHistory.ts +130 -0
- package/src/lib/theme/errors/index.ts +12 -0
- package/src/lib/theme/generators/cssFile.ts +79 -0
- package/src/lib/theme/generators/generateCSS.ts +89 -0
- package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +4 -14
- package/src/lib/theme/generators/index.ts +19 -0
- package/src/lib/theme/i18n/rtl.ts +7 -7
- package/src/lib/theme/index.ts +120 -15
- package/src/lib/theme/runtime/ThemeApplicator.ts +53 -95
- package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
- package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
- package/src/lib/theme/runtime/index.ts +1 -2
- package/src/lib/theme/runtime/useTheme.ts +1 -2
- package/src/lib/theme/test/testTheme.ts +385 -0
- package/src/lib/theme/tokens/index.ts +12 -0
- package/src/lib/theme/tokens/tokens.ts +721 -0
- package/src/lib/theme/types.ts +6 -42
- package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
- package/src/lib/theme/utils/index.ts +11 -0
- package/src/lib/theme/utils/injectCSS.ts +90 -0
- package/src/lib/theme/utils/themeHelpers.ts +78 -0
- package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
- package/src/lib/theme-tools.ts +8 -9
- package/src/lib/types/components.ts +93 -34
- package/src/lib/types/partProps.ts +0 -16
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/fontPreloader.ts +148 -0
- package/src/lib/utils/index.ts +11 -0
- package/src/lib/utils/memoryMonitor.ts +189 -0
- package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
- package/src/styles/01-settings/_settings.fonts.scss +2 -5
- package/src/styles/02-tools/_tools.button.scss +66 -79
- package/src/styles/06-components/_components.atomix-glass.scss +13 -3
- package/src/styles/06-components/_components.navbar.scss +0 -6
- package/src/styles/06-components/_components.pagination.scss +88 -0
- package/scripts/build-themes.js +0 -208
- package/scripts/sync-theme-config.js +0 -309
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
- package/src/lib/theme/composeTheme.ts +0 -370
- package/src/lib/theme/core/ThemeCache.ts +0 -283
- package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
- package/src/lib/theme/core/ThemeEngine.ts +0 -657
- package/src/lib/theme/createThemeFromConfig.ts +0 -132
- package/src/lib/theme/devtools/CLI.ts +0 -364
- package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
- package/src/lib/theme/runtime/ThemeManager.ts +0 -442
- package/src/styles/03-generic/_generated-root.css +0 -5
- package/src/themes/README.md +0 -442
- package/src/themes/themes.config.js +0 -35
- /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
- /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
|
-
});
|