kimu-core 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.editorconfig +116 -30
  2. package/.gitattributes +81 -11
  3. package/.github/FUNDING.yml +8 -8
  4. package/.github/kimu-copilot-instructions.md +3779 -3779
  5. package/.github/workflows/deploy-demo.yml +39 -39
  6. package/.nvmrc +1 -0
  7. package/.prettierignore +44 -0
  8. package/.prettierrc +16 -0
  9. package/FUNDING.md +31 -31
  10. package/icon.svg +10 -10
  11. package/kimu-core-0.5.0.tgz +0 -0
  12. package/package.json +10 -3
  13. package/scripts/minify-css-assets.js +82 -82
  14. package/src/core/index.ts +47 -47
  15. package/src/core/kimu-global-styles.ts +136 -136
  16. package/src/core/kimu-reactive.ts +196 -196
  17. package/src/extensions/{kimu-home → app-root}/component.ts +5 -5
  18. package/src/extensions/extensions-manifest.json +4 -4
  19. package/src/main.ts +3 -3
  20. package/src/modules-repository/api-axios/CHANGELOG.md +48 -48
  21. package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -178
  22. package/src/modules-repository/api-axios/README.md +304 -304
  23. package/src/modules-repository/api-axios/api-axios-service.ts +355 -355
  24. package/src/modules-repository/api-axios/examples.ts +293 -293
  25. package/src/modules-repository/api-axios/index.ts +19 -19
  26. package/src/modules-repository/api-axios/interfaces.ts +71 -71
  27. package/src/modules-repository/api-axios/module.ts +41 -41
  28. package/src/modules-repository/api-core/CHANGELOG.md +42 -42
  29. package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -192
  30. package/src/modules-repository/api-core/README.md +435 -435
  31. package/src/modules-repository/api-core/api-core-service.ts +289 -289
  32. package/src/modules-repository/api-core/examples.ts +432 -432
  33. package/src/modules-repository/api-core/index.ts +8 -8
  34. package/src/modules-repository/api-core/interfaces.ts +83 -83
  35. package/src/modules-repository/api-core/module.ts +30 -30
  36. package/src/modules-repository/event-bus/README.md +273 -273
  37. package/src/modules-repository/event-bus/event-bus-service.ts +176 -176
  38. package/src/modules-repository/event-bus/module.ts +30 -30
  39. package/src/modules-repository/notification/README.md +423 -423
  40. package/src/modules-repository/notification/module.ts +30 -30
  41. package/src/modules-repository/notification/notification-service.ts +436 -436
  42. package/src/modules-repository/router/README.it.md +61 -10
  43. package/src/modules-repository/router/README.md +61 -10
  44. package/src/modules-repository/router/router-config.ts.example +61 -0
  45. package/src/modules-repository/router/router.ts +18 -0
  46. package/src/modules-repository/state/README.md +409 -409
  47. package/src/modules-repository/state/module.ts +30 -30
  48. package/src/modules-repository/state/state-service.ts +296 -296
  49. package/src/modules-repository/theme/README.md +311 -267
  50. package/src/modules-repository/theme/module.ts +30 -30
  51. package/src/modules-repository/theme/pre-build.js +40 -40
  52. package/src/modules-repository/theme/theme-service.ts +411 -389
  53. package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -78
  54. package/src/modules-repository/theme/themes/theme-cozy.css +111 -111
  55. package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -150
  56. package/src/modules-repository/theme/themes/theme-dark.css +79 -79
  57. package/src/modules-repository/theme/themes/theme-forest.css +171 -171
  58. package/src/modules-repository/theme/themes/theme-gold.css +100 -100
  59. package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -126
  60. package/src/modules-repository/theme/themes/theme-lava.css +101 -101
  61. package/src/modules-repository/theme/themes/theme-lavender.css +90 -90
  62. package/src/modules-repository/theme/themes/theme-light.css +79 -79
  63. package/src/modules-repository/theme/themes/theme-matrix.css +103 -103
  64. package/src/modules-repository/theme/themes/theme-midnight.css +81 -81
  65. package/src/modules-repository/theme/themes/theme-nord.css +94 -94
  66. package/src/modules-repository/theme/themes/theme-ocean.css +84 -84
  67. package/src/modules-repository/theme/themes/theme-retro80s.css +343 -343
  68. package/src/modules-repository/theme/themes/theme-sunset.css +62 -62
  69. package/src/modules-repository/theme/themes-config-default.json +19 -0
  70. package/src/modules-repository/theme/themes-config.d.ts +27 -27
  71. package/src/modules-repository/theme/{themes-config.json → themes-config.json.example} +223 -213
  72. /package/src/extensions/{kimu-home → app-root}/lang/en.json +0 -0
  73. /package/src/extensions/{kimu-home → app-root}/lang/it.json +0 -0
  74. /package/src/extensions/{kimu-home → app-root}/style.css +0 -0
  75. /package/src/extensions/{kimu-home → app-root}/view.html +0 -0
