canvasframework 0.3.21 → 0.3.23

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,201 @@
1
+ /**
2
+ * Console DevTools pour CanvasFramework
3
+ */
4
+ class DevToolsConsole {
5
+ constructor(framework) {
6
+ this.framework = framework;
7
+ this.commands = new Map();
8
+ this.history = [];
9
+ this.historyIndex = -1;
10
+
11
+ this.setupCommands();
12
+ this.setupConsole();
13
+ }
14
+
15
+ setupCommands() {
16
+ this.registerCommand('help', 'Affiche cette aide', () => this.showHelp());
17
+ this.registerCommand('clear', 'Efface la console', () => this.clear());
18
+ this.registerCommand('fps', 'Toggle FPS display', () => {
19
+ this.framework.enableFpsDisplay(!this.framework.showFps);
20
+ return 'FPS display ' + (this.framework.showFps ? 'enabled' : 'disabled');
21
+ });
22
+
23
+ this.registerCommand('inspect', 'Toggle inspection overlay', () => {
24
+ this.framework.toggleInspection();
25
+ return 'Inspection overlay toggled';
26
+ });
27
+
28
+ this.registerCommand('components', 'List all components', () => {
29
+ return this.framework.components.map((comp, i) =>
30
+ `${i}: ${comp.constructor.name} [${comp.x},${comp.y}] ${comp.width}x${comp.height}`
31
+ ).join('\n');
32
+ });
33
+
34
+ this.registerCommand('route', 'Show routing info', () => {
35
+ return `Current: ${this.framework.currentRoute}\nParams: ${JSON.stringify(this.framework.currentParams)}\nHistory: ${this.framework.history.length} entries`;
36
+ });
37
+
38
+ this.registerCommand('theme', 'Toggle dark/light theme', () => {
39
+ this.framework.toggleDarkMode();
40
+ return `Theme: ${this.framework.theme === this.framework.lightTheme ? 'light' : 'dark'}`;
41
+ });
42
+
43
+ this.registerCommand('perf', 'Show performance stats', () => {
44
+ const stats = [];
45
+ stats.push(`FPS: ${this.framework.fps}`);
46
+ stats.push(`Components: ${this.framework.components.length}`);
47
+ stats.push(`Dirty: ${Array.from(this.framework.dirtyComponents).length}`);
48
+ stats.push(`Memory: ${performance.memory ? Math.round(performance.memory.usedJSHeapSize / 1024 / 1024) + 'MB' : 'N/A'}`);
49
+ return stats.join('\n');
50
+ });
51
+ }
52
+
53
+ registerCommand(name, description, handler) {
54
+ this.commands.set(name, { description, handler });
55
+ }
56
+
57
+ setupConsole() {
58
+ // Créer la console
59
+ this.consoleElement = document.createElement('div');
60
+ this.consoleElement.style.cssText = `
61
+ position: fixed;
62
+ bottom: 0;
63
+ left: 0;
64
+ right: 0;
65
+ background: rgba(0, 0, 0, 0.9);
66
+ color: #0f0;
67
+ font-family: monospace;
68
+ font-size: 12px;
69
+ padding: 10px;
70
+ z-index: 10001;
71
+ display: none;
72
+ max-height: 300px;
73
+ overflow-y: auto;
74
+ border-top: 1px solid #333;
75
+ `;
76
+
77
+ // Input
78
+ this.input = document.createElement('input');
79
+ this.input.type = 'text';
80
+ this.input.style.cssText = `
81
+ width: 100%;
82
+ background: transparent;
83
+ border: none;
84
+ color: #0f0;
85
+ font-family: monospace;
86
+ font-size: 12px;
87
+ outline: none;
88
+ `;
89
+ this.input.placeholder = 'Enter DevTools command (type "help" for list)...';
90
+
91
+ // Output
92
+ this.output = document.createElement('div');
93
+ this.output.style.cssText = `
94
+ margin-bottom: 10px;
95
+ white-space: pre-wrap;
96
+ `;
97
+
98
+ this.consoleElement.appendChild(this.output);
99
+ this.consoleElement.appendChild(this.input);
100
+ document.body.appendChild(this.consoleElement);
101
+
102
+ // Événements
103
+ this.input.addEventListener('keydown', this.handleInput.bind(this));
104
+
105
+ // Hotkey pour ouvrir la console
106
+ document.addEventListener('keydown', (e) => {
107
+ if (e.key === '`' && e.ctrlKey) {
108
+ e.preventDefault();
109
+ this.toggle();
110
+ }
111
+ });
112
+ }
113
+
114
+ handleInput(e) {
115
+ if (e.key === 'Enter') {
116
+ const command = this.input.value.trim();
117
+ if (command) {
118
+ this.executeCommand(command);
119
+ this.history.push(command);
120
+ this.historyIndex = this.history.length;
121
+ this.input.value = '';
122
+ }
123
+ } else if (e.key === 'ArrowUp') {
124
+ e.preventDefault();
125
+ if (this.historyIndex > 0) {
126
+ this.historyIndex--;
127
+ this.input.value = this.history[this.historyIndex];
128
+ }
129
+ } else if (e.key === 'ArrowDown') {
130
+ e.preventDefault();
131
+ if (this.historyIndex < this.history.length - 1) {
132
+ this.historyIndex++;
133
+ this.input.value = this.history[this.historyIndex];
134
+ } else {
135
+ this.historyIndex = this.history.length;
136
+ this.input.value = '';
137
+ }
138
+ } else if (e.key === 'Tab') {
139
+ e.preventDefault();
140
+ this.autoComplete();
141
+ }
142
+ }
143
+
144
+ executeCommand(commandStr) {
145
+ const [cmd, ...args] = commandStr.split(' ');
146
+ const command = this.commands.get(cmd);
147
+
148
+ if (command) {
149
+ try {
150
+ const result = command.handler(...args);
151
+ this.log(`> ${commandStr}\n${result !== undefined ? result : ''}`);
152
+ } catch (error) {
153
+ this.log(`> ${commandStr}\nError: ${error.message}`);
154
+ }
155
+ } else {
156
+ this.log(`> ${commandStr}\nUnknown command: ${cmd}. Type "help" for list.`);
157
+ }
158
+ }
159
+
160
+ autoComplete() {
161
+ const partial = this.input.value.trim();
162
+ if (!partial) return;
163
+
164
+ const matches = Array.from(this.commands.keys())
165
+ .filter(cmd => cmd.startsWith(partial));
166
+
167
+ if (matches.length === 1) {
168
+ this.input.value = matches[0];
169
+ } else if (matches.length > 1) {
170
+ this.log(`Suggestions: ${matches.join(', ')}`);
171
+ }
172
+ }
173
+
174
+ showHelp() {
175
+ const helpText = Array.from(this.commands.entries())
176
+ .map(([name, { description }]) => `${name.padEnd(15)} ${description}`)
177
+ .join('\n');
178
+ return `Available commands:\n${helpText}`;
179
+ }
180
+
181
+ log(message) {
182
+ this.output.textContent += message + '\n';
183
+ this.consoleElement.scrollTop = this.consoleElement.scrollHeight;
184
+ }
185
+
186
+ clear() {
187
+ this.output.textContent = '';
188
+ }
189
+
190
+ toggle() {
191
+ this.consoleElement.style.display =
192
+ this.consoleElement.style.display === 'none' ? 'block' : 'none';
193
+
194
+ if (this.consoleElement.style.display === 'block') {
195
+ this.input.focus();
196
+ this.log('CanvasFramework DevTools Console - Ctrl+` to close');
197
+ }
198
+ }
199
+ }
200
+
201
+ export default DevToolsConsole;
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Overlay d'inspection pour CanvasFramework
3
+ */
4
+
5
+ class InspectionOverlay {
6
+ constructor(framework) {
7
+ this.framework = framework;
8
+ this.isEnabled = false;
9
+ this.hoveredComp = null;
10
+ this.selectedComp = null;
11
+ this.showGrid = false;
12
+ this.showBounds = true;
13
+ this.showMetrics = true;
14
+ this.gridSize = 50;
15
+
16
+ this.setup();
17
+ }
18
+
19
+ setup() {
20
+ // Créer le canvas overlay
21
+ this.overlayCanvas = document.createElement('canvas');
22
+ this.overlayCanvas.style.cssText = `
23
+ position: absolute;
24
+ top: 0;
25
+ left: 0;
26
+ pointer-events: none;
27
+ z-index: 9998;
28
+ `;
29
+
30
+ this.overlayCtx = this.overlayCanvas.getContext('2d');
31
+
32
+ // Redimensionner avec le canvas principal
33
+ this.syncWithMainCanvas();
34
+
35
+ // Événements
36
+ this.framework.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
37
+ this.framework.canvas.addEventListener('click', this.handleClick.bind(this));
38
+
39
+ window.addEventListener('resize', this.syncWithMainCanvas.bind(this));
40
+ }
41
+
42
+ syncWithMainCanvas() {
43
+ const rect = this.framework.canvas.getBoundingClientRect();
44
+ this.overlayCanvas.width = rect.width * (window.devicePixelRatio || 1);
45
+ this.overlayCanvas.height = rect.height * (window.devicePixelRatio || 1);
46
+ this.overlayCanvas.style.width = rect.width + 'px';
47
+ this.overlayCanvas.style.height = rect.height + 'px';
48
+
49
+ if (this.overlayCtx) {
50
+ this.overlayCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
51
+ }
52
+ }
53
+
54
+ handleMouseMove(e) {
55
+ if (!this.isEnabled) return;
56
+
57
+ const rect = this.framework.canvas.getBoundingClientRect();
58
+ const x = e.clientX - rect.left;
59
+ const y = e.clientY - rect.top;
60
+
61
+ // Trouver le composant sous la souris
62
+ this.hoveredComp = this.findComponentAt(x, y);
63
+ this.render();
64
+ }
65
+
66
+ handleClick(e) {
67
+ if (!this.isEnabled) return;
68
+
69
+ if (this.hoveredComp) {
70
+ this.selectedComp = this.hoveredComp;
71
+
72
+ // Ouvrir DevTools si disponible
73
+ if (window.devTools) {
74
+ window.devTools.selectComponent(this.selectedComp);
75
+ window.devTools.switchTab('properties');
76
+ }
77
+ }
78
+ }
79
+
80
+ findComponentAt(x, y) {
81
+ const adjustedY = y - this.framework.scrollOffset;
82
+
83
+ for (let i = this.framework.components.length - 1; i >= 0; i--) {
84
+ const comp = this.framework.components[i];
85
+ if (comp.visible && comp.isPointInside(x, adjustedY)) {
86
+ return comp;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+
92
+ render() {
93
+ if (!this.isEnabled) return;
94
+
95
+ this.overlayCtx.clearRect(0, 0,
96
+ this.overlayCanvas.width / (window.devicePixelRatio || 1),
97
+ this.overlayCanvas.height / (window.devicePixelRatio || 1)
98
+ );
99
+
100
+ // Grille
101
+ if (this.showGrid) {
102
+ this.drawGrid();
103
+ }
104
+
105
+ // Tous les composants
106
+ if (this.showBounds) {
107
+ this.drawAllComponents();
108
+ }
109
+
110
+ // Composant survolé
111
+ if (this.hoveredComp) {
112
+ this.drawHoveredComponent();
113
+ }
114
+
115
+ // Composant sélectionné
116
+ if (this.selectedComp) {
117
+ this.drawSelectedComponent();
118
+ }
119
+
120
+ // Métriques
121
+ if (this.showMetrics) {
122
+ this.drawMetrics();
123
+ }
124
+ }
125
+
126
+ drawGrid() {
127
+ this.overlayCtx.save();
128
+ this.overlayCtx.strokeStyle = 'rgba(100, 100, 100, 0.3)';
129
+ this.overlayCtx.lineWidth = 1;
130
+
131
+ // Lignes verticales
132
+ for (let x = 0; x < this.overlayCanvas.width; x += this.gridSize) {
133
+ this.overlayCtx.beginPath();
134
+ this.overlayCtx.moveTo(x, 0);
135
+ this.overlayCtx.lineTo(x, this.overlayCanvas.height);
136
+ this.overlayCtx.stroke();
137
+ }
138
+
139
+ // Lignes horizontales
140
+ for (let y = 0; y < this.overlayCanvas.height; y += this.gridSize) {
141
+ this.overlayCtx.beginPath();
142
+ this.overlayCtx.moveTo(0, y);
143
+ this.overlayCtx.lineTo(this.overlayCanvas.width, y);
144
+ this.overlayCtx.stroke();
145
+ }
146
+
147
+ this.overlayCtx.restore();
148
+ }
149
+
150
+ drawAllComponents() {
151
+ this.framework.components.forEach(comp => {
152
+ if (!comp.visible) return;
153
+
154
+ const isFixed = this.framework.isFixedComponent(comp);
155
+ const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
156
+
157
+ this.overlayCtx.save();
158
+ this.overlayCtx.strokeStyle = 'rgba(0, 150, 255, 0.2)';
159
+ this.overlayCtx.lineWidth = 1;
160
+ this.overlayCtx.strokeRect(comp.x, y, comp.width, comp.height);
161
+ this.overlayCtx.restore();
162
+ });
163
+ }
164
+
165
+ drawHoveredComponent() {
166
+ const comp = this.hoveredComp;
167
+ const isFixed = this.framework.isFixedComponent(comp);
168
+ const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
169
+
170
+ this.overlayCtx.save();
171
+
172
+ // Fond semi-transparent
173
+ this.overlayCtx.fillStyle = 'rgba(100, 200, 255, 0.1)';
174
+ this.overlayCtx.fillRect(comp.x, y, comp.width, comp.height);
175
+
176
+ // Bordure
177
+ this.overlayCtx.strokeStyle = '#4ec9b0';
178
+ this.overlayCtx.lineWidth = 2;
179
+ this.overlayCtx.strokeRect(comp.x, y, comp.width, comp.height);
180
+
181
+ // Infos
182
+ this.overlayCtx.fillStyle = '#4ec9b0';
183
+ this.overlayCtx.font = '12px monospace';
184
+ this.overlayCtx.fillText(
185
+ `${comp.constructor.name} (${comp.width}x${comp.height})`,
186
+ comp.x,
187
+ y - 5
188
+ );
189
+
190
+ this.overlayCtx.restore();
191
+ }
192
+
193
+ drawSelectedComponent() {
194
+ const comp = this.selectedComp;
195
+ if (!comp.visible) return;
196
+
197
+ const isFixed = this.framework.isFixedComponent(comp);
198
+ const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
199
+
200
+ this.overlayCtx.save();
201
+
202
+ // Bordure rouge pointillée
203
+ this.overlayCtx.strokeStyle = '#ff5555';
204
+ this.overlayCtx.lineWidth = 2;
205
+ this.overlayCtx.setLineDash([5, 3]);
206
+ this.overlayCtx.strokeRect(comp.x - 2, y - 2, comp.width + 4, comp.height + 4);
207
+
208
+ // Mesures
209
+ this.overlayCtx.strokeStyle = '#ff5555';
210
+ this.overlayCtx.lineWidth = 1;
211
+
212
+ // Largeur
213
+ this.overlayCtx.beginPath();
214
+ this.overlayCtx.moveTo(comp.x, y - 20);
215
+ this.overlayCtx.lineTo(comp.x + comp.width, y - 20);
216
+ this.overlayCtx.moveTo(comp.x, y - 25);
217
+ this.overlayCtx.lineTo(comp.x, y - 15);
218
+ this.overlayCtx.moveTo(comp.x + comp.width, y - 25);
219
+ this.overlayCtx.lineTo(comp.x + comp.width, y - 15);
220
+ this.overlayCtx.stroke();
221
+
222
+ this.overlayCtx.fillStyle = '#ff5555';
223
+ this.overlayCtx.font = '10px monospace';
224
+ this.overlayCtx.textAlign = 'center';
225
+ this.overlayCtx.fillText(
226
+ `${comp.width}px`,
227
+ comp.x + comp.width / 2,
228
+ y - 25
229
+ );
230
+
231
+ // Hauteur
232
+ this.overlayCtx.beginPath();
233
+ this.overlayCtx.moveTo(comp.x - 20, y);
234
+ this.overlayCtx.lineTo(comp.x - 20, y + comp.height);
235
+ this.overlayCtx.moveTo(comp.x - 25, y);
236
+ this.overlayCtx.lineTo(comp.x - 15, y);
237
+ this.overlayCtx.moveTo(comp.x - 25, y + comp.height);
238
+ this.overlayCtx.lineTo(comp.x - 15, y + comp.height);
239
+ this.overlayCtx.stroke();
240
+
241
+ this.overlayCtx.save();
242
+ this.overlayCtx.translate(comp.x - 30, y + comp.height / 2);
243
+ this.overlayCtx.rotate(-Math.PI / 2);
244
+ this.overlayCtx.fillText(
245
+ `${comp.height}px`,
246
+ 0,
247
+ 0
248
+ );
249
+ this.overlayCtx.restore();
250
+
251
+ this.overlayCtx.restore();
252
+ }
253
+
254
+ drawMetrics() {
255
+ this.overlayCtx.save();
256
+ this.overlayCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';
257
+ this.overlayCtx.fillRect(10, 10, 200, 90);
258
+
259
+ this.overlayCtx.fillStyle = '#fff';
260
+ this.overlayCtx.font = '12px monospace';
261
+ this.overlayCtx.textAlign = 'left';
262
+
263
+ this.overlayCtx.fillText(`Composants: ${this.framework.components.length}`, 20, 30);
264
+ this.overlayCtx.fillText(`FPS: ${this.framework.fps}`, 20, 50);
265
+ this.overlayCtx.fillText(`Scroll: ${Math.round(this.framework.scrollOffset)}px`, 20, 70);
266
+ this.overlayCtx.fillText(`Route: ${this.framework.currentRoute}`, 20, 90);
267
+
268
+ if (this.hoveredComp) {
269
+ this.overlayCtx.fillText(
270
+ `Survol: ${this.hoveredComp.constructor.name}`,
271
+ 20,
272
+ 110
273
+ );
274
+ }
275
+
276
+ this.overlayCtx.restore();
277
+ }
278
+
279
+ enable() {
280
+ this.isEnabled = true;
281
+ document.body.appendChild(this.overlayCanvas);
282
+ this.render();
283
+ }
284
+
285
+ disable() {
286
+ this.isEnabled = false;
287
+ if (this.overlayCanvas.parentNode) {
288
+ this.overlayCanvas.parentNode.removeChild(this.overlayCanvas);
289
+ }
290
+ }
291
+
292
+ toggle() {
293
+ if (this.isEnabled) {
294
+ this.disable();
295
+ } else {
296
+ this.enable();
297
+ }
298
+ }
299
+
300
+ setOptions(options) {
301
+ Object.assign(this, options);
302
+ if (this.isEnabled) {
303
+ this.render();
304
+ }
305
+ }
306
+ }
307
+
308
+ export default InspectionOverlay;