meno-core 1.0.0
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/bin/cli.ts +281 -0
- package/build-static.ts +298 -0
- package/bunfig.toml +39 -0
- package/entries/client-router.tsx +111 -0
- package/entries/server-router.tsx +71 -0
- package/lib/client/ClientInitializer.test.ts +9 -0
- package/lib/client/ClientInitializer.test.ts.skip +92 -0
- package/lib/client/ClientInitializer.ts +60 -0
- package/lib/client/ErrorBoundary.test.tsx +595 -0
- package/lib/client/ErrorBoundary.tsx +230 -0
- package/lib/client/componentRegistry.test.ts +165 -0
- package/lib/client/componentRegistry.ts +18 -0
- package/lib/client/contexts/ThemeContext.tsx +73 -0
- package/lib/client/core/ComponentBuilder.test.ts +677 -0
- package/lib/client/core/ComponentBuilder.ts +660 -0
- package/lib/client/core/ComponentRenderer.test.tsx +176 -0
- package/lib/client/core/ComponentRenderer.tsx +83 -0
- package/lib/client/core/cmsTemplateProcessor.ts +129 -0
- package/lib/client/elementRegistry.ts +81 -0
- package/lib/client/hmr/HMRManager.tsx +179 -0
- package/lib/client/hmr/index.ts +5 -0
- package/lib/client/hmrWebSocket.test.ts +9 -0
- package/lib/client/hmrWebSocket.ts +250 -0
- package/lib/client/hooks/useColorVariables.test.ts +166 -0
- package/lib/client/hooks/useColorVariables.ts +249 -0
- package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
- package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
- package/lib/client/hydration/HydrationUtils.test.ts +154 -0
- package/lib/client/hydration/HydrationUtils.ts +35 -0
- package/lib/client/i18nConfigService.test.ts +74 -0
- package/lib/client/i18nConfigService.ts +78 -0
- package/lib/client/index.ts +56 -0
- package/lib/client/navigation.test.ts +441 -0
- package/lib/client/navigation.ts +23 -0
- package/lib/client/responsiveStyleResolver.test.ts +491 -0
- package/lib/client/responsiveStyleResolver.ts +184 -0
- package/lib/client/routing/RouteLoader.test.ts +635 -0
- package/lib/client/routing/RouteLoader.ts +347 -0
- package/lib/client/routing/Router.tsx +382 -0
- package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
- package/lib/client/scripts/ScriptExecutor.ts +171 -0
- package/lib/client/scripts/formHandler.ts +103 -0
- package/lib/client/styleProcessor.test.ts +126 -0
- package/lib/client/styleProcessor.ts +92 -0
- package/lib/client/styles/StyleInjector.test.ts +354 -0
- package/lib/client/styles/StyleInjector.ts +154 -0
- package/lib/client/templateEngine.test.ts +660 -0
- package/lib/client/templateEngine.ts +667 -0
- package/lib/client/theme.test.ts +173 -0
- package/lib/client/theme.ts +159 -0
- package/lib/client/utils/toast.ts +46 -0
- package/lib/server/createServer.ts +170 -0
- package/lib/server/cssGenerator.test.ts +172 -0
- package/lib/server/cssGenerator.ts +58 -0
- package/lib/server/fileWatcher.ts +134 -0
- package/lib/server/index.ts +55 -0
- package/lib/server/jsonLoader.test.ts +103 -0
- package/lib/server/jsonLoader.ts +350 -0
- package/lib/server/middleware/cors.test.ts +177 -0
- package/lib/server/middleware/cors.ts +69 -0
- package/lib/server/middleware/errorHandler.test.ts +208 -0
- package/lib/server/middleware/errorHandler.ts +63 -0
- package/lib/server/middleware/index.ts +9 -0
- package/lib/server/middleware/logger.test.ts +233 -0
- package/lib/server/middleware/logger.ts +99 -0
- package/lib/server/pageCache.test.ts +167 -0
- package/lib/server/pageCache.ts +97 -0
- package/lib/server/projectContext.ts +51 -0
- package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
- package/lib/server/providers/fileSystemPageProvider.ts +83 -0
- package/lib/server/routes/api/cms.test.ts +177 -0
- package/lib/server/routes/api/cms.ts +82 -0
- package/lib/server/routes/api/colors.ts +59 -0
- package/lib/server/routes/api/components.ts +70 -0
- package/lib/server/routes/api/config.test.ts +9 -0
- package/lib/server/routes/api/config.ts +28 -0
- package/lib/server/routes/api/core-routes.ts +182 -0
- package/lib/server/routes/api/functions.ts +170 -0
- package/lib/server/routes/api/index.ts +69 -0
- package/lib/server/routes/api/pages.ts +95 -0
- package/lib/server/routes/api/shared.test.ts +81 -0
- package/lib/server/routes/api/shared.ts +31 -0
- package/lib/server/routes/editor.test.ts +9 -0
- package/lib/server/routes/index.ts +104 -0
- package/lib/server/routes/pages.ts +161 -0
- package/lib/server/routes/static.ts +107 -0
- package/lib/server/services/ColorService.ts +193 -0
- package/lib/server/services/cmsService.test.ts +388 -0
- package/lib/server/services/cmsService.ts +296 -0
- package/lib/server/services/componentService.test.ts +276 -0
- package/lib/server/services/componentService.ts +346 -0
- package/lib/server/services/configService.ts +156 -0
- package/lib/server/services/fileWatcherService.ts +67 -0
- package/lib/server/services/index.ts +10 -0
- package/lib/server/services/pageService.test.ts +258 -0
- package/lib/server/services/pageService.ts +240 -0
- package/lib/server/ssrRenderer.test.ts +1005 -0
- package/lib/server/ssrRenderer.ts +878 -0
- package/lib/server/utilityClassGenerator.ts +11 -0
- package/lib/server/utils/index.ts +5 -0
- package/lib/server/utils/jsonLineMapper.test.ts +100 -0
- package/lib/server/utils/jsonLineMapper.ts +166 -0
- package/lib/server/validateStyleCoverage.test.ts +9 -0
- package/lib/server/validateStyleCoverage.ts +167 -0
- package/lib/server/websocketManager.test.ts +9 -0
- package/lib/server/websocketManager.ts +95 -0
- package/lib/shared/attributeNodeUtils.test.ts +152 -0
- package/lib/shared/attributeNodeUtils.ts +50 -0
- package/lib/shared/breakpoints.test.ts +166 -0
- package/lib/shared/breakpoints.ts +65 -0
- package/lib/shared/colorProperties.test.ts +111 -0
- package/lib/shared/colorProperties.ts +40 -0
- package/lib/shared/colorVariableUtils.test.ts +319 -0
- package/lib/shared/colorVariableUtils.ts +97 -0
- package/lib/shared/constants.test.ts +175 -0
- package/lib/shared/constants.ts +116 -0
- package/lib/shared/cssGeneration.ts +481 -0
- package/lib/shared/cssProperties.test.ts +252 -0
- package/lib/shared/cssProperties.ts +338 -0
- package/lib/shared/elementUtils.test.ts +245 -0
- package/lib/shared/elementUtils.ts +90 -0
- package/lib/shared/fontLoader.ts +97 -0
- package/lib/shared/i18n.test.ts +313 -0
- package/lib/shared/i18n.ts +286 -0
- package/lib/shared/index.ts +50 -0
- package/lib/shared/interfaces/contentProvider.test.ts +9 -0
- package/lib/shared/interfaces/contentProvider.ts +121 -0
- package/lib/shared/nodeUtils.test.ts +320 -0
- package/lib/shared/nodeUtils.ts +220 -0
- package/lib/shared/pathArrayUtils.test.ts +315 -0
- package/lib/shared/pathArrayUtils.ts +17 -0
- package/lib/shared/pathUtils.test.ts +260 -0
- package/lib/shared/pathUtils.ts +244 -0
- package/lib/shared/paths/Path.test.ts +74 -0
- package/lib/shared/paths/Path.ts +23 -0
- package/lib/shared/paths/PathConverter.test.ts +232 -0
- package/lib/shared/paths/PathConverter.ts +141 -0
- package/lib/shared/paths/PathUtils.ts +290 -0
- package/lib/shared/paths/PathValidator.test.ts +193 -0
- package/lib/shared/paths/PathValidator.ts +53 -0
- package/lib/shared/paths/index.ts +48 -0
- package/lib/shared/propResolver.test.ts +639 -0
- package/lib/shared/propResolver.ts +124 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
- package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
- package/lib/shared/registry/ClientRegistry.test.ts +26 -0
- package/lib/shared/registry/ClientRegistry.ts +15 -0
- package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
- package/lib/shared/registry/ComponentRegistry.ts +100 -0
- package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
- package/lib/shared/registry/NodeTypeManager.ts +94 -0
- package/lib/shared/registry/RegistryManager.test.ts +58 -0
- package/lib/shared/registry/RegistryManager.ts +60 -0
- package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
- package/lib/shared/registry/SSRRegistry.test.ts +26 -0
- package/lib/shared/registry/SSRRegistry.ts +15 -0
- package/lib/shared/registry/createNodeType.ts +175 -0
- package/lib/shared/registry/defineNodeType.ts +73 -0
- package/lib/shared/registry/fieldPresets.ts +109 -0
- package/lib/shared/registry/index.ts +50 -0
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
- package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
- package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
- package/lib/shared/registry/nodeTypes/index.ts +75 -0
- package/lib/shared/responsiveScaling.test.ts +268 -0
- package/lib/shared/responsiveScaling.ts +194 -0
- package/lib/shared/responsiveStyleUtils.test.ts +300 -0
- package/lib/shared/responsiveStyleUtils.ts +139 -0
- package/lib/shared/slugTranslator.test.ts +325 -0
- package/lib/shared/slugTranslator.ts +177 -0
- package/lib/shared/styleNodeUtils.test.ts +132 -0
- package/lib/shared/styleNodeUtils.ts +102 -0
- package/lib/shared/styleUtils.test.ts +238 -0
- package/lib/shared/styleUtils.ts +63 -0
- package/lib/shared/themeDefaults.test.ts +113 -0
- package/lib/shared/themeDefaults.ts +103 -0
- package/lib/shared/tree/PathBuilder.ts +383 -0
- package/lib/shared/treePathUtils.test.ts +539 -0
- package/lib/shared/treePathUtils.ts +339 -0
- package/lib/shared/types/api.ts +58 -0
- package/lib/shared/types/cms.ts +95 -0
- package/lib/shared/types/colors.ts +45 -0
- package/lib/shared/types/components.ts +121 -0
- package/lib/shared/types/errors.test.ts +103 -0
- package/lib/shared/types/errors.ts +69 -0
- package/lib/shared/types/index.ts +96 -0
- package/lib/shared/types/nodes.ts +20 -0
- package/lib/shared/types/rendering.ts +61 -0
- package/lib/shared/types/styles.ts +38 -0
- package/lib/shared/types.ts +11 -0
- package/lib/shared/utilityClassConfig.ts +287 -0
- package/lib/shared/utilityClassMapper.test.ts +140 -0
- package/lib/shared/utilityClassMapper.ts +229 -0
- package/lib/shared/utils/fileUtils.test.ts +99 -0
- package/lib/shared/utils/fileUtils.ts +56 -0
- package/lib/shared/utils.test.ts +261 -0
- package/lib/shared/utils.ts +84 -0
- package/lib/shared/validation/index.ts +7 -0
- package/lib/shared/validation/propValidator.test.ts +178 -0
- package/lib/shared/validation/propValidator.ts +238 -0
- package/lib/shared/validation/schemas.test.ts +177 -0
- package/lib/shared/validation/schemas.ts +401 -0
- package/lib/shared/validation/validators.test.ts +109 -0
- package/lib/shared/validation/validators.ts +304 -0
- package/lib/test-utils/dom-setup.ts +55 -0
- package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
- package/lib/test-utils/factories/DomMockFactory.ts +487 -0
- package/lib/test-utils/factories/EventMockFactory.ts +244 -0
- package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
- package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
- package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
- package/lib/test-utils/factories/index.ts +11 -0
- package/lib/test-utils/fixtures.ts +134 -0
- package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
- package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
- package/lib/test-utils/helpers/index.ts +6 -0
- package/lib/test-utils/helpers.test.ts +73 -0
- package/lib/test-utils/helpers.ts +90 -0
- package/lib/test-utils/index.ts +17 -0
- package/lib/test-utils/mockFactories.ts +92 -0
- package/lib/test-utils/mocks.ts +341 -0
- package/package.json +38 -0
- package/templates/index-router.html +34 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { getThemeColors, type Theme, type ThemeColors } from './theme';
|
|
3
|
+
|
|
4
|
+
describe('theme', () => {
|
|
5
|
+
describe('getThemeColors', () => {
|
|
6
|
+
test('returns light theme colors for light theme', () => {
|
|
7
|
+
const colors = getThemeColors('light');
|
|
8
|
+
expect(colors).toBeDefined();
|
|
9
|
+
expect(colors.sidebar).toBeDefined();
|
|
10
|
+
expect(colors.propsPanel).toBeDefined();
|
|
11
|
+
expect(colors.scrollbar).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('returns dark theme colors for dark theme', () => {
|
|
15
|
+
const colors = getThemeColors('dark');
|
|
16
|
+
expect(colors).toBeDefined();
|
|
17
|
+
expect(colors.sidebar).toBeDefined();
|
|
18
|
+
expect(colors.propsPanel).toBeDefined();
|
|
19
|
+
expect(colors.scrollbar).toBeDefined();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('light theme has correct sidebar background', () => {
|
|
23
|
+
const colors = getThemeColors('light');
|
|
24
|
+
expect(colors.sidebar.background).toBe('#ffffff');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('dark theme has correct sidebar background', () => {
|
|
28
|
+
const colors = getThemeColors('dark');
|
|
29
|
+
expect(colors.sidebar.background).toBe('#1e1e1e');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('light theme has all required sidebar properties', () => {
|
|
33
|
+
const colors = getThemeColors('light');
|
|
34
|
+
expect(colors.sidebar.background).toBeDefined();
|
|
35
|
+
expect(colors.sidebar.border).toBeDefined();
|
|
36
|
+
expect(colors.sidebar.text).toBeDefined();
|
|
37
|
+
expect(colors.sidebar.textSecondary).toBeDefined();
|
|
38
|
+
expect(colors.sidebar.textMuted).toBeDefined();
|
|
39
|
+
expect(colors.sidebar.headerBackground).toBeDefined();
|
|
40
|
+
expect(colors.sidebar.tabActiveBackground).toBeDefined();
|
|
41
|
+
expect(colors.sidebar.tabActiveBorder).toBeDefined();
|
|
42
|
+
expect(colors.sidebar.tabInactiveBackground).toBeDefined();
|
|
43
|
+
expect(colors.sidebar.footerBackground).toBeDefined();
|
|
44
|
+
expect(colors.sidebar.buttonPrimaryBackground).toBeDefined();
|
|
45
|
+
expect(colors.sidebar.buttonPrimaryColor).toBeDefined();
|
|
46
|
+
expect(colors.sidebar.hoverBackground).toBeDefined();
|
|
47
|
+
expect(colors.sidebar.selectedBackground).toBeDefined();
|
|
48
|
+
expect(colors.sidebar.selectedText).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('dark theme has all required sidebar properties', () => {
|
|
52
|
+
const colors = getThemeColors('dark');
|
|
53
|
+
expect(colors.sidebar.background).toBeDefined();
|
|
54
|
+
expect(colors.sidebar.border).toBeDefined();
|
|
55
|
+
expect(colors.sidebar.text).toBeDefined();
|
|
56
|
+
expect(colors.sidebar.textSecondary).toBeDefined();
|
|
57
|
+
expect(colors.sidebar.textMuted).toBeDefined();
|
|
58
|
+
expect(colors.sidebar.headerBackground).toBeDefined();
|
|
59
|
+
expect(colors.sidebar.tabActiveBackground).toBeDefined();
|
|
60
|
+
expect(colors.sidebar.tabActiveBorder).toBeDefined();
|
|
61
|
+
expect(colors.sidebar.tabInactiveBackground).toBeDefined();
|
|
62
|
+
expect(colors.sidebar.footerBackground).toBeDefined();
|
|
63
|
+
expect(colors.sidebar.buttonPrimaryBackground).toBeDefined();
|
|
64
|
+
expect(colors.sidebar.buttonPrimaryColor).toBeDefined();
|
|
65
|
+
expect(colors.sidebar.hoverBackground).toBeDefined();
|
|
66
|
+
expect(colors.sidebar.selectedBackground).toBeDefined();
|
|
67
|
+
expect(colors.sidebar.selectedText).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('light theme has all required propsPanel properties', () => {
|
|
71
|
+
const colors = getThemeColors('light');
|
|
72
|
+
expect(colors.propsPanel.background).toBeDefined();
|
|
73
|
+
expect(colors.propsPanel.backgroundSecondary).toBeDefined();
|
|
74
|
+
expect(colors.propsPanel.backgroundTertiary).toBeDefined();
|
|
75
|
+
expect(colors.propsPanel.border).toBeDefined();
|
|
76
|
+
expect(colors.propsPanel.borderSecondary).toBeDefined();
|
|
77
|
+
expect(colors.propsPanel.text).toBeDefined();
|
|
78
|
+
expect(colors.propsPanel.textSecondary).toBeDefined();
|
|
79
|
+
expect(colors.propsPanel.textMuted).toBeDefined();
|
|
80
|
+
expect(colors.propsPanel.codeString).toBeDefined();
|
|
81
|
+
expect(colors.propsPanel.codeNumber).toBeDefined();
|
|
82
|
+
expect(colors.propsPanel.codeKey).toBeDefined();
|
|
83
|
+
expect(colors.propsPanel.codeType).toBeDefined();
|
|
84
|
+
expect(colors.propsPanel.buttonPrimary).toBeDefined();
|
|
85
|
+
expect(colors.propsPanel.buttonPrimaryHover).toBeDefined();
|
|
86
|
+
expect(colors.propsPanel.buttonSecondary).toBeDefined();
|
|
87
|
+
expect(colors.propsPanel.buttonDanger).toBeDefined();
|
|
88
|
+
expect(colors.propsPanel.buttonDangerHover).toBeDefined();
|
|
89
|
+
expect(colors.propsPanel.inputBackground).toBeDefined();
|
|
90
|
+
expect(colors.propsPanel.inputBorder).toBeDefined();
|
|
91
|
+
expect(colors.propsPanel.hoverBackground).toBeDefined();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('dark theme has all required propsPanel properties', () => {
|
|
95
|
+
const colors = getThemeColors('dark');
|
|
96
|
+
expect(colors.propsPanel.background).toBeDefined();
|
|
97
|
+
expect(colors.propsPanel.backgroundSecondary).toBeDefined();
|
|
98
|
+
expect(colors.propsPanel.backgroundTertiary).toBeDefined();
|
|
99
|
+
expect(colors.propsPanel.border).toBeDefined();
|
|
100
|
+
expect(colors.propsPanel.borderSecondary).toBeDefined();
|
|
101
|
+
expect(colors.propsPanel.text).toBeDefined();
|
|
102
|
+
expect(colors.propsPanel.textSecondary).toBeDefined();
|
|
103
|
+
expect(colors.propsPanel.textMuted).toBeDefined();
|
|
104
|
+
expect(colors.propsPanel.codeString).toBeDefined();
|
|
105
|
+
expect(colors.propsPanel.codeNumber).toBeDefined();
|
|
106
|
+
expect(colors.propsPanel.codeKey).toBeDefined();
|
|
107
|
+
expect(colors.propsPanel.codeType).toBeDefined();
|
|
108
|
+
expect(colors.propsPanel.buttonPrimary).toBeDefined();
|
|
109
|
+
expect(colors.propsPanel.buttonPrimaryHover).toBeDefined();
|
|
110
|
+
expect(colors.propsPanel.buttonSecondary).toBeDefined();
|
|
111
|
+
expect(colors.propsPanel.buttonDanger).toBeDefined();
|
|
112
|
+
expect(colors.propsPanel.buttonDangerHover).toBeDefined();
|
|
113
|
+
expect(colors.propsPanel.inputBackground).toBeDefined();
|
|
114
|
+
expect(colors.propsPanel.inputBorder).toBeDefined();
|
|
115
|
+
expect(colors.propsPanel.hoverBackground).toBeDefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('light theme has all required scrollbar properties', () => {
|
|
119
|
+
const colors = getThemeColors('light');
|
|
120
|
+
expect(colors.scrollbar.track).toBeDefined();
|
|
121
|
+
expect(colors.scrollbar.thumb).toBeDefined();
|
|
122
|
+
expect(colors.scrollbar.thumbHover).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('dark theme has all required scrollbar properties', () => {
|
|
126
|
+
const colors = getThemeColors('dark');
|
|
127
|
+
expect(colors.scrollbar.track).toBeDefined();
|
|
128
|
+
expect(colors.scrollbar.thumb).toBeDefined();
|
|
129
|
+
expect(colors.scrollbar.thumbHover).toBeDefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('light and dark themes have different colors', () => {
|
|
133
|
+
const light = getThemeColors('light');
|
|
134
|
+
const dark = getThemeColors('dark');
|
|
135
|
+
expect(light.sidebar.background).not.toBe(dark.sidebar.background);
|
|
136
|
+
expect(light.propsPanel.background).not.toBe(dark.propsPanel.background);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('light theme colors are valid hex colors', () => {
|
|
140
|
+
const colors = getThemeColors('light');
|
|
141
|
+
const hexPattern = /^#[0-9a-f]{6}$|^#[0-9a-f]{3}$|^transparent$/i;
|
|
142
|
+
expect(hexPattern.test(colors.sidebar.background)).toBe(true);
|
|
143
|
+
expect(hexPattern.test(colors.sidebar.border)).toBe(true);
|
|
144
|
+
expect(hexPattern.test(colors.propsPanel.background)).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('dark theme colors are valid hex colors or transparent', () => {
|
|
148
|
+
const colors = getThemeColors('dark');
|
|
149
|
+
const hexPattern = /^#[0-9a-f]{6}$|^#[0-9a-f]{3}$|^transparent$/i;
|
|
150
|
+
expect(hexPattern.test(colors.sidebar.background)).toBe(true);
|
|
151
|
+
expect(hexPattern.test(colors.sidebar.border)).toBe(true);
|
|
152
|
+
expect(hexPattern.test(colors.propsPanel.background)).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('returns consistent reference for same theme', () => {
|
|
156
|
+
const light1 = getThemeColors('light');
|
|
157
|
+
const light2 = getThemeColors('light');
|
|
158
|
+
expect(light1).toEqual(light2);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('light theme button colors are accessible', () => {
|
|
162
|
+
const colors = getThemeColors('light');
|
|
163
|
+
expect(colors.sidebar.buttonPrimaryBackground).toBe('#0366d6');
|
|
164
|
+
expect(colors.sidebar.buttonPrimaryColor).toBe('#ffffff');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('dark theme button colors are accessible', () => {
|
|
168
|
+
const colors = getThemeColors('dark');
|
|
169
|
+
expect(colors.sidebar.buttonPrimaryBackground).toBe('#0e639c');
|
|
170
|
+
expect(colors.sidebar.buttonPrimaryColor).toBe('#ffffff');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
export type Theme = 'light' | 'dark';
|
|
2
|
+
|
|
3
|
+
export interface ThemeColors {
|
|
4
|
+
sidebar: {
|
|
5
|
+
background: string;
|
|
6
|
+
border: string;
|
|
7
|
+
text: string;
|
|
8
|
+
textSecondary: string;
|
|
9
|
+
textMuted: string;
|
|
10
|
+
headerBackground: string;
|
|
11
|
+
tabActiveBackground: string;
|
|
12
|
+
tabActiveBorder: string;
|
|
13
|
+
tabInactiveBackground: string;
|
|
14
|
+
footerBackground: string;
|
|
15
|
+
buttonPrimaryBackground: string;
|
|
16
|
+
buttonPrimaryColor: string;
|
|
17
|
+
hoverBackground: string;
|
|
18
|
+
selectedBackground: string;
|
|
19
|
+
selectedText: string;
|
|
20
|
+
};
|
|
21
|
+
scrollbar: {
|
|
22
|
+
track: string;
|
|
23
|
+
thumb: string;
|
|
24
|
+
thumbHover: string;
|
|
25
|
+
};
|
|
26
|
+
propsPanel: {
|
|
27
|
+
background: string;
|
|
28
|
+
backgroundSecondary: string;
|
|
29
|
+
backgroundTertiary: string;
|
|
30
|
+
border: string;
|
|
31
|
+
borderSecondary: string;
|
|
32
|
+
text: string;
|
|
33
|
+
textSecondary: string;
|
|
34
|
+
textMuted: string;
|
|
35
|
+
codeString: string;
|
|
36
|
+
codeNumber: string;
|
|
37
|
+
codeKey: string;
|
|
38
|
+
codeType: string;
|
|
39
|
+
buttonPrimary: string;
|
|
40
|
+
buttonPrimaryHover: string;
|
|
41
|
+
buttonSecondary: string;
|
|
42
|
+
buttonDanger: string;
|
|
43
|
+
buttonDangerHover: string;
|
|
44
|
+
inputBackground: string;
|
|
45
|
+
inputBorder: string;
|
|
46
|
+
hoverBackground: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const lightThemeColors: ThemeColors = {
|
|
51
|
+
sidebar: {
|
|
52
|
+
background: '#ffffff',
|
|
53
|
+
border: '#e1e4e8',
|
|
54
|
+
text: '#24292e',
|
|
55
|
+
textSecondary: '#586069',
|
|
56
|
+
textMuted: '#6a737d',
|
|
57
|
+
headerBackground: '#f6f8fa',
|
|
58
|
+
tabActiveBackground: '#ffffff',
|
|
59
|
+
tabActiveBorder: '#0366d6',
|
|
60
|
+
tabInactiveBackground: 'transparent',
|
|
61
|
+
footerBackground: '#f6f8fa',
|
|
62
|
+
buttonPrimaryBackground: '#0366d6',
|
|
63
|
+
buttonPrimaryColor: '#ffffff',
|
|
64
|
+
hoverBackground: '#f1f3f5',
|
|
65
|
+
selectedBackground: '#0366d6',
|
|
66
|
+
selectedText: '#ffffff',
|
|
67
|
+
},
|
|
68
|
+
scrollbar: {
|
|
69
|
+
track: '#f1f3f5',
|
|
70
|
+
thumb: '#c1c9d1',
|
|
71
|
+
thumbHover: '#a8b2bd',
|
|
72
|
+
},
|
|
73
|
+
propsPanel: {
|
|
74
|
+
background: '#ffffff',
|
|
75
|
+
backgroundSecondary: '#f6f8fa',
|
|
76
|
+
backgroundTertiary: '#f1f3f5',
|
|
77
|
+
border: '#e1e4e8',
|
|
78
|
+
borderSecondary: '#d1d5da',
|
|
79
|
+
text: '#24292e',
|
|
80
|
+
textSecondary: '#586069',
|
|
81
|
+
textMuted: '#6a737d',
|
|
82
|
+
codeString: '#032f62',
|
|
83
|
+
codeNumber: '#005cc5',
|
|
84
|
+
codeKey: '#005cc5',
|
|
85
|
+
codeType: '#6f42c1',
|
|
86
|
+
buttonPrimary: '#0366d6',
|
|
87
|
+
buttonPrimaryHover: '#0256cc',
|
|
88
|
+
buttonSecondary: '#586069',
|
|
89
|
+
buttonDanger: '#d73a49',
|
|
90
|
+
buttonDangerHover: '#cb2431',
|
|
91
|
+
inputBackground: '#ffffff',
|
|
92
|
+
inputBorder: '#d1d5da',
|
|
93
|
+
hoverBackground: '#f1f3f5',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const darkThemeColors: ThemeColors = {
|
|
98
|
+
sidebar: {
|
|
99
|
+
background: '#1e1e1e',
|
|
100
|
+
border: '#333333',
|
|
101
|
+
text: '#cccccc',
|
|
102
|
+
textSecondary: '#cccccc',
|
|
103
|
+
textMuted: '#888888',
|
|
104
|
+
headerBackground: '#2d2d2d',
|
|
105
|
+
tabActiveBackground: '#1e1e1e',
|
|
106
|
+
tabActiveBorder: '#007acc',
|
|
107
|
+
tabInactiveBackground: 'transparent',
|
|
108
|
+
footerBackground: '#252526',
|
|
109
|
+
buttonPrimaryBackground: '#0e639c',
|
|
110
|
+
buttonPrimaryColor: '#ffffff',
|
|
111
|
+
hoverBackground: '#2a2d2e',
|
|
112
|
+
selectedBackground: '#37373d',
|
|
113
|
+
selectedText: '#ffffff',
|
|
114
|
+
},
|
|
115
|
+
scrollbar: {
|
|
116
|
+
track: '#1e1e1e',
|
|
117
|
+
thumb: '#424242',
|
|
118
|
+
thumbHover: '#4e4e4e',
|
|
119
|
+
},
|
|
120
|
+
propsPanel: {
|
|
121
|
+
background: '#1e1e1e',
|
|
122
|
+
backgroundSecondary: '#2d2d2d',
|
|
123
|
+
backgroundTertiary: '#252525',
|
|
124
|
+
border: '#333333',
|
|
125
|
+
borderSecondary: '#444444',
|
|
126
|
+
text: '#cccccc',
|
|
127
|
+
textSecondary: '#cccccc',
|
|
128
|
+
textMuted: '#888888',
|
|
129
|
+
codeString: '#ce9178',
|
|
130
|
+
codeNumber: '#b5cea8',
|
|
131
|
+
codeKey: '#9cdcfe',
|
|
132
|
+
codeType: '#4ec9b0',
|
|
133
|
+
buttonPrimary: '#007acc',
|
|
134
|
+
buttonPrimaryHover: '#0098ff',
|
|
135
|
+
buttonSecondary: '#007acc',
|
|
136
|
+
buttonDanger: '#f48771',
|
|
137
|
+
buttonDangerHover: '#ff6b6b',
|
|
138
|
+
inputBackground: '#1e1e1e',
|
|
139
|
+
inputBorder: '#444444',
|
|
140
|
+
hoverBackground: '#2a2d2e',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export function getThemeColors(theme: Theme): ThemeColors {
|
|
145
|
+
return theme === 'light' ? lightThemeColors : darkThemeColors;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function loadThemeConfig(): Promise<Theme> {
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch('/api/config', { cache: 'no-store' });
|
|
151
|
+
if (response.ok) {
|
|
152
|
+
const config = await response.json();
|
|
153
|
+
return config.editor?.theme || 'dark';
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
}
|
|
157
|
+
return 'dark'; // Default to dark
|
|
158
|
+
}
|
|
159
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast Notification Helper
|
|
3
|
+
* Utility functions for showing toast notifications
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useToastStore } from '../stores/ToastStore';
|
|
7
|
+
import { ApiError } from '../services/api/errors';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Show an error toast notification
|
|
11
|
+
*/
|
|
12
|
+
export function showErrorToast(message: string, duration?: number): void {
|
|
13
|
+
useToastStore.getState().addToast(message, 'error', duration);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Show a success toast notification
|
|
18
|
+
*/
|
|
19
|
+
export function showSuccessToast(message: string, duration?: number): void {
|
|
20
|
+
useToastStore.getState().addToast(message, 'success', duration);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Show a warning toast notification
|
|
25
|
+
*/
|
|
26
|
+
export function showWarningToast(message: string, duration?: number): void {
|
|
27
|
+
useToastStore.getState().addToast(message, 'warning', duration);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Show an info toast notification
|
|
32
|
+
*/
|
|
33
|
+
export function showInfoToast(message: string, duration?: number): void {
|
|
34
|
+
useToastStore.getState().addToast(message, 'info', duration);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Show an API error as a toast notification
|
|
39
|
+
*/
|
|
40
|
+
export function showApiErrorToast(error: ApiError | Error, duration?: number): void {
|
|
41
|
+
const message = error instanceof ApiError
|
|
42
|
+
? error.toUserMessage()
|
|
43
|
+
: error.message || 'An unexpected error occurred';
|
|
44
|
+
showErrorToast(message, duration);
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Factory
|
|
3
|
+
* Creates a configurable Bun server for @meno/core
|
|
4
|
+
* Can be extended by @meno/studio for editor functionality
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PageService } from './services/pageService';
|
|
8
|
+
import type { ComponentService } from './services/componentService';
|
|
9
|
+
import type { CMSService } from './services/cmsService';
|
|
10
|
+
import type { CMSProvider } from '../shared/interfaces/contentProvider';
|
|
11
|
+
import { WebSocketManager } from './websocketManager';
|
|
12
|
+
import { handleRoutes, type RouteContext } from './routes';
|
|
13
|
+
import { SERVER_PORT, MAX_PORT_ATTEMPTS, HMR_ROUTE } from '../shared/constants';
|
|
14
|
+
|
|
15
|
+
// Bun WebSocket type
|
|
16
|
+
type BunWebSocket = {
|
|
17
|
+
send(data: string | ArrayBuffer | Uint8Array): void;
|
|
18
|
+
readyState: number;
|
|
19
|
+
close(code?: number, reason?: string): void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Custom message handler type
|
|
23
|
+
export type WSMessageHandler = (ws: BunWebSocket, data: unknown) => void;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Server configuration
|
|
27
|
+
*/
|
|
28
|
+
export interface ServerConfig {
|
|
29
|
+
port?: number;
|
|
30
|
+
pageService: PageService;
|
|
31
|
+
componentService: ComponentService;
|
|
32
|
+
wsManager: WebSocketManager;
|
|
33
|
+
cmsService?: CMSService;
|
|
34
|
+
cmsProvider?: CMSProvider;
|
|
35
|
+
additionalRoutes?: AdditionalRouteHandler[];
|
|
36
|
+
onWSMessage?: WSMessageHandler;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Additional route handler type
|
|
41
|
+
*/
|
|
42
|
+
export type AdditionalRouteHandler = (
|
|
43
|
+
req: Request,
|
|
44
|
+
url: URL,
|
|
45
|
+
context: RouteContext
|
|
46
|
+
) => Promise<Response | undefined>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Server factory result
|
|
50
|
+
*/
|
|
51
|
+
export interface ServerResult {
|
|
52
|
+
server: ReturnType<typeof Bun.serve>;
|
|
53
|
+
port: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a Bun server with the given configuration
|
|
58
|
+
*/
|
|
59
|
+
export function createServer(config: ServerConfig): ServerResult {
|
|
60
|
+
const {
|
|
61
|
+
port: requestedPort = SERVER_PORT,
|
|
62
|
+
pageService,
|
|
63
|
+
componentService,
|
|
64
|
+
wsManager,
|
|
65
|
+
cmsService,
|
|
66
|
+
cmsProvider,
|
|
67
|
+
additionalRoutes = [],
|
|
68
|
+
onWSMessage,
|
|
69
|
+
} = config;
|
|
70
|
+
|
|
71
|
+
const routeContext: RouteContext = {
|
|
72
|
+
pageService,
|
|
73
|
+
componentService,
|
|
74
|
+
cmsService,
|
|
75
|
+
cmsProvider,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
function createBunConfig(port: number) {
|
|
79
|
+
return {
|
|
80
|
+
port,
|
|
81
|
+
development: {
|
|
82
|
+
hmr: true,
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async fetch(req: Request, server: any) {
|
|
86
|
+
const url = new URL(req.url);
|
|
87
|
+
|
|
88
|
+
// Check additional routes first (allows studio to inject routes)
|
|
89
|
+
for (const handler of additionalRoutes) {
|
|
90
|
+
const response = await handler(req, url, routeContext);
|
|
91
|
+
if (response) {
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle all routes through centralized router
|
|
97
|
+
const response = await handleRoutes(req, url, server, routeContext);
|
|
98
|
+
return response || new Response('Not Found', { status: 404 });
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
websocket: {
|
|
102
|
+
open(ws: BunWebSocket) {
|
|
103
|
+
wsManager.addClient(ws);
|
|
104
|
+
},
|
|
105
|
+
message(ws: BunWebSocket, message: unknown) {
|
|
106
|
+
try {
|
|
107
|
+
const data = JSON.parse(message as string);
|
|
108
|
+
|
|
109
|
+
// Respond to ping with pong (for heartbeat)
|
|
110
|
+
if (data.type === 'ping') {
|
|
111
|
+
ws.send('pong');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Allow custom message handlers
|
|
116
|
+
if (onWSMessage) {
|
|
117
|
+
onWSMessage(ws, data);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Ignore non-JSON messages
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
close(ws: BunWebSocket) {
|
|
124
|
+
wsManager.removeClient(ws);
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Try to start server, incrementing port if busy
|
|
131
|
+
let server: ReturnType<typeof Bun.serve> | undefined;
|
|
132
|
+
let actualPort = requestedPort;
|
|
133
|
+
|
|
134
|
+
for (let portAttempt = 0; portAttempt < MAX_PORT_ATTEMPTS; portAttempt++) {
|
|
135
|
+
const currentPort = requestedPort + portAttempt;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
server = Bun.serve(createBunConfig(currentPort));
|
|
139
|
+
actualPort = server.port ?? currentPort;
|
|
140
|
+
if (portAttempt > 0) {
|
|
141
|
+
console.log(`Warning: Port ${requestedPort} was busy, using port ${actualPort} instead`);
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
} catch (error: unknown) {
|
|
145
|
+
const err = error as { code?: string };
|
|
146
|
+
if (err?.code === 'EADDRINUSE') {
|
|
147
|
+
if (portAttempt >= MAX_PORT_ATTEMPTS - 1) {
|
|
148
|
+
console.error(
|
|
149
|
+
`Failed to start server: All ports from ${requestedPort} to ${requestedPort + MAX_PORT_ATTEMPTS - 1} are busy`
|
|
150
|
+
);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!server) {
|
|
160
|
+
throw new Error('Failed to start server');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { server, port: actualPort };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Re-export types for convenience
|
|
168
|
+
*/
|
|
169
|
+
export { WebSocketManager };
|
|
170
|
+
export type { RouteContext };
|