canvasframework 0.3.23 → 0.3.24

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.
@@ -0,0 +1,611 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Lecteur audio avec gestion directe des événements
5
+ * @class
6
+ * @extends Component
7
+ */
8
+ class AudioPlayer extends Component {
9
+ constructor(framework, options = {}) {
10
+ super(framework, options);
11
+ this.src = options.src || '';
12
+ this.playing = false;
13
+ this.platform = framework.platform;
14
+ this.showControls = true;
15
+ this.controlsTimeout = null;
16
+ this.currentTime = 0;
17
+ this.duration = 0;
18
+ this.progress = 0;
19
+ this.volume = 1;
20
+ this.showVolume = false;
21
+ this.loaded = false;
22
+ this.isLoading = false;
23
+ this.userInteracted = false;
24
+
25
+ // Élément audio HTML caché
26
+ this.audioElement = null;
27
+
28
+ // Gestion directe des événements
29
+ this.canvas = framework.canvas;
30
+ this.isMouseDownOnPlayer = false;
31
+ this.clickStartTime = 0;
32
+ this.clickThreshold = 200; // ms
33
+
34
+ this.controlsHeight = 50;
35
+
36
+ // Callbacks
37
+ this.onPlay = options.onPlay;
38
+ this.onPause = options.onPause;
39
+ this.onEnded = options.onEnded;
40
+
41
+ // NE PAS utiliser les handlers du framework
42
+ this.onPress = null;
43
+ this.onMove = null;
44
+ this.onRelease = null;
45
+
46
+ // Attacher les événements directement au canvas
47
+ this.setupDirectEventListeners();
48
+
49
+ // Initialiser l'audio
50
+ this.initAudio();
51
+ }
52
+
53
+ setupDirectEventListeners() {
54
+ // Stocker les listeners originaux pour pouvoir les retirer plus tard
55
+ this.originalListeners = {
56
+ mousedown: this.canvas.onmousedown,
57
+ mouseup: this.canvas.onmouseup,
58
+ mousemove: this.canvas.onmousemove,
59
+ touchstart: this.canvas.ontouchstart,
60
+ touchend: this.canvas.ontouchend,
61
+ touchmove: this.canvas.ontouchmove
62
+ };
63
+
64
+ // Attacher nos propres handlers
65
+ this.canvas.addEventListener('mousedown', this.handleCanvasMouseDown.bind(this));
66
+ this.canvas.addEventListener('mouseup', this.handleCanvasMouseUp.bind(this));
67
+ this.canvas.addEventListener('mousemove', this.handleCanvasMouseMove.bind(this));
68
+
69
+ this.canvas.addEventListener('touchstart', this.handleCanvasTouchStart.bind(this));
70
+ this.canvas.addEventListener('touchend', this.handleCanvasTouchEnd.bind(this));
71
+ this.canvas.addEventListener('touchmove', this.handleCanvasTouchMove.bind(this));
72
+ }
73
+
74
+ removeDirectEventListeners() {
75
+ // Retirer nos listeners
76
+ this.canvas.removeEventListener('mousedown', this.handleCanvasMouseDown);
77
+ this.canvas.removeEventListener('mouseup', this.handleCanvasMouseUp);
78
+ this.canvas.removeEventListener('mousemove', this.handleCanvasMouseMove);
79
+
80
+ this.canvas.removeEventListener('touchstart', this.handleCanvasTouchStart);
81
+ this.canvas.removeEventListener('touchend', this.handleCanvasTouchEnd);
82
+ this.canvas.removeEventListener('touchmove', this.handleCanvasTouchMove);
83
+
84
+ // Restaurer les listeners originaux
85
+ Object.keys(this.originalListeners).forEach(event => {
86
+ if (this.originalListeners[event]) {
87
+ this.canvas[`on${event}`] = this.originalListeners[event];
88
+ }
89
+ });
90
+ }
91
+
92
+ handleCanvasMouseDown(e) {
93
+ const rect = this.canvas.getBoundingClientRect();
94
+ const x = e.clientX - rect.left;
95
+ const y = e.clientY - rect.top;
96
+
97
+ if (this.isPointInside(x, y)) {
98
+ e.stopPropagation();
99
+ this.isMouseDownOnPlayer = true;
100
+ this.clickStartTime = Date.now();
101
+ this.handleInteraction(x, y, 'start');
102
+ return true; // Empêche la propagation au framework
103
+ }
104
+ }
105
+
106
+ handleCanvasMouseUp(e) {
107
+ if (!this.isMouseDownOnPlayer) return;
108
+
109
+ const rect = this.canvas.getBoundingClientRect();
110
+ const x = e.clientX - rect.left;
111
+ const y = e.clientY - rect.top;
112
+
113
+ const clickDuration = Date.now() - this.clickStartTime;
114
+
115
+ if (clickDuration < this.clickThreshold && this.isPointInside(x, y)) {
116
+ e.stopPropagation();
117
+ this.handleInteraction(x, y, 'end');
118
+ }
119
+
120
+ this.isMouseDownOnPlayer = false;
121
+ return true; // Empêche la propagation au framework
122
+ }
123
+
124
+ handleCanvasMouseMove(e) {
125
+ if (!this.isMouseDownOnPlayer) return;
126
+
127
+ const rect = this.canvas.getBoundingClientRect();
128
+ const x = e.clientX - rect.left;
129
+ const y = e.clientY - rect.top;
130
+
131
+ if (this.isPointInside(x, y)) {
132
+ e.stopPropagation();
133
+ this.handleInteraction(x, y, 'move');
134
+ return true;
135
+ }
136
+ }
137
+
138
+ handleCanvasTouchStart(e) {
139
+ const touch = e.touches[0];
140
+ const rect = this.canvas.getBoundingClientRect();
141
+ const x = touch.clientX - rect.left;
142
+ const y = touch.clientY - rect.top;
143
+
144
+ if (this.isPointInside(x, y)) {
145
+ e.stopPropagation();
146
+ e.preventDefault();
147
+ this.isMouseDownOnPlayer = true;
148
+ this.clickStartTime = Date.now();
149
+ this.handleInteraction(x, y, 'start');
150
+ return true;
151
+ }
152
+ }
153
+
154
+ handleCanvasTouchEnd(e) {
155
+ if (!this.isMouseDownOnPlayer) return;
156
+
157
+ const touch = e.changedTouches[0];
158
+ const rect = this.canvas.getBoundingClientRect();
159
+ const x = touch.clientX - rect.left;
160
+ const y = touch.clientY - rect.top;
161
+
162
+ const clickDuration = Date.now() - this.clickStartTime;
163
+
164
+ if (clickDuration < this.clickThreshold && this.isPointInside(x, y)) {
165
+ e.stopPropagation();
166
+ e.preventDefault();
167
+ this.handleInteraction(x, y, 'end');
168
+ }
169
+
170
+ this.isMouseDownOnPlayer = false;
171
+ return true;
172
+ }
173
+
174
+ handleCanvasTouchMove(e) {
175
+ if (!this.isMouseDownOnPlayer) return;
176
+
177
+ const touch = e.touches[0];
178
+ const rect = this.canvas.getBoundingClientRect();
179
+ const x = touch.clientX - rect.left;
180
+ const y = touch.clientY - rect.top;
181
+
182
+ if (this.isPointInside(x, y)) {
183
+ e.stopPropagation();
184
+ e.preventDefault();
185
+ this.handleInteraction(x, y, 'move');
186
+ return true;
187
+ }
188
+ }
189
+
190
+ handleInteraction(x, y, type) {
191
+ // Ajuster les coordonnées pour le scrolling
192
+ const adjustedY = y - this.framework.scrollOffset;
193
+
194
+ // Afficher les contrôles
195
+ this.showControls = true;
196
+ this.showControlsTemporarily();
197
+
198
+ // Marquer l'interaction utilisateur
199
+ if (!this.userInteracted) {
200
+ this.userInteracted = true;
201
+ this.showInteractionMessage = false;
202
+ }
203
+
204
+ // Calculer la position relative
205
+ const localX = x - this.x;
206
+ const localY = adjustedY - this.y;
207
+
208
+ // Bouton play/pause central
209
+ const centerX = this.width / 2;
210
+ const centerY = this.height / 2;
211
+
212
+ const buttonSize = 40;
213
+ const isInButton = localX >= centerX - buttonSize && localX <= centerX + buttonSize &&
214
+ localY >= centerY - buttonSize && localY <= centerY + buttonSize;
215
+
216
+ // Barre de progression
217
+ const progressBarY = this.height - 30;
218
+ const isInProgressBar = this.showControls && this.loaded &&
219
+ localY >= progressBarY && localY <= progressBarY + 15;
220
+
221
+ // Traiter selon le type d'événement
222
+ switch (type) {
223
+ case 'start':
224
+ // Marquer le début du clic
225
+ break;
226
+
227
+ case 'move':
228
+ if (isInProgressBar) {
229
+ const newProgress = Math.max(0, Math.min(100, (localX / this.width) * 100));
230
+ this.seekTo((this.duration * newProgress) / 100);
231
+ }
232
+ break;
233
+
234
+ case 'end':
235
+ // Bouton play/pause
236
+ if (isInButton) {
237
+ console.log('AudioPlayer: Bouton cliqué (gestion directe)');
238
+ if (!this.loaded && !this.isLoading) {
239
+ this.initAudio();
240
+ } else if (this.loaded) {
241
+ this.togglePlay();
242
+ }
243
+ }
244
+ break;
245
+ }
246
+ }
247
+
248
+ initAudio() {
249
+ if (!this.src) {
250
+ console.warn('AudioPlayer: Pas de source audio fournie');
251
+ return;
252
+ }
253
+
254
+ console.log('AudioPlayer: Initialisation de l\'audio:', this.src);
255
+
256
+ this.isLoading = true;
257
+
258
+ try {
259
+ // Créer l'élément audio
260
+ this.audioElement = document.createElement('audio');
261
+ this.audioElement.src = this.src;
262
+ this.audioElement.preload = 'auto';
263
+ this.audioElement.crossOrigin = 'anonymous';
264
+ this.audioElement.style.display = 'none';
265
+ this.audioElement.style.position = 'absolute';
266
+ this.audioElement.style.left = '-9999px';
267
+
268
+ this.audioElement.autoplay = false;
269
+ this.audioElement.controls = false;
270
+
271
+ document.body.appendChild(this.audioElement);
272
+
273
+ // Événements de l'audio
274
+ this.audioElement.addEventListener('loadedmetadata', () => {
275
+ console.log('AudioPlayer: Métadonnées chargées');
276
+ this.duration = this.audioElement.duration;
277
+ this.loaded = true;
278
+ this.isLoading = false;
279
+ });
280
+
281
+ this.audioElement.addEventListener('timeupdate', () => {
282
+ this.currentTime = this.audioElement.currentTime;
283
+ this.progress = (this.currentTime / this.duration) * 100;
284
+ });
285
+
286
+ this.audioElement.addEventListener('ended', () => {
287
+ console.log('AudioPlayer: Audio terminé');
288
+ this.playing = false;
289
+ if (this.onEnded) this.onEnded();
290
+ });
291
+
292
+ this.audioElement.addEventListener('play', () => {
293
+ console.log('AudioPlayer: Lecture démarrée');
294
+ this.playing = true;
295
+ if (this.onPlay) this.onPlay();
296
+ });
297
+
298
+ this.audioElement.addEventListener('pause', () => {
299
+ console.log('AudioPlayer: Lecture en pause');
300
+ this.playing = false;
301
+ if (this.onPause) this.onPause();
302
+ });
303
+
304
+ this.audioElement.addEventListener('error', (e) => {
305
+ console.error('AudioPlayer: Erreur audio:', e);
306
+ this.isLoading = false;
307
+ this.loaded = false;
308
+ });
309
+
310
+ this.audioElement.addEventListener('canplaythrough', () => {
311
+ console.log('AudioPlayer: Prêt à jouer');
312
+ this.loaded = true;
313
+ this.isLoading = false;
314
+ });
315
+
316
+ // Démarrer le chargement
317
+ this.audioElement.load();
318
+
319
+ } catch (error) {
320
+ console.error('AudioPlayer: Erreur d\'initialisation:', error);
321
+ this.isLoading = false;
322
+ this.loaded = false;
323
+ }
324
+ }
325
+
326
+ play() {
327
+ if (!this.loaded || !this.audioElement) {
328
+ console.log('AudioPlayer: Impossible de jouer - pas chargé');
329
+ return;
330
+ }
331
+
332
+ // Vérifier l'interaction utilisateur
333
+ if (!this.userInteracted) {
334
+ console.log('AudioPlayer: Attente interaction utilisateur...');
335
+ this.showInteractionMessage = true;
336
+ setTimeout(() => {
337
+ this.showInteractionMessage = false;
338
+ }, 2000);
339
+ return;
340
+ }
341
+
342
+ console.log('AudioPlayer: Lancement de la lecture');
343
+
344
+ const playPromise = this.audioElement.play();
345
+
346
+ if (playPromise !== undefined) {
347
+ playPromise
348
+ .then(() => {
349
+ console.log('AudioPlayer: Lecture réussie');
350
+ this.playing = true;
351
+ this.showControlsTemporarily();
352
+ })
353
+ .catch(error => {
354
+ console.error('AudioPlayer: Erreur de lecture:', error);
355
+ this.playing = false;
356
+
357
+ if (error.name === 'NotAllowedError') {
358
+ console.log('AudioPlayer: Interaction nécessaire');
359
+ this.userInteracted = false;
360
+ }
361
+ });
362
+ }
363
+ }
364
+
365
+ pause() {
366
+ if (!this.playing || !this.audioElement) return;
367
+
368
+ console.log('AudioPlayer: Mise en pause');
369
+ this.audioElement.pause();
370
+ this.playing = false;
371
+ this.showControlsTemporarily();
372
+ }
373
+
374
+ stop() {
375
+ if (this.audioElement) {
376
+ this.audioElement.pause();
377
+ this.audioElement.currentTime = 0;
378
+ }
379
+ this.playing = false;
380
+ this.currentTime = 0;
381
+ this.progress = 0;
382
+ }
383
+
384
+ togglePlay() {
385
+ console.log('AudioPlayer: togglePlay - état actuel:', this.playing);
386
+
387
+ if (!this.loaded) {
388
+ console.log('AudioPlayer: Pas encore chargé');
389
+ if (!this.isLoading) {
390
+ this.initAudio();
391
+ }
392
+ return;
393
+ }
394
+
395
+ if (!this.userInteracted) {
396
+ console.log('AudioPlayer: Interaction utilisateur déclenchée');
397
+ this.userInteracted = true;
398
+ this.showInteractionMessage = false;
399
+ }
400
+
401
+ if (this.playing) {
402
+ this.pause();
403
+ } else {
404
+ this.play();
405
+ }
406
+ }
407
+
408
+ seekTo(time) {
409
+ if (!this.loaded || !this.audioElement) return;
410
+
411
+ const wasPlaying = this.playing;
412
+
413
+ if (wasPlaying) {
414
+ this.audioElement.pause();
415
+ }
416
+
417
+ const newTime = Math.max(0, Math.min(time, this.duration));
418
+ this.audioElement.currentTime = newTime;
419
+ this.currentTime = newTime;
420
+ this.progress = (this.currentTime / this.duration) * 100;
421
+
422
+ if (wasPlaying) {
423
+ this.audioElement.play().catch(e => {
424
+ console.error('AudioPlayer: Erreur reprise après seek:', e);
425
+ });
426
+ }
427
+ }
428
+
429
+ setVolume(value) {
430
+ this.volume = Math.max(0, Math.min(1, value));
431
+ if (this.audioElement) {
432
+ this.audioElement.volume = this.volume;
433
+ }
434
+ }
435
+
436
+ showControlsTemporarily() {
437
+ this.showControls = true;
438
+ clearTimeout(this.controlsTimeout);
439
+ this.controlsTimeout = setTimeout(() => {
440
+ if (this.playing) this.showControls = false;
441
+ }, 3000);
442
+ }
443
+
444
+ update(deltaTime) {
445
+ // Mise à jour automatique via timeupdate
446
+ }
447
+
448
+ draw(ctx) {
449
+ ctx.save();
450
+
451
+ // Fond du lecteur
452
+ ctx.fillStyle = '#111111';
453
+ ctx.fillRect(this.x, this.y, this.width, this.height);
454
+
455
+ // Message d'interaction
456
+ if (this.showInteractionMessage) {
457
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
458
+ ctx.font = 'bold 14px Arial';
459
+ ctx.textAlign = 'center';
460
+ ctx.textBaseline = 'middle';
461
+ ctx.fillText('Cliquez pour activer l\'audio', this.x + this.width / 2, this.y + this.height / 2);
462
+ ctx.restore();
463
+ return;
464
+ }
465
+
466
+ // Indicateur de chargement
467
+ if (this.isLoading) {
468
+ ctx.fillStyle = '#FFFFFF';
469
+ ctx.font = '14px Arial';
470
+ ctx.textAlign = 'center';
471
+ ctx.textBaseline = 'middle';
472
+ ctx.fillText('Chargement...', this.x + this.width / 2, this.y + this.height / 2);
473
+ ctx.restore();
474
+ return;
475
+ }
476
+
477
+ // Indicateur si non chargé
478
+ if (!this.loaded && !this.isLoading) {
479
+ ctx.fillStyle = '#FFFFFF';
480
+ ctx.font = '14px Arial';
481
+ ctx.textAlign = 'center';
482
+ ctx.textBaseline = 'middle';
483
+ ctx.fillText('Audio non disponible', this.x + this.width / 2, this.y + this.height / 2);
484
+ ctx.restore();
485
+ return;
486
+ }
487
+
488
+ // Bouton play/pause
489
+ const centerX = this.x + this.width / 2;
490
+ const centerY = this.y + this.height / 2;
491
+
492
+ if (!this.playing || this.showControls) {
493
+ // Cercle de fond
494
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
495
+ ctx.beginPath();
496
+ ctx.arc(centerX, centerY, 40, 0, Math.PI * 2);
497
+ ctx.fill();
498
+
499
+ // Icône
500
+ ctx.fillStyle = '#FFFFFF';
501
+ if (this.playing) {
502
+ // Icône pause
503
+ const barWidth = 8;
504
+ const barHeight = 30;
505
+ const gap = 5;
506
+
507
+ ctx.fillRect(centerX - barWidth - gap/2, centerY - barHeight/2, barWidth, barHeight);
508
+ ctx.fillRect(centerX + gap/2, centerY - barHeight/2, barWidth, barHeight);
509
+ } else {
510
+ // Icône play
511
+ const triangleSize = 25;
512
+ ctx.beginPath();
513
+ ctx.moveTo(centerX - triangleSize/2, centerY - triangleSize);
514
+ ctx.lineTo(centerX - triangleSize/2, centerY + triangleSize);
515
+ ctx.lineTo(centerX + triangleSize, centerY);
516
+ ctx.closePath();
517
+ ctx.fill();
518
+ }
519
+ }
520
+
521
+ // Contrôles en bas
522
+ if (this.showControls && this.loaded) {
523
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
524
+ ctx.fillRect(this.x, this.y + this.height - this.controlsHeight, this.width, this.controlsHeight);
525
+
526
+ const progressX = this.x + 15;
527
+ const progressY = this.y + this.height - 25;
528
+ const progressWidth = this.width - 30;
529
+
530
+ // Barre de progression
531
+ ctx.fillStyle = '#555555';
532
+ ctx.fillRect(progressX, progressY, progressWidth, 6);
533
+ ctx.fillStyle = '#333333';
534
+ ctx.fillRect(progressX, progressY, progressWidth, 2);
535
+
536
+ // Progression actuelle
537
+ ctx.fillStyle = '#FF4444';
538
+ const currentProgressWidth = (progressWidth * this.progress) / 100;
539
+ ctx.fillRect(progressX, progressY, currentProgressWidth, 6);
540
+ ctx.fillStyle = '#FF0000';
541
+ ctx.fillRect(progressX, progressY, currentProgressWidth, 2);
542
+
543
+ // Curseur
544
+ if (currentProgressWidth > 0) {
545
+ const thumbX = progressX + currentProgressWidth;
546
+ ctx.fillStyle = '#FFFFFF';
547
+ ctx.beginPath();
548
+ ctx.arc(thumbX, progressY + 3, 8, 0, Math.PI * 2);
549
+ ctx.fill();
550
+
551
+ ctx.strokeStyle = '#000000';
552
+ ctx.lineWidth = 2;
553
+ ctx.stroke();
554
+ }
555
+
556
+ // Temps
557
+ const currentTimeStr = this.formatTime(this.currentTime);
558
+ const totalTimeStr = this.formatTime(this.duration);
559
+
560
+ ctx.fillStyle = '#FFFFFF';
561
+ ctx.font = 'bold 12px Arial';
562
+ ctx.textAlign = 'left';
563
+ ctx.fillText(currentTimeStr, this.x + 15, this.y + this.height - 35);
564
+
565
+ ctx.textAlign = 'right';
566
+ ctx.fillText(totalTimeStr, this.x + this.width - 15, this.y + this.height - 35);
567
+ }
568
+
569
+ ctx.restore();
570
+ }
571
+
572
+ formatTime(seconds) {
573
+ if (isNaN(seconds) || seconds === Infinity) {
574
+ return "0:00";
575
+ }
576
+
577
+ const mins = Math.floor(seconds / 60);
578
+ const secs = Math.floor(seconds % 60);
579
+ return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
580
+ }
581
+
582
+ isPointInside(x, y) {
583
+ const adjustedY = y - this.framework.scrollOffset;
584
+ return x >= this.x && x <= this.x + this.width &&
585
+ adjustedY >= this.y && adjustedY <= this.y + this.height;
586
+ }
587
+
588
+ remove() {
589
+ // Arrêter la lecture
590
+ this.stop();
591
+
592
+ // Retirer les event listeners
593
+ this.removeDirectEventListeners();
594
+
595
+ // Supprimer l'élément audio
596
+ if (this.audioElement && this.audioElement.parentNode) {
597
+ this.audioElement.pause();
598
+ this.audioElement.src = '';
599
+ this.audioElement.load();
600
+ this.audioElement.parentNode.removeChild(this.audioElement);
601
+ this.audioElement = null;
602
+ }
603
+
604
+ // Nettoyer le timeout
605
+ clearTimeout(this.controlsTimeout);
606
+
607
+ console.log('AudioPlayer: Nettoyé et supprimé');
608
+ }
609
+ }
610
+
611
+ export default AudioPlayer;
@@ -53,6 +53,7 @@ import InputDatalist from '../components/InputDatalist.js';
53
53
  import Banner from '../components/Banner.js';
