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,325 @@
|
|
|
1
|
+
import { KimuEngine } from './kimu-engine';
|
|
2
|
+
import { KimuExtensionManager } from './kimu-extension-manager';
|
|
3
|
+
import { KimuExtensionMeta } from './kimu-types';
|
|
4
|
+
import { KimuPathConfig } from './kimu-path-config';
|
|
5
|
+
import { KimuGlobalStyles } from './kimu-global-styles';
|
|
6
|
+
import {
|
|
7
|
+
initReactiveProperties,
|
|
8
|
+
getObservedAttributes,
|
|
9
|
+
handleAttributeChange
|
|
10
|
+
} from './kimu-reactive';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The `KimuComponentElement` class serves as the base class for all components and extensions
|
|
14
|
+
* in the Kimu framework. It provides lifecycle hooks, metadata management, and utility methods
|
|
15
|
+
* for rendering and managing resources.
|
|
16
|
+
*
|
|
17
|
+
* Key functionalities:
|
|
18
|
+
* - Provides lifecycle hooks (`onInit`, `onRender`, `onDestroy`) for component management.
|
|
19
|
+
* - Manages metadata associated with the component.
|
|
20
|
+
* - Handles template rendering and resource loading.
|
|
21
|
+
* - Supports dependency injection and external asset management.
|
|
22
|
+
*
|
|
23
|
+
* This class is designed to be extended by custom components and used with the `@KimuComponent` decorator.
|
|
24
|
+
*/
|
|
25
|
+
export abstract class KimuComponentElement extends HTMLElement {
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Static method to get observed attributes from reactive properties
|
|
29
|
+
* This enables automatic attribute reflection for @property decorated properties
|
|
30
|
+
*/
|
|
31
|
+
static get observedAttributes(): string[] {
|
|
32
|
+
return getObservedAttributes(this);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Global optimization settings - simplified */
|
|
36
|
+
private static _optimizationSettings = {
|
|
37
|
+
enableTemplateCache: true,
|
|
38
|
+
enableFileCache: true,
|
|
39
|
+
enableRenderDebouncing: true,
|
|
40
|
+
enableErrorBoundaries: true, // New: Safe error isolation
|
|
41
|
+
cacheMaxSize: 50, // New: Cache size limiting
|
|
42
|
+
enableAssetPreloading: false // New: Asset preloading (opt-in)
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configure global optimization settings for all components
|
|
47
|
+
*/
|
|
48
|
+
static configureOptimizations(settings: Partial<typeof KimuComponentElement._optimizationSettings>) {
|
|
49
|
+
Object.assign(this._optimizationSettings, settings);
|
|
50
|
+
|
|
51
|
+
// Apply cache size settings to KimuEngine
|
|
52
|
+
if (settings.cacheMaxSize !== undefined) {
|
|
53
|
+
KimuEngine.configureCaching(settings.cacheMaxSize);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get current optimization settings (for debugging)
|
|
59
|
+
*/
|
|
60
|
+
static getOptimizationSettings() {
|
|
61
|
+
return { ...this._optimizationSettings };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Preload critical assets for better performance
|
|
66
|
+
*/
|
|
67
|
+
static async preloadAssets(assetPaths: string[]): Promise<void> {
|
|
68
|
+
if (!this._optimizationSettings.enableAssetPreloading) {
|
|
69
|
+
console.warn('[KimuComponentElement] Asset preloading is disabled');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return KimuEngine.preloadAssets(assetPaths);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Force refresh without optimizations (for debugging)
|
|
78
|
+
*/
|
|
79
|
+
async forceRefresh(): Promise<void> {
|
|
80
|
+
if (!this._renderFn) {
|
|
81
|
+
console.warn('[KimuComponentElement] ⚠️ Template render function (_renderFn) not initialized for component:', this.tagName);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Force render immediately without debouncing
|
|
86
|
+
const currentData = this.getData();
|
|
87
|
+
await KimuEngine.render(this, currentData, this._renderFn!);
|
|
88
|
+
this.onRender();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Private reference to the KimuApp singleton (lazy loaded) */
|
|
92
|
+
private _app: any = null;
|
|
93
|
+
/** Each component must provide data for rendering */
|
|
94
|
+
getData(): Record<string, any> {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Lifecycle: initialization */
|
|
99
|
+
onInit(): void { }
|
|
100
|
+
|
|
101
|
+
/** Called every time after a render or refresh */
|
|
102
|
+
onRender(): void { }
|
|
103
|
+
|
|
104
|
+
/** Lifecycle: destruction */
|
|
105
|
+
onDestroy(): void { }
|
|
106
|
+
|
|
107
|
+
/** Optional error handling hook - override in components that need custom error handling */
|
|
108
|
+
onError?(error: Error): void;
|
|
109
|
+
|
|
110
|
+
/** Hook for loading the template */
|
|
111
|
+
private _renderFn?: (html: any, data: Record<string, any>) => any;
|
|
112
|
+
|
|
113
|
+
/** Lifecycle: destruction (override for custom cleanup, calls onDestroy by default) */
|
|
114
|
+
_onDestroyInternal(): void {
|
|
115
|
+
this.onDestroy();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the KimuApp singleton, importing it dynamically only once.
|
|
120
|
+
* Usage: await this.getApp() in your extension/component.
|
|
121
|
+
*/
|
|
122
|
+
protected async getApp(): Promise<any> {
|
|
123
|
+
if (this._app) {
|
|
124
|
+
return this._app;
|
|
125
|
+
}
|
|
126
|
+
// Dynamically import KimuApp to avoid circular dependencies
|
|
127
|
+
const { KimuApp } = await import('./kimu-app');
|
|
128
|
+
this._app = KimuApp.getInstance();
|
|
129
|
+
return this._app;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Constructor for the Kimu component.
|
|
134
|
+
* Initializes the shadow DOM and attaches it to the component.
|
|
135
|
+
*/
|
|
136
|
+
constructor() {
|
|
137
|
+
super();
|
|
138
|
+
this.attachShadow({ mode: 'open' });
|
|
139
|
+
//console.log("\n\n###### META DATA: ", this.getMeta());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Called when an observed attribute changes
|
|
144
|
+
* This is part of the Web Components lifecycle and enables attribute reflection
|
|
145
|
+
*/
|
|
146
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
|
|
147
|
+
// Handle reactive property attribute changes
|
|
148
|
+
handleAttributeChange(this, name, oldValue, newValue);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Shortcut for querying the DOM */
|
|
152
|
+
$(selector: string): HTMLElement | null {
|
|
153
|
+
return this.shadowRoot?.querySelector(selector) ?? null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Retrieves metadata associated with the component */
|
|
157
|
+
protected getMeta(): KimuExtensionMeta {
|
|
158
|
+
return (this.constructor as any).__kimu_meta__ as KimuExtensionMeta;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Hook called automatically by the browser when the component is connected to the DOM.
|
|
163
|
+
* Method automatically called by the browser when the component is connected.
|
|
164
|
+
*/
|
|
165
|
+
async connectedCallback(): Promise<void> {
|
|
166
|
+
const meta = this.getMeta();
|
|
167
|
+
//console.log('[KimuComponentElement] Init tag:', this.tagName, 'meta:', meta);
|
|
168
|
+
if (!meta || !meta.tag) {
|
|
169
|
+
console.warn('[KimuComponentElement] ❌ No metadata found for the component:', this.tagName , " - meta:", meta);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Initialize reactive properties from HTML attributes
|
|
174
|
+
initReactiveProperties(this);
|
|
175
|
+
|
|
176
|
+
// Load dependencies - use Promise.all for parallel loading
|
|
177
|
+
if (meta.dependencies?.length) {
|
|
178
|
+
const manager = KimuExtensionManager.getInstance();
|
|
179
|
+
await Promise.all(meta.dependencies.map(dep => manager.load(dep)));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Inject all registered global styles (kimu-style.css + theme CSS, etc.)
|
|
183
|
+
const globalStyles = KimuGlobalStyles.getGlobalStyles();
|
|
184
|
+
for (const style of globalStyles) {
|
|
185
|
+
await KimuEngine.injectStyle(this, style.path, style.id);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Inject external styles (if present, just once)
|
|
189
|
+
if (meta.style) {
|
|
190
|
+
let styleId = `kimu-style-ext-${meta.tag}`;
|
|
191
|
+
const cssPath = KimuPathConfig.resolvePath(`extensions/${meta.basePath}/${meta.style}`);
|
|
192
|
+
await KimuEngine.injectStyle(this, cssPath, styleId);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Compile the template on the first connection
|
|
196
|
+
if (meta.template) {
|
|
197
|
+
const templatePath = KimuPathConfig.resolvePath(`extensions/${meta.basePath}/${meta.template}`);
|
|
198
|
+
// Use template caching based on global settings
|
|
199
|
+
const useCache = KimuComponentElement._optimizationSettings.enableTemplateCache;
|
|
200
|
+
this._renderFn = await KimuEngine.loadTemplate(templatePath, useCache);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Initial render - wait for completion before calling onInit
|
|
204
|
+
await this.refresh(); // Render the template with data and wait for completion
|
|
205
|
+
// Call the initialization hook - DOM is now guaranteed to be ready
|
|
206
|
+
this.onInit(); // Call once, after DOM is fully rendered
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Forces a refresh of the interface component
|
|
211
|
+
* Note: For @property decorated properties, this is called automatically.
|
|
212
|
+
* Manual calls are only needed for non-reactive state changes.
|
|
213
|
+
*/
|
|
214
|
+
async refresh(): Promise<void> {
|
|
215
|
+
if (!this._renderFn) {
|
|
216
|
+
console.warn('[KimuComponentElement] ⚠️ Template render function (_renderFn) not initialized for component:', this.tagName);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Skip debouncing if disabled or call directly
|
|
221
|
+
if (!KimuComponentElement._optimizationSettings.enableRenderDebouncing) {
|
|
222
|
+
await this._doRender();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Use the same RAF batching mechanism as kimu-reactive
|
|
227
|
+
// This ensures consistent behavior across manual and reactive updates
|
|
228
|
+
if ((this as any).__kimu_render_pending__) {
|
|
229
|
+
return; // Already scheduled
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
(this as any).__kimu_render_pending__ = true;
|
|
233
|
+
|
|
234
|
+
return new Promise<void>((resolve) => {
|
|
235
|
+
requestAnimationFrame(async () => {
|
|
236
|
+
try {
|
|
237
|
+
await this._doRender();
|
|
238
|
+
resolve();
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('[KimuComponentElement] Render error:', error);
|
|
241
|
+
resolve(); // Resolve anyway to prevent hanging
|
|
242
|
+
} finally {
|
|
243
|
+
(this as any).__kimu_render_pending__ = false;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Internal render method - simplified without snapshot optimization
|
|
251
|
+
*/
|
|
252
|
+
private async _doRender(): Promise<void> {
|
|
253
|
+
if (!KimuComponentElement._optimizationSettings.enableErrorBoundaries) {
|
|
254
|
+
// Original behavior without error boundaries
|
|
255
|
+
const currentData = this.getData();
|
|
256
|
+
await KimuEngine.render(this, currentData, this._renderFn!);
|
|
257
|
+
this.onRender();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Safe rendering with error boundaries
|
|
262
|
+
try {
|
|
263
|
+
const currentData = this.getData();
|
|
264
|
+
await KimuEngine.render(this, currentData, this._renderFn!);
|
|
265
|
+
this.onRender();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(`[${this.tagName}] Render error:`, error);
|
|
268
|
+
console.error('Component data:', this.getData());
|
|
269
|
+
|
|
270
|
+
// Fallback UI - safe and informative
|
|
271
|
+
if (this.shadowRoot) {
|
|
272
|
+
this.shadowRoot.innerHTML = `
|
|
273
|
+
<div style="
|
|
274
|
+
color: #721c24;
|
|
275
|
+
background-color: #f8d7da;
|
|
276
|
+
border: 1px solid #f5c6cb;
|
|
277
|
+
padding: 10px;
|
|
278
|
+
border-radius: 4px;
|
|
279
|
+
font-family: monospace;
|
|
280
|
+
font-size: 12px;
|
|
281
|
+
">
|
|
282
|
+
<strong>⚠️ Component Error</strong><br>
|
|
283
|
+
<em>${this.tagName}</em><br>
|
|
284
|
+
${error instanceof Error ? error.message : 'Unknown error'}
|
|
285
|
+
</div>
|
|
286
|
+
`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Call error hook if available
|
|
290
|
+
if (typeof this.onError === 'function') {
|
|
291
|
+
try {
|
|
292
|
+
this.onError(error instanceof Error ? error : new Error(String(error)));
|
|
293
|
+
} catch (hookError) {
|
|
294
|
+
console.error(`[${this.tagName}] Error in onError hook:`, hookError);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Hook called automatically when the component is disconnected from the DOM.
|
|
302
|
+
* Method automatically called on logout.
|
|
303
|
+
*/
|
|
304
|
+
disconnectedCallback(): void {
|
|
305
|
+
// Clean up RAF batching state
|
|
306
|
+
(this as any).__kimu_render_pending__ = false;
|
|
307
|
+
|
|
308
|
+
// Call the destruction hook
|
|
309
|
+
this._onDestroyInternal();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Loads a resource from an external file */
|
|
313
|
+
loadResource(file: string): Promise<any> {
|
|
314
|
+
const path = this.getMeta()?.path;
|
|
315
|
+
if (!path) throw new Error('Extension without valid path');
|
|
316
|
+
const resourcePath = KimuPathConfig.resolvePath(`extensions/${path}/resources/${file}`);
|
|
317
|
+
return fetch(resourcePath).then(r => r.json());
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Loads an asset from an external file */
|
|
321
|
+
loadAssetUrl(file: string): string {
|
|
322
|
+
const path = this.getMeta()?.path;
|
|
323
|
+
return KimuPathConfig.resolvePath(`extensions/${path}/assets/${file}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { KimuExtensionMeta } from "./kimu-types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decorator to define a Kimu component with static metadata.
|
|
5
|
+
*
|
|
6
|
+
* This script defines the `@KimuComponent` decorator, which is used to register
|
|
7
|
+
* custom components in the Kimu framework with static metadata.
|
|
8
|
+
*
|
|
9
|
+
* Key functionalities:
|
|
10
|
+
* - Allows defining metadata for components, such as tag name, template path, and style path.
|
|
11
|
+
* - Automatically sets default paths for the template and style if not provided.
|
|
12
|
+
* - Registers the custom element with the browser using `customElements.define`.
|
|
13
|
+
* - Attaches static metadata directly to the class for later use.
|
|
14
|
+
*
|
|
15
|
+
* @param meta - Metadata describing the component, including tag name, paths, and dependencies.
|
|
16
|
+
*/
|
|
17
|
+
export function KimuComponent(meta: KimuExtensionMeta) {
|
|
18
|
+
return function <T extends CustomElementConstructor>(target: T) {
|
|
19
|
+
// Sets the base path as a fallback (e.g., extensions/hello)
|
|
20
|
+
const basePath = meta.path ?? `extensions/${meta.tag}`;
|
|
21
|
+
|
|
22
|
+
// Sets default paths for the template and style if not specified
|
|
23
|
+
meta.basePath = basePath;
|
|
24
|
+
meta.template = meta.template ?? 'view.html'; //`${basePath}/view.html`;
|
|
25
|
+
meta.style = meta.style ?? 'style.css'; //`${basePath}/style.css`;
|
|
26
|
+
|
|
27
|
+
// Registers the custom element with the browser
|
|
28
|
+
customElements.define(meta.tag, target);
|
|
29
|
+
|
|
30
|
+
// Attaches static metadata directly to the class for later use
|
|
31
|
+
(target as any).__kimu_meta__ = meta;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { TemplateResult } from 'lit';
|
|
2
|
+
import { KimuRender } from './kimu-render';
|
|
3
|
+
import { KimuAssetManager } from './kimu-asset-manager';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `KimuEngine` class provides core functionalities for rendering, template management,
|
|
7
|
+
* and component loading in the Kimu framework. It acts as a bridge between the rendering engine
|
|
8
|
+
* (Lit) and the asset manager, enabling dynamic and reactive UI updates.
|
|
9
|
+
*
|
|
10
|
+
* Key functionalities:
|
|
11
|
+
* - Injects styles into the Shadow DOM of components.
|
|
12
|
+
* - Loads and compiles HTML templates into Lit rendering functions.
|
|
13
|
+
* - Provides reactive rendering capabilities using Lit.
|
|
14
|
+
* - Dynamically loads and registers custom components.
|
|
15
|
+
*/
|
|
16
|
+
export class KimuEngine {
|
|
17
|
+
|
|
18
|
+
/** Cache for compiled templates to avoid recompilation */
|
|
19
|
+
private static _templateCache = new Map<string, any>();
|
|
20
|
+
|
|
21
|
+
/** Cache access tracking for LRU eviction */
|
|
22
|
+
private static _cacheAccessTime = new Map<string, number>();
|
|
23
|
+
|
|
24
|
+
/** Maximum cache size to prevent memory issues */
|
|
25
|
+
private static _maxCacheSize = 50;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Safely evict oldest cache entries when limit is reached
|
|
29
|
+
*/
|
|
30
|
+
private static _evictOldestEntries(): void {
|
|
31
|
+
if (this._templateCache.size < this._maxCacheSize) return;
|
|
32
|
+
|
|
33
|
+
// Get entries sorted by access time (oldest first)
|
|
34
|
+
const entriesToEvict = Array.from(this._cacheAccessTime.entries())
|
|
35
|
+
.sort(([, a], [, b]) => a - b)
|
|
36
|
+
.slice(0, Math.floor(this._templateCache.size * 0.2)) // Evict 20%
|
|
37
|
+
.map(([path]) => path);
|
|
38
|
+
|
|
39
|
+
// Remove from both caches
|
|
40
|
+
entriesToEvict.forEach(path => {
|
|
41
|
+
this._templateCache.delete(path);
|
|
42
|
+
this._cacheAccessTime.delete(path);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (entriesToEvict.length > 0) {
|
|
46
|
+
console.log(`[KimuEngine] Evicted ${entriesToEvict.length} old template cache entries`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Configure cache settings
|
|
52
|
+
*/
|
|
53
|
+
static configureCaching(maxSize: number = 50): void {
|
|
54
|
+
this._maxCacheSize = maxSize;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Clear all caches (for debugging/testing)
|
|
59
|
+
*/
|
|
60
|
+
static clearCaches(): void {
|
|
61
|
+
this._templateCache.clear();
|
|
62
|
+
this._cacheAccessTime.clear();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Preload critical assets to improve performance
|
|
67
|
+
*/
|
|
68
|
+
static async preloadAssets(paths: string[]): Promise<void> {
|
|
69
|
+
if (paths.length === 0) return;
|
|
70
|
+
|
|
71
|
+
console.log(`[KimuEngine] Preloading ${paths.length} assets...`);
|
|
72
|
+
|
|
73
|
+
// Preload in parallel but with limited concurrency to avoid overwhelming the browser
|
|
74
|
+
const BATCH_SIZE = 5;
|
|
75
|
+
const batches = [];
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < paths.length; i += BATCH_SIZE) {
|
|
78
|
+
batches.push(paths.slice(i, i + BATCH_SIZE));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const batch of batches) {
|
|
82
|
+
const promises = batch.map(async (path) => {
|
|
83
|
+
try {
|
|
84
|
+
if (path.endsWith('.html')) {
|
|
85
|
+
// Preload template
|
|
86
|
+
await this.loadTemplate(path, true);
|
|
87
|
+
} else if (path.endsWith('.css')) {
|
|
88
|
+
// Preload style (just fetch to cache)
|
|
89
|
+
await KimuAssetManager.fetchFile(path);
|
|
90
|
+
} else {
|
|
91
|
+
// Generic asset preload
|
|
92
|
+
await KimuAssetManager.fetchFile(path);
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn(`[KimuEngine] Failed to preload asset: ${path}`, error);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await Promise.all(promises);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`[KimuEngine] Preloading completed`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Injects an inline style from a file into the Shadow DOM of a component.
|
|
107
|
+
*
|
|
108
|
+
* @param component - The target component.
|
|
109
|
+
* @param stylePath - Path to the style file.
|
|
110
|
+
* @param styleId - Optional ID for the style element.
|
|
111
|
+
*/
|
|
112
|
+
static async injectStyle(component: HTMLElement, stylePath: string, styleId: string | null = null): Promise<void> {
|
|
113
|
+
return KimuAssetManager.injectStyle(component, stylePath, styleId);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Loads and compiles an HTML template from a file into a Lit rendering function.
|
|
118
|
+
*
|
|
119
|
+
* @param path - Path to the template file.
|
|
120
|
+
* @param useCache - Whether to use template cache (default: true)
|
|
121
|
+
* @returns A compiled Lit rendering function.
|
|
122
|
+
* @throws Error if the template file is not found.
|
|
123
|
+
*/
|
|
124
|
+
static async loadTemplate(path: string, useCache: boolean = true) {
|
|
125
|
+
// Check cache first (if caching is enabled)
|
|
126
|
+
if (useCache && this._templateCache.has(path)) {
|
|
127
|
+
// Update access time for LRU
|
|
128
|
+
this._cacheAccessTime.set(path, Date.now());
|
|
129
|
+
return this._templateCache.get(path);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//console.log(`[KimuEngine] 🔄 Loading template: ${path}`);
|
|
133
|
+
const template: string | null = await KimuAssetManager.fetchFile(path);
|
|
134
|
+
if (!template) {
|
|
135
|
+
throw new Error(`[KimuEngine] ⚠️ Template not found: ${template}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const compiledTemplate = KimuRender.loadTemplate(template);
|
|
139
|
+
|
|
140
|
+
// Cache the compiled template (if caching is enabled)
|
|
141
|
+
if (useCache) {
|
|
142
|
+
// Check if we need to evict old entries first
|
|
143
|
+
this._evictOldestEntries();
|
|
144
|
+
|
|
145
|
+
this._templateCache.set(path, compiledTemplate);
|
|
146
|
+
this._cacheAccessTime.set(path, Date.now());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return compiledTemplate;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Compiles an HTML string into a dynamic Lit rendering function.
|
|
154
|
+
*
|
|
155
|
+
* @param template - The HTML string to compile.
|
|
156
|
+
* @returns A Lit rendering function.
|
|
157
|
+
*/
|
|
158
|
+
static compileTemplate(template: string): (html: typeof import('lit').html, data: Record<string, any>) => TemplateResult {
|
|
159
|
+
return KimuRender.compileTemplate(template);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Performs reactive rendering using Lit.
|
|
164
|
+
*
|
|
165
|
+
* @param component - The target component.
|
|
166
|
+
* @param data - Data to be passed to the rendering function.
|
|
167
|
+
* @param renderFn - The Lit rendering function.
|
|
168
|
+
*/
|
|
169
|
+
static render(component: HTMLElement, data: Record<string, any>, renderFn: (html: any, data: Record<string, any>) => TemplateResult): void {
|
|
170
|
+
KimuRender.render(component, data, renderFn);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Loads a component from a specified path and registers it if not already registered.
|
|
175
|
+
*
|
|
176
|
+
* @param tag - The tag name of the component.
|
|
177
|
+
* @param path - Path to the component module.
|
|
178
|
+
* @returns The loaded module.
|
|
179
|
+
*/
|
|
180
|
+
static async loadComponent(tag: string, path: string): Promise<any> {
|
|
181
|
+
if (customElements.get(tag)) {
|
|
182
|
+
return; // Already registered
|
|
183
|
+
}
|
|
184
|
+
const module = await import(/* @vite-ignore */ path);
|
|
185
|
+
return module;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
}
|