kimu-core 0.4.1
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/.editorconfig +30 -0
- package/.gitattributes +11 -0
- package/.github/FUNDING.yml +8 -0
- package/.github/copilot-instructions.md +103 -0
- package/.github/kimu-copilot-instructions.md +3779 -0
- package/.github/workflows/deploy-demo.yml +39 -0
- package/AUTHORS.md +20 -0
- package/CHANGELOG.md +20 -0
- package/CODE_GUIDELINES.md +165 -0
- package/CODE_OF_CONDUCT.md +47 -0
- package/CONTRIBUTING.md +62 -0
- package/FUNDING.md +31 -0
- package/ISSUE_GUIDELINES.md +74 -0
- package/LICENSE +17 -0
- package/LICENSE.it.md +17 -0
- package/MPL-2.0.txt +373 -0
- package/NOTICE +65 -0
- package/README-KIMU.md +40 -0
- package/README.it.md +208 -0
- package/README.md +266 -0
- package/SECURITY.md +64 -0
- package/docs/get-started-en.md +207 -0
- package/docs/images/icon.svg +64 -0
- package/docs/images/logo_kimu.png +0 -0
- package/docs/index.md +29 -0
- package/env/dev.config.json +6 -0
- package/env/local.config.json +6 -0
- package/env/prod.config.json +6 -0
- package/env/staging.config.json +6 -0
- package/env/test.config.json +4 -0
- package/icon.svg +10 -0
- package/logo_kimu.png +0 -0
- package/package.json +79 -0
- package/public/favicon.svg +64 -0
- package/public/logo_kimu.svg +1 -0
- package/scripts/build-all-config.js +59 -0
- package/scripts/build-all-core.js +65 -0
- package/scripts/build-all-extensions.js +64 -0
- package/scripts/build-all-modules.js +99 -0
- package/scripts/build-extension.js +60 -0
- package/scripts/clear-kimu-build.js +31 -0
- package/scripts/generate-kimu-build-config.js +79 -0
- package/scripts/install-module.js +162 -0
- package/scripts/list-modules.js +109 -0
- package/scripts/minify-css-assets.js +82 -0
- package/scripts/remove-module.js +122 -0
- package/scripts/utils/fix-imports.js +85 -0
- package/src/assets/index.css +43 -0
- package/src/assets/kimu-style.css +84 -0
- package/src/assets/style.css +116 -0
- package/src/config/kimu-base-config.json +5 -0
- package/src/core/index.ts +47 -0
- package/src/core/kimu-app.ts +76 -0
- package/src/core/kimu-asset-manager.ts +167 -0
- package/src/core/kimu-component-element.ts +325 -0
- package/src/core/kimu-component.ts +33 -0
- package/src/core/kimu-engine.ts +188 -0
- package/src/core/kimu-extension-manager.ts +281 -0
- package/src/core/kimu-global-styles.ts +136 -0
- package/src/core/kimu-module-manager.ts +69 -0
- package/src/core/kimu-module.ts +21 -0
- package/src/core/kimu-path-config.ts +127 -0
- package/src/core/kimu-reactive.ts +196 -0
- package/src/core/kimu-render.ts +91 -0
- package/src/core/kimu-store.ts +147 -0
- package/src/core/kimu-types.ts +65 -0
- package/src/extensions/.gitkeep +0 -0
- package/src/extensions/extensions-manifest.json +13 -0
- package/src/extensions/kimu-home/component.ts +80 -0
- package/src/extensions/kimu-home/lang/en.json +5 -0
- package/src/extensions/kimu-home/lang/it.json +5 -0
- package/src/extensions/kimu-home/style.css +61 -0
- package/src/extensions/kimu-home/view.html +51 -0
- package/src/index.html +26 -0
- package/src/main.ts +68 -0
- package/src/modules/.gitkeep +0 -0
- package/src/modules/README.md +79 -0
- package/src/modules/i18n/README.it.md +63 -0
- package/src/modules/i18n/README.md +63 -0
- package/src/modules/i18n/kimu-global-lang.ts +26 -0
- package/src/modules/i18n/kimu-i18n-service.ts +108 -0
- package/src/modules/i18n/manifest.json +22 -0
- package/src/modules/i18n/module.ts +39 -0
- package/src/modules/modules-manifest.json +12 -0
- package/src/modules-repository/README.md +108 -0
- package/src/modules-repository/api-axios/CHANGELOG.md +48 -0
- package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -0
- package/src/modules-repository/api-axios/README.md +304 -0
- package/src/modules-repository/api-axios/api-axios-service.ts +355 -0
- package/src/modules-repository/api-axios/examples.ts +293 -0
- package/src/modules-repository/api-axios/index.ts +19 -0
- package/src/modules-repository/api-axios/interfaces.ts +71 -0
- package/src/modules-repository/api-axios/module.ts +41 -0
- package/src/modules-repository/api-core/CHANGELOG.md +42 -0
- package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -0
- package/src/modules-repository/api-core/README.md +435 -0
- package/src/modules-repository/api-core/api-core-service.ts +289 -0
- package/src/modules-repository/api-core/examples.ts +432 -0
- package/src/modules-repository/api-core/index.ts +8 -0
- package/src/modules-repository/api-core/interfaces.ts +83 -0
- package/src/modules-repository/api-core/module.ts +30 -0
- package/src/modules-repository/event-bus/README.md +273 -0
- package/src/modules-repository/event-bus/event-bus-service.ts +176 -0
- package/src/modules-repository/event-bus/module.ts +30 -0
- package/src/modules-repository/i18n/README.it.md +63 -0
- package/src/modules-repository/i18n/README.md +63 -0
- package/src/modules-repository/i18n/kimu-global-lang.ts +26 -0
- package/src/modules-repository/i18n/kimu-i18n-service.ts +108 -0
- package/src/modules-repository/i18n/manifest.json +22 -0
- package/src/modules-repository/i18n/module.ts +39 -0
- package/src/modules-repository/notification/README.md +423 -0
- package/src/modules-repository/notification/module.ts +30 -0
- package/src/modules-repository/notification/notification-service.ts +436 -0
- package/src/modules-repository/router/README.it.md +39 -0
- package/src/modules-repository/router/README.md +39 -0
- package/src/modules-repository/router/manifest.json +21 -0
- package/src/modules-repository/router/module.ts +23 -0
- package/src/modules-repository/router/router.ts +144 -0
- package/src/modules-repository/state/README.md +409 -0
- package/src/modules-repository/state/module.ts +30 -0
- package/src/modules-repository/state/state-service.ts +296 -0
- package/src/modules-repository/theme/README.md +267 -0
- package/src/modules-repository/theme/module.ts +30 -0
- package/src/modules-repository/theme/pre-build.js +40 -0
- package/src/modules-repository/theme/theme-service.ts +389 -0
- package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -0
- package/src/modules-repository/theme/themes/theme-cozy.css +111 -0
- package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -0
- package/src/modules-repository/theme/themes/theme-dark.css +79 -0
- package/src/modules-repository/theme/themes/theme-forest.css +171 -0
- package/src/modules-repository/theme/themes/theme-gold.css +100 -0
- package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -0
- package/src/modules-repository/theme/themes/theme-lava.css +101 -0
- package/src/modules-repository/theme/themes/theme-lavender.css +90 -0
- package/src/modules-repository/theme/themes/theme-light.css +79 -0
- package/src/modules-repository/theme/themes/theme-matrix.css +103 -0
- package/src/modules-repository/theme/themes/theme-midnight.css +81 -0
- package/src/modules-repository/theme/themes/theme-nord.css +94 -0
- package/src/modules-repository/theme/themes/theme-ocean.css +84 -0
- package/src/modules-repository/theme/themes/theme-retro80s.css +343 -0
- package/src/modules-repository/theme/themes/theme-sunset.css +62 -0
- package/src/modules-repository/theme/themes-config.d.ts +27 -0
- package/src/modules-repository/theme/themes-config.json +213 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +33 -0
- package/vite.config.ts +99 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Service for KIMU-Core
|
|
3
|
+
*
|
|
4
|
+
* Provides reactive state management with observers and computed properties.
|
|
5
|
+
* Allows components to share and react to state changes.
|
|
6
|
+
*
|
|
7
|
+
* @module StateService
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type StateListener<T = any> = (newValue: T, oldValue: T) => void;
|
|
12
|
+
export type ComputedGetter<T = any> = () => T;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* StateService - Reactive state management for KIMU applications
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { stateService } from './modules/state/state-service';
|
|
20
|
+
*
|
|
21
|
+
* // Set initial state
|
|
22
|
+
* stateService.setState('user', { id: 1, name: 'John' });
|
|
23
|
+
*
|
|
24
|
+
* // Watch for changes
|
|
25
|
+
* stateService.watch('user', (newUser, oldUser) => {
|
|
26
|
+
* console.log('User changed:', newUser);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Update state
|
|
30
|
+
* stateService.setState('user', { id: 1, name: 'Jane' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class StateService {
|
|
34
|
+
private state: Map<string, any> = new Map();
|
|
35
|
+
private observers: Map<string, Set<StateListener>> = new Map();
|
|
36
|
+
private computed: Map<string, ComputedGetter> = new Map();
|
|
37
|
+
private computedCache: Map<string, any> = new Map();
|
|
38
|
+
private debugMode: boolean = false;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Enable or disable debug mode for state logging
|
|
42
|
+
*/
|
|
43
|
+
setDebugMode(enabled: boolean): void {
|
|
44
|
+
this.debugMode = enabled;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set a state value
|
|
49
|
+
*
|
|
50
|
+
* @param key - State key
|
|
51
|
+
* @param value - New value
|
|
52
|
+
*/
|
|
53
|
+
setState<T = any>(key: string, value: T): void {
|
|
54
|
+
const oldValue = this.state.get(key);
|
|
55
|
+
|
|
56
|
+
// Check if value actually changed
|
|
57
|
+
if (oldValue === value || this.deepEqual(oldValue, value)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.state.set(key, value);
|
|
62
|
+
|
|
63
|
+
if (this.debugMode) {
|
|
64
|
+
console.log(`[State] Set: ${key}`, value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Notify observers
|
|
68
|
+
this.notifyObservers(key, value, oldValue);
|
|
69
|
+
|
|
70
|
+
// Invalidate computed properties that may depend on this key
|
|
71
|
+
this.invalidateComputedCache();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get a state value
|
|
76
|
+
*
|
|
77
|
+
* @param key - State key
|
|
78
|
+
* @param defaultValue - Default value if key doesn't exist
|
|
79
|
+
* @returns Current state value
|
|
80
|
+
*/
|
|
81
|
+
getState<T = any>(key: string, defaultValue?: T): T | undefined {
|
|
82
|
+
return this.state.has(key) ? this.state.get(key) : defaultValue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Update nested state properties
|
|
87
|
+
*
|
|
88
|
+
* @param key - State key
|
|
89
|
+
* @param updates - Partial updates to merge
|
|
90
|
+
*/
|
|
91
|
+
updateState<T = any>(key: string, updates: Partial<T>): void {
|
|
92
|
+
const current = this.getState<T>(key) || {} as T;
|
|
93
|
+
const updated = { ...current, ...updates };
|
|
94
|
+
this.setState(key, updated);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a state key
|
|
99
|
+
*
|
|
100
|
+
* @param key - State key to delete
|
|
101
|
+
*/
|
|
102
|
+
deleteState(key: string): void {
|
|
103
|
+
const oldValue = this.state.get(key);
|
|
104
|
+
this.state.delete(key);
|
|
105
|
+
|
|
106
|
+
if (this.debugMode) {
|
|
107
|
+
console.log(`[State] Deleted: ${key}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Notify observers with undefined as new value
|
|
111
|
+
this.notifyObservers(key, undefined, oldValue);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if a state key exists
|
|
116
|
+
*
|
|
117
|
+
* @param key - State key
|
|
118
|
+
* @returns True if key exists
|
|
119
|
+
*/
|
|
120
|
+
hasState(key: string): boolean {
|
|
121
|
+
return this.state.has(key);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Watch for state changes
|
|
126
|
+
*
|
|
127
|
+
* @param key - State key to watch
|
|
128
|
+
* @param listener - Callback when state changes
|
|
129
|
+
* @returns Unwatch function
|
|
130
|
+
*/
|
|
131
|
+
watch<T = any>(key: string, listener: StateListener<T>): () => void {
|
|
132
|
+
if (!this.observers.has(key)) {
|
|
133
|
+
this.observers.set(key, new Set());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.observers.get(key)!.add(listener);
|
|
137
|
+
|
|
138
|
+
if (this.debugMode) {
|
|
139
|
+
console.log(`[State] Watching: ${key}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Return unwatch function
|
|
143
|
+
return () => this.unwatch(key, listener);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Stop watching a state key
|
|
148
|
+
*
|
|
149
|
+
* @param key - State key
|
|
150
|
+
* @param listener - Listener to remove
|
|
151
|
+
*/
|
|
152
|
+
unwatch(key: string, listener: StateListener): void {
|
|
153
|
+
if (this.observers.has(key)) {
|
|
154
|
+
this.observers.get(key)!.delete(listener);
|
|
155
|
+
|
|
156
|
+
if (this.observers.get(key)!.size === 0) {
|
|
157
|
+
this.observers.delete(key);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.debugMode) {
|
|
161
|
+
console.log(`[State] Unwatched: ${key}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Define a computed property
|
|
168
|
+
*
|
|
169
|
+
* @param key - Computed property key
|
|
170
|
+
* @param getter - Function that computes the value
|
|
171
|
+
*/
|
|
172
|
+
defineComputed<T = any>(key: string, getter: ComputedGetter<T>): void {
|
|
173
|
+
this.computed.set(key, getter);
|
|
174
|
+
this.computedCache.delete(key); // Invalidate cache
|
|
175
|
+
|
|
176
|
+
if (this.debugMode) {
|
|
177
|
+
console.log(`[State] Computed defined: ${key}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get a computed property value (cached)
|
|
183
|
+
*
|
|
184
|
+
* @param key - Computed property key
|
|
185
|
+
* @returns Computed value
|
|
186
|
+
*/
|
|
187
|
+
getComputed<T = any>(key: string): T | undefined {
|
|
188
|
+
if (!this.computed.has(key)) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Return cached value if available
|
|
193
|
+
if (this.computedCache.has(key)) {
|
|
194
|
+
return this.computedCache.get(key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Compute and cache
|
|
198
|
+
const getter = this.computed.get(key)!;
|
|
199
|
+
const value = getter();
|
|
200
|
+
this.computedCache.set(key, value);
|
|
201
|
+
|
|
202
|
+
if (this.debugMode) {
|
|
203
|
+
console.log(`[State] Computed: ${key}`, value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get all state as a plain object
|
|
211
|
+
*/
|
|
212
|
+
getAll(): Record<string, any> {
|
|
213
|
+
return Object.fromEntries(this.state.entries());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Clear all state
|
|
218
|
+
*/
|
|
219
|
+
clear(): void {
|
|
220
|
+
this.state.clear();
|
|
221
|
+
this.observers.clear();
|
|
222
|
+
this.computed.clear();
|
|
223
|
+
this.computedCache.clear();
|
|
224
|
+
|
|
225
|
+
if (this.debugMode) {
|
|
226
|
+
console.log('[State] Cleared all state');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reset state to initial values
|
|
232
|
+
*
|
|
233
|
+
* @param initialState - Initial state object
|
|
234
|
+
*/
|
|
235
|
+
reset(initialState: Record<string, any> = {}): void {
|
|
236
|
+
this.clear();
|
|
237
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
238
|
+
this.setState(key, value);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Subscribe to multiple state keys
|
|
244
|
+
*
|
|
245
|
+
* @param keys - Array of state keys
|
|
246
|
+
* @param listener - Callback when any key changes
|
|
247
|
+
* @returns Unsubscribe function
|
|
248
|
+
*/
|
|
249
|
+
watchMultiple(keys: string[], listener: (key: string, newValue: any, oldValue: any) => void): () => void {
|
|
250
|
+
const unwatchers = keys.map(key =>
|
|
251
|
+
this.watch(key, (newValue, oldValue) => listener(key, newValue, oldValue))
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return () => unwatchers.forEach(unwatch => unwatch());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Private helpers
|
|
258
|
+
|
|
259
|
+
private notifyObservers(key: string, newValue: any, oldValue: any): void {
|
|
260
|
+
if (this.observers.has(key)) {
|
|
261
|
+
const listeners = Array.from(this.observers.get(key)!);
|
|
262
|
+
listeners.forEach(listener => {
|
|
263
|
+
try {
|
|
264
|
+
listener(newValue, oldValue);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error(`[State] Error in observer for "${key}":`, error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private invalidateComputedCache(): void {
|
|
273
|
+
this.computedCache.clear();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private deepEqual(a: any, b: any): boolean {
|
|
277
|
+
if (a === b) return true;
|
|
278
|
+
if (a == null || b == null) return false;
|
|
279
|
+
if (typeof a !== 'object' || typeof b !== 'object') return false;
|
|
280
|
+
|
|
281
|
+
const keysA = Object.keys(a);
|
|
282
|
+
const keysB = Object.keys(b);
|
|
283
|
+
|
|
284
|
+
if (keysA.length !== keysB.length) return false;
|
|
285
|
+
|
|
286
|
+
for (const key of keysA) {
|
|
287
|
+
if (!keysB.includes(key)) return false;
|
|
288
|
+
if (!this.deepEqual(a[key], b[key])) return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Export singleton instance
|
|
296
|
+
export const stateService = new StateService();
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Theme Module - CSS-Based Theme Management
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The **Theme Module** provides a complete CSS-based theme management system for KIMU-Core applications. It supports multiple predefined themes (Light, Dark, Ocean, Cozy) and allows easy registration of custom themes through external CSS files.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
The theme module uses **KIMU Global Styles** system to inject theme CSS into all extensions automatically:
|
|
10
|
+
|
|
11
|
+
1. **KimuGlobalStyles** (core): Manages CSS files that should be injected in all extensions
|
|
12
|
+
2. **ThemeService**: Registers the active theme CSS as a global style
|
|
13
|
+
3. **KimuComponentElement**: Automatically injects all global styles into each extension's shadow root
|
|
14
|
+
4. **themes-config.json**: Configuration file with all available themes
|
|
15
|
+
|
|
16
|
+
This architecture ensures:
|
|
17
|
+
- ✅ Theme module is **optional** - framework works without it (fallback styles in `style.css`)
|
|
18
|
+
- ✅ Themes are automatically applied to **all extensions** (including shadow DOM)
|
|
19
|
+
- ✅ Theme changes propagate to **existing extensions** dynamically
|
|
20
|
+
- ✅ **No hardcoded dependencies** in core framework
|
|
21
|
+
- ✅ **Easy theme management** via JSON configuration file
|
|
22
|
+
|
|
23
|
+
## Module Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/modules/theme/
|
|
27
|
+
├── themes/ # Theme source CSS files
|
|
28
|
+
│ ├── theme-light.css
|
|
29
|
+
│ ├── theme-dark.css
|
|
30
|
+
│ ├── theme-ocean.css
|
|
31
|
+
│ ├── theme-cozy.css
|
|
32
|
+
│ ├── theme-nord.css
|
|
33
|
+
│ ├── theme-forest.css
|
|
34
|
+
│ ├── theme-high-contrast.css
|
|
35
|
+
│ └── theme-cyberpunk.css
|
|
36
|
+
├── themes-config.json # Theme configuration
|
|
37
|
+
├── pre-build.js # Pre-build script (copies themes to public/)
|
|
38
|
+
├── theme-service.ts # Main service
|
|
39
|
+
└── README.md # This file
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Build Process**: The pre-build script automatically copies all CSS files from `themes/` to `public/themes/` before compilation.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- 🎨 **CSS-Based Themes**: Each theme is a separate CSS file for maximum flexibility
|
|
47
|
+
- ☀️ **Light Mode**: Clean and bright theme for daytime use
|
|
48
|
+
- 🌙 **Dark Mode**: Dark theme for low-light environments
|
|
49
|
+
- 🌊 **Ocean Theme**: Ocean-inspired dark theme with blue-green tones
|
|
50
|
+
- 🌸 **Cozy Theme**: Warm, feminine and cozy theme with soft colors
|
|
51
|
+
- ❄️ **Nord Theme**: Cold and relaxing Nordic theme for developers
|
|
52
|
+
- 🌲 **Forest Theme**: Natural green theme inspired by nature
|
|
53
|
+
- ⚡ **High Contrast Theme**: WCAG AAA accessible theme
|
|
54
|
+
- 🎮 **Cyberpunk Theme**: Futuristic neon aesthetic
|
|
55
|
+
- 🔄 **Auto Mode**: Automatically follows system color scheme preferences
|
|
56
|
+
- 💾 **Persistence**: User preferences saved in localStorage
|
|
57
|
+
- 🎯 **System Integration**: MediaQuery API for system preference detection
|
|
58
|
+
- 🔌 **Extensible**: Easy registration of custom CSS themes
|
|
59
|
+
- 🌐 **Global Styles**: Themes automatically injected in all extensions via Global Styles system
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Basic Usage
|
|
64
|
+
|
|
65
|
+
\`\`\`typescript
|
|
66
|
+
import { themeService } from './modules/theme/theme-service';
|
|
67
|
+
|
|
68
|
+
// Set theme
|
|
69
|
+
themeService.setMode('dark'); // Switch to dark theme
|
|
70
|
+
themeService.setMode('light'); // Switch to light theme
|
|
71
|
+
themeService.setMode('ocean'); // Switch to ocean theme
|
|
72
|
+
themeService.setMode('cozy'); // Switch to cozy theme
|
|
73
|
+
themeService.setMode('auto'); // Use system preference
|
|
74
|
+
|
|
75
|
+
// Quick toggle
|
|
76
|
+
themeService.toggle(); // Toggle between light/dark
|
|
77
|
+
|
|
78
|
+
// Get current theme
|
|
79
|
+
const mode = themeService.getMode();
|
|
80
|
+
const themeName = themeService.getCurrentThemeName();
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
### Listen to Theme Changes
|
|
84
|
+
|
|
85
|
+
\`\`\`typescript
|
|
86
|
+
themeService.onChange((themeName: string, mode: string) => {
|
|
87
|
+
console.log(\`Theme changed to: \${themeName}\`);
|
|
88
|
+
});
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
## Available Themes
|
|
92
|
+
|
|
93
|
+
### 1. Light Theme
|
|
94
|
+
Clean and bright theme optimized for daytime use.
|
|
95
|
+
|
|
96
|
+
### 2. Dark Theme
|
|
97
|
+
Dark theme for low-light environments and reduced eye strain.
|
|
98
|
+
|
|
99
|
+
### 3. Ocean Theme
|
|
100
|
+
Ocean-inspired dark theme with blue-green tones.
|
|
101
|
+
|
|
102
|
+
### 4. Cozy Theme
|
|
103
|
+
Warm, feminine and cozy theme with soft pastel colors.
|
|
104
|
+
|
|
105
|
+
## Creating Custom Themes
|
|
106
|
+
|
|
107
|
+
### Step 1: Create CSS File
|
|
108
|
+
|
|
109
|
+
\`\`\`css
|
|
110
|
+
/* my-custom-theme.css */
|
|
111
|
+
:root {
|
|
112
|
+
--kimu-primary: #your-color;
|
|
113
|
+
--kimu-background: #your-background;
|
|
114
|
+
--kimu-text-primary: #your-text-color;
|
|
115
|
+
/* ...other variables... */
|
|
116
|
+
}
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
### Step 2: Register the Theme
|
|
120
|
+
|
|
121
|
+
\`\`\`typescript
|
|
122
|
+
themeService.registerCssTheme({
|
|
123
|
+
name: 'my-theme',
|
|
124
|
+
mode: 'dark',
|
|
125
|
+
cssPath: '/themes/my-custom-theme.css',
|
|
126
|
+
description: 'My awesome custom theme'
|
|
127
|
+
});
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
### Step 3: Apply the Theme
|
|
131
|
+
|
|
132
|
+
\`\`\`typescript
|
|
133
|
+
themeService.setMode('my-theme');
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
## Using CSS Variables
|
|
137
|
+
|
|
138
|
+
\`\`\`css
|
|
139
|
+
.my-component {
|
|
140
|
+
background: var(--kimu-background);
|
|
141
|
+
color: var(--kimu-text-primary);
|
|
142
|
+
border: 1px solid var(--kimu-border);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.my-button {
|
|
146
|
+
background: var(--kimu-primary);
|
|
147
|
+
color: white;
|
|
148
|
+
}
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
## Module Independence & Fallback System
|
|
152
|
+
|
|
153
|
+
### How It Works
|
|
154
|
+
|
|
155
|
+
The theme system is designed to work as an **optional module**:
|
|
156
|
+
|
|
157
|
+
1. **Without Theme Module**:
|
|
158
|
+
- Default CSS variables are defined in `src/assets/style.css`
|
|
159
|
+
- Application has basic Light theme styling
|
|
160
|
+
- All components work normally with default colors
|
|
161
|
+
|
|
162
|
+
2. **With Theme Module**:
|
|
163
|
+
- Theme CSS files **override** default values from `style.css`
|
|
164
|
+
- Dynamic theme switching available
|
|
165
|
+
- User preferences persist across sessions
|
|
166
|
+
|
|
167
|
+
### Architecture
|
|
168
|
+
|
|
169
|
+
\`\`\`
|
|
170
|
+
┌─────────────────────────────────────────────┐
|
|
171
|
+
│ style.css (ALWAYS loaded) │
|
|
172
|
+
│ :root { --kimu-primary: #667eea; } │ ← DEFAULT values
|
|
173
|
+
└─────────────────────────────────────────────┘
|
|
174
|
+
↓
|
|
175
|
+
┌─────────────────────────────────────────────┐
|
|
176
|
+
│ theme-dark.css (OPTIONAL, loaded by module)│
|
|
177
|
+
│ :root { --kimu-primary: #818cf8; } │ ← OVERRIDES defaults
|
|
178
|
+
└─────────────────────────────────────────────┘
|
|
179
|
+
↓
|
|
180
|
+
┌─────────────────────────────────────────────┐
|
|
181
|
+
│ Components use variables │
|
|
182
|
+
│ button { background: var(--kimu-primary); }│ ← Uses active value
|
|
183
|
+
└─────────────────────────────────────────────┘
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
### Benefits
|
|
187
|
+
|
|
188
|
+
✅ **App works without theme module** - defaults provide basic styling
|
|
189
|
+
✅ **No breaking changes** - removing theme module doesn't break the app
|
|
190
|
+
✅ **Gradual adoption** - themes can be added later
|
|
191
|
+
✅ **Consistent API** - components always use same CSS variables
|
|
192
|
+
|
|
193
|
+
### CSS Variable Priority
|
|
194
|
+
|
|
195
|
+
When theme module is active:
|
|
196
|
+
\`\`\`
|
|
197
|
+
Theme CSS values > Default style.css values > Browser defaults
|
|
198
|
+
\`\`\`
|
|
199
|
+
|
|
200
|
+
When theme module is NOT installed:
|
|
201
|
+
\`\`\`
|
|
202
|
+
Default style.css values > Browser defaults
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
## Theme Configuration File
|
|
206
|
+
|
|
207
|
+
### Structure
|
|
208
|
+
|
|
209
|
+
The theme module uses a JSON configuration file (`themes-config.json`) to define available themes:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"themes": [
|
|
214
|
+
{
|
|
215
|
+
"name": "light",
|
|
216
|
+
"mode": "light",
|
|
217
|
+
"cssPath": "theme-light.css",
|
|
218
|
+
"description": "Clean and bright theme"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "dark",
|
|
222
|
+
"mode": "dark",
|
|
223
|
+
"cssPath": "theme-dark.css",
|
|
224
|
+
"description": "Dark theme for low-light environments"
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
"defaultTheme": "light"
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Adding New Themes
|
|
232
|
+
|
|
233
|
+
To add a new theme, simply:
|
|
234
|
+
|
|
235
|
+
1. Create the CSS file in `src/modules/theme/themes/` (e.g., `theme-nord.css`)
|
|
236
|
+
2. Add entry to `src/modules/theme/themes-config.json`:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"name": "nord",
|
|
241
|
+
"mode": "dark",
|
|
242
|
+
"cssPath": "theme-nord.css",
|
|
243
|
+
"description": "Cold and relaxing Nordic theme"
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
3. Run `npm run build` to copy themes to `public/themes/` (via pre-build script)
|
|
248
|
+
4. The pre-build script automatically copies all CSS files from `src/modules/theme/themes/` to `public/themes/`
|
|
249
|
+
|
|
250
|
+
**Benefits of configuration file:**
|
|
251
|
+
- ✅ No code changes needed to add themes
|
|
252
|
+
- ✅ Easy to maintain and extend
|
|
253
|
+
- ✅ Clear separation of configuration and logic
|
|
254
|
+
- ✅ Can be easily customized per deployment
|
|
255
|
+
|
|
256
|
+
### Theme Configuration Properties
|
|
257
|
+
|
|
258
|
+
| Property | Type | Required | Description |
|
|
259
|
+
|----------|------|----------|-------------|
|
|
260
|
+
| `name` | string | ✅ | Unique theme identifier (used in `setMode()`) |
|
|
261
|
+
| `mode` | 'light' \| 'dark' | ✅ | Base mode for system preference detection |
|
|
262
|
+
| `cssPath` | string | ✅ | CSS file name in `public/themes/` folder |
|
|
263
|
+
| `description` | string | ❌ | Human-readable theme description |
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
This module is part of KIMU-Core and is licensed under the Mozilla Public License 2.0 (MPL-2.0).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Module for KIMU-Core
|
|
3
|
+
*
|
|
4
|
+
* Provides centralized theme management with dark/light/auto modes.
|
|
5
|
+
*
|
|
6
|
+
* @module ThemeModule
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { KimuModule } from '../../core/kimu-module';
|
|
10
|
+
import { themeService } from './theme-service'
|
|
11
|
+
/**
|
|
12
|
+
* ThemeModule - Module class for theme management integration
|
|
13
|
+
*/
|
|
14
|
+
export default class ThemeModule extends KimuModule {
|
|
15
|
+
constructor(name = 'theme', version = '1.0.0', options?: any) {
|
|
16
|
+
super(name, version, options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the theme service instance
|
|
21
|
+
*/
|
|
22
|
+
getService() {
|
|
23
|
+
return themeService;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Re-export for convenience
|
|
28
|
+
export { themeService } from './theme-service';
|
|
29
|
+
export { ThemeService } from './theme-service';
|
|
30
|
+
export type { ThemeMode, CssTheme } from './theme-service';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-Build Script for Theme Module
|
|
3
|
+
*
|
|
4
|
+
* This script runs BEFORE the theme module is compiled.
|
|
5
|
+
* It copies CSS theme files to public/themes/ so they're available
|
|
6
|
+
* during development and will be included in the Vite build.
|
|
7
|
+
*
|
|
8
|
+
* This approach keeps the module self-contained and independent.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
|
|
13
|
+
export default async function preBuild({ moduleDir, rootDir }) {
|
|
14
|
+
const themesDir = path.join(moduleDir, 'themes');
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(themesDir)) {
|
|
17
|
+
console.log('⚠️ No themes directory found, skipping theme copy');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const cssFiles = fs.readdirSync(themesDir).filter(f => f.endsWith('.css'));
|
|
22
|
+
|
|
23
|
+
if (cssFiles.length === 0) {
|
|
24
|
+
console.log('⚠️ No CSS files found in themes directory');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Copy to public/themes/ (Vite will include them in build automatically)
|
|
29
|
+
const publicThemesDir = path.join(rootDir, 'public', 'themes');
|
|
30
|
+
fs.mkdirSync(publicThemesDir, { recursive: true });
|
|
31
|
+
|
|
32
|
+
for (const cssFile of cssFiles) {
|
|
33
|
+
fs.copyFileSync(
|
|
34
|
+
path.join(themesDir, cssFile),
|
|
35
|
+
path.join(publicThemesDir, cssFile)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(`📄 Theme module: Copied ${cssFiles.length} CSS files to public/themes/`);
|
|
40
|
+
}
|