kimu-core 0.4.1 → 0.4.2

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 (67) 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/package.json +9 -2
  12. package/scripts/minify-css-assets.js +82 -82
  13. package/src/core/index.ts +47 -47
  14. package/src/core/kimu-global-styles.ts +136 -136
  15. package/src/core/kimu-reactive.ts +196 -196
  16. package/src/modules-repository/api-axios/CHANGELOG.md +48 -48
  17. package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -178
  18. package/src/modules-repository/api-axios/README.md +304 -304
  19. package/src/modules-repository/api-axios/api-axios-service.ts +355 -355
  20. package/src/modules-repository/api-axios/examples.ts +293 -293
  21. package/src/modules-repository/api-axios/index.ts +19 -19
  22. package/src/modules-repository/api-axios/interfaces.ts +71 -71
  23. package/src/modules-repository/api-axios/module.ts +41 -41
  24. package/src/modules-repository/api-core/CHANGELOG.md +42 -42
  25. package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -192
  26. package/src/modules-repository/api-core/README.md +435 -435
  27. package/src/modules-repository/api-core/api-core-service.ts +289 -289
  28. package/src/modules-repository/api-core/examples.ts +432 -432
  29. package/src/modules-repository/api-core/index.ts +8 -8
  30. package/src/modules-repository/api-core/interfaces.ts +83 -83
  31. package/src/modules-repository/api-core/module.ts +30 -30
  32. package/src/modules-repository/event-bus/README.md +273 -273
  33. package/src/modules-repository/event-bus/event-bus-service.ts +176 -176
  34. package/src/modules-repository/event-bus/module.ts +30 -30
  35. package/src/modules-repository/notification/README.md +423 -423
  36. package/src/modules-repository/notification/module.ts +30 -30
  37. package/src/modules-repository/notification/notification-service.ts +436 -436
  38. package/src/modules-repository/router/README.it.md +61 -10
  39. package/src/modules-repository/router/README.md +61 -10
  40. package/src/modules-repository/router/router-config.ts.example +61 -0
  41. package/src/modules-repository/router/router.ts +18 -0
  42. package/src/modules-repository/state/README.md +409 -409
  43. package/src/modules-repository/state/module.ts +30 -30
  44. package/src/modules-repository/state/state-service.ts +296 -296
  45. package/src/modules-repository/theme/README.md +311 -267
  46. package/src/modules-repository/theme/module.ts +30 -30
  47. package/src/modules-repository/theme/pre-build.js +40 -40
  48. package/src/modules-repository/theme/theme-service.ts +411 -389
  49. package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -78
  50. package/src/modules-repository/theme/themes/theme-cozy.css +111 -111
  51. package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -150
  52. package/src/modules-repository/theme/themes/theme-dark.css +79 -79
  53. package/src/modules-repository/theme/themes/theme-forest.css +171 -171
  54. package/src/modules-repository/theme/themes/theme-gold.css +100 -100
  55. package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -126
  56. package/src/modules-repository/theme/themes/theme-lava.css +101 -101
  57. package/src/modules-repository/theme/themes/theme-lavender.css +90 -90
  58. package/src/modules-repository/theme/themes/theme-light.css +79 -79
  59. package/src/modules-repository/theme/themes/theme-matrix.css +103 -103
  60. package/src/modules-repository/theme/themes/theme-midnight.css +81 -81
  61. package/src/modules-repository/theme/themes/theme-nord.css +94 -94
  62. package/src/modules-repository/theme/themes/theme-ocean.css +84 -84
  63. package/src/modules-repository/theme/themes/theme-retro80s.css +343 -343
  64. package/src/modules-repository/theme/themes/theme-sunset.css +62 -62
  65. package/src/modules-repository/theme/themes-config-default.json +19 -0
  66. package/src/modules-repository/theme/themes-config.d.ts +27 -27
  67. package/src/modules-repository/theme/{themes-config.json → themes-config.json.example} +223 -213
@@ -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();