@@ -1,436 +1,436 @@
1
- /**
2
- * Notification Service for KIMU-Core
3
- *
4
- * Provides toast notifications, alerts, and user feedback system.
5
- * Supports different notification types, durations, and positions.
6
- *
7
- * @module NotificationService
8
- * @version 1.0.0
9
- */
10
-
11
- export type NotificationType = 'success' | 'error' | 'warning' | 'info';
12
- export type NotificationPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';
13
-
14
- export interface NotificationOptions {
15
- type?: NotificationType;
16
- duration?: number; // in milliseconds, 0 = no auto-dismiss
17
- position?: NotificationPosition;
18
- dismissible?: boolean;
19
- icon?: string;
20
- onClick?: () => void;
21
- onClose?: () => void;
22
- }
23
-
24
- export interface Notification {
25
- id: string;
26
- message: string;
27
- type: NotificationType;
28
- duration: number;
29
- position: NotificationPosition;
30
- dismissible: boolean;
31
- icon?: string;
32
- timestamp: number;
33
- element?: HTMLElement;
34
- timeoutId?: number;
35
- onClick?: () => void;
36
- onClose?: () => void;
37
- }
38
-
39
- /**
40
- * NotificationService - Toast notifications and alerts
41
- *
42
- * @example
43
- * ```typescript
44
- * import { notificationService } from './modules/notification/notification-service';
45
- *
46
- * // Show success notification
47
- * notificationService.success('Operation completed!');
48
- *
49
- * // Show error with custom duration
50
- * notificationService.error('Something went wrong!', { duration: 5000 });
51
- *
52
- * // Show info with custom position
53
- * notificationService.info('New message received', { position: 'bottom-right' });
54
- * ```
55
- */
56
- export class NotificationService {
57
- private notifications: Map<string, Notification> = new Map();
58
- private container?: HTMLElement;
59
- private defaultDuration: number = 3000;
60
- private defaultPosition: NotificationPosition = 'top-right';
61
- private maxNotifications: number = 5;
62
- private debugMode: boolean = false;
63
-
64
- constructor() {
65
- this.initContainer();
66
- }
67
-
68
- /**
69
- * Enable or disable debug mode
70
- */
71
- setDebugMode(enabled: boolean): void {
72
- this.debugMode = enabled;
73
- }
74
-
75
- /**
76
- * Set default notification duration
77
- */
78
- setDefaultDuration(duration: number): void {
79
- this.defaultDuration = duration;
80
- }
81
-
82
- /**
83
- * Set default notification position
84
- */
85
- setDefaultPosition(position: NotificationPosition): void {
86
- this.defaultPosition = position;
87
- }
88
-
89
- /**
90
- * Set maximum number of visible notifications
91
- */
92
- setMaxNotifications(max: number): void {
93
- this.maxNotifications = max;
94
- }
95
-
96
- /**
97
- * Show a success notification
98
- */
99
- success(message: string, options?: NotificationOptions): string {
100
- return this.show(message, { ...options, type: 'success' });
101
- }
102
-
103
- /**
104
- * Show an error notification
105
- */
106
- error(message: string, options?: NotificationOptions): string {
107
- return this.show(message, { ...options, type: 'error' });
108
- }
109
-
110
- /**
111
- * Show a warning notification
112
- */
113
- warning(message: string, options?: NotificationOptions): string {
114
- return this.show(message, { ...options, type: 'warning' });
115
- }
116
-
117
- /**
118
- * Show an info notification
119
- */
120
- info(message: string, options?: NotificationOptions): string {
121
- return this.show(message, { ...options, type: 'info' });
122
- }
123
-
124
- /**
125
- * Show a notification with custom options
126
- */
127
- show(message: string, options: NotificationOptions = {}): string {
128
- const id = this.generateId();
129
-
130
- const notification: Notification = {
131
- id,
132
- message,
133
- type: options.type || 'info',
134
- duration: options.duration !== undefined ? options.duration : this.defaultDuration,
135
- position: options.position || this.defaultPosition,
136
- dismissible: options.dismissible !== undefined ? options.dismissible : true,
137
- icon: options.icon || this.getDefaultIcon(options.type || 'info'),
138
- timestamp: Date.now(),
139
- onClick: options.onClick,
140
- onClose: options.onClose
141
- };
142
-
143
- if (this.debugMode) {
144
- console.log('[Notification] Show:', notification);
145
- }
146
-
147
- // Remove oldest notification if max limit reached
148
- if (this.notifications.size >= this.maxNotifications) {
149
- const oldestId = Array.from(this.notifications.keys())[0];
150
- this.dismiss(oldestId);
151
- }
152
-
153
- this.notifications.set(id, notification);
154
- this.renderNotification(notification);
155
-
156
- // Auto-dismiss if duration > 0
157
- if (notification.duration > 0) {
158
- notification.timeoutId = window.setTimeout(() => {
159
- this.dismiss(id);
160
- }, notification.duration);
161
- }
162
-
163
- return id;
164
- }
165
-
166
- /**
167
- * Dismiss a specific notification
168
- */
169
- dismiss(id: string): void {
170
- const notification = this.notifications.get(id);
171
- if (!notification) return;
172
-
173
- if (this.debugMode) {
174
- console.log('[Notification] Dismiss:', id);
175
- }
176
-
177
- // Clear timeout
178
- if (notification.timeoutId) {
179
- clearTimeout(notification.timeoutId);
180
- }
181
-
182
- // Remove element with animation
183
- if (notification.element) {
184
- notification.element.classList.add('kimu-notification-exit');
185
- setTimeout(() => {
186
- notification.element?.remove();
187
- }, 300);
188
- }
189
-
190
- // Call onClose callback
191
- if (notification.onClose) {
192
- notification.onClose();
193
- }
194
-
195
- this.notifications.delete(id);
196
- }
197
-
198
- /**
199
- * Dismiss all notifications
200
- */
201
- dismissAll(): void {
202
- const ids = Array.from(this.notifications.keys());
203
- ids.forEach(id => this.dismiss(id));
204
- }
205
-
206
- /**
207
- * Get all active notifications
208
- */
209
- getAll(): Notification[] {
210
- return Array.from(this.notifications.values());
211
- }
212
-
213
- /**
214
- * Get notification count
215
- */
216
- getCount(): number {
217
- return this.notifications.size;
218
- }
219
-
220
- // Private methods
221
-
222
- private initContainer(): void {
223
- if (typeof document === 'undefined') return;
224
-
225
- this.container = document.createElement('div');
226
- this.container.id = 'kimu-notification-container';
227
- this.injectStyles();
228
- document.body.appendChild(this.container);
229
- }
230
-
231
- private renderNotification(notification: Notification): void {
232
- if (!this.container) return;
233
-
234
- const element = document.createElement('div');
235
- element.className = `kimu-notification kimu-notification-${notification.type} kimu-notification-${notification.position}`;
236
- element.setAttribute('data-id', notification.id);
237
-
238
- const content = `
239
- <div class="kimu-notification-content">
240
- ${notification.icon ? `<span class="kimu-notification-icon">${notification.icon}</span>` : ''}
241
- <span class="kimu-notification-message">${this.escapeHtml(notification.message)}</span>
242
- </div>
243
- ${notification.dismissible ? '<button class="kimu-notification-close" aria-label="Close">×</button>' : ''}
244
- `;
245
-
246
- element.innerHTML = content;
247
-
248
- // Add click handlers
249
- if (notification.onClick) {
250
- element.addEventListener('click', (e) => {
251
- if (!(e.target as HTMLElement).classList.contains('kimu-notification-close')) {
252
- notification.onClick!();
253
- }
254
- });
255
- }
256
-
257
- if (notification.dismissible) {
258
- const closeBtn = element.querySelector('.kimu-notification-close');
259
- closeBtn?.addEventListener('click', (e) => {
260
- e.stopPropagation();
261
- this.dismiss(notification.id);
262
- });
263
- }
264
-
265
- notification.element = element;
266
- this.container.appendChild(element);
267
-
268
- // Trigger animation
269
- setTimeout(() => element.classList.add('kimu-notification-enter'), 10);
270
- }
271
-
272
- private generateId(): string {
273
- return `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
274
- }
275
-
276
- private getDefaultIcon(type: NotificationType): string {
277
- const icons = {
278
- success: '✅',
279
- error: '❌',
280
- warning: '⚠️',
281
- info: 'ℹ️'
282
- };
283
- return icons[type];
284
- }
285
-
286
- private escapeHtml(text: string): string {
287
- const div = document.createElement('div');
288
- div.textContent = text;
289
- return div.innerHTML;
290
- }
291
-
292
- private injectStyles(): void {
293
- if (typeof document === 'undefined') return;
294
- if (document.getElementById('kimu-notification-styles')) return;
295
-
296
- const style = document.createElement('style');
297
- style.id = 'kimu-notification-styles';
298
- style.textContent = `
299
- #kimu-notification-container {
300
- position: fixed;
301
- z-index: 9999;
302
- pointer-events: none;
303
- }
304
-
305
- .kimu-notification {
306
- position: fixed;
307
- min-width: 280px;
308
- max-width: 400px;
309
- padding: 16px 20px;
310
- margin: 12px;
311
- background: white;
312
- border-radius: 8px;
313
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
314
- display: flex;
315
- align-items: center;
316
- justify-content: space-between;
317
- opacity: 0;
318
- transform: translateY(-20px);
319
- transition: all 0.3s ease;
320
- pointer-events: auto;
321
- cursor: default;
322
- }
323
-
324
- .kimu-notification-enter {
325
- opacity: 1;
326
- transform: translateY(0);
327
- }
328
-
329
- .kimu-notification-exit {
330
- opacity: 0;
331
- transform: translateY(-20px);
332
- }
333
-
334
- .kimu-notification-content {
335
- display: flex;
336
- align-items: center;
337
- gap: 12px;
338
- flex: 1;
339
- }
340
-
341
- .kimu-notification-icon {
342
- font-size: 20px;
343
- line-height: 1;
344
- }
345
-
346
- .kimu-notification-message {
347
- color: #333;
348
- font-size: 14px;
349
- line-height: 1.5;
350
- }
351
-
352
- .kimu-notification-close {
353
- background: none;
354
- border: none;
355
- font-size: 24px;
356
- line-height: 1;
357
- color: #999;
358
- cursor: pointer;
359
- padding: 0;
360
- margin-left: 12px;
361
- transition: color 0.2s;
362
- }
363
-
364
- .kimu-notification-close:hover {
365
- color: #333;
366
- }
367
-
368
- /* Types */
369
- .kimu-notification-success {
370
- background: #f0fdf4;
371
- border-left: 4px solid #22c55e;
372
- }
373
-
374
- .kimu-notification-error {
375
- background: #fef2f2;
376
- border-left: 4px solid #ef4444;
377
- }
378
-
379
- .kimu-notification-warning {
380
- background: #fffbeb;
381
- border-left: 4px solid #f59e0b;
382
- }
383
-
384
- .kimu-notification-info {
385
- background: #eff6ff;
386
- border-left: 4px solid #3b82f6;
387
- }
388
-
389
- /* Positions */
390
- .kimu-notification-top-right {
391
- top: 0;
392
- right: 0;
393
- }
394
-
395
- .kimu-notification-top-left {
396
- top: 0;
397
- left: 0;
398
- }
399
-
400
- .kimu-notification-bottom-right {
401
- bottom: 0;
402
- right: 0;
403
- }
404
-
405
- .kimu-notification-bottom-left {
406
- bottom: 0;
407
- left: 0;
408
- }
409
-
410
- .kimu-notification-top-center {
411
- top: 0;
412
- left: 50%;
413
- transform: translateX(-50%) translateY(-20px);
414
- }
415
-
416
- .kimu-notification-top-center.kimu-notification-enter {
417
- transform: translateX(-50%) translateY(0);
418
- }
419
-
420
- .kimu-notification-bottom-center {
421
- bottom: 0;
422
- left: 50%;
423
- transform: translateX(-50%) translateY(20px);
424
- }
425
-
426
- .kimu-notification-bottom-center.kimu-notification-enter {
427
- transform: translateX(-50%) translateY(0);
428
- }
429
- `;
430
-
431
- document.head.appendChild(style);
432
- }
433
- }
434
-
435
- // Export singleton instance
436
- export const notificationService = new NotificationService();
1
+ /**
2
+ * Notification Service for KIMU-Core
3
+ *
4
+ * Provides toast notifications, alerts, and user feedback system.
5
+ * Supports different notification types, durations, and positions.
6
+ *
7
+ * @module NotificationService
8
+ * @version 1.0.0
9
+ */
10
+
11
+ export type NotificationType = 'success' | 'error' | 'warning' | 'info';
12
+ export type NotificationPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';
13
+
14
+ export interface NotificationOptions {
15
+ type?: NotificationType;
16
+ duration?: number; // in milliseconds, 0 = no auto-dismiss
17
+ position?: NotificationPosition;
18
+ dismissible?: boolean;
19
+ icon?: string;
20
+ onClick?: () => void;
21
+ onClose?: () => void;
22
+ }
23
+
24
+ export interface Notification {
25
+ id: string;
26
+ message: string;
27
+ type: NotificationType;
28
+ duration: number;
29
+ position: NotificationPosition;
30
+ dismissible: boolean;
31
+ icon?: string;
32
+ timestamp: number;
33
+ element?: HTMLElement;
34
+ timeoutId?: number;
35
+ onClick?: () => void;
36
+ onClose?: () => void;
37
+ }
38
+
39
+ /**
40
+ * NotificationService - Toast notifications and alerts
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * import { notificationService } from './modules/notification/notification-service';
45
+ *
46
+ * // Show success notification
47
+ * notificationService.success('Operation completed!');
48
+ *
49
+ * // Show error with custom duration
50
+ * notificationService.error('Something went wrong!', { duration: 5000 });
51
+ *
52
+ * // Show info with custom position
53
+ * notificationService.info('New message received', { position: 'bottom-right' });
54
+ * ```
55
+ */
56
+ export class NotificationService {
57
+ private notifications: Map<string, Notification> = new Map();
58
+ private container?: HTMLElement;
59
+ private defaultDuration: number = 3000;
60
+ private defaultPosition: NotificationPosition = 'top-right';
61
+ private maxNotifications: number = 5;
62
+ private debugMode: boolean = false;
63
+
64
+ constructor() {
65
+ this.initContainer();
66
+ }
67
+
68
+ /**
69
+ * Enable or disable debug mode
70
+ */
71
+ setDebugMode(enabled: boolean): void {
72
+ this.debugMode = enabled;
73
+ }
74
+
75
+ /**
76
+ * Set default notification duration
77
+ */
78
+ setDefaultDuration(duration: number): void {
79
+ this.defaultDuration = duration;
80
+ }
81
+
82
+ /**
83
+ * Set default notification position
84
+ */
85
+ setDefaultPosition(position: NotificationPosition): void {
86
+ this.defaultPosition = position;
87
+ }
88
+
89
+ /**
90
+ * Set maximum number of visible notifications
91
+ */
92
+ setMaxNotifications(max: number): void {
93
+ this.maxNotifications = max;
94
+ }
95
+
96
+ /**
97
+ * Show a success notification
98
+ */
99
+ success(message: string, options?: NotificationOptions): string {
100
+ return this.show(message, { ...options, type: 'success' });
101
+ }
102
+
103
+ /**
104
+ * Show an error notification
105
+ */
106
+ error(message: string, options?: NotificationOptions): string {
107
+ return this.show(message, { ...options, type: 'error' });
108
+ }
109
+
110
+ /**
111
+ * Show a warning notification
112
+ */
113
+ warning(message: string, options?: NotificationOptions): string {
114
+ return this.show(message, { ...options, type: 'warning' });
115
+ }
116
+
117
+ /**
118
+ * Show an info notification
119
+ */
120
+ info(message: string, options?: NotificationOptions): string {
121
+ return this.show(message, { ...options, type: 'info' });
122
+ }
123
+
124
+ /**
125
+ * Show a notification with custom options
126
+ */
127
+ show(message: string, options: NotificationOptions = {}): string {
128
+ const id = this.generateId();
129
+
130
+ const notification: Notification = {
131
+ id,
132
+ message,
133
+ type: options.type || 'info',
134
+ duration: options.duration !== undefined ? options.duration : this.defaultDuration,
135
+ position: options.position || this.defaultPosition,
136
+ dismissible: options.dismissible !== undefined ? options.dismissible : true,
137
+ icon: options.icon || this.getDefaultIcon(options.type || 'info'),
138
+ timestamp: Date.now(),
139
+ onClick: options.onClick,
140
+ onClose: options.onClose
141
+ };
142
+
143
+ if (this.debugMode) {
144
+ console.log('[Notification] Show:', notification);
145
+ }
146
+
147
+ // Remove oldest notification if max limit reached
148
+ if (this.notifications.size >= this.maxNotifications) {
149
+ const oldestId = Array.from(this.notifications.keys())[0];
150
+ this.dismiss(oldestId);
151
+ }
152
+
153
+ this.notifications.set(id, notification);
154
+ this.renderNotification(notification);
155
+
156
+ // Auto-dismiss if duration > 0
157
+ if (notification.duration > 0) {
158
+ notification.timeoutId = window.setTimeout(() => {
159
+ this.dismiss(id);
160
+ }, notification.duration);
161
+ }
162
+
163
+ return id;
164
+ }
165
+
166
+ /**
167
+ * Dismiss a specific notification
168
+ */
169
+ dismiss(id: string): void {
170
+ const notification = this.notifications.get(id);
171
+ if (!notification) return;
172
+
173
+ if (this.debugMode) {
174
+ console.log('[Notification] Dismiss:', id);
175
+ }
176
+
177
+ // Clear timeout
178
+ if (notification.timeoutId) {
179
+ clearTimeout(notification.timeoutId);
180
+ }
181
+
182
+ // Remove element with animation
183
+ if (notification.element) {
184
+ notification.element.classList.add('kimu-notification-exit');
185
+ setTimeout(() => {
186
+ notification.element?.remove();
187
+ }, 300);
188
+ }
189
+
190
+ // Call onClose callback
191
+ if (notification.onClose) {
192
+ notification.onClose();
193
+ }
194
+
195
+ this.notifications.delete(id);
196
+ }
197
+
198
+ /**
199
+ * Dismiss all notifications
200
+ */
201
+ dismissAll(): void {
202
+ const ids = Array.from(this.notifications.keys());
203
+ ids.forEach(id => this.dismiss(id));
204
+ }
205
+
206
+ /**
207
+ * Get all active notifications
208
+ */
209
+ getAll(): Notification[] {
210
+ return Array.from(this.notifications.values());
211
+ }
212
+
213
+ /**
214
+ * Get notification count
215
+ */
216
+ getCount(): number {
217
+ return this.notifications.size;
218
+ }
219
+
220
+ // Private methods
221
+
222
+ private initContainer(): void {
223
+ if (typeof document === 'undefined') return;
224
+
225
+ this.container = document.createElement('div');
226
+ this.container.id = 'kimu-notification-container';
227
+ this.injectStyles();
228
+ document.body.appendChild(this.container);
229
+ }
230
+
231
+ private renderNotification(notification: Notification): void {
232
+ if (!this.container) return;
233
+
234
+ const element = document.createElement('div');
235
+ element.className = `kimu-notification kimu-notification-${notification.type} kimu-notification-${notification.position}`;
236
+ element.setAttribute('data-id', notification.id);
237
+
238
+ const content = `
239
+ <div class="kimu-notification-content">
240
+ ${notification.icon ? `<span class="kimu-notification-icon">${notification.icon}</span>` : ''}
241
+ <span class="kimu-notification-message">${this.escapeHtml(notification.message)}</span>
242
+ </div>
243
+ ${notification.dismissible ? '<button class="kimu-notification-close" aria-label="Close">×</button>' : ''}
244
+ `;
245
+
246
+ element.innerHTML = content;
247
+
248
+ // Add click handlers
249
+ if (notification.onClick) {
250
+ element.addEventListener('click', (e) => {
251
+ if (!(e.target as HTMLElement).classList.contains('kimu-notification-close')) {
252
+ notification.onClick!();
253
+ }
254
+ });
255
+ }
256
+
257
+ if (notification.dismissible) {
258
+ const closeBtn = element.querySelector('.kimu-notification-close');
259
+ closeBtn?.addEventListener('click', (e) => {
260
+ e.stopPropagation();
261
+ this.dismiss(notification.id);
262
+ });
263
+ }
264
+
265
+ notification.element = element;
266
+ this.container.appendChild(element);
267
+
268
+ // Trigger animation
269
+ setTimeout(() => element.classList.add('kimu-notification-enter'), 10);
270
+ }
271
+
272
+ private generateId(): string {
273
+ return `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
274
+ }
275
+
276
+ private getDefaultIcon(type: NotificationType): string {
277
+ const icons = {
278
+ success: '✅',
279
+ error: '❌',
280
+ warning: '⚠️',
281
+ info: 'ℹ️'
282
+ };
283
+ return icons[type];
284
+ }
285
+
286
+ private escapeHtml(text: string): string {
287
+ const div = document.createElement('div');
288
+ div.textContent = text;
289
+ return div.innerHTML;
290
+ }
291
+
292
+ private injectStyles(): void {
293
+ if (typeof document === 'undefined') return;
294
+ if (document.getElementById('kimu-notification-styles')) return;
295
+
296
+ const style = document.createElement('style');
297
+ style.id = 'kimu-notification-styles';
298
+ style.textContent = `
299
+ #kimu-notification-container {
300
+ position: fixed;
301
+ z-index: 9999;
302
+ pointer-events: none;
303
+ }
304
+
305
+ .kimu-notification {
306
+ position: fixed;
307
+ min-width: 280px;
308
+ max-width: 400px;
309
+ padding: 16px 20px;
310
+ margin: 12px;
311
+ background: white;
312
+ border-radius: 8px;
313
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: space-between;
317
+ opacity: 0;
318
+ transform: translateY(-20px);
319
+ transition: all 0.3s ease;
320
+ pointer-events: auto;
321
+ cursor: default;
322
+ }
323
+
324
+ .kimu-notification-enter {
325
+ opacity: 1;
326
+ transform: translateY(0);
327
+ }
328
+
329
+ .kimu-notification-exit {
330
+ opacity: 0;
331
+ transform: translateY(-20px);
332
+ }
333
+
334
+ .kimu-notification-content {
335
+ display: flex;
336
+ align-items: center;
337
+ gap: 12px;
338
+ flex: 1;
339
+ }
340
+
341
+ .kimu-notification-icon {
342
+ font-size: 20px;
343
+ line-height: 1;
344
+ }
345
+
346
+ .kimu-notification-message {
347
+ color: #333;
348
+ font-size: 14px;
349
+ line-height: 1.5;
350
+ }
351
+
352
+ .kimu-notification-close {
353
+ background: none;
354
+ border: none;
355
+ font-size: 24px;
356
+ line-height: 1;
357
+ color: #999;
358
+ cursor: pointer;
359
+ padding: 0;
360
+ margin-left: 12px;
361
+ transition: color 0.2s;
362
+ }
363
+
364
+ .kimu-notification-close:hover {
365
+ color: #333;
366
+ }
367
+
368
+ /* Types */
369
+ .kimu-notification-success {
370
+ background: #f0fdf4;
371
+ border-left: 4px solid #22c55e;
372
+ }
373
+
374
+ .kimu-notification-error {
375
+ background: #fef2f2;
376
+ border-left: 4px solid #ef4444;
377
+ }
378
+
379
+ .kimu-notification-warning {
380
+ background: #fffbeb;
381
+ border-left: 4px solid #f59e0b;
382
+ }
383
+
384
+ .kimu-notification-info {
385
+ background: #eff6ff;
386
+ border-left: 4px solid #3b82f6;
387
+ }
388
+
389
+ /* Positions */
390
+ .kimu-notification-top-right {
391
+ top: 0;
392
+ right: 0;
393
+ }
394
+
395
+ .kimu-notification-top-left {
396
+ top: 0;
397
+ left: 0;
398
+ }
399
+
400
+ .kimu-notification-bottom-right {
401
+ bottom: 0;
402
+ right: 0;
403
+ }
404
+
405
+ .kimu-notification-bottom-left {
406
+ bottom: 0;
407
+ left: 0;
408
+ }
409
+
410
+ .kimu-notification-top-center {
411
+ top: 0;
412
+ left: 50%;
413
+ transform: translateX(-50%) translateY(-20px);
414
+ }
415
+
416
+ .kimu-notification-top-center.kimu-notification-enter {
417
+ transform: translateX(-50%) translateY(0);
418
+ }
419
+
420
+ .kimu-notification-bottom-center {
421
+ bottom: 0;
422
+ left: 50%;
423
+ transform: translateX(-50%) translateY(20px);
424
+ }
425
+
426
+ .kimu-notification-bottom-center.kimu-notification-enter {
427
+ transform: translateX(-50%) translateY(0);
428
+ }
429
+ `;
430
+
431
+ document.head.appendChild(style);
432
+ }
433
+ }
434
+
435
+ // Export singleton instance
436
+ export const notificationService = new NotificationService();