@flight-framework/devtools 0.0.20 → 1.0.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/dist/panel.js CHANGED
@@ -66,6 +66,7 @@ export function injectDevToolsPanel() {
66
66
  isOpen: false,
67
67
  currentTab: 'routes',
68
68
  routeFilter: '',
69
+ requestFilter: '',
69
70
  consoleFilter: 'all',
70
71
  expandedItems: new Set(),
71
72
  width: savedSize.width,
@@ -75,16 +76,24 @@ export function injectDevToolsPanel() {
75
76
  isDragging: false,
76
77
  isResizing: false,
77
78
  };
79
+ // Hotkey: Shift+D to toggle DevTools
80
+ document.addEventListener('keydown', (e) => {
81
+ if (e.shiftKey && e.key === 'D') {
82
+ e.preventDefault();
83
+ panelState.isOpen = !panelState.isOpen;
84
+ render(devTools.getState());
85
+ }
86
+ });
78
87
  function render(state) {
79
88
  // Skip re-render during drag/resize to prevent DOM destruction
80
89
  if (panelState.isDragging || panelState.isResizing)
81
90
  return;
82
91
  const options = devTools.getOptions();
83
- container.innerHTML = `
84
- <div class="flight-devtools ${options.position} ${panelState.isOpen ? 'open' : ''}">
85
- ${panelState.isOpen ? '' : `<button class="flight-devtools-toggle" data-action="toggle">${getFlightLogo()}</button>`}
86
- ${panelState.isOpen ? renderPanel(state) : renderBadge(state)}
87
- </div>
92
+ container.innerHTML = `
93
+ <div class="flight-devtools ${options.position} ${panelState.isOpen ? 'open' : ''}">
94
+ ${panelState.isOpen ? '' : `<button class="flight-devtools-toggle" data-action="toggle">${getFlightLogo()}</button>`}
95
+ ${panelState.isOpen ? renderPanel(state) : renderBadge(state)}
96
+ </div>
88
97
  `;
89
98
  // Attach event listeners
90
99
  attachEventListeners(state);
@@ -106,33 +115,33 @@ export function injectDevToolsPanel() {
106
115
  const positionStyle = panelState.x !== null && panelState.y !== null
107
116
  ? `position:fixed;left:${panelState.x}px;top:${panelState.y}px;right:auto;bottom:auto;`
108
117
  : '';
109
- return `
110
- <div class="flight-devtools-panel" style="width:${panelState.width}px;height:${panelState.height}px;${positionStyle}">
111
- <div class="flight-devtools-resize-handle-top" data-resize="top"></div>
112
- <div class="flight-devtools-resize-handle-left" data-resize="left"></div>
113
- <div class="flight-devtools-resize-handle-corner" data-resize="corner"></div>
114
- <div class="flight-devtools-header">
115
- <div class="flight-devtools-header-title">
116
- <span class="flight-devtools-logo">Flight DevTools</span>
117
- <span class="flight-devtools-version">v0.0.20</span>
118
- </div>
119
- <button class="flight-devtools-close" data-action="toggle">x</button>
120
- </div>
121
- <div class="flight-devtools-tabs">
122
- ${tabs.map(tab => `
123
- <button
124
- class="${panelState.currentTab === tab.id ? 'active' : ''}"
125
- data-action="tab"
126
- data-tab="${tab.id}"
127
- >
128
- ${tab.label}${tab.count !== null ? ` <span class="count">${tab.count}</span>` : ''}
129
- </button>
130
- `).join('')}
131
- </div>
132
- <div class="flight-devtools-content">
133
- ${renderTabContent(state)}
134
- </div>
135
- </div>
118
+ return `
119
+ <div class="flight-devtools-panel" style="width:${panelState.width}px;height:${panelState.height}px;${positionStyle}">
120
+ <div class="flight-devtools-resize-handle-top" data-resize="top"></div>
121
+ <div class="flight-devtools-resize-handle-left" data-resize="left"></div>
122
+ <div class="flight-devtools-resize-handle-corner" data-resize="corner"></div>
123
+ <div class="flight-devtools-header">
124
+ <div class="flight-devtools-header-title">
125
+ <span class="flight-devtools-logo">Flight DevTools</span>
126
+ <span class="flight-devtools-version">v0.0.21</span>
127
+ </div>
128
+ <button class="flight-devtools-close" data-action="toggle">x</button>
129
+ </div>
130
+ <div class="flight-devtools-tabs">
131
+ ${tabs.map(tab => `
132
+ <button
133
+ class="${panelState.currentTab === tab.id ? 'active' : ''}"
134
+ data-action="tab"
135
+ data-tab="${tab.id}"
136
+ >
137
+ ${tab.label}${tab.count !== null ? ` <span class="count">${tab.count}</span>` : ''}
138
+ </button>
139
+ `).join('')}
140
+ </div>
141
+ <div class="flight-devtools-content">
142
+ ${renderTabContent(state)}
143
+ </div>
144
+ </div>
136
145
  `;
137
146
  }
138
147
  function renderTabContent(state) {
@@ -154,131 +163,148 @@ export function injectDevToolsPanel() {
154
163
  : routes;
155
164
  const pageRoutes = filtered.filter(r => r.type === 'page');
156
165
  const apiRoutes = filtered.filter(r => r.type === 'api');
157
- return `
158
- <div class="flight-devtools-toolbar">
159
- <input
160
- type="text"
161
- placeholder="Filter routes..."
162
- value="${panelState.routeFilter}"
163
- data-action="filter-routes"
164
- class="flight-devtools-search"
165
- />
166
- </div>
167
- <div class="flight-devtools-list">
168
- ${pageRoutes.length > 0 ? `
169
- <div class="flight-devtools-section">
170
- <div class="flight-devtools-section-title">Pages (${pageRoutes.length})</div>
171
- ${pageRoutes.map(route => renderRouteItem(route)).join('')}
172
- </div>
173
- ` : ''}
174
- ${apiRoutes.length > 0 ? `
175
- <div class="flight-devtools-section">
176
- <div class="flight-devtools-section-title">API Routes (${apiRoutes.length})</div>
177
- ${apiRoutes.map(route => renderRouteItem(route)).join('')}
178
- </div>
179
- ` : ''}
180
- </div>
166
+ return `
167
+ <div class="flight-devtools-toolbar">
168
+ <input
169
+ type="text"
170
+ placeholder="Filter routes..."
171
+ value="${panelState.routeFilter}"
172
+ data-action="filter-routes"
173
+ class="flight-devtools-search"
174
+ />
175
+ </div>
176
+ <div class="flight-devtools-list">
177
+ ${pageRoutes.length > 0 ? `
178
+ <div class="flight-devtools-section">
179
+ <div class="flight-devtools-section-title">Pages (${pageRoutes.length})</div>
180
+ ${pageRoutes.map(route => renderRouteItem(route)).join('')}
181
+ </div>
182
+ ` : ''}
183
+ ${apiRoutes.length > 0 ? `
184
+ <div class="flight-devtools-section">
185
+ <div class="flight-devtools-section-title">API Routes (${apiRoutes.length})</div>
186
+ ${apiRoutes.map(route => renderRouteItem(route)).join('')}
187
+ </div>
188
+ ` : ''}
189
+ </div>
181
190
  `;
182
191
  }
183
192
  function renderRouteItem(route) {
184
193
  const isExpanded = panelState.expandedItems.has(route.path);
185
194
  const hasParams = route.path.includes(':') || route.path.includes('[');
186
- return `
187
- <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${route.path}">
188
- <div class="flight-devtools-item-header">
189
- <span class="method ${route.method.toLowerCase()}">${route.method}</span>
190
- <span class="path">${route.path}</span>
191
- ${hasParams ? '<span class="badge dynamic">Dynamic</span>' : ''}
192
- ${route.slot ? `<span class="badge slot">@${route.slot}</span>` : ''}
193
- <span class="expand-icon">${isExpanded ? '▼' : '▶'}</span>
194
- </div>
195
- ${isExpanded ? `
196
- <div class="flight-devtools-item-details">
197
- <div class="detail-row">
198
- <span class="detail-label">File:</span>
199
- <span class="detail-value mono">${route.filePath}</span>
200
- </div>
201
- ${route.params ? `
202
- <div class="detail-row">
203
- <span class="detail-label">Params:</span>
204
- <span class="detail-value">${route.params.join(', ')}</span>
205
- </div>
206
- ` : ''}
207
- </div>
208
- ` : ''}
209
- </div>
195
+ return `
196
+ <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${route.path}">
197
+ <div class="flight-devtools-item-header">
198
+ <span class="method ${route.method.toLowerCase()}">${route.method}</span>
199
+ <span class="path">${route.path}</span>
200
+ ${hasParams ? '<span class="badge dynamic">Dynamic</span>' : ''}
201
+ ${route.slot ? `<span class="badge slot">@${route.slot}</span>` : ''}
202
+ <span class="expand-icon">${isExpanded ? '▼' : '▶'}</span>
203
+ </div>
204
+ ${isExpanded ? `
205
+ <div class="flight-devtools-item-details">
206
+ <div class="detail-row">
207
+ <span class="detail-label">File:</span>
208
+ <span class="detail-value mono">${route.filePath}</span>
209
+ </div>
210
+ ${route.params ? `
211
+ <div class="detail-row">
212
+ <span class="detail-label">Params:</span>
213
+ <span class="detail-value">${route.params.join(', ')}</span>
214
+ </div>
215
+ ` : ''}
216
+ </div>
217
+ ` : ''}
218
+ </div>
210
219
  `;
211
220
  }
212
221
  function renderRequests(requests) {
213
222
  if (requests.length === 0) {
214
223
  return '<div class="flight-devtools-empty">No requests captured yet<br/><small>Make some fetch requests to see them here</small></div>';
215
224
  }
216
- return `
217
- <div class="flight-devtools-toolbar">
218
- <span class="flight-devtools-info">${requests.length} requests</span>
219
- <button data-action="clear-requests" class="flight-devtools-btn-small">Clear</button>
220
- </div>
221
- <div class="flight-devtools-list">
222
- ${requests.slice(0, 50).map(req => renderRequestItem(req)).join('')}
223
- </div>
225
+ // Filter requests based on search
226
+ const filter = panelState.requestFilter.toLowerCase();
227
+ const filteredRequests = filter
228
+ ? requests.filter(r => r.path.toLowerCase().includes(filter) || r.method.toLowerCase().includes(filter))
229
+ : requests;
230
+ return `
231
+ <div class="flight-devtools-toolbar">
232
+ <input
233
+ type="text"
234
+ class="flight-devtools-search"
235
+ placeholder="Filter requests..."
236
+ value="${panelState.requestFilter}"
237
+ data-action="filter-requests"
238
+ />
239
+ </div>
240
+ <div class="flight-devtools-toolbar">
241
+ <span class="flight-devtools-info">${filteredRequests.length}/${requests.length} requests</span>
242
+ <div class="flight-devtools-btn-group">
243
+ <button data-action="export-requests" class="flight-devtools-btn-small">Export</button>
244
+ <button data-action="clear-requests" class="flight-devtools-btn-small">Clear</button>
245
+ </div>
246
+ </div>
247
+ <div class="flight-devtools-list">
248
+ ${filteredRequests.slice(0, 50).map(req => renderRequestItem(req)).join('')}
249
+ </div>
224
250
  `;
225
251
  }
226
252
  function renderRequestItem(req) {
227
253
  const isExpanded = panelState.expandedItems.has(req.id);
228
254
  const statusClass = `status-${Math.floor(req.status / 100)}xx`;
229
- return `
230
- <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${req.id}">
231
- <div class="flight-devtools-item-header">
232
- <span class="method ${req.method.toLowerCase()}">${req.method}</span>
233
- <span class="path">${req.path}</span>
234
- <span class="status ${statusClass}">${req.status}</span>
235
- <span class="duration">${formatDuration(req.duration)}</span>
236
- <span class="time">${formatRelativeTime(req.timestamp)}</span>
237
- </div>
238
- ${isExpanded ? `
239
- <div class="flight-devtools-item-details">
240
- <div class="detail-row">
241
- <span class="detail-label">Duration:</span>
242
- <span class="detail-value">${formatDuration(req.duration)}</span>
243
- </div>
244
- ${req.size !== undefined ? `
245
- <div class="detail-row">
246
- <span class="detail-label">Size:</span>
247
- <span class="detail-value">${formatBytes(req.size)}</span>
248
- </div>
249
- ` : ''}
250
- ${req.type ? `
251
- <div class="detail-row">
252
- <span class="detail-label">Type:</span>
253
- <span class="detail-value">${req.type}</span>
254
- </div>
255
- ` : ''}
256
- <div class="detail-row">
257
- <span class="detail-label">Time:</span>
258
- <span class="detail-value">${new Date(req.timestamp).toLocaleTimeString()}</span>
259
- </div>
260
- </div>
261
- ` : ''}
262
- </div>
255
+ return `
256
+ <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${req.id}">
257
+ <div class="flight-devtools-item-header">
258
+ <span class="method ${req.method.toLowerCase()}">${req.method}</span>
259
+ <span class="path">${req.path}</span>
260
+ <span class="status ${statusClass}">${req.status}</span>
261
+ <span class="duration">${formatDuration(req.duration)}</span>
262
+ <span class="time">${formatRelativeTime(req.timestamp)}</span>
263
+ </div>
264
+ ${isExpanded ? `
265
+ <div class="flight-devtools-item-details">
266
+ <div class="detail-row">
267
+ <span class="detail-label">Duration:</span>
268
+ <span class="detail-value">${formatDuration(req.duration)}</span>
269
+ </div>
270
+ ${req.size !== undefined ? `
271
+ <div class="detail-row">
272
+ <span class="detail-label">Size:</span>
273
+ <span class="detail-value">${formatBytes(req.size)}</span>
274
+ </div>
275
+ ` : ''}
276
+ ${req.type ? `
277
+ <div class="detail-row">
278
+ <span class="detail-label">Type:</span>
279
+ <span class="detail-value">${req.type}</span>
280
+ </div>
281
+ ` : ''}
282
+ <div class="detail-row">
283
+ <span class="detail-label">Time:</span>
284
+ <span class="detail-value">${new Date(req.timestamp).toLocaleTimeString()}</span>
285
+ </div>
286
+ </div>
287
+ ` : ''}
288
+ </div>
263
289
  `;
264
290
  }
265
291
  function renderAdapters(state) {
266
292
  if (state.adapters.length === 0) {
267
293
  return '<div class="flight-devtools-empty">No adapters registered</div>';
268
294
  }
269
- return `
270
- <div class="flight-devtools-list">
271
- ${state.adapters.map(adapter => `
272
- <div class="flight-devtools-item">
273
- <div class="flight-devtools-item-header">
274
- <span class="adapter-icon">${getAdapterIcon(adapter.type)}</span>
275
- <span class="adapter-name">${adapter.name}</span>
276
- <span class="adapter-type">${adapter.type}</span>
277
- <span class="adapter-status status-${adapter.status}">${adapter.status}</span>
278
- </div>
279
- </div>
280
- `).join('')}
281
- </div>
295
+ return `
296
+ <div class="flight-devtools-list">
297
+ ${state.adapters.map(adapter => `
298
+ <div class="flight-devtools-item">
299
+ <div class="flight-devtools-item-header">
300
+ <span class="adapter-icon">${getAdapterIcon(adapter.type)}</span>
301
+ <span class="adapter-name">${adapter.name}</span>
302
+ <span class="adapter-type">${adapter.type}</span>
303
+ <span class="adapter-status status-${adapter.status}">${adapter.status}</span>
304
+ </div>
305
+ </div>
306
+ `).join('')}
307
+ </div>
282
308
  `;
283
309
  }
284
310
  function getAdapterIcon(type) {
@@ -306,22 +332,22 @@ export function injectDevToolsPanel() {
306
332
  { label: 'FCP', value: perf.fcp, unit: 'ms', good: 1800, desc: 'First Contentful Paint' },
307
333
  { label: 'TTFB', value: perf.ttfb, unit: 'ms', good: 800, desc: 'Time to First Byte' },
308
334
  ].filter(m => m.value !== undefined);
309
- return `
310
- <div class="flight-devtools-list">
311
- <div class="flight-devtools-section-title">Core Web Vitals</div>
312
- <div class="flight-devtools-metrics">
335
+ return `
336
+ <div class="flight-devtools-list">
337
+ <div class="flight-devtools-section-title">Core Web Vitals</div>
338
+ <div class="flight-devtools-metrics">
313
339
  ${metrics.map(m => {
314
340
  const status = m.value <= m.good ? 'good' : m.value <= m.good * 2 ? 'needs-improvement' : 'poor';
315
- return `
316
- <div class="flight-devtools-metric ${status}">
317
- <div class="metric-label">${m.label}</div>
318
- <div class="metric-value">${m.unit ? formatDuration(m.value) : m.value.toFixed(3)}</div>
319
- <div class="metric-desc">${m.desc}</div>
320
- </div>
341
+ return `
342
+ <div class="flight-devtools-metric ${status}">
343
+ <div class="metric-label">${m.label}</div>
344
+ <div class="metric-value">${m.unit ? formatDuration(m.value) : m.value.toFixed(3)}</div>
345
+ <div class="metric-desc">${m.desc}</div>
346
+ </div>
321
347
  `;
322
- }).join('')}
323
- </div>
324
- </div>
348
+ }).join('')}
349
+ </div>
350
+ </div>
325
351
  `;
326
352
  }
327
353
  function renderConsole(state) {
@@ -332,30 +358,30 @@ export function injectDevToolsPanel() {
332
358
  const filtered = panelState.consoleFilter === 'all'
333
359
  ? entries
334
360
  : entries.filter(e => e.level === panelState.consoleFilter);
335
- return `
336
- <div class="flight-devtools-toolbar">
337
- <div class="flight-devtools-filter-group">
338
- ${['all', 'log', 'warn', 'error'].map(f => `
339
- <button
340
- class="flight-devtools-filter-btn ${panelState.consoleFilter === f ? 'active' : ''}"
341
- data-action="filter-console"
342
- data-filter="${f}"
343
- >${f}</button>
344
- `).join('')}
345
- </div>
346
- <button data-action="clear-console" class="flight-devtools-btn-small">Clear</button>
347
- </div>
348
- <div class="flight-devtools-list console-list">
361
+ return `
362
+ <div class="flight-devtools-toolbar">
363
+ <div class="flight-devtools-filter-group">
364
+ ${['all', 'log', 'warn', 'error'].map(f => `
365
+ <button
366
+ class="flight-devtools-filter-btn ${panelState.consoleFilter === f ? 'active' : ''}"
367
+ data-action="filter-console"
368
+ data-filter="${f}"
369
+ >${f}</button>
370
+ `).join('')}
371
+ </div>
372
+ <button data-action="clear-console" class="flight-devtools-btn-small">Clear</button>
373
+ </div>
374
+ <div class="flight-devtools-list console-list">
349
375
  ${filtered.length === 0
350
376
  ? '<div class="flight-devtools-empty">No console entries</div>'
351
- : filtered.slice(0, 100).map(entry => `
352
- <div class="flight-devtools-console-item level-${entry.level}">
353
- <span class="console-time">${formatRelativeTime(entry.timestamp)}</span>
354
- <span class="console-level">${entry.level}</span>
355
- <span class="console-message">${escapeHtml(entry.message)}</span>
356
- </div>
357
- `).join('')}
358
- </div>
377
+ : filtered.slice(0, 100).map(entry => `
378
+ <div class="flight-devtools-console-item level-${entry.level}">
379
+ <span class="console-time">${formatRelativeTime(entry.timestamp)}</span>
380
+ <span class="console-level">${entry.level}</span>
381
+ <span class="console-message">${escapeHtml(entry.message)}</span>
382
+ </div>
383
+ `).join('')}
384
+ </div>
359
385
  `;