54
54
  import Chart from '../components/Chart.js';
55
55
  import SliverAppBar from '../components/SliverAppBar.js';
56
+ import AudioPlayer from '../components/AudioPlayer.js';
56
57
 
57
58
  // Utils
58
59
  import SafeArea from '../utils/SafeArea.js';
package/core/UIBuilder.js CHANGED
@@ -54,6 +54,7 @@ import InputDatalist from '../components/InputDatalist.js';
54
54
  import Banner from '../components/Banner.js';
55
55
  import Chart from '../components/Chart.js';
56
56
  import SliverAppBar from '../components/SliverAppBar.js';
57
+ import AudioPlayer from '../components/AudioPlayer.js';
57
58
 
58
59
  // Features
59
60
  import PullToRefresh from '../features/PullToRefresh.js';
@@ -118,6 +119,7 @@ const Components = {
118
119
  Divider,
119
120
  FileUpload,
120
121
  Table,
122
+ AudioPlayer,
121
123
  TreeView,
122
124
  SearchInput,
123
125
  ImageCarousel,
package/index.js CHANGED
@@ -61,6 +61,7 @@ export { default as InputDatalist } from './components/InputDatalist.js';
61
61
  export { default as Banner } from './components/Banner.js';
62
62
  export { default as Chart } from './components/Chart.js';
63
63
  export { default as SliverAppBar } from './components/SliverAppBar.js';
64
+ export { default as AudioPlayer } from './components/AudioPlayer.js';
64
65
 
65
66
  // Utils
66
67
  export { default as SafeArea } from './utils/SafeArea.js';
@@ -103,7 +104,7 @@ export { default as FeatureFlags } from './manager/FeatureFlags.js';
103
104
 
104
105
  // Version du framework
105
106
 
106
- export const VERSION = '0.3.23';
107
+ export const VERSION = '0.3.24';
107
108
 
108
109
 
109
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "description": "Canvas-based cross-platform UI framework (Material & Cupertino)",
5
5
  "type": "module",
6
6
  "main": "./index.js",