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,281 @@
|
|
|
1
|
+
import { KimuStore } from './kimu-store';
|
|
2
|
+
import { KimuExtensionMeta } from './kimu-types';
|
|
3
|
+
import { KimuPathConfig } from './kimu-path-config';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get the extension manifest path with proper base path resolution
|
|
7
|
+
*/
|
|
8
|
+
const getExtManifestPath = () => KimuPathConfig.resolvePath('/extensions/extensions-manifest.json');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The `KimuExtensionManager` class manages extensions in the Kimu framework.
|
|
12
|
+
* It handles the initialization, loading, and runtime management of extensions.
|
|
13
|
+
*
|
|
14
|
+
* Key functionalities:
|
|
15
|
+
* - Loads the initial manifest of extensions.
|
|
16
|
+
* - Synchronizes the database with the manifest.
|
|
17
|
+
* - Dynamically loads extensions at runtime.
|
|
18
|
+
* - Provides methods to list, save, disable, and remove extensions.
|
|
19
|
+
* - Maintains a registry of loaded extensions to avoid duplicate loading.
|
|
20
|
+
*/
|
|
21
|
+
export class KimuExtensionManager {
|
|
22
|
+
|
|
23
|
+
/** Singleton instance of the extension manager */
|
|
24
|
+
private static _instance: KimuExtensionManager;
|
|
25
|
+
|
|
26
|
+
/** Registered extensions in the system (stored in IndexedDB) */
|
|
27
|
+
private _extensions: Map<string, KimuExtensionMeta> = new Map<string, KimuExtensionMeta>();
|
|
28
|
+
|
|
29
|
+
/** Map to track whether an extension has been loaded */
|
|
30
|
+
private _loaded: Map<string, boolean> = new Map();
|
|
31
|
+
|
|
32
|
+
/** Map to track loading promises to avoid duplicate loading */
|
|
33
|
+
private _loadingPromises: Map<string, Promise<void>> = new Map();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves the singleton instance of the extension manager.
|
|
37
|
+
* If the instance does not exist, it initializes a new one.
|
|
38
|
+
*/
|
|
39
|
+
static getInstance(): KimuExtensionManager {
|
|
40
|
+
if (!this._instance) {
|
|
41
|
+
// Check if an existing instance is stored in the global window object
|
|
42
|
+
const existing = (window as any).__kimu_extension_manager__;
|
|
43
|
+
if (existing) {
|
|
44
|
+
this._instance = existing;
|
|
45
|
+
} else {
|
|
46
|
+
this._instance = new KimuExtensionManager();
|
|
47
|
+
(window as any).__kimu_extension_manager__ = this._instance;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return this._instance;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initializes IndexedDB and loads the initial manifest on the first run.
|
|
55
|
+
*/
|
|
56
|
+
async init(): Promise<void> {
|
|
57
|
+
// Initialize the extension store (IndexedDB)
|
|
58
|
+
await KimuStore.init();
|
|
59
|
+
// Check if the database is empty (first run)
|
|
60
|
+
// If empty, loads the initial manifest and saves it to the database
|
|
61
|
+
const isEmpty = await KimuStore.isEmpty();
|
|
62
|
+
const manifest = await this.loadInitialManifest();
|
|
63
|
+
if (isEmpty) {
|
|
64
|
+
// Load the initial manifest and save extensions to the database
|
|
65
|
+
console.log('[KimuExtensionManager] ⚠️ Database is empty, loading initial manifest...');
|
|
66
|
+
for (const entry of manifest) {
|
|
67
|
+
await KimuStore.save(entry);
|
|
68
|
+
}
|
|
69
|
+
console.log('[KimuExtensionManager] ✅ KimuStore (IndexedDB) initialized with the manifest JSON');
|
|
70
|
+
} else {
|
|
71
|
+
//console.log('[KimuExtensionManager] 🔄 DB present: sync with json manifest');
|
|
72
|
+
await this.syncWithManifest(manifest);
|
|
73
|
+
}
|
|
74
|
+
const all = await KimuStore.list();
|
|
75
|
+
this._extensions.clear();
|
|
76
|
+
this._loaded.clear();
|
|
77
|
+
for (const ext of all) {
|
|
78
|
+
this._extensions.set(ext.tag, ext);
|
|
79
|
+
this._loaded.set(ext.tag, false);
|
|
80
|
+
}
|
|
81
|
+
// console.log('[KimuExtensionManager] 📦 Extensions available:', this._extensions);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Synchronizes the database with the initial manifest of extensions.
|
|
86
|
+
* Adds new extensions from the manifest and updates existing ones if necessary.
|
|
87
|
+
* If it is present in the DB but not in the manifest, remove it.
|
|
88
|
+
*/
|
|
89
|
+
private async syncWithManifest(manifest: KimuExtensionMeta[]): Promise<void> {
|
|
90
|
+
const current = await KimuStore.list();
|
|
91
|
+
const currentMap = new Map(current.map(e => [e.tag, e]));
|
|
92
|
+
|
|
93
|
+
for (const entry of manifest) {
|
|
94
|
+
const existing = currentMap.get(entry.tag);
|
|
95
|
+
|
|
96
|
+
if (!existing) {
|
|
97
|
+
console.log(`[KimuExtensionManager] ➕ Adding new extension: ${entry.tag}`);
|
|
98
|
+
await KimuStore.save(entry);
|
|
99
|
+
} else {
|
|
100
|
+
// Puoi raffinare il confronto: qui controlliamo solo se version o path sono cambiati
|
|
101
|
+
const changed =
|
|
102
|
+
existing.version !== entry.version ||
|
|
103
|
+
existing.path !== entry.path ||
|
|
104
|
+
existing.name !== entry.name;
|
|
105
|
+
|
|
106
|
+
if (changed) {
|
|
107
|
+
console.log(`[KimuExtensionManager] 🔁 Updating extension: ${entry.tag}`);
|
|
108
|
+
await KimuStore.save(entry);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Loads the initial manifest of extensions (only once).
|
|
116
|
+
*/
|
|
117
|
+
private async loadInitialManifest(): Promise<KimuExtensionMeta[]> {
|
|
118
|
+
// console.log('[KimuExtensionManager] Loading initial manifest...');
|
|
119
|
+
// Load the extension manifest
|
|
120
|
+
try {
|
|
121
|
+
const manifestPath = getExtManifestPath();
|
|
122
|
+
const res = await fetch(manifestPath);
|
|
123
|
+
if (!res.ok) {
|
|
124
|
+
throw new Error('File not found');
|
|
125
|
+
}
|
|
126
|
+
const manifest: KimuExtensionMeta[] = await res.json();
|
|
127
|
+
return manifest;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.warn('[KimuExtensionManager] ⚠️ Initial manifest not loaded:', err);
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Reloads the extension manifest from the remote source.
|
|
136
|
+
*/
|
|
137
|
+
async reloadManifest(): Promise<void> {
|
|
138
|
+
// console.log('[KimuExtensionManager] Reload manifest...');
|
|
139
|
+
const newManifest = await this.loadInitialManifest();
|
|
140
|
+
for (const entry of newManifest) {
|
|
141
|
+
await this.save(entry);
|
|
142
|
+
}
|
|
143
|
+
console.log('[KimuExtensionManager] 🔁 Manifest manually reloaded');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Returns all extensions currently registered in memory.
|
|
148
|
+
*/
|
|
149
|
+
listAvailable(): KimuExtensionMeta[] {
|
|
150
|
+
return Array.from(this._extensions.values());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Returns all extensions saved in IndexedDB.
|
|
155
|
+
*/
|
|
156
|
+
async list(): Promise<KimuExtensionMeta[]> {
|
|
157
|
+
return await KimuStore.list();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Retrieves an extension from IndexedDB by its tag.
|
|
162
|
+
*/
|
|
163
|
+
get(tag: string): KimuExtensionMeta | undefined {
|
|
164
|
+
return this._extensions.get(tag);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns the tags of extensions already loaded dynamically.
|
|
169
|
+
*/
|
|
170
|
+
getTags(): string[] {
|
|
171
|
+
return Array.from(this._extensions.keys());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Checks if an extension has been loaded into the system.
|
|
176
|
+
*/
|
|
177
|
+
isLoaded(tag: string): boolean {
|
|
178
|
+
return this._loaded.get(tag) ?? false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Dynamically loads an extension at runtime (only once).
|
|
183
|
+
*/
|
|
184
|
+
async load(tag: string): Promise<void> {
|
|
185
|
+
//console.log(`[KimuExtensionManager] Load extension: ${tag}`);
|
|
186
|
+
|
|
187
|
+
// Check if the extension is already loaded
|
|
188
|
+
if (!this._extensions.has(tag)) {
|
|
189
|
+
console.log(`[KimuExtensionManager] ⚠️ Extension not present: ${tag}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check if already loaded
|
|
194
|
+
if (this.isLoaded(tag)) {
|
|
195
|
+
//console.log(`[KimuExtensionManager] ⚠️ Extension already loaded: ${tag}`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if already loading
|
|
200
|
+
if (this._loadingPromises.has(tag)) {
|
|
201
|
+
return this._loadingPromises.get(tag);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ext = this._extensions.get(tag);
|
|
205
|
+
if (!ext) {
|
|
206
|
+
console.warn(`[KimuExtensionManager] ⚠️ Extension not found: ${tag}`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!ext.path) {
|
|
211
|
+
console.warn(`[KimuExtensionManager] ⚠️ Path not defined for extension: ${tag}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Create loading promise
|
|
216
|
+
const loadingPromise = this._loadExtension(tag, ext);
|
|
217
|
+
this._loadingPromises.set(tag, loadingPromise);
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
await loadingPromise;
|
|
221
|
+
} finally {
|
|
222
|
+
this._loadingPromises.delete(tag);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Internal method to load an extension
|
|
228
|
+
*/
|
|
229
|
+
private async _loadExtension(tag: string, ext: KimuExtensionMeta): Promise<void> {
|
|
230
|
+
try {
|
|
231
|
+
const componentPath = KimuPathConfig.resolvePath(`/extensions/${ext.path}/component.js`);
|
|
232
|
+
await import(/* @vite-ignore */ componentPath);
|
|
233
|
+
this._loaded.set(tag, true);
|
|
234
|
+
// console.log(`[KimuExtensionManager] ✅ Extension loaded: ${tag}`);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.error(`[KimuExtensionManager] ❌ Error loading Extension tag "${tag}":`, err);
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Saves an extension to the database.
|
|
243
|
+
*/
|
|
244
|
+
async save(entry: KimuExtensionMeta): Promise<void> {
|
|
245
|
+
await KimuStore.save(entry);
|
|
246
|
+
this._extensions.set(entry.tag, entry);
|
|
247
|
+
this._loaded.set(entry.tag, false);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Disables an extension.
|
|
252
|
+
*/
|
|
253
|
+
async disable(tag: string): Promise<void> {
|
|
254
|
+
const ext = await this.get(tag);
|
|
255
|
+
if (ext) {
|
|
256
|
+
ext.enabled = false;
|
|
257
|
+
await this.save(ext);
|
|
258
|
+
console.log(`[KimuExtensionManager] ⛔ Extension disabled: ${tag}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Removes an extension from the database.
|
|
264
|
+
*/
|
|
265
|
+
async remove(tag: string): Promise<void> {
|
|
266
|
+
await KimuStore.remove(tag);
|
|
267
|
+
this._extensions.delete(tag);
|
|
268
|
+
this._loaded.delete(tag);
|
|
269
|
+
console.log(`[KimuExtensionManager] 🗑️ Extension removed: ${tag}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Resets the database completely. Clears all data (use with caution).
|
|
274
|
+
*/
|
|
275
|
+
async reset(): Promise<void> {
|
|
276
|
+
await KimuStore.clear();
|
|
277
|
+
this._extensions.clear();
|
|
278
|
+
this._loaded.clear();
|
|
279
|
+
console.log('[KimuExtensionManager] 🔄 Complete database reset');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KimuGlobalStyles manages global CSS files that should be injected into all extensions.
|
|
3
|
+
* This allows modules (like the theme module) to register additional global styles
|
|
4
|
+
* without hardcoding dependencies in the core framework.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* - Core always injects 'assets/kimu-style.css' as the base style
|
|
8
|
+
* - Modules can register additional global CSS using registerGlobalStyle()
|
|
9
|
+
* - All registered styles are automatically injected into every extension's shadow root
|
|
10
|
+
*/
|
|
11
|
+
export class KimuGlobalStyles {
|
|
12
|
+
/** List of global CSS files to inject in all extensions */
|
|
13
|
+
private static _globalStyles: Array<{ id: string; path: string }> = [
|
|
14
|
+
// Default KIMU base styles - always included
|
|
15
|
+
{ id: 'kimu-style-default', path: 'assets/kimu-style.css' }
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register a global CSS file to be injected in all extensions.
|
|
20
|
+
* This is useful for modules that want to provide global theming or styling.
|
|
21
|
+
*
|
|
22
|
+
* @param id - Unique identifier for the style (e.g., 'theme-dark', 'theme-light')
|
|
23
|
+
* @param path - Path to the CSS file relative to the base path
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // In a theme module
|
|
27
|
+
* KimuGlobalStyles.registerGlobalStyle('theme-active', 'themes/theme-dark.css');
|
|
28
|
+
*/
|
|
29
|
+
static registerGlobalStyle(id: string, path: string): void {
|
|
30
|
+
// Check if already registered
|
|
31
|
+
const existing = this._globalStyles.find(s => s.id === id);
|
|
32
|
+
if (existing) {
|
|
33
|
+
// Update path if already registered
|
|
34
|
+
existing.path = path;
|
|
35
|
+
console.log(`[KimuGlobalStyles] Updated global style: ${id} -> ${path}`);
|
|
36
|
+
} else {
|
|
37
|
+
// Add new global style
|
|
38
|
+
this._globalStyles.push({ id, path });
|
|
39
|
+
console.log(`[KimuGlobalStyles] Registered global style: ${id} -> ${path}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Unregister a global CSS file.
|
|
45
|
+
*
|
|
46
|
+
* @param id - Unique identifier of the style to remove
|
|
47
|
+
*/
|
|
48
|
+
static unregisterGlobalStyle(id: string): void {
|
|
49
|
+
const index = this._globalStyles.findIndex(s => s.id === id);
|
|
50
|
+
if (index !== -1) {
|
|
51
|
+
this._globalStyles.splice(index, 1);
|
|
52
|
+
console.log(`[KimuGlobalStyles] Unregistered global style: ${id}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get all registered global styles.
|
|
58
|
+
* Used internally by KimuComponentElement to inject styles into extensions.
|
|
59
|
+
*
|
|
60
|
+
* @returns Array of registered global styles
|
|
61
|
+
*/
|
|
62
|
+
static getGlobalStyles(): Array<{ id: string; path: string }> {
|
|
63
|
+
return [...this._globalStyles]; // Return a copy to prevent external modifications
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Clear all registered styles (useful for testing or reset scenarios).
|
|
68
|
+
* Note: This will also remove the default kimu-style.css.
|
|
69
|
+
*/
|
|
70
|
+
static clearAllStyles(): void {
|
|
71
|
+
this._globalStyles = [
|
|
72
|
+
{ id: 'kimu-style-default', path: 'assets/kimu-style.css' }
|
|
73
|
+
];
|
|
74
|
+
console.log('[KimuGlobalStyles] Cleared all global styles, keeping only default');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the number of registered global styles.
|
|
79
|
+
*/
|
|
80
|
+
static getStyleCount(): number {
|
|
81
|
+
return this._globalStyles.length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if a specific style ID is registered.
|
|
86
|
+
*/
|
|
87
|
+
static hasStyle(id: string): boolean {
|
|
88
|
+
return this._globalStyles.some(s => s.id === id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Update a specific global style in all currently loaded extensions.
|
|
93
|
+
* This is useful when a theme changes and needs to be propagated to all extensions.
|
|
94
|
+
*
|
|
95
|
+
* @param id - Style ID to update (e.g., 'kimu-theme-active')
|
|
96
|
+
*
|
|
97
|
+
* Note: This method uses KimuEngine.injectStyle() to update styles in all shadow roots.
|
|
98
|
+
* It requires KimuEngine to be imported dynamically to avoid circular dependencies.
|
|
99
|
+
*/
|
|
100
|
+
static async updateStyleInAllExtensions(id: string): Promise<void> {
|
|
101
|
+
const style = this._globalStyles.find(s => s.id === id);
|
|
102
|
+
if (!style) {
|
|
103
|
+
console.warn(`[KimuGlobalStyles] Style "${id}" not found, cannot update`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Find all custom elements (extensions) in the DOM
|
|
108
|
+
const allCustomElements = document.querySelectorAll('*');
|
|
109
|
+
const extensions: HTMLElement[] = [];
|
|
110
|
+
|
|
111
|
+
allCustomElements.forEach((el) => {
|
|
112
|
+
// Check if element is a custom element with shadow root
|
|
113
|
+
if (el.tagName.includes('-') && (el as any).shadowRoot) {
|
|
114
|
+
extensions.push(el as HTMLElement);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (extensions.length === 0) {
|
|
119
|
+
console.log('[KimuGlobalStyles] No extensions found to update');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Dynamically import KimuEngine to avoid circular dependency
|
|
124
|
+
const { KimuEngine } = await import('./kimu-engine');
|
|
125
|
+
|
|
126
|
+
// Update style in all extensions
|
|
127
|
+
console.log(`[KimuGlobalStyles] Updating style "${id}" in ${extensions.length} extensions`);
|
|
128
|
+
for (const ext of extensions) {
|
|
129
|
+
try {
|
|
130
|
+
await KimuEngine.injectStyle(ext, style.path, style.id);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.warn(`[KimuGlobalStyles] Failed to update style in ${ext.tagName}:`, error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { KimuModule } from './kimu-module';
|
|
2
|
+
import { KimuModuleOptions } from './kimu-types';
|
|
3
|
+
import { KimuPathConfig } from './kimu-path-config';
|
|
4
|
+
|
|
5
|
+
export class KimuModuleManager {
|
|
6
|
+
private static instance: KimuModuleManager | null = null;
|
|
7
|
+
private modules: Map<string, KimuModule> = new Map();
|
|
8
|
+
|
|
9
|
+
static getInstance(): KimuModuleManager {
|
|
10
|
+
if (!KimuModuleManager.instance) {
|
|
11
|
+
KimuModuleManager.instance = new KimuModuleManager();
|
|
12
|
+
}
|
|
13
|
+
return KimuModuleManager.instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async loadModule(name: string, options?: KimuModuleOptions): Promise<KimuModule> {
|
|
17
|
+
// Check if module already exists (singleton support)
|
|
18
|
+
if (this.modules.has(name)) {
|
|
19
|
+
const existing = this.modules.get(name)!;
|
|
20
|
+
if (existing.singleton) {
|
|
21
|
+
return existing;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Import dinamico del modulo con base path
|
|
26
|
+
const modPath = KimuPathConfig.resolvePath(`/modules/${name}/module.js`);
|
|
27
|
+
const modImport = await import(/* @vite-ignore */ modPath);
|
|
28
|
+
const instance: KimuModule = new modImport.default(name, '1.0.0', options);
|
|
29
|
+
|
|
30
|
+
if (instance.onInit) await instance.onInit();
|
|
31
|
+
this.modules.set(name, instance);
|
|
32
|
+
return instance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getModule(name: string): KimuModule | undefined {
|
|
36
|
+
return this.modules.get(name);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async closeModule(name: string): Promise<void> {
|
|
40
|
+
const mod = this.modules.get(name);
|
|
41
|
+
if (mod && mod.onClose) await mod.onClose();
|
|
42
|
+
this.modules.delete(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getService(name: string): any {
|
|
46
|
+
const mod = this.modules.get(name);
|
|
47
|
+
return mod ? mod.getService() : undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Convenience methods for commonly used modules
|
|
51
|
+
async getI18nService(options?: KimuModuleOptions): Promise<any> {
|
|
52
|
+
const module = await this.loadModule('i18n', options);
|
|
53
|
+
return module.getService();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getRouterService(options?: KimuModuleOptions): Promise<any> {
|
|
57
|
+
const module = await this.loadModule('router', options);
|
|
58
|
+
return module.getService();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Static convenience methods for global access
|
|
62
|
+
static async getI18nService(options?: KimuModuleOptions): Promise<any> {
|
|
63
|
+
return KimuModuleManager.getInstance().getI18nService(options);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static async getRouterService(options?: KimuModuleOptions): Promise<any> {
|
|
67
|
+
return KimuModuleManager.getInstance().getRouterService(options);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { KimuModuleOptions } from './kimu-types';
|
|
2
|
+
|
|
3
|
+
export abstract class KimuModule {
|
|
4
|
+
public name: string;
|
|
5
|
+
public version: string;
|
|
6
|
+
public singleton?: boolean = false;
|
|
7
|
+
public options?: KimuModuleOptions;
|
|
8
|
+
|
|
9
|
+
constructor(name: string, version: string, options?: KimuModuleOptions) {
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.version = version;
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Lifecycle hooks
|
|
16
|
+
onInit?(): void | Promise<void>;
|
|
17
|
+
onClose?(): void | Promise<void>;
|
|
18
|
+
|
|
19
|
+
// API per servizi/esportazioni
|
|
20
|
+
abstract getService(): any;
|
|
21
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { KimuBuildConfig } from '../config/kimu-build-config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* KimuPathConfig handles base path configuration for deployments in subdirectories.
|
|
5
|
+
* It integrates with the existing build configuration system and supports:
|
|
6
|
+
* - Automatic initialization from generated build config
|
|
7
|
+
* - Runtime path resolution for all framework resources
|
|
8
|
+
* - Support for deployment in any server subdirectory
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* - Paths are configured in env/*.config.json files
|
|
12
|
+
* - Can be overridden with environment variables (KIMU_BASE_PATH)
|
|
13
|
+
* - Automatically resolves all framework resource paths
|
|
14
|
+
*/
|
|
15
|
+
export class KimuPathConfig {
|
|
16
|
+
/** Current base path (without trailing slash, empty for root) */
|
|
17
|
+
private static _basePath: string | null = null;
|
|
18
|
+
|
|
19
|
+
/** Initialization flag to avoid multiple initializations */
|
|
20
|
+
private static _initialized: boolean = false;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initialize from generated build config.
|
|
24
|
+
* Called automatically on first path resolution.
|
|
25
|
+
*/
|
|
26
|
+
static initialize(): void {
|
|
27
|
+
if (this._initialized) return;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Get base path from generated configuration
|
|
31
|
+
const configBasePath = (KimuBuildConfig.build as any)['base-path'] || '/';
|
|
32
|
+
this.setBasePath(configBasePath);
|
|
33
|
+
this._initialized = true;
|
|
34
|
+
|
|
35
|
+
console.log(`[KimuPathConfig] Initialized with base path: "${this._basePath || '/'}"`);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.warn('[KimuPathConfig] Failed to initialize from build config, using root path', error);
|
|
38
|
+
this.setBasePath('/');
|
|
39
|
+
this._initialized = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Set base path manually (mainly for testing or runtime override).
|
|
45
|
+
* @param path The base path to set (e.g., "/", "/dist/", "/my-app/")
|
|
46
|
+
*/
|
|
47
|
+
static setBasePath(path: string): void {
|
|
48
|
+
// Normalize path: ensure starts with / and doesn't end with / (except root)
|
|
49
|
+
if (!path.startsWith('/')) path = '/' + path;
|
|
50
|
+
if (path.length > 1 && path.endsWith('/')) path = path.slice(0, -1);
|
|
51
|
+
|
|
52
|
+
// Store as empty string for root path, or the normalized path
|
|
53
|
+
this._basePath = path === '/' ? '' : path;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current base path.
|
|
58
|
+
* @returns The current base path (empty string for root)
|
|
59
|
+
*/
|
|
60
|
+
static getBasePath(): string {
|
|
61
|
+
if (!this._initialized) this.initialize();
|
|
62
|
+
return this._basePath || '';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve an absolute path with the configured base path.
|
|
67
|
+
* @param path The path to resolve (e.g., "/extensions/manifest.json")
|
|
68
|
+
* @returns The resolved path with base path prepended
|
|
69
|
+
*/
|
|
70
|
+
static resolvePath(path: string): string {
|
|
71
|
+
if (!this._initialized) this.initialize();
|
|
72
|
+
|
|
73
|
+
// Skip external URLs
|
|
74
|
+
if (this.isExternalUrl(path)) return path;
|
|
75
|
+
|
|
76
|
+
// Skip relative paths (they're already relative to current location)
|
|
77
|
+
if (!path.startsWith('/')) return path;
|
|
78
|
+
|
|
79
|
+
// Apply base path to absolute paths
|
|
80
|
+
const basePath = this._basePath || '';
|
|
81
|
+
return basePath + path;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if a path is an external URL.
|
|
86
|
+
* @param path The path to check
|
|
87
|
+
* @returns True if the path is an external URL
|
|
88
|
+
*/
|
|
89
|
+
private static isExternalUrl(path: string): boolean {
|
|
90
|
+
return path.startsWith('http://') ||
|
|
91
|
+
path.startsWith('https://') ||
|
|
92
|
+
path.startsWith('//') ||
|
|
93
|
+
path.startsWith('mailto:') ||
|
|
94
|
+
path.startsWith('tel:');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Force re-initialization (mainly for testing).
|
|
99
|
+
*/
|
|
100
|
+
static reset(): void {
|
|
101
|
+
this._initialized = false;
|
|
102
|
+
this._basePath = null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get full URL for a path (combines base URL from config with resolved path).
|
|
107
|
+
* @param path The path to convert to full URL
|
|
108
|
+
* @returns Full URL with base URL and path
|
|
109
|
+
*/
|
|
110
|
+
static getFullUrl(path: string): string {
|
|
111
|
+
if (!this._initialized) this.initialize();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const webUrl = (KimuBuildConfig.build as any)['web-url'] || window.location.origin;
|
|
115
|
+
const resolvedPath = this.resolvePath(path);
|
|
116
|
+
|
|
117
|
+
// If path is already a full URL, return as-is
|
|
118
|
+
if (this.isExternalUrl(resolvedPath)) return resolvedPath;
|
|
119
|
+
|
|
120
|
+
// Combine base URL with resolved path
|
|
121
|
+
return webUrl + resolvedPath;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.warn('[KimuPathConfig] Failed to get full URL, returning resolved path', error);
|
|
124
|
+
return this.resolvePath(path);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|