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.
Files changed (231) hide show
  1. package/bin/cli.ts +281 -0
  2. package/build-static.ts +298 -0
  3. package/bunfig.toml +39 -0
  4. package/entries/client-router.tsx +111 -0
  5. package/entries/server-router.tsx +71 -0
  6. package/lib/client/ClientInitializer.test.ts +9 -0
  7. package/lib/client/ClientInitializer.test.ts.skip +92 -0
  8. package/lib/client/ClientInitializer.ts +60 -0
  9. package/lib/client/ErrorBoundary.test.tsx +595 -0
  10. package/lib/client/ErrorBoundary.tsx +230 -0
  11. package/lib/client/componentRegistry.test.ts +165 -0
  12. package/lib/client/componentRegistry.ts +18 -0
  13. package/lib/client/contexts/ThemeContext.tsx +73 -0
  14. package/lib/client/core/ComponentBuilder.test.ts +677 -0
  15. package/lib/client/core/ComponentBuilder.ts +660 -0
  16. package/lib/client/core/ComponentRenderer.test.tsx +176 -0
  17. package/lib/client/core/ComponentRenderer.tsx +83 -0
  18. package/lib/client/core/cmsTemplateProcessor.ts +129 -0
  19. package/lib/client/elementRegistry.ts +81 -0
  20. package/lib/client/hmr/HMRManager.tsx +179 -0
  21. package/lib/client/hmr/index.ts +5 -0
  22. package/lib/client/hmrWebSocket.test.ts +9 -0
  23. package/lib/client/hmrWebSocket.ts +250 -0
  24. package/lib/client/hooks/useColorVariables.test.ts +166 -0
  25. package/lib/client/hooks/useColorVariables.ts +249 -0
  26. package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
  27. package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
  28. package/lib/client/hydration/HydrationUtils.test.ts +154 -0
  29. package/lib/client/hydration/HydrationUtils.ts +35 -0
  30. package/lib/client/i18nConfigService.test.ts +74 -0
  31. package/lib/client/i18nConfigService.ts +78 -0
  32. package/lib/client/index.ts +56 -0
  33. package/lib/client/navigation.test.ts +441 -0
  34. package/lib/client/navigation.ts +23 -0
  35. package/lib/client/responsiveStyleResolver.test.ts +491 -0
  36. package/lib/client/responsiveStyleResolver.ts +184 -0
  37. package/lib/client/routing/RouteLoader.test.ts +635 -0
  38. package/lib/client/routing/RouteLoader.ts +347 -0
  39. package/lib/client/routing/Router.tsx +382 -0
  40. package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
  41. package/lib/client/scripts/ScriptExecutor.ts +171 -0
  42. package/lib/client/scripts/formHandler.ts +103 -0
  43. package/lib/client/styleProcessor.test.ts +126 -0
  44. package/lib/client/styleProcessor.ts +92 -0
  45. package/lib/client/styles/StyleInjector.test.ts +354 -0
  46. package/lib/client/styles/StyleInjector.ts +154 -0
  47. package/lib/client/templateEngine.test.ts +660 -0
  48. package/lib/client/templateEngine.ts +667 -0
  49. package/lib/client/theme.test.ts +173 -0
  50. package/lib/client/theme.ts +159 -0
  51. package/lib/client/utils/toast.ts +46 -0
  52. package/lib/server/createServer.ts +170 -0
  53. package/lib/server/cssGenerator.test.ts +172 -0
  54. package/lib/server/cssGenerator.ts +58 -0
  55. package/lib/server/fileWatcher.ts +134 -0
  56. package/lib/server/index.ts +55 -0
  57. package/lib/server/jsonLoader.test.ts +103 -0
  58. package/lib/server/jsonLoader.ts +350 -0
  59. package/lib/server/middleware/cors.test.ts +177 -0
  60. package/lib/server/middleware/cors.ts +69 -0
  61. package/lib/server/middleware/errorHandler.test.ts +208 -0
  62. package/lib/server/middleware/errorHandler.ts +63 -0
  63. package/lib/server/middleware/index.ts +9 -0
  64. package/lib/server/middleware/logger.test.ts +233 -0
  65. package/lib/server/middleware/logger.ts +99 -0
  66. package/lib/server/pageCache.test.ts +167 -0
  67. package/lib/server/pageCache.ts +97 -0
  68. package/lib/server/projectContext.ts +51 -0
  69. package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
  70. package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
  71. package/lib/server/providers/fileSystemPageProvider.ts +83 -0
  72. package/lib/server/routes/api/cms.test.ts +177 -0
  73. package/lib/server/routes/api/cms.ts +82 -0
  74. package/lib/server/routes/api/colors.ts +59 -0
  75. package/lib/server/routes/api/components.ts +70 -0
  76. package/lib/server/routes/api/config.test.ts +9 -0
  77. package/lib/server/routes/api/config.ts +28 -0
  78. package/lib/server/routes/api/core-routes.ts +182 -0
  79. package/lib/server/routes/api/functions.ts +170 -0
  80. package/lib/server/routes/api/index.ts +69 -0
  81. package/lib/server/routes/api/pages.ts +95 -0
  82. package/lib/server/routes/api/shared.test.ts +81 -0
  83. package/lib/server/routes/api/shared.ts +31 -0
  84. package/lib/server/routes/editor.test.ts +9 -0
  85. package/lib/server/routes/index.ts +104 -0
  86. package/lib/server/routes/pages.ts +161 -0
  87. package/lib/server/routes/static.ts +107 -0
  88. package/lib/server/services/ColorService.ts +193 -0
  89. package/lib/server/services/cmsService.test.ts +388 -0
  90. package/lib/server/services/cmsService.ts +296 -0
  91. package/lib/server/services/componentService.test.ts +276 -0
  92. package/lib/server/services/componentService.ts +346 -0
  93. package/lib/server/services/configService.ts +156 -0
  94. package/lib/server/services/fileWatcherService.ts +67 -0
  95. package/lib/server/services/index.ts +10 -0
  96. package/lib/server/services/pageService.test.ts +258 -0
  97. package/lib/server/services/pageService.ts +240 -0
  98. package/lib/server/ssrRenderer.test.ts +1005 -0
  99. package/lib/server/ssrRenderer.ts +878 -0
  100. package/lib/server/utilityClassGenerator.ts +11 -0
  101. package/lib/server/utils/index.ts +5 -0
  102. package/lib/server/utils/jsonLineMapper.test.ts +100 -0
  103. package/lib/server/utils/jsonLineMapper.ts +166 -0
  104. package/lib/server/validateStyleCoverage.test.ts +9 -0
  105. package/lib/server/validateStyleCoverage.ts +167 -0
  106. package/lib/server/websocketManager.test.ts +9 -0
  107. package/lib/server/websocketManager.ts +95 -0
  108. package/lib/shared/attributeNodeUtils.test.ts +152 -0
  109. package/lib/shared/attributeNodeUtils.ts +50 -0
  110. package/lib/shared/breakpoints.test.ts +166 -0
  111. package/lib/shared/breakpoints.ts +65 -0
  112. package/lib/shared/colorProperties.test.ts +111 -0
  113. package/lib/shared/colorProperties.ts +40 -0
  114. package/lib/shared/colorVariableUtils.test.ts +319 -0
  115. package/lib/shared/colorVariableUtils.ts +97 -0
  116. package/lib/shared/constants.test.ts +175 -0
  117. package/lib/shared/constants.ts +116 -0
  118. package/lib/shared/cssGeneration.ts +481 -0
  119. package/lib/shared/cssProperties.test.ts +252 -0
  120. package/lib/shared/cssProperties.ts +338 -0
  121. package/lib/shared/elementUtils.test.ts +245 -0
  122. package/lib/shared/elementUtils.ts +90 -0
  123. package/lib/shared/fontLoader.ts +97 -0
  124. package/lib/shared/i18n.test.ts +313 -0
  125. package/lib/shared/i18n.ts +286 -0
  126. package/lib/shared/index.ts +50 -0
  127. package/lib/shared/interfaces/contentProvider.test.ts +9 -0
  128. package/lib/shared/interfaces/contentProvider.ts +121 -0
  129. package/lib/shared/nodeUtils.test.ts +320 -0
  130. package/lib/shared/nodeUtils.ts +220 -0
  131. package/lib/shared/pathArrayUtils.test.ts +315 -0
  132. package/lib/shared/pathArrayUtils.ts +17 -0
  133. package/lib/shared/pathUtils.test.ts +260 -0
  134. package/lib/shared/pathUtils.ts +244 -0
  135. package/lib/shared/paths/Path.test.ts +74 -0
  136. package/lib/shared/paths/Path.ts +23 -0
  137. package/lib/shared/paths/PathConverter.test.ts +232 -0
  138. package/lib/shared/paths/PathConverter.ts +141 -0
  139. package/lib/shared/paths/PathUtils.ts +290 -0
  140. package/lib/shared/paths/PathValidator.test.ts +193 -0
  141. package/lib/shared/paths/PathValidator.ts +53 -0
  142. package/lib/shared/paths/index.ts +48 -0
  143. package/lib/shared/propResolver.test.ts +639 -0
  144. package/lib/shared/propResolver.ts +124 -0
  145. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
  146. package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
  147. package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
  148. package/lib/shared/registry/ClientRegistry.test.ts +26 -0
  149. package/lib/shared/registry/ClientRegistry.ts +15 -0
  150. package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
  151. package/lib/shared/registry/ComponentRegistry.ts +100 -0
  152. package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
  153. package/lib/shared/registry/NodeTypeManager.ts +94 -0
  154. package/lib/shared/registry/RegistryManager.test.ts +58 -0
  155. package/lib/shared/registry/RegistryManager.ts +60 -0
  156. package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
  157. package/lib/shared/registry/SSRRegistry.test.ts +26 -0
  158. package/lib/shared/registry/SSRRegistry.ts +15 -0
  159. package/lib/shared/registry/createNodeType.ts +175 -0
  160. package/lib/shared/registry/defineNodeType.ts +73 -0
  161. package/lib/shared/registry/fieldPresets.ts +109 -0
  162. package/lib/shared/registry/index.ts +50 -0
  163. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
  164. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
  165. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
  166. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
  167. package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
  168. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
  169. package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
  170. package/lib/shared/registry/nodeTypes/index.ts +75 -0
  171. package/lib/shared/responsiveScaling.test.ts +268 -0
  172. package/lib/shared/responsiveScaling.ts +194 -0
  173. package/lib/shared/responsiveStyleUtils.test.ts +300 -0
  174. package/lib/shared/responsiveStyleUtils.ts +139 -0
  175. package/lib/shared/slugTranslator.test.ts +325 -0
  176. package/lib/shared/slugTranslator.ts +177 -0
  177. package/lib/shared/styleNodeUtils.test.ts +132 -0
  178. package/lib/shared/styleNodeUtils.ts +102 -0
  179. package/lib/shared/styleUtils.test.ts +238 -0
  180. package/lib/shared/styleUtils.ts +63 -0
  181. package/lib/shared/themeDefaults.test.ts +113 -0
  182. package/lib/shared/themeDefaults.ts +103 -0
  183. package/lib/shared/tree/PathBuilder.ts +383 -0
  184. package/lib/shared/treePathUtils.test.ts +539 -0
  185. package/lib/shared/treePathUtils.ts +339 -0
  186. package/lib/shared/types/api.ts +58 -0
  187. package/lib/shared/types/cms.ts +95 -0
  188. package/lib/shared/types/colors.ts +45 -0
  189. package/lib/shared/types/components.ts +121 -0
  190. package/lib/shared/types/errors.test.ts +103 -0
  191. package/lib/shared/types/errors.ts +69 -0
  192. package/lib/shared/types/index.ts +96 -0
  193. package/lib/shared/types/nodes.ts +20 -0
  194. package/lib/shared/types/rendering.ts +61 -0
  195. package/lib/shared/types/styles.ts +38 -0
  196. package/lib/shared/types.ts +11 -0
  197. package/lib/shared/utilityClassConfig.ts +287 -0
  198. package/lib/shared/utilityClassMapper.test.ts +140 -0
  199. package/lib/shared/utilityClassMapper.ts +229 -0
  200. package/lib/shared/utils/fileUtils.test.ts +99 -0
  201. package/lib/shared/utils/fileUtils.ts +56 -0
  202. package/lib/shared/utils.test.ts +261 -0
  203. package/lib/shared/utils.ts +84 -0
  204. package/lib/shared/validation/index.ts +7 -0
  205. package/lib/shared/validation/propValidator.test.ts +178 -0
  206. package/lib/shared/validation/propValidator.ts +238 -0
  207. package/lib/shared/validation/schemas.test.ts +177 -0
  208. package/lib/shared/validation/schemas.ts +401 -0
  209. package/lib/shared/validation/validators.test.ts +109 -0
  210. package/lib/shared/validation/validators.ts +304 -0
  211. package/lib/test-utils/dom-setup.ts +55 -0
  212. package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
  213. package/lib/test-utils/factories/DomMockFactory.ts +487 -0
  214. package/lib/test-utils/factories/EventMockFactory.ts +244 -0
  215. package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
  216. package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
  217. package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
  218. package/lib/test-utils/factories/index.ts +11 -0
  219. package/lib/test-utils/fixtures.ts +134 -0
  220. package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
  221. package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
  222. package/lib/test-utils/helpers/index.ts +6 -0
  223. package/lib/test-utils/helpers.test.ts +73 -0
  224. package/lib/test-utils/helpers.ts +90 -0
  225. package/lib/test-utils/index.ts +17 -0
  226. package/lib/test-utils/mockFactories.ts +92 -0
  227. package/lib/test-utils/mocks.ts +341 -0
  228. package/package.json +38 -0
  229. package/templates/index-router.html +34 -0
  230. package/tsconfig.json +14 -0
  231. 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 };