canvasframework 0.3.28 → 0.3.29

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.
@@ -27,6 +27,7 @@ class RadioButton extends Component {
27
27
  this.group = options.group || 'default';
28
28
  this.checked = options.checked || false;
29
29
  this.label = options.label || '';
30
+ this.labelColor = options.labelColor || '#000000'; // Nouvelle propriété
30
31
  this.platform = framework.platform;
31
32
  this.circleSize = 24; // Taille du cercle
32
33
  this.circleRadius = 10; // Rayon du cercle
@@ -123,7 +124,7 @@ class RadioButton extends Component {
123
124
 
124
125
  // Label
125
126
  if (this.label) {
126
- ctx.fillStyle = '#000000';
127
+ ctx.fillStyle = this.labelColor; // Au lieu de '#000000'
127
128
  ctx.font = '16px -apple-system, sans-serif';
128
129
  ctx.textAlign = 'left';
129
130
  ctx.textBaseline = 'middle';
@@ -180,7 +180,6 @@ class CanvasFramework {
180
180
  // Thèmes
181
181
  this.lightTheme = lightTheme;
182
182
  this.darkTheme = darkTheme;
183
- this.theme = lightTheme; // thème par défaut
184
183
  // État actuel + préférence
185
184
  this.themeMode = options.themeMode || 'system'; // 'light', 'dark', 'system'
186
185
  this.userThemeOverride = null; // null = suit system, sinon 'light' ou 'dark'
@@ -196,7 +195,8 @@ class CanvasFramework {
196
195
  }
197
196
 
198
197
  this.components = [];
199
- this.applyThemeFromSystem();
198
+ this.theme = lightTheme; // thème par défaut
199
+ //this.applyThemeFromSystem();
200
200
  this.state = {};
201
201
  // NOUVELLE OPTION: choisir entre Canvas 2D et WebGL
202
202
  this.useWebGL = options.useWebGL !== false; // true par défaut
@@ -286,17 +286,23 @@ class CanvasFramework {
286
286
  * Détecte le thème système et applique si mode = 'system'
287
287
  */
288
288
  applyThemeFromSystem() {
289
- if (this.themeMode === 'system') {
290
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
291
- const newTheme = prefersDark ? this.darkTheme : this.lightTheme;
292
- this.setTheme(newTheme);
293
- } else {
294
- // Mode forcé
295
- this.setTheme(
296
- this.themeMode === 'dark' ? this.darkTheme : this.lightTheme
297
- );
298
- }
299
- }
289
+ // Vérifier que tout est initialisé
290
+ if (!this.lightTheme || !this.darkTheme) {
291
+ console.warn('Thèmes non initialisés');
292
+ return;
293
+ }
294
+
295
+ if (this.themeMode === 'system') {
296
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
297
+ const newTheme = prefersDark ? this.darkTheme : this.lightTheme;
298
+ this.setTheme(newTheme);
299
+ } else {
300
+ // Mode forcé
301
+ this.setTheme(
302
+ this.themeMode === 'dark' ? this.darkTheme : this.lightTheme
303
+ );
304
+ }
305
+ }
300
306
 
301
307
  /**
302
308
  * Écoute les changements système (ex: utilisateur bascule dark mode)
@@ -431,7 +437,7 @@ class CanvasFramework {
431
437
  set: (value) => {
432
438
  // Si value est blanc/noir ou une couleur “neutre”, tu remplaces par theme
433
439
  if (value === '#FFFFFF' || value === '#000000') {
434
- originalFillStyle.set.call(ctx, theme.text);
440
+ originalFillStyle.set.call(ctx, value);
435
441
  } else {
436
442
  originalFillStyle.set.call(ctx, value);
437
443
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
4
4
  "description": "Canvas-based cross-platform UI framework (Material & Cupertino)",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/utils/DevTools.js CHANGED
@@ -19,6 +19,16 @@ class DevTools {
19
19
  memory: [],
20
20
  drawCalls: []
21
21
  };
22
+ // Dans le constructeur, ajoutez :
23
+ this.consoleLogs = [];
24
+ this.maxConsoleLogs = 100;
25
+ this.consoleFilters = {
26
+ log: true,
27
+ warn: true,
28
+ error: true,
29
+ debug: true,
30
+ info: true
31
+ };
22
32
  this.setupUI();
23
33
  }
24
34
 
@@ -79,20 +89,50 @@ class DevTools {
79
89
 
80
90
  // Tabs
81
91
  this.tabs = document.createElement('div');
82
- this.tabs.style.cssText = `
83
- display: flex;
84
- background: #2d2d2d;
85
- border-bottom: 1px solid #333;
86
- `;
87
-
88
- const tabConfigs = [
89
- { id: 'components', label: 'Composants' },
90
- { id: 'performance', label: 'Performance' },
91
- { id: 'hierarchy', label: 'Hiérarchie' },
92
- { id: 'properties', label: 'Propriétés' },
93
- { id: 'events', label: 'Événements' },
94
- { id: 'routing', label: 'Routing' }
95
- ];
92
+ this.tabs.style.cssText = `
93
+ display: flex;
94
+ background: #2d2d2d;
95
+ border-bottom: 1px solid #333;
96
+ overflow-x: auto;
97
+ overflow-y: hidden;
98
+ scrollbar-width: thin;
99
+ scrollbar-color: #555 #2d2d2d;
100
+ `;
101
+
102
+ // Ajouter un style pour la scrollbar webkit (Chrome, Safari, Edge)
103
+ const style = document.createElement('style');
104
+ style.textContent = `
105
+ #devtools-tabs::-webkit-scrollbar {
106
+ height: 6px;
107
+ }
108
+
109
+ #devtools-tabs::-webkit-scrollbar-track {
110
+ background: #2d2d2d;
111
+ }
112
+
113
+ #devtools-tabs::-webkit-scrollbar-thumb {
114
+ background: #555;
115
+ border-radius: 3px;
116
+ }
117
+
118
+ #devtools-tabs::-webkit-scrollbar-thumb:hover {
119
+ background: #666;
120
+ }
121
+ `;
122
+ document.head.appendChild(style);
123
+
124
+ // Ajouter un ID pour cibler avec le CSS
125
+ this.tabs.id = 'devtools-tabs';
126
+
127
+ const tabConfigs = [
128
+ { id: 'components', label: 'Composants' },
129
+ { id: 'performance', label: 'Performance' },
130
+ { id: 'hierarchy', label: 'Hiérarchie' },
131
+ { id: 'properties', label: 'Propriétés' },
132
+ { id: 'events', label: 'Événements' },
133
+ { id: 'routing', label: 'Routing' },
134
+ { id: 'console', label: 'Console' } // <-- NOUVEAU
135
+ ];
96
136
 
97
137
  tabConfigs.forEach(config => {
98
138
  const tab = document.createElement('button');
@@ -126,6 +166,8 @@ class DevTools {
126
166
  this.createPropertiesPanel();
127
167
  this.createEventsPanel();
128
168
  this.createRoutingPanel();
169
+ // Ajoutez après createRoutingPanel() :
170
+ this.createConsolePanel();
129
171
 
130
172
  // Assembler le conteneur
131
173
  this.container.appendChild(this.header);
@@ -447,7 +489,330 @@ class DevTools {
447
489
  this.content.appendChild(panel);
448
490
  this.panels.routing = panel;
449
491
  }
492
+
493
+ /**
494
+ * Crée le panneau de console
495
+ */
496
+ createConsolePanel() {
497
+ const panel = document.createElement('div');
498
+ panel.id = 'console-panel';
499
+ panel.style.display = 'none';
500
+
501
+ // Filtres
502
+ const filters = document.createElement('div');
503
+ filters.style.cssText = `
504
+ display: flex;
505
+ gap: 10px;
506
+ padding: 8px;
507
+ background: #252526;
508
+ border-radius: 4px;
509
+ margin-bottom: 10px;
510
+ flex-wrap: wrap;
511
+ `;
512
+
513
+ const filterTypes = [
514
+ { type: 'log', label: 'Log', color: '#d4d4d4' },
515
+ { type: 'info', label: 'Info', color: '#4ec9b0' },
516
+ { type: 'warn', label: 'Warn', color: '#dcdcaa' },
517
+ { type: 'error', label: 'Error', color: '#f48771' },
518
+ { type: 'debug', label: 'Debug', color: '#569cd6' }
519
+ ];
520
+
521
+ filterTypes.forEach(({ type, label, color }) => {
522
+ const filterBtn = document.createElement('button');
523
+ filterBtn.textContent = label;
524
+ filterBtn.dataset.type = type;
525
+ filterBtn.style.cssText = `
526
+ padding: 4px 12px;
527
+ background: ${this.consoleFilters[type] ? color : '#555'};
528
+ color: ${this.consoleFilters[type] ? '#000' : '#aaa'};
529
+ border: none;
530
+ border-radius: 3px;
531
+ cursor: pointer;
532
+ font-size: 11px;
533
+ font-weight: bold;
534
+ transition: all 0.2s;
535
+ `;
536
+
537
+ filterBtn.onclick = () => {
538
+ this.consoleFilters[type] = !this.consoleFilters[type];
539
+ filterBtn.style.background = this.consoleFilters[type] ? color : '#555';
540
+ filterBtn.style.color = this.consoleFilters[type] ? '#000' : '#aaa';
541
+ this.updateConsolePanel();
542
+ };
543
+
544
+ filters.appendChild(filterBtn);
545
+ });
546
+
547
+ // Liste des logs
548
+ const consoleList = document.createElement('div');
549
+ consoleList.id = 'console-list';
550
+ consoleList.style.cssText = `
551
+ max-height: 400px;
552
+ overflow-y: auto;
553
+ border: 1px solid #333;
554
+ border-radius: 4px;
555
+ background: #1e1e1e;
556
+ font-family: 'Consolas', 'Monaco', monospace;
557
+ font-size: 11px;
558
+ `;
559
+
560
+ // Contrôles
561
+ const controls = document.createElement('div');
562
+ controls.style.cssText = `
563
+ display: flex;
564
+ gap: 8px;
565
+ margin-top: 10px;
566
+ `;
567
+
568
+ const clearBtn = document.createElement('button');
569
+ clearBtn.textContent = 'Clear Console';
570
+ clearBtn.style.cssText = `
571
+ flex: 1;
572
+ padding: 6px;
573
+ background: #555;
574
+ color: #fff;
575
+ border: none;
576
+ border-radius: 4px;
577
+ cursor: pointer;
578
+ `;
579
+ clearBtn.onclick = () => this.clearConsole();
580
+
581
+ const preserveLogCheckbox = document.createElement('label');
582
+ preserveLogCheckbox.style.cssText = `
583
+ display: flex;
584
+ align-items: center;
585
+ gap: 5px;
586
+ color: #ccc;
587
+ cursor: pointer;
588
+ `;
589
+ preserveLogCheckbox.innerHTML = `
590
+ <input type="checkbox" id="preserve-log">
591
+ <span>Preserve log</span>
592
+ `;
593
+
594
+ controls.appendChild(clearBtn);
595
+ controls.appendChild(preserveLogCheckbox);
596
+
597
+ panel.appendChild(filters);
598
+ panel.appendChild(consoleList);
599
+ panel.appendChild(controls);
600
+
601
+ this.content.appendChild(panel);
602
+ this.panels.console = panel;
603
+ this.consoleList = consoleList;
604
+ }
605
+
606
+ /**
607
+ * Met à jour le panneau console
608
+ */
609
+ updateConsolePanel() {
610
+ if (!this.isOpen || this.currentTab !== 'console') return;
611
+
612
+ this.consoleList.innerHTML = '';
613
+
614
+ this.consoleLogs
615
+ .filter(log => this.consoleFilters[log.type])
616
+ .forEach(log => {
617
+ const logItem = document.createElement('div');
618
+ logItem.style.cssText = `
619
+ padding: 6px 10px;
620
+ border-bottom: 1px solid #2d2d2d;
621
+ display: flex;
622
+ align-items: flex-start;
623
+ gap: 8px;
624
+ `;
625
+
626
+ const colors = {
627
+ log: '#d4d4d4',
628
+ info: '#4ec9b0',
629
+ warn: '#dcdcaa',
630
+ error: '#f48771',
631
+ debug: '#569cd6'
632
+ };
633
+
634
+ const icons = {
635
+ log: '○',
636
+ info: 'ℹ',
637
+ warn: '⚠',
638
+ error: '✖',
639
+ debug: '⚙'
640
+ };
450
641
 
642
+ const timestamp = document.createElement('span');
643
+ timestamp.textContent = log.timestamp;
644
+ timestamp.style.cssText = `
645
+ color: #888;
646
+ font-size: 10px;
647
+ min-width: 80px;
648
+ `;
649
+
650
+ const icon = document.createElement('span');
651
+ icon.textContent = icons[log.type];
652
+ icon.style.cssText = `
653
+ color: ${colors[log.type]};
654
+ min-width: 20px;
655
+ font-weight: bold;
656
+ `;
657
+
658
+ const message = document.createElement('div');
659
+ message.style.cssText = `
660
+ flex: 1;
661
+ color: ${colors[log.type]};
662
+ word-break: break-word;
663
+ `;
664
+
665
+ // Formater le message
666
+ if (typeof log.args[0] === 'object') {
667
+ // Fonction pour gérer les références circulaires
668
+ const safeStringify = (obj, indent = 2) => {
669
+ const seen = new WeakSet();
670
+ return JSON.stringify(obj, (key, value) => {
671
+ // Ignorer les propriétés framework pour éviter les cycles
672
+ if (key === 'framework') return '[CanvasFramework]';
673
+
674
+ if (typeof value === 'object' && value !== null) {
675
+ // Détection de cycle
676
+ if (seen.has(value)) {
677
+ return '[Circular]';
678
+ }
679
+ seen.add(value);
680
+
681
+ // Limiter la profondeur pour les gros objets
682
+ if (key === 'components' && Array.isArray(value)) {
683
+ return `[Array(${value.length})]`;
684
+ }
685
+ }
686
+ return value;
687
+ }, indent);
688
+ };
689
+
690
+ try {
691
+ message.innerHTML = `<pre style="margin: 0;">${safeStringify(log.args[0])}</pre>`;
692
+ } catch (e) {
693
+ message.textContent = '[Objet complexe non affichable]';
694
+ }
695
+ } else {
696
+ message.textContent = log.args.map(arg => {
697
+ if (typeof arg === 'object' && arg !== null) {
698
+ // Pour les objets dans les arguments
699
+ const seen = new WeakSet();
700
+ try {
701
+ return JSON.stringify(arg, (key, value) => {
702
+ if (key === 'framework') return '[Framework]';
703
+ if (typeof value === 'object' && value !== null) {
704
+ if (seen.has(value)) return '[Circular]';
705
+ seen.add(value);
706
+ }
707
+ return value;
708
+ });
709
+ } catch (e) {
710
+ return '[Object]';
711
+ }
712
+ }
713
+ return String(arg);
714
+ }).join(' ');
715
+ }
716
+
717
+ // Stack trace pour les erreurs
718
+ if (log.type === 'error' && log.stack) {
719
+ const stackTrace = document.createElement('div');
720
+ stackTrace.style.cssText = `
721
+ margin-top: 5px;
722
+ padding: 5px;
723
+ background: #2d2d2d;
724
+ border-radius: 3px;
725
+ font-size: 10px;
726
+ color: #888;
727
+ max-height: 100px;
728
+ overflow-y: auto;
729
+ `;
730
+ stackTrace.textContent = log.stack;
731
+ message.appendChild(stackTrace);
732
+ }
733
+
734
+ logItem.appendChild(timestamp);
735
+ logItem.appendChild(icon);
736
+ logItem.appendChild(message);
737
+
738
+ this.consoleList.appendChild(logItem);
739
+ });
740
+
741
+ // Auto-scroll vers le bas
742
+ this.consoleList.scrollTop = this.consoleList.scrollHeight;
743
+ }
744
+
745
+ /**
746
+ * Ajoute un log à la console
747
+ */
748
+ addConsoleLog(type, args, stack = null) {
749
+ const timestamp = new Date().toLocaleTimeString();
750
+
751
+ this.consoleLogs.push({
752
+ type,
753
+ args,
754
+ timestamp,
755
+ stack
756
+ });
757
+
758
+ // Limiter le nombre de logs
759
+ if (this.consoleLogs.length > this.maxConsoleLogs) {
760
+ this.consoleLogs.shift();
761
+ }
762
+
763
+ if (this.isOpen && this.currentTab === 'console') {
764
+ this.updateConsolePanel();
765
+ }
766
+ }
767
+
768
+ /**
769
+ * Vide la console
770
+ */
771
+ clearConsole() {
772
+ const preserveLog = document.getElementById('preserve-log')?.checked;
773
+
774
+ if (!preserveLog) {
775
+ this.consoleLogs = [];
776
+ this.updateConsolePanel();
777
+ }
778
+ }
779
+
780
+ /**
781
+ * Intercepte les méthodes console natives
782
+ */
783
+ interceptConsole() {
784
+ const originalConsole = {
785
+ log: console.log,
786
+ info: console.info,
787
+ warn: console.warn,
788
+ error: console.error,
789
+ debug: console.debug
790
+ };
791
+
792
+ ['log', 'info', 'warn', 'error', 'debug'].forEach(method => {
793
+ console[method] = (...args) => {
794
+ // Appeler la méthode originale
795
+ originalConsole[method].apply(console, args);
796
+
797
+ // Ajouter au DevTools
798
+ const stack = method === 'error' ? new Error().stack : null;
799
+ this.addConsoleLog(method, args, stack);
800
+ };
801
+ });
802
+
803
+ // Sauvegarder pour restauration
804
+ this.originalConsole = originalConsole;
805
+ }
806
+
807
+ /**
808
+ * Restaure les méthodes console natives
809
+ */
810
+ restoreConsole() {
811
+ if (this.originalConsole) {
812
+ Object.assign(console, this.originalConsole);
813
+ }
814
+ }
815
+
451
816
  /**
452
817
  * Configure les raccourcis clavier
453
818
  */
@@ -504,14 +869,16 @@ class DevTools {
504
869
  });
505
870
 
506
871
  if (tabId === 'performance') {
507
- this.updatePerformancePanel();
508
- } else if (tabId === 'hierarchy') {
509
- this.updateHierarchyPanel();
510
- } else if (tabId === 'components') {
511
- this.updateComponentsPanel();
512
- } else if (tabId === 'routing') {
513
- this.updateRoutingPanel();
514
- }
872
+ this.updatePerformancePanel();
873
+ } else if (tabId === 'hierarchy') {
874
+ this.updateHierarchyPanel();
875
+ } else if (tabId === 'components') {
876
+ this.updateComponentsPanel();
877
+ } else if (tabId === 'routing') {
878
+ this.updateRoutingPanel();
879
+ } else if (tabId === 'console') { // <-- NOUVEAU
880
+ this.updateConsolePanel();
881
+ }
515
882
  }
516
883
 
517
884
  /**
@@ -1143,6 +1510,8 @@ class DevTools {
1143
1510
  * Nettoie les références et restaure le framework
1144
1511
  */
1145
1512
  cleanup() {
1513
+ this.restoreConsole();
1514
+
1146
1515
  // Restaurer les méthodes originales si elles existent
1147
1516
  if (this.framework) {
1148
1517
  if (this.originalNavigate) {
@@ -1190,6 +1559,8 @@ class DevTools {
1190
1559
  */
1191
1560
  attachToFramework() {
1192
1561
  window.devTools = this;
1562
+ // Intercepter la console
1563
+ this.interceptConsole();
1193
1564
 
1194
1565
  // Sauvegarder les références originales
1195
1566
  this.originalNavigate = this.framework.navigate;