360
386
  }
361
387
  function escapeHtml(text) {
@@ -387,6 +413,21 @@ export function injectDevToolsPanel() {
387
413
  case 'clear-requests':
388
414
  devTools.clearRequests();
389
415
  break;
416
+ case 'export-requests':
417
+ const exportData = {
418
+ timestamp: new Date().toISOString(),
419
+ requests: devTools.getState().requests,
420
+ console: devTools.getState().console,
421
+ errors: devTools.getState().errors,
422
+ };
423
+ const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
424
+ const url = URL.createObjectURL(blob);
425
+ const a = document.createElement('a');
426
+ a.href = url;
427
+ a.download = `flight-devtools-${Date.now()}.json`;
428
+ a.click();
429
+ URL.revokeObjectURL(url);
430
+ break;
390
431
  case 'clear-console':
391
432
  devTools.clearConsole();
392
433
  devTools.clearErrors();
@@ -406,6 +447,14 @@ export function injectDevToolsPanel() {
406
447
  render(devTools.getState());
407
448
  });
408
449
  }
450
+ // Request filter input
451
+ const requestFilterInput = container.querySelector('[data-action="filter-requests"]');
452
+ if (requestFilterInput) {
453
+ requestFilterInput.addEventListener('input', (e) => {
454
+ panelState.requestFilter = e.target.value;
455
+ render(devTools.getState());
456
+ });
457
+ }
409
458
  // Resize handlers
