canvasframework 0.3.28 → 0.4.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.
- package/components/AndroidDatePickerDialog.js +2 -2
- package/components/RadioButton.js +2 -1
- package/core/CanvasFramework.js +64 -36
- package/package.json +1 -1
- package/utils/DevTools.js +393 -22
|
@@ -170,7 +170,7 @@ class AndroidDatePickerDialog extends Component {
|
|
|
170
170
|
ctx.shadowColor = 'transparent';
|
|
171
171
|
|
|
172
172
|
// Header coloré
|
|
173
|
-
ctx.fillStyle =
|
|
173
|
+
ctx.fillStyle = this.headerBgColor;
|
|
174
174
|
ctx.beginPath();
|
|
175
175
|
this.roundRect(ctx, dialogX, dialogY, this.dialogWidth, this.headerHeight, 4);
|
|
176
176
|
ctx.rect(dialogX, dialogY + this.headerHeight - 4, this.dialogWidth, 4);
|
|
@@ -191,7 +191,7 @@ class AndroidDatePickerDialog extends Component {
|
|
|
191
191
|
const selectedMonth = monthNames[this.selectedDate.getMonth()];
|
|
192
192
|
const selectedDayNum = this.selectedDate.getDate();
|
|
193
193
|
|
|
194
|
-
ctx.fillStyle = '#
|
|
194
|
+
ctx.fillStyle = '#ffffff';
|
|
195
195
|
ctx.font = 'bold 32px Roboto, sans-serif';
|
|
196
196
|
ctx.textBaseline = 'middle';
|
|
197
197
|
ctx.fillText(`${selectedDay}, ${selectedMonth} ${selectedDayNum}`,
|
|
@@ -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';
|
package/core/CanvasFramework.js
CHANGED
|
@@ -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,13 @@ class CanvasFramework {
|
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
this.components = [];
|
|
199
|
-
this.
|
|
198
|
+
this.theme = lightTheme; // thème par défaut
|
|
199
|
+
// ✅ AJOUTER ICI :
|
|
200
|
+
this._cachedMaxScroll = 0;
|
|
201
|
+
this._maxScrollDirty = true;
|
|
202
|
+
this.resizeTimeout = null;
|
|
203
|
+
|
|
204
|
+
//this.applyThemeFromSystem();
|
|
200
205
|
this.state = {};
|
|
201
206
|
// NOUVELLE OPTION: choisir entre Canvas 2D et WebGL
|
|
202
207
|
this.useWebGL = options.useWebGL !== false; // true par défaut
|
|
@@ -286,17 +291,23 @@ class CanvasFramework {
|
|
|
286
291
|
* Détecte le thème système et applique si mode = 'system'
|
|
287
292
|
*/
|
|
288
293
|
applyThemeFromSystem() {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
// ✅ Vérifier que tout est initialisé
|
|
295
|
+
if (!this.lightTheme || !this.darkTheme) {
|
|
296
|
+
console.warn('Thèmes non initialisés');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (this.themeMode === 'system') {
|
|
301
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
302
|
+
const newTheme = prefersDark ? this.darkTheme : this.lightTheme;
|
|
303
|
+
this.setTheme(newTheme);
|
|
304
|
+
} else {
|
|
305
|
+
// Mode forcé
|
|
306
|
+
this.setTheme(
|
|
307
|
+
this.themeMode === 'dark' ? this.darkTheme : this.lightTheme
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
300
311
|
|
|
301
312
|
/**
|
|
302
313
|
* Écoute les changements système (ex: utilisateur bascule dark mode)
|
|
@@ -431,7 +442,7 @@ class CanvasFramework {
|
|
|
431
442
|
set: (value) => {
|
|
432
443
|
// Si value est blanc/noir ou une couleur “neutre”, tu remplaces par theme
|
|
433
444
|
if (value === '#FFFFFF' || value === '#000000') {
|
|
434
|
-
originalFillStyle.set.call(ctx,
|
|
445
|
+
originalFillStyle.set.call(ctx, value);
|
|
435
446
|
} else {
|
|
436
447
|
originalFillStyle.set.call(ctx, value);
|
|
437
448
|
}
|
|
@@ -1225,13 +1236,18 @@ class CanvasFramework {
|
|
|
1225
1236
|
}
|
|
1226
1237
|
|
|
1227
1238
|
getMaxScroll() {
|
|
1239
|
+
if (!this._maxScrollDirty) return this._cachedMaxScroll;
|
|
1240
|
+
|
|
1228
1241
|
let maxY = 0;
|
|
1229
1242
|
for (const comp of this.components) {
|
|
1230
1243
|
if (this.isFixedComponent(comp) || !comp.visible) continue;
|
|
1231
1244
|
const bottom = comp.y + comp.height;
|
|
1232
1245
|
if (bottom > maxY) maxY = bottom;
|
|
1233
1246
|
}
|
|
1234
|
-
|
|
1247
|
+
|
|
1248
|
+
this._cachedMaxScroll = Math.max(0, maxY - this.height + 50);
|
|
1249
|
+
this._maxScrollDirty = false;
|
|
1250
|
+
return this._cachedMaxScroll;
|
|
1235
1251
|
}
|
|
1236
1252
|
|
|
1237
1253
|
/*getMaxScroll() {
|
|
@@ -1245,32 +1261,38 @@ class CanvasFramework {
|
|
|
1245
1261
|
}*/
|
|
1246
1262
|
|
|
1247
1263
|
handleResize() {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
this.
|
|
1252
|
-
|
|
1264
|
+
if (this.resizeTimeout) clearTimeout(this.resizeTimeout); // ✅ AJOUTER
|
|
1265
|
+
|
|
1266
|
+
this.resizeTimeout = setTimeout(() => { // ✅ AJOUTER
|
|
1267
|
+
if (!this.useWebGL) {
|
|
1268
|
+
this.width = window.innerWidth;
|
|
1269
|
+
this.height = window.innerHeight;
|
|
1270
|
+
this.setupCanvas();
|
|
1253
1271
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1272
|
+
for (const comp of this.components) {
|
|
1273
|
+
if (comp._resize) {
|
|
1274
|
+
comp._resize(this.width, this.height);
|
|
1275
|
+
}
|
|
1257
1276
|
}
|
|
1277
|
+
this._maxScrollDirty = true; // ✅ AJOUTER
|
|
1258
1278
|
}
|
|
1259
|
-
}
|
|
1279
|
+
}, 150); // ✅ AJOUTER (throttle 150ms)
|
|
1260
1280
|
}
|
|
1261
1281
|
|
|
1262
1282
|
add(component) {
|
|
1263
1283
|
this.components.push(component);
|
|
1264
|
-
|
|
1284
|
+
component._mount();
|
|
1285
|
+
this._maxScrollDirty = true; // ✅ AJOUTER CETTE LIGNE
|
|
1265
1286
|
return component;
|
|
1266
1287
|
}
|
|
1267
1288
|
|
|
1268
1289
|
remove(component) {
|
|
1269
1290
|
const index = this.components.indexOf(component);
|
|
1270
1291
|
if (index > -1) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1292
|
+
component._unmount();
|
|
1293
|
+
this.components.splice(index, 1);
|
|
1294
|
+
this._maxScrollDirty = true; // ✅ AJOUTER CETTE LIGNE
|
|
1295
|
+
}
|
|
1274
1296
|
}
|
|
1275
1297
|
|
|
1276
1298
|
markComponentDirty(component) {
|
|
@@ -1491,14 +1513,20 @@ class CanvasFramework {
|
|
|
1491
1513
|
}
|
|
1492
1514
|
|
|
1493
1515
|
// Scrollable
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1516
|
+
this.ctx.save();
|
|
1517
|
+
this.ctx.translate(0, this.scrollOffset);
|
|
1518
|
+
for (let comp of scrollableComponents) {
|
|
1519
|
+
if (comp.visible) {
|
|
1520
|
+
// ✅ Viewport culling : ne dessiner que ce qui est visible
|
|
1521
|
+
const screenY = comp.y + this.scrollOffset;
|
|
1522
|
+
const isInViewport = screenY + comp.height >= -100 && screenY <= this.height + 100;
|
|
1523
|
+
|
|
1524
|
+
if (isInViewport) {
|
|
1525
|
+
comp.draw(this.ctx);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
this.ctx.restore();
|
|
1502
1530
|
|
|
1503
1531
|
// Fixed
|
|
1504
1532
|
for (let comp of fixedComponents) {
|
package/package.json
CHANGED
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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;
|