410
459
  const resizeHandles = container.querySelectorAll('[data-resize]');
411
460
  resizeHandles.forEach(handle => {
@@ -521,546 +570,551 @@ export function injectDevToolsPanel() {
521
570
  function getFlightLogo() {
522
571
  // Flight logo SVG - exact copy from favicon.svg with optimized viewBox for button display
523
572
  // Following SVGO best practices: proper viewBox, inlined transforms, appropriate sizing
524
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="40" height="40" fill="currentColor">
525
- <g transform="translate(0,800) scale(0.1,-0.1)">
526
- <path d="M 4056 6148 c31 -40 81 -114 110 -165 53 -89 119 -231 112 -239 -2 -2 -23 6 -45 17 -133 65 -342 65 -475 0 -26 -13 -38 -15 -38 -6 0 18 94 206 136 272 34 55 135 193 140 193 2 0 28 -33 60 -72z m90 -449 c34 -11 90 -39 125 -62 l64 -43 24 -99 c13 -55 25 -106 27 -112 3 -10 107 -13 480 -13 l476 0 -5 -22 c-3 -13 -18 -88 -32 -167 -14 -80 -28 -148 -31 -153 -3 -4 -182 -8 -399 -8 l-393 0 -27 -82 c-86 -263 -277 -536 -531 -758 -131 -114 -245 -201 -484 -370 -96 -67 -222 -159 -280 -204 -143 -110 -364 -332 -428 -429 -62 -94 -67 -84 -11 25 120 240 288 420 669 719 268 209 377 305 511 444 180 188 290 363 352 561 41 134 39 143 -11 44 -98 -195 -226 -366 -406 -545 -112 -111 -204 -191 -210 -184 -13 15 -39 379 -43 601 -6 283 3 419 37 588 33 162 33 160 102 207 134 91 274 111 424 62z m889 -1121 c-4 -24 -17 -101 -28 -173 l-22 -130 -305 -5 -305 -5 -18 -115 c-32 -215 -98 -615 -103 -632 -5 -17 -26 -18 -258 -18 l-253 0 -17 108 c-9 59 -16 116 -16 128 0 13 30 44 93 94 256 206 529 539 612 748 l17 42 305 0 304 0 -6 -42z m-1506 -283 l13 -134 -32 -27 c-78 -66 -261 -204 -270 -204 -10 0 -8 55 6 165 7 64 60 143 161 243 52 50 97 92 102 92 4 0 13 -61 20 -135z m105 -676 l7 -56 -113 -71 c-218 -136 -405 -290 -515 -423 -110 -133 -108 -132 -77 -69 69 142 178 280 330 414 118 106 346 278 355 268 4 -4 10 -32 13 -63z m558 -251 c12 -21 20 -40 17 -42 -6 -6 -413 -10 -419 -5 -3 3 4 24 14 47 l19 42 174 -2 174 -3 21 -37z m-330 -190 c-24 -46 -27 -64 -26 -140 0 -48 -3 -88 -7 -88 -19 0 -41 92 -37 151 4 56 8 65 41 95 52 48 60 42 29 -18z m188 22 c41 -44 48 -104 22 -183 -22 -66 -72 -180 -77 -175 -2 2 -21 46 -44 99 -58 135 -57 223 3 271 32 26 64 22 96 -12z m85 18 c38 -17 68 -73 69 -127 1 -44 -28 -141 -42 -141 -4 0 -7 40 -7 88 0 76 -4 95 -27 140 -15 29 -25 52 -23 52 2 0 16 -5 30 -12z m243 -267 c-10 -17 -12 -17 -27 -2 -15 14 -15 17 -1 28 22 17 43 -3 28 -26z m-248 -158 c20 -84 53 -177 97 -275 25 -57 27 -58 65 -58 42 0 98 -26 98 -46 0 -16 -51 -55 -170 -134 -242 -158 -507 -256 -730 -267 -124 -6 -167 4 -190 46 -26 50 0 131 65 205 44 50 44 43 0 -30 -47 -79 -50 -134 -9 -171 40 -36 60 -30 67 18 6 42 44 94 78 104 13 4 17 17 17 55 0 99 68 170 165 170 l44 0 31 73 c40 95 86 236 102 312 7 33 17 64 21 69 10 12 42 -59 84 -188 l33 -99 17 59 c22 77 87 246 92 240 2 -2 12 -39 23 -83z m456 27 c161 -71 188 -222 73 -416 -47 -81 -215 -247 -324 -321 -170 -115 -370 -202 -577 -253 -148 -36 -205 -38 -93 -4 384 120 667 287 867 513 160 181 181 361 51 438 -55 32 -138 52 -218 54 -45 0 -55 3 -40 10 36 17 207 3 261 -21z m-253 -111 c3 -11 15 -23 26 -26 12 -3 21 -9 21 -13 0 -4 -9 -10 -21 -13 -11 -3 -23 -15 -26 -26 -7 -27 -19 -27 -26 -1 -3 11 -15 24 -27 28 l-22 8 26 17 c14 10 26 24 26 32 0 22 17 18 23 -6z m231 -18 c3 -5 0 -14 -8 -20 -10 -8 -16 -8 -25 3 -9 11 -9 15 1 19 20 8 26 8 32 -2z m-876 -99 c-17 -20 -40 5 -26 28 11 17 13 17 24 2 9 -12 9 -21 2 -30z m-145 -102 c22 -8 22 -8 -4 -18 -15 -6 -29 -21 -33 -34 -5 -17 -9 -19 -13 -8 -3 8 -16 25 -30 36 -25 22 -25 22 -4 29 11 3 24 18 27 32 l7 26 14 -27 c8 -15 24 -31 36 -36z m953 -67 c-124 -160 -309 -303 -529 -408 -356 -171 -686 -202 -767 -73 -28 43 -22 132 10 181 12 17 19 23 15 12 -4 -11 -10 -43 -12 -71 -5 -46 -2 -54 27 -87 37 -43 79 -57 173 -57 295 0 769 215 1027 467 52 51 96 93 98 93 2 0 -17 -26 -42 -57z m-1162 -2 c3 -5 -1 -14 -8 -20 -16 -13 -32 2 -21 19 8 12 22 13 29 1z m1332 -182 c10 -17 -13 -36 -27 -22 -12 12 -4 33 11 33 5 0 12 -5 16 -11z m-298 -239 c7 -11 20 -20 30 -20 12 -1 9 -5 -10 -16 -16 -8 -28 -21 -28 -29 0 -22 -17 -18 -23 5 -3 11 -12 20 -21 20 -23 0 -20 17 4 23 11 3 20 12 20 21 0 22 13 20 28 -4z m-164 -108 c2 -4 1 -14 -4 -22 -7 -11 -12 -12 -21 -3 -6 6 -8 16 -5 22 8 13 23 14 30 3z"/>
527
- <path d="M3938 5509 c-57 -16 -114 -71 -133 -130 -29 -86 -4 -168 67 -222 136 -103 328 -10 328 160 0 132 -131 228 -262 192z"/>
528
- <path d="M3923 2465 c-10 -10 -9 -16 1 -24 10 -8 16 -8 25 3 16 19 -9 40 -26 21z"/>
529
- <path d="M4110 2440 l-25 -27 27 -27 c25 -24 27 -25 33 -7 3 10 16 22 28 25 l22 7 -23 9 c-12 5 -26 18 -30 29 -7 18 -9 17 -32 -9z"/>
530
- <path d="M3851 2307 c-9 -11 -8 -17 3 -26 11 -9 15 -8 20 5 9 23 -8 38 -23 21z"/>
531
- <path d="M3665 2266 c-5 -18 -18 -33 -39 -41 l-31 -13 28 -7 c18 -5 33 -18 41 -39 l13 -31 8 29 c5 18 18 33 39 41 l31 13 -28 7 c-18 5 -33 18 -41 39 l-13 31 -8 -29z"/>
532
- </g>
573
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="40" height="40" fill="currentColor">
574
+ <g transform="translate(0,800) scale(0.1,-0.1)">
575
+ <path d="M 4056 6148 c31 -40 81 -114 110 -165 53 -89 119 -231 112 -239 -2 -2 -23 6 -45 17 -133 65 -342 65 -475 0 -26 -13 -38 -15 -38 -6 0 18 94 206 136 272 34 55 135 193 140 193 2 0 28 -33 60 -72z m90 -449 c34 -11 90 -39 125 -62 l64 -43 24 -99 c13 -55 25 -106 27 -112 3 -10 107 -13 480 -13 l476 0 -5 -22 c-3 -13 -18 -88 -32 -167 -14 -80 -28 -148 -31 -153 -3 -4 -182 -8 -399 -8 l-393 0 -27 -82 c-86 -263 -277 -536 -531 -758 -131 -114 -245 -201 -484 -370 -96 -67 -222 -159 -280 -204 -143 -110 -364 -332 -428 -429 -62 -94 -67 -84 -11 25 120 240 288 420 669 719 268 209 377 305 511 444 180 188 290 363 352 561 41 134 39 143 -11 44 -98 -195 -226 -366 -406 -545 -112 -111 -204 -191 -210 -184 -13 15 -39 379 -43 601 -6 283 3 419 37 588 33 162 33 160 102 207 134 91 274 111 424 62z m889 -1121 c-4 -24 -17 -101 -28 -173 l-22 -130 -305 -5 -305 -5 -18 -115 c-32 -215 -98 -615 -103 -632 -5 -17 -26 -18 -258 -18 l-253 0 -17 108 c-9 59 -16 116 -16 128 0 13 30 44 93 94 256 206 529 539 612 748 l17 42 305 0 304 0 -6 -42z m-1506 -283 l13 -134 -32 -27 c-78 -66 -261 -204 -270 -204 -10 0 -8 55 6 165 7 64 60 143 161 243 52 50 97 92 102 92 4 0 13 -61 20 -135z m105 -676 l7 -56 -113 -71 c-218 -136 -405 -290 -515 -423 -110 -133 -108 -132 -77 -69 69 142 178 280 330 414 118 106 346 278 355 268 4 -4 10 -32 13 -63z m558 -251 c12 -21 20 -40 17 -42 -6 -6 -413 -10 -419 -5 -3 3 4 24 14 47 l19 42 174 -2 174 -3 21 -37z m-330 -190 c-24 -46 -27 -64 -26 -140 0 -48 -3 -88 -7 -88 -19 0 -41 92 -37 151 4 56 8 65 41 95 52 48 60 42 29 -18z m188 22 c41 -44 48 -104 22 -183 -22 -66 -72 -180 -77 -175 -2 2 -21 46 -44 99 -58 135 -57 223 3 271 32 26 64 22 96 -12z m85 18 c38 -17 68 -73 69 -127 1 -44 -28 -141 -42 -141 -4 0 -7 40 -7 88 0 76 -4 95 -27 140 -15 29 -25 52 -23 52 2 0 16 -5 30 -12z m243 -267 c-10 -17 -12 -17 -27 -2 -15 14 -15 17 -1 28 22 17 43 -3 28 -26z m-248 -158 c20 -84 53 -177 97 -275 25 -57 27 -58 65 -58 42 0 98 -26 98 -46 0 -16 -51 -55 -170 -134 -242 -158 -507 -256 -730 -267 -124 -6 -167 4 -190 46 -26 50 0 131 65 205 44 50 44 43 0 -30 -47 -79 -50 -134 -9 -171 40 -36 60 -30 67 18 6 42 44 94 78 104 13 4 17 17 17 55 0 99 68 170 165 170 l44 0 31 73 c40 95 86 236 102 312 7 33 17 64 21 69 10 12 42 -59 84 -188 l33 -99 17 59 c22 77 87 246 92 240 2 -2 12 -39 23 -83z m456 27 c161 -71 188 -222 73 -416 -47 -81 -215 -247 -324 -321 -170 -115 -370 -202 -577 -253 -148 -36 -205 -38 -93 -4 384 120 667 287 867 513 160 181 181 361 51 438 -55 32 -138 52 -218 54 -45 0 -55 3 -40 10 36 17 207 3 261 -21z m-253 -111 c3 -11 15 -23 26 -26 12 -3 21 -9 21 -13 0 -4 -9 -10 -21 -13 -11 -3 -23 -15 -26 -26 -7 -27 -19 -27 -26 -1 -3 11 -15 24 -27 28 l-22 8 26 17 c14 10 26 24 26 32 0 22 17 18 23 -6z m231 -18 c3 -5 0 -14 -8 -20 -10 -8 -16 -8 -25 3 -9 11 -9 15 1 19 20 8 26 8 32 -2z m-876 -99 c-17 -20 -40 5 -26 28 11 17 13 17 24 2 9 -12 9 -21 2 -30z m-145 -102 c22 -8 22 -8 -4 -18 -15 -6 -29 -21 -33 -34 -5 -17 -9 -19 -13 -8 -3 8 -16 25 -30 36 -25 22 -25 22 -4 29 11 3 24 18 27 32 l7 26 14 -27 c8 -15 24 -31 36 -36z m953 -67 c-124 -160 -309 -303 -529 -408 -356 -171 -686 -202 -767 -73 -28 43 -22 132 10 181 12 17 19 23 15 12 -4 -11 -10 -43 -12 -71 -5 -46 -2 -54 27 -87 37 -43 79 -57 173 -57 295 0 769 215 1027 467 52 51 96 93 98 93 2 0 -17 -26 -42 -57z m-1162 -2 c3 -5 -1 -14 -8 -20 -16 -13 -32 2 -21 19 8 12 22 13 29 1z m1332 -182 c10 -17 -13 -36 -27 -22 -12 12 -4 33 11 33 5 0 12 -5 16 -11z m-298 -239 c7 -11 20 -20 30 -20 12 -1 9 -5 -10 -16 -16 -8 -28 -21 -28 -29 0 -22 -17 -18 -23 5 -3 11 -12 20 -21 20 -23 0 -20 17 4 23 11 3 20 12 20 21 0 22 13 20 28 -4z m-164 -108 c2 -4 1 -14 -4 -22 -7 -11 -12 -12 -21 -3 -6 6 -8 16 -5 22 8 13 23 14 30 3z"/>
576
+ <path d="M3938 5509 c-57 -16 -114 -71 -133 -130 -29 -86 -4 -168 67 -222 136 -103 328 -10 328 160 0 132 -131 228 -262 192z"/>
577
+ <path d="M3923 2465 c-10 -10 -9 -16 1 -24 10 -8 16 -8 25 3 16 19 -9 40 -26 21z"/>
578
+ <path d="M4110 2440 l-25 -27 27 -27 c25 -24 27 -25 33 -7 3 10 16 22 28 25 l22 7 -23 9 c-12 5 -26 18 -30 29 -7 18 -9 17 -32 -9z"/>
579
+ <path d="M3851 2307 c-9 -11 -8 -17 3 -26 11 -9 15 -8 20 5 9 23 -8 38 -23 21z"/>
580
+ <path d="M3665 2266 c-5 -18 -18 -33 -39 -41 l-31 -13 28 -7 c18 -5 33 -18 41 -39 l13 -31 8 29 c5 18 18 33 39 41 l31 13 -28 7 c-18 5 -33 18 -41 39 l-13 31 -8 -29z"/>
581
+ </g>
533
582
  </svg>`;
534
583
  }
535
584
  function getFlightLogoSmall() {
536
585
  // Small Flight logo for the header (16x16)
537
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="16" height="16" fill="currentColor" style="vertical-align:middle;margin-right:6px;">
538
- <g transform="translate(0,800) scale(0.1,-0.1)">
539
- <path d="M 4056 6148 c31 -40 81 -114 110 -165 53 -89 119 -231 112 -239 -2 -2 -23 6 -45 17 -133 65 -342 65 -475 0 -26 -13 -38 -15 -38 -6 0 18 94 206 136 272 34 55 135 193 140 193 2 0 28 -33 60 -72z m90 -449 c34 -11 90 -39 125 -62 l64 -43 24 -99 c13 -55 25 -106 27 -112 3 -10 107 -13 480 -13 l476 0 -5 -22 c-3 -13 -18 -88 -32 -167 -14 -80 -28 -148 -31 -153 -3 -4 -182 -8 -399 -8 l-393 0 -27 -82 c-86 -263 -277 -536 -531 -758 -131 -114 -245 -201 -484 -370 -96 -67 -222 -159 -280 -204 -143 -110 -364 -332 -428 -429 -62 -94 -67 -84 -11 25 120 240 288 420 669 719 268 209 377 305 511 444 180 188 290 363 352 561 41 134 39 143 -11 44 -98 -195 -226 -366 -406 -545 -112 -111 -204 -191 -210 -184 -13 15 -39 379 -43 601 -6 283 3 419 37 588 33 162 33 160 102 207 134 91 274 111 424 62z"/>
540
- <path d="M3938 5509 c-57 -16 -114 -71 -133 -130 -29 -86 -4 -168 67 -222 136 -103 328 -10 328 160 0 132 -131 228 -262 192z"/>
541
- </g>
586
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="16" height="16" fill="currentColor" style="vertical-align:middle;margin-right:6px;">
587
+ <g transform="translate(0,800) scale(0.1,-0.1)">
588
+ <path d="M 4056 6148 c31 -40 81 -114 110 -165 53 -89 119 -231 112 -239 -2 -2 -23 6 -45 17 -133 65 -342 65 -475 0 -26 -13 -38 -15 -38 -6 0 18 94 206 136 272 34 55 135 193 140 193 2 0 28 -33 60 -72z m90 -449 c34 -11 90 -39 125 -62 l64 -43 24 -99 c13 -55 25 -106 27 -112 3 -10 107 -13 480 -13 l476 0 -5 -22 c-3 -13 -18 -88 -32 -167 -14 -80 -28 -148 -31 -153 -3 -4 -182 -8 -399 -8 l-393 0 -27 -82 c-86 -263 -277 -536 -531 -758 -131 -114 -245 -201 -484 -370 -96 -67 -222 -159 -280 -204 -143 -110 -364 -332 -428 -429 -62 -94 -67 -84 -11 25 120 240 288 420 669 719 268 209 377 305 511 444 180 188 290 363 352 561 41 134 39 143 -11 44 -98 -195 -226 -366 -406 -545 -112 -111 -204 -191 -210 -184 -13 15 -39 379 -43 601 -6 283 3 419 37 588 33 162 33 160 102 207 134 91 274 111 424 62z"/>
589
+ <path d="M3938 5509 c-57 -16 -114 -71 -133 -130 -29 -86 -4 -168 67 -222 136 -103 328 -10 328 160 0 132 -131 228 -262 192z"/>
590
+ </g>
542
591
  </svg>`;
543
592
  }
544
593
  // ============================================================================
545
594
  // Styles
546
595
  // ============================================================================
547
596
  function getDevToolsStyles() {
548
- return `
549
- #flight-devtools {
550
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
551
- font-size: 12px;
552
- line-height: 1.4;
553
- --green: #22c55e;
554
- --green-dark: #16a34a;
555
- --bg: #0f0f0f;
556
- --bg-2: #1a1a1a;
557
- --bg-3: #252525;
558
- --border: #333;
559
- --text: #e5e5e5;
560
- --text-muted: #888;
561
- }
562
-
563
- .flight-devtools {
564
- position: fixed;
565
- z-index: 999999;
566
- }
567
-
568
- .flight-devtools.bottom-right { bottom: 16px; right: 16px; }
569
- .flight-devtools.bottom-left { bottom: 16px; left: 16px; }
570
- .flight-devtools.top-right { top: 16px; right: 16px; }
571
- .flight-devtools.top-left { top: 16px; left: 16px; }
572
-
573
- .flight-devtools-toggle {
574
- width: 48px;
575
- height: 48px;
576
- border-radius: 50%;
577
- border: none;
578
- background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
579
- color: white;
580
- font-size: 20px;
581
- cursor: pointer;
582
- box-shadow: 0 4px 20px rgba(34, 197, 94, 0.4);
583
- transition: transform 0.2s, box-shadow 0.2s;
584
- display: flex;
585
- align-items: center;
586
- justify-content: center;
587
- padding: 0;
588
- }
589
-
590
- .flight-devtools-toggle:hover {
591
- transform: scale(1.05);
592
- box-shadow: 0 6px 24px rgba(34, 197, 94, 0.5);
593
- }
594
-
595
- .flight-devtools.open .flight-devtools-toggle {
596
- position: absolute;
597
- top: 8px;
598
- right: 8px;
599
- width: 28px;
600
- height: 28px;
601
- font-size: 14px;
602
- background: rgba(0,0,0,0.3);
603
- box-shadow: none;
604
- z-index: 10;
605
- }
606
-
607
- .flight-devtools-badge {
608
- position: absolute;
609
- top: -4px;
610
- right: -4px;
611
- width: 18px;
612
- height: 18px;
613
- border-radius: 50%;
614
- background: #ef4444;
615
- color: white;
616
- font-size: 10px;
617
- font-weight: 600;
618
- display: flex;
619
- align-items: center;
620
- justify-content: center;
621
- }
622
-
623
- .flight-devtools-panel {
624
- position: absolute;
625
- bottom: 0;
626
- right: 0;
627
- width: 480px;
628
- height: 500px;
629
- max-height: 80vh;
630
- background: var(--bg);
631
- border: 1px solid var(--border);
632
- border-radius: 12px;
633
- overflow: hidden;
634
- box-shadow: 0 8px 40px rgba(0,0,0,0.5);
635
- display: flex;
636
- flex-direction: column;
637
- }
638
-
639
- .flight-devtools-resize-handle-top {
640
- position: absolute;
641
- top: 0;
642
- left: 20px;
643
- right: 20px;
644
- height: 6px;
645
- cursor: ns-resize;
646
- z-index: 10;
647
- }
648
-
649
- .flight-devtools-resize-handle-left {
650
- position: absolute;
651
- left: 0;
652
- top: 20px;
653
- bottom: 20px;
654
- width: 6px;
655
- cursor: ew-resize;
656
- z-index: 10;
657
- }
658
-
659
- .flight-devtools-resize-handle-corner {
660
- position: absolute;
661
- top: 0;
662
- left: 0;
663
- width: 16px;
664
- height: 16px;
665
- cursor: nwse-resize;
666
- z-index: 11;
667
- }
668
-
669
- .flight-devtools-header {
670
- padding: 12px 16px;
671
- background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
672
- color: white;
673
- display: flex;
674
- align-items: center;
675
- justify-content: space-between;
676
- gap: 12px;
677
- cursor: move;
678
- user-select: none;
679
- }
680
-
681
- .flight-devtools-header-title {
682
- display: flex;
683
- align-items: center;
684
- gap: 8px;
685
- }
686
-
687
- .flight-devtools-logo {
688
- font-weight: 600;
689
- font-size: 14px;
690
- }
691
-
692
- .flight-devtools-version {
693
- font-size: 10px;
694
- opacity: 0.7;
695
- background: rgba(0,0,0,0.15);
696
- padding: 2px 6px;
697
- border-radius: 4px;
698
- }
699
-
700
- .flight-devtools-close {
701
- width: 28px;
702
- height: 28px;
703
- border: none;
704
- background: rgba(0,0,0,0.2);
705
- color: white;
706
- border-radius: 6px;
707
- cursor: pointer;
708
- font-size: 16px;
709
- line-height: 1;
710
- display: flex;
711
- align-items: center;
712
- justify-content: center;
713
- transition: background 0.2s;
714
- flex-shrink: 0;
715
- }
716
-
717
- .flight-devtools-close:hover {
718
- background: rgba(0,0,0,0.4);
719
- }
720
-
721
- .flight-devtools-tabs {
722
- display: flex;
723
- background: var(--bg-2);
724
- border-bottom: 1px solid var(--border);
725
- }
726
-
727
- .flight-devtools-tabs button {
728
- flex: 1;
729
- padding: 10px 8px;
730
- border: none;
731
- background: transparent;
732
- color: var(--text-muted);
733
- font-size: 11px;
734
- cursor: pointer;
735
- border-bottom: 2px solid transparent;
736
- transition: all 0.2s;
737
- }
738
-
739
- .flight-devtools-tabs button:hover {
740
- color: var(--text);
741
- background: var(--bg-3);
742
- }
743
-
744
- .flight-devtools-tabs button.active {
745
- color: var(--green);
746
- border-bottom-color: var(--green);
747
- }
748
-
749
- .flight-devtools-tabs .count {
750
- display: inline-block;
751
- min-width: 18px;
752
- padding: 1px 5px;
753
- margin-left: 4px;
754
- background: var(--bg-3);
755
- border-radius: 10px;
756
- font-size: 10px;
757
- }
758
-
759
- .flight-devtools-content {
760
- flex: 1;
761
- overflow-y: auto;
762
- min-height: 0;
763
- }
764
-
765
- .flight-devtools-toolbar {
766
- display: flex;
767
- align-items: center;
768
- justify-content: space-between;
769
- padding: 8px 12px;
770
- background: var(--bg-2);
771
- border-bottom: 1px solid var(--border);
772
- }
773
-
774
- .flight-devtools-search {
775
- flex: 1;
776
- padding: 6px 10px;
777
- background: var(--bg-3);
778
- border: 1px solid var(--border);
779
- border-radius: 6px;
780
- color: var(--text);
781
- font-size: 12px;
782
- }
783
-
784
- .flight-devtools-search:focus {
785
- outline: none;
786
- border-color: var(--green);
787
- }
788
-
789
- .flight-devtools-btn-small {
790
- padding: 4px 10px;
791
- background: var(--bg-3);
792
- border: 1px solid var(--border);
793
- border-radius: 4px;
794
- color: var(--text-muted);
795
- font-size: 11px;
796
- cursor: pointer;
797
- }
798
-
799
- .flight-devtools-btn-small:hover {
800
- background: var(--border);
801
- color: var(--text);
802
- }
803
-
804
- .flight-devtools-info {
805
- color: var(--text-muted);
806
- font-size: 11px;
807
- }
808
-
809
- .flight-devtools-list {
810
- padding: 8px;
811
- }
812
-
813
- .flight-devtools-section {
814
- margin-bottom: 16px;
815
- }
816
-
817
- .flight-devtools-section-title {
818
- padding: 8px 8px 4px;
819
- color: var(--text-muted);
820
- font-size: 10px;
821
- font-weight: 600;
822
- text-transform: uppercase;
823
- letter-spacing: 0.5px;
824
- }
825
-
826
- .flight-devtools-item {
827
- padding: 8px 10px;
828
- border-radius: 6px;
829
- cursor: pointer;
830
- transition: background 0.15s;
831
- }
832
-
833
- .flight-devtools-item:hover {
834
- background: var(--bg-2);
835
- }
836
-
837
- .flight-devtools-item.expanded {
838
- background: var(--bg-2);
839
- }
840
-
841
- .flight-devtools-item-header {
842
- display: flex;
843
- align-items: center;
844
- gap: 8px;
845
- }
846
-
847
- .flight-devtools-item-details {
848
- margin-top: 8px;
849
- padding: 8px;
850
- background: var(--bg-3);
851
- border-radius: 4px;
852
- }
853
-
854
- .detail-row {
855
- display: flex;
856
- padding: 3px 0;
857
- }
858
-
859
- .detail-label {
860
- width: 80px;
861
- color: var(--text-muted);
862
- }
863
-
864
- .detail-value {
865
- flex: 1;
866
- color: var(--text);
867
- }
868
-
869
- .detail-value.mono {
870
- font-family: 'SF Mono', Monaco, monospace;
871
- font-size: 11px;
872
- }
873
-
874
- .method {
875
- font-weight: 600;
876
- font-size: 10px;
877
- padding: 2px 6px;
878
- border-radius: 3px;
879
- background: var(--bg-3);
880
- }
881
-
882
- .method.get { color: #22c55e; }
883
- .method.post { color: #3b82f6; }
884
- .method.put { color: #f59e0b; }
885
- .method.patch { color: #f59e0b; }
886
- .method.delete { color: #ef4444; }
887
-
888
- .path {
889
- flex: 1;
890
- font-family: 'SF Mono', Monaco, monospace;
891
- font-size: 11px;
892
- color: var(--text);
893
- overflow: hidden;
894
- text-overflow: ellipsis;
895
- white-space: nowrap;
896
- }
897
-
898
- .badge {
899
- font-size: 9px;
900
- padding: 2px 5px;
901
- border-radius: 3px;
902
- background: var(--bg-3);
903
- color: var(--text-muted);
904
- }
905
-
906
- .badge.dynamic { color: #a78bfa; }
907
- .badge.slot { color: #60a5fa; }
908
-
909
- .expand-icon {
910
- color: var(--text-muted);
911
- font-size: 8px;
912
- }
913
-
914
- .status { font-weight: 600; }
915
- .status.status-2xx { color: #22c55e; }
916
- .status.status-3xx { color: #f59e0b; }
917
- .status.status-4xx { color: #ef4444; }
918
- .status.status-5xx { color: #a855f7; }
919
-
920
- .duration, .time {
921
- color: var(--text-muted);
922
- font-size: 11px;
923
- }
924
-
925
- .adapter-icon {
926
- font-size: 16px;
927
- }
928
-
929
- .adapter-name {
930
- flex: 1;
931
- font-weight: 500;
932
- }
933
-
934
- .adapter-type {
935
- color: var(--text-muted);
936
- font-size: 10px;
937
- }
938
-
939
- .adapter-status {
940
- font-size: 10px;
941
- padding: 2px 6px;
942
- border-radius: 3px;
943
- }
944
-
945
- .adapter-status.status-connected { background: rgba(34, 197, 94, 0.2); color: #22c55e; }
946
- .adapter-status.status-disconnected { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
947
- .adapter-status.status-error { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
948
-
949
- .flight-devtools-metrics {
950
- display: grid;
951
- grid-template-columns: repeat(3, 1fr);
952
- gap: 8px;
953
- padding: 8px;
954
- }
955
-
956
- .flight-devtools-metric {
957
- padding: 12px;
958
- background: var(--bg-2);
959
- border-radius: 8px;
960
- text-align: center;
961
- }
962
-
963
- .flight-devtools-metric.good { border-left: 3px solid #22c55e; }
964
- .flight-devtools-metric.needs-improvement { border-left: 3px solid #f59e0b; }
965
- .flight-devtools-metric.poor { border-left: 3px solid #ef4444; }
966
-
967
- .metric-label {
968
- font-weight: 600;
969
- font-size: 11px;
970
- color: var(--text);
971
- }
972
-
973
- .metric-value {
974
- font-size: 18px;
975
- font-weight: 600;
976
- margin: 4px 0;
977
- }
978
-
979
- .flight-devtools-metric.good .metric-value { color: #22c55e; }
980
- .flight-devtools-metric.needs-improvement .metric-value { color: #f59e0b; }
981
- .flight-devtools-metric.poor .metric-value { color: #ef4444; }
982
-
983
- .metric-desc {
984
- font-size: 9px;
985
- color: var(--text-muted);
986
- }
987
-
988
- .flight-devtools-filter-group {
989
- display: flex;
990
- gap: 4px;
991
- }
992
-
993
- .flight-devtools-filter-btn {
994
- padding: 4px 8px;
995
- background: transparent;
996
- border: 1px solid var(--border);
997
- border-radius: 4px;
998
- color: var(--text-muted);
999
- font-size: 10px;
1000
- cursor: pointer;
1001
- text-transform: capitalize;
1002
- }
1003
-
1004
- .flight-devtools-filter-btn.active {
1005
- background: var(--green);
1006
- border-color: var(--green);
1007
- color: white;
1008
- }
1009
-
1010
- .console-list {
1011
- font-family: 'SF Mono', Monaco, monospace;
1012
- font-size: 11px;
1013
- }
1014
-
1015
- .flight-devtools-console-item {
1016
- display: flex;
1017
- gap: 8px;
1018
- padding: 6px 8px;
1019
- border-bottom: 1px solid var(--border);
1020
- }
1021
-
1022
- .flight-devtools-console-item.level-error {
1023
- background: rgba(239, 68, 68, 0.1);
1024
- }
1025
-
1026
- .flight-devtools-console-item.level-warn {
1027
- background: rgba(245, 158, 11, 0.1);
1028
- }
1029
-
1030
- .console-time {
1031
- color: var(--text-muted);
1032
- flex-shrink: 0;
1033
- }
1034
-
1035
- .console-level {
1036
- flex-shrink: 0;
1037
- width: 40px;
1038
- font-weight: 600;
1039
- }
1040
-
1041
- .level-log .console-level { color: #888; }
1042
- .level-info .console-level { color: #3b82f6; }
1043
- .level-warn .console-level { color: #f59e0b; }
1044
- .level-error .console-level { color: #ef4444; }
1045
-
1046
- .console-message {
1047
- flex: 1;
1048
- word-break: break-word;
1049
- color: var(--text);
1050
- }
1051
-
1052
- .flight-devtools-empty {
1053
- padding: 40px 20px;
1054
- text-align: center;
1055
- color: var(--text-muted);
1056
- }
1057
-
1058
- .flight-devtools-empty small {
1059
- display: block;
1060
- margin-top: 8px;
1061
- font-size: 11px;
1062
- opacity: 0.7;
1063
- }
597
+ return `
598
+ #flight-devtools {
599
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
600
+ font-size: 12px;
601
+ line-height: 1.4;
602
+ --green: #22c55e;
603
+ --green-dark: #16a34a;
604
+ --bg: #0f0f0f;
605
+ --bg-2: #1a1a1a;
606
+ --bg-3: #252525;
607
+ --border: #333;
608
+ --text: #e5e5e5;
609
+ --text-muted: #888;
610
+ }
611
+
612
+ .flight-devtools {
613
+ position: fixed;
614
+ z-index: 999999;
615
+ }
616
+
617
+ .flight-devtools.bottom-right { bottom: 16px; right: 16px; }
618
+ .flight-devtools.bottom-left { bottom: 16px; left: 16px; }
619
+ .flight-devtools.top-right { top: 16px; right: 16px; }
620
+ .flight-devtools.top-left { top: 16px; left: 16px; }
621
+
622
+ .flight-devtools-toggle {
623
+ width: 48px;
624
+ height: 48px;
625
+ border-radius: 50%;
626
+ border: none;
627
+ background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
628
+ color: white;
629
+ font-size: 20px;
630
+ cursor: pointer;
631
+ box-shadow: 0 4px 20px rgba(34, 197, 94, 0.4);
632
+ transition: transform 0.2s, box-shadow 0.2s;
633
+ display: flex;
634
+ align-items: center;
635
+ justify-content: center;
636
+ padding: 0;
637
+ }
638
+
639
+ .flight-devtools-toggle:hover {
640
+ transform: scale(1.05);
641
+ box-shadow: 0 6px 24px rgba(34, 197, 94, 0.5);
642
+ }
643
+
644
+ .flight-devtools.open .flight-devtools-toggle {
645
+ position: absolute;
646
+ top: 8px;
647
+ right: 8px;
648
+ width: 28px;
649
+ height: 28px;
650
+ font-size: 14px;
651
+ background: rgba(0,0,0,0.3);
652
+ box-shadow: none;
653
+ z-index: 10;
654
+ }
655
+
656
+ .flight-devtools-badge {
657
+ position: absolute;
658
+ top: -4px;
659
+ right: -4px;
660
+ width: 18px;
661
+ height: 18px;
662
+ border-radius: 50%;
663
+ background: #ef4444;
664
+ color: white;
665
+ font-size: 10px;
666
+ font-weight: 600;
667
+ display: flex;
668
+ align-items: center;
669
+ justify-content: center;
670
+ }
671
+
672
+ .flight-devtools-panel {
673
+ position: absolute;
674
+ bottom: 0;
675
+ right: 0;
676
+ width: 480px;
677
+ height: 500px;
678
+ max-height: 80vh;
679
+ background: var(--bg);
680
+ border: 1px solid var(--border);
681
+ border-radius: 12px;
682
+ overflow: hidden;
683
+ box-shadow: 0 8px 40px rgba(0,0,0,0.5);
684
+ display: flex;
685
+ flex-direction: column;
686
+ }
687
+
688
+ .flight-devtools-resize-handle-top {
689
+ position: absolute;
690
+ top: 0;
691
+ left: 20px;
692
+ right: 20px;
693
+ height: 6px;
694
+ cursor: ns-resize;
695
+ z-index: 10;
696
+ }
697
+
698
+ .flight-devtools-resize-handle-left {
699
+ position: absolute;
700
+ left: 0;
701
+ top: 20px;
702
+ bottom: 20px;
703
+ width: 6px;
704
+ cursor: ew-resize;
705
+ z-index: 10;
706
+ }
707
+
708
+ .flight-devtools-resize-handle-corner {
709
+ position: absolute;
710
+ top: 0;
711
+ left: 0;
712
+ width: 16px;
713
+ height: 16px;
714
+ cursor: nwse-resize;
715
+ z-index: 11;
716
+ }
717
+
718
+ .flight-devtools-header {
719
+ padding: 12px 16px;
720
+ background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
721
+ color: white;
722
+ display: flex;
723
+ align-items: center;
724
+ justify-content: space-between;
725
+ gap: 12px;
726
+ cursor: move;
727
+ user-select: none;
728
+ }
729
+
730
+ .flight-devtools-header-title {
731
+ display: flex;
732
+ align-items: center;
733
+ gap: 8px;
734
+ }
735
+
736
+ .flight-devtools-logo {
737
+ font-weight: 600;
738
+ font-size: 14px;
739
+ }
740
+
741
+ .flight-devtools-version {
742
+ font-size: 10px;
743
+ opacity: 0.7;
744
+ background: rgba(0,0,0,0.15);
745
+ padding: 2px 6px;
746
+ border-radius: 4px;
747
+ }
748
+
749
+ .flight-devtools-close {
750
+ width: 28px;
751
+ height: 28px;
752
+ border: none;
753
+ background: rgba(0,0,0,0.2);
754
+ color: white;
755
+ border-radius: 6px;
756
+ cursor: pointer;
757
+ font-size: 16px;
758
+ line-height: 1;
759
+ display: flex;
760
+ align-items: center;
761
+ justify-content: center;
762
+ transition: background 0.2s;
763
+ flex-shrink: 0;
764
+ }
765
+
766
+ .flight-devtools-close:hover {
767
+ background: rgba(0,0,0,0.4);
768
+ }
769
+
770
+ .flight-devtools-tabs {
771
+ display: flex;
772
+ background: var(--bg-2);
773
+ border-bottom: 1px solid var(--border);
774
+ }
775
+
776
+ .flight-devtools-tabs button {
777
+ flex: 1;
778
+ padding: 10px 8px;
779
+ border: none;
780
+ background: transparent;
781
+ color: var(--text-muted);
782
+ font-size: 11px;
783
+ cursor: pointer;
784
+ border-bottom: 2px solid transparent;
785
+ transition: all 0.2s;
786
+ }
787
+
788
+ .flight-devtools-tabs button:hover {
789
+ color: var(--text);
790
+ background: var(--bg-3);
791
+ }
792
+
793
+ .flight-devtools-tabs button.active {
794
+ color: var(--green);
795
+ border-bottom-color: var(--green);
796
+ }
797
+
798
+ .flight-devtools-tabs .count {
799
+ display: inline-block;
800
+ min-width: 18px;
801
+ padding: 1px 5px;
802
+ margin-left: 4px;
803
+ background: var(--bg-3);
804
+ border-radius: 10px;
805
+ font-size: 10px;
806
+ }
807
+
808
+ .flight-devtools-content {
809
+ flex: 1;
810
+ overflow-y: auto;
811
+ min-height: 0;
812
+ }
813
+
814
+ .flight-devtools-toolbar {
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: space-between;
818
+ padding: 8px 12px;
819
+ background: var(--bg-2);
820
+ border-bottom: 1px solid var(--border);
821
+ }
822
+
823
+ .flight-devtools-search {
824
+ flex: 1;
825
+ padding: 6px 10px;
826
+ background: var(--bg-3);
827
+ border: 1px solid var(--border);
828
+ border-radius: 6px;
829
+ color: var(--text);
830
+ font-size: 12px;
831
+ }
832
+
833
+ .flight-devtools-search:focus {
834
+ outline: none;
835
+ border-color: var(--green);
836
+ }
837
+
838
+ .flight-devtools-btn-small {
839
+ padding: 4px 10px;
840
+ background: var(--bg-3);
841
+ border: 1px solid var(--border);
842
+ border-radius: 4px;
843
+ color: var(--text-muted);
844
+ font-size: 11px;
845
+ cursor: pointer;
846
+ }
847
+
848
+ .flight-devtools-btn-small:hover {
849
+ background: var(--border);
850
+ color: var(--text);
851
+ }
852
+
853
+ .flight-devtools-btn-group {
854
+ display: flex;
855
+ gap: 6px;
856
+ }
857
+
858
+ .flight-devtools-info {
859
+ color: var(--text-muted);
860
+ font-size: 11px;
861
+ }
862
+
863
+ .flight-devtools-list {
864
+ padding: 8px;
865
+ }
866
+
867
+ .flight-devtools-section {
868
+ margin-bottom: 16px;
869
+ }
870
+
871
+ .flight-devtools-section-title {
872
+ padding: 8px 8px 4px;
873
+ color: var(--text-muted);
874
+ font-size: 10px;
875
+ font-weight: 600;
876
+ text-transform: uppercase;
877
+ letter-spacing: 0.5px;
878
+ }
879
+
880
+ .flight-devtools-item {
881
+ padding: 8px 10px;
882
+ border-radius: 6px;
883
+ cursor: pointer;
884
+ transition: background 0.15s;
885
+ }
886
+
887
+ .flight-devtools-item:hover {
888
+ background: var(--bg-2);
889
+ }
890
+
891
+ .flight-devtools-item.expanded {
892
+ background: var(--bg-2);
893
+ }
894
+
895
+ .flight-devtools-item-header {
896
+ display: flex;
897
+ align-items: center;
898
+ gap: 8px;
899
+ }
900
+
901
+ .flight-devtools-item-details {
902
+ margin-top: 8px;
903
+ padding: 8px;
904
+ background: var(--bg-3);
905
+ border-radius: 4px;
906
+ }
907
+
908
+ .detail-row {
909
+ display: flex;
910
+ padding: 3px 0;
911
+ }
912
+
913
+ .detail-label {
914
+ width: 80px;
915
+ color: var(--text-muted);
916
+ }
917
+
918
+ .detail-value {
919
+ flex: 1;
920
+ color: var(--text);
921
+ }
922
+
923
+ .detail-value.mono {
924
+ font-family: 'SF Mono', Monaco, monospace;
925
+ font-size: 11px;
926
+ }
927
+
928
+ .method {
929
+ font-weight: 600;
930
+ font-size: 10px;
931
+ padding: 2px 6px;
932
+ border-radius: 3px;
933
+ background: var(--bg-3);
934
+ }
935
+
936
+ .method.get { color: #22c55e; }
937
+ .method.post { color: #3b82f6; }
938
+ .method.put { color: #f59e0b; }
939
+ .method.patch { color: #f59e0b; }
940
+ .method.delete { color: #ef4444; }
941
+
942
+ .path {
943
+ flex: 1;
944
+ font-family: 'SF Mono', Monaco, monospace;
945
+ font-size: 11px;
946
+ color: var(--text);
947
+ overflow: hidden;
948
+ text-overflow: ellipsis;
949
+ white-space: nowrap;
950
+ }
951
+
952
+ .badge {
953
+ font-size: 9px;
954
+ padding: 2px 5px;
955
+ border-radius: 3px;
956
+ background: var(--bg-3);
957
+ color: var(--text-muted);
958
+ }
959
+
960
+ .badge.dynamic { color: #a78bfa; }
961
+ .badge.slot { color: #60a5fa; }
962
+
963
+ .expand-icon {
964
+ color: var(--text-muted);
965
+ font-size: 8px;
966
+ }
967
+
968
+ .status { font-weight: 600; }
969
+ .status.status-2xx { color: #22c55e; }
970
+ .status.status-3xx { color: #f59e0b; }
971
+ .status.status-4xx { color: #ef4444; }
972
+ .status.status-5xx { color: #a855f7; }
973
+
974
+ .duration, .time {
975
+ color: var(--text-muted);
976
+ font-size: 11px;
977
+ }
978
+
979
+ .adapter-icon {
980
+ font-size: 16px;
981
+ }
982
+
983
+ .adapter-name {
984
+ flex: 1;
985
+ font-weight: 500;
986
+ }
987
+
988
+ .adapter-type {
989
+ color: var(--text-muted);
990
+ font-size: 10px;
991
+ }
992
+
993
+ .adapter-status {
994
+ font-size: 10px;
995
+ padding: 2px 6px;
996
+ border-radius: 3px;
997
+ }
998
+
999
+ .adapter-status.status-connected { background: rgba(34, 197, 94, 0.2); color: #22c55e; }
1000
+ .adapter-status.status-disconnected { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
1001
+ .adapter-status.status-error { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
1002
+
1003
+ .flight-devtools-metrics {
1004
+ display: grid;
1005
+ grid-template-columns: repeat(3, 1fr);
1006
+ gap: 8px;
1007
+ padding: 8px;
1008
+ }
1009
+
1010
+ .flight-devtools-metric {
1011
+ padding: 12px;
1012
+ background: var(--bg-2);
1013
+ border-radius: 8px;
1014
+ text-align: center;
1015
+ }
1016
+
1017
+ .flight-devtools-metric.good { border-left: 3px solid #22c55e; }
1018
+ .flight-devtools-metric.needs-improvement { border-left: 3px solid #f59e0b; }
1019
+ .flight-devtools-metric.poor { border-left: 3px solid #ef4444; }
1020
+
1021
+ .metric-label {
1022
+ font-weight: 600;
1023
+ font-size: 11px;
1024
+ color: var(--text);
1025
+ }
1026
+
1027
+ .metric-value {
1028
+ font-size: 18px;
1029
+ font-weight: 600;
1030
+ margin: 4px 0;
1031
+ }
1032
+
1033
+ .flight-devtools-metric.good .metric-value { color: #22c55e; }
1034
+ .flight-devtools-metric.needs-improvement .metric-value { color: #f59e0b; }
1035
+ .flight-devtools-metric.poor .metric-value { color: #ef4444; }
1036
+
1037
+ .metric-desc {
1038
+ font-size: 9px;
1039
+ color: var(--text-muted);
1040
+ }
1041
+
1042
+ .flight-devtools-filter-group {
1043
+ display: flex;
1044
+ gap: 4px;
1045
+ }
1046
+
1047
+ .flight-devtools-filter-btn {
1048
+ padding: 4px 8px;
1049
+ background: transparent;
1050
+ border: 1px solid var(--border);
1051
+ border-radius: 4px;
1052
+ color: var(--text-muted);
1053
+ font-size: 10px;
1054
+ cursor: pointer;
1055
+ text-transform: capitalize;
1056
+ }
1057
+
1058
+ .flight-devtools-filter-btn.active {
1059
+ background: var(--green);
1060
+ border-color: var(--green);
1061
+ color: white;
1062
+ }
1063
+
1064
+ .console-list {
1065
+ font-family: 'SF Mono', Monaco, monospace;
1066
+ font-size: 11px;
1067
+ }
1068
+
1069
+ .flight-devtools-console-item {
1070
+ display: flex;
1071
+ gap: 8px;
1072
+ padding: 6px 8px;
1073
+ border-bottom: 1px solid var(--border);
1074
+ }
1075
+
1076
+ .flight-devtools-console-item.level-error {
1077
+ background: rgba(239, 68, 68, 0.1);
1078
+ }
1079
+
1080
+ .flight-devtools-console-item.level-warn {
1081
+ background: rgba(245, 158, 11, 0.1);
1082
+ }
1083
+
1084
+ .console-time {
1085
+ color: var(--text-muted);
1086
+ flex-shrink: 0;
1087
+ }
1088
+
1089
+ .console-level {
1090
+ flex-shrink: 0;
1091
+ width: 40px;
1092
+ font-weight: 600;
1093
+ }
1094
+
1095
+ .level-log .console-level { color: #888; }
1096
+ .level-info .console-level { color: #3b82f6; }
1097
+ .level-warn .console-level { color: #f59e0b; }
1098
+ .level-error .console-level { color: #ef4444; }
1099
+
1100
+ .console-message {
1101
+ flex: 1;
1102
+ word-break: break-word;
1103
+ color: var(--text);
1104
+ }
1105
+
1106
+ .flight-devtools-empty {
1107
+ padding: 40px 20px;
1108
+ text-align: center;
1109
+ color: var(--text-muted);
1110
+ }
1111
+
1112
+ .flight-devtools-empty small {
1113
+ display: block;
1114
+ margin-top: 8px;
1115
+ font-size: 11px;
1116
+ opacity: 0.7;
1117
+ }
1064
1118
  `;
1065
1119
  }
1066
1120
  //# sourceMappingURL=panel.js.map