@flight-framework/devtools 1.0.1 → 2.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
@@ -5,6 +5,8 @@
5
5
  * and improved data visualization.
6
6
  */
7
7
  import { getDevTools, formatBytes, formatDuration, formatRelativeTime } from './index.js';
8
+ import { getHydrationState, renderHydrationPanel, hydrationPanelStyles } from './hydration-panel.js';
9
+ import { getBundleState, renderBundlePanel, bundlePanelStyles } from './bundle-panel.js';
8
10
  const PANEL_STORAGE_KEY = 'flight-devtools-panel-size';
9
11
  const MIN_WIDTH = 320;
10
12
  const MIN_HEIGHT = 300;
@@ -89,11 +91,11 @@ export function injectDevToolsPanel() {
89
91
  if (panelState.isDragging || panelState.isResizing)
90
92
  return;
91
93
  const options = devTools.getOptions();
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>
94
+ container.innerHTML = `
95
+ <div class="flight-devtools ${options.position} ${panelState.isOpen ? 'open' : ''}">
96
+ ${panelState.isOpen ? '' : `<button class="flight-devtools-toggle" data-action="toggle">${getFlightLogo()}</button>`}
97
+ ${panelState.isOpen ? renderPanel(state) : renderBadge(state)}
98
+ </div>
97
99
  `;
98
100
  // Attach event listeners
99
101
  attachEventListeners(state);
@@ -105,9 +107,13 @@ export function injectDevToolsPanel() {
105
107
  return `<span class="flight-devtools-badge">${errorCount}</span>`;
106
108
  }
107
109
  function renderPanel(state) {
110
+ const hydrationState = getHydrationState();
111
+ const bundleState = getBundleState();
108
112
  const tabs = [
109
113
  { id: 'routes', label: 'Routes', count: state.routes.length },
110
114
  { id: 'requests', label: 'Requests', count: state.requests.length },
115
+ { id: 'hydration', label: 'Hydration', count: hydrationState.metrics.totalIslands },
116
+ { id: 'bundle', label: 'Bundle', count: bundleState.metrics.totalChunks },
111
117
  { id: 'adapters', label: 'Adapters', count: state.adapters.length },
112
118
  { id: 'performance', label: 'Perf', count: null },
113
119
  { id: 'console', label: 'Console', count: state.console.length + state.errors.length },
@@ -115,39 +121,47 @@ export function injectDevToolsPanel() {
115
121
  const positionStyle = panelState.x !== null && panelState.y !== null
116
122
  ? `position:fixed;left:${panelState.x}px;top:${panelState.y}px;right:auto;bottom:auto;`
117
123
  : '';
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>
124
+ return `
125
+ <div class="flight-devtools-panel" style="width:${panelState.width}px;height:${panelState.height}px;${positionStyle}">
126
+ <div class="flight-devtools-resize-handle-top" data-resize="top"></div>
127
+ <div class="flight-devtools-resize-handle-left" data-resize="left"></div>
128
+ <div class="flight-devtools-resize-handle-corner" data-resize="corner"></div>
129
+ <div class="flight-devtools-header">
130
+ <div class="flight-devtools-header-title">
131
+ <span class="flight-devtools-logo">Flight DevTools</span>
132
+ <span class="flight-devtools-version">v1.0.1</span>
133
+ </div>
134
+ <button class="flight-devtools-close" data-action="toggle">x</button>
135
+ </div>
136
+ <div class="flight-devtools-tabs">
137
+ ${tabs.map(tab => `
138
+ <button
139
+ class="${panelState.currentTab === tab.id ? 'active' : ''}"
140
+ data-action="tab"
141
+ data-tab="${tab.id}"
142
+ >
143
+ ${tab.label}${tab.count !== null ? ` <span class="count">${tab.count}</span>` : ''}
144
+ </button>
145
+ `).join('')}
146
+ </div>
147
+ <div class="flight-devtools-content">
148
+ ${renderTabContent(state)}
149
+ </div>
150
+ </div>
145
151
  `;
146
152
  }
147
153
  function renderTabContent(state) {
148
154
  switch (panelState.currentTab) {
149
155
  case 'routes': return renderRoutes(state.routes);
150
156
  case 'requests': return renderRequests(state.requests);
157
+ case 'hydration': {
158
+ const { islands, metrics } = getHydrationState();
159
+ return renderHydrationPanel(islands, metrics);
160
+ }
161
+ case 'bundle': {
162
+ const { chunks, metrics } = getBundleState();
163
+ return renderBundlePanel(chunks, metrics);
164
+ }
151
165
  case 'adapters': return renderAdapters(state);
152
166
  case 'performance': return renderPerformance(state);
153
167
  case 'console': return renderConsole(state);
@@ -163,59 +177,59 @@ export function injectDevToolsPanel() {
163
177
  : routes;
164
178
  const pageRoutes = filtered.filter(r => r.type === 'page');
165
179
  const apiRoutes = filtered.filter(r => r.type === 'api');
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>
180
+ return `
181
+ <div class="flight-devtools-toolbar">
182
+ <input
183
+ type="text"
184
+ placeholder="Filter routes..."
185
+ value="${panelState.routeFilter}"
186
+ data-action="filter-routes"
187
+ class="flight-devtools-search"
188
+ />
189
+ </div>
190
+ <div class="flight-devtools-list">
191
+ ${pageRoutes.length > 0 ? `
192
+ <div class="flight-devtools-section">
193
+ <div class="flight-devtools-section-title">Pages (${pageRoutes.length})</div>
194
+ ${pageRoutes.map(route => renderRouteItem(route)).join('')}
195
+ </div>
196
+ ` : ''}
197
+ ${apiRoutes.length > 0 ? `
198
+ <div class="flight-devtools-section">
199
+ <div class="flight-devtools-section-title">API Routes (${apiRoutes.length})</div>
200
+ ${apiRoutes.map(route => renderRouteItem(route)).join('')}
201
+ </div>
202
+ ` : ''}
203
+ </div>
190
204
  `;
191
205
  }
192
206
  function renderRouteItem(route) {
193
207
  const isExpanded = panelState.expandedItems.has(route.path);
194
208
  const hasParams = route.path.includes(':') || route.path.includes('[');
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>
209
+ return `
210
+ <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${route.path}">
211
+ <div class="flight-devtools-item-header">
212
+ <span class="method ${route.method.toLowerCase()}">${route.method}</span>
213
+ <span class="path">${route.path}</span>
214
+ ${hasParams ? '<span class="badge dynamic">Dynamic</span>' : ''}
215
+ ${route.slot ? `<span class="badge slot">@${route.slot}</span>` : ''}
216
+ <span class="expand-icon">${isExpanded ? '▼' : '▶'}</span>
217
+ </div>
218
+ ${isExpanded ? `
219
+ <div class="flight-devtools-item-details">
220
+ <div class="detail-row">
221
+ <span class="detail-label">File:</span>
222
+ <span class="detail-value mono">${route.filePath}</span>
223
+ </div>
224
+ ${route.params ? `
225
+ <div class="detail-row">
226
+ <span class="detail-label">Params:</span>
227
+ <span class="detail-value">${route.params.join(', ')}</span>
228
+ </div>
229
+ ` : ''}
230
+ </div>
231
+ ` : ''}
232
+ </div>
219
233
  `;
220
234
  }
221
235
  function renderRequests(requests) {
@@ -227,84 +241,84 @@ export function injectDevToolsPanel() {
227
241
  const filteredRequests = filter
228
242
  ? requests.filter(r => r.path.toLowerCase().includes(filter) || r.method.toLowerCase().includes(filter))
229
243
  : 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>
244
+ return `
245
+ <div class="flight-devtools-toolbar">
246
+ <input
247
+ type="text"
248
+ class="flight-devtools-search"
249
+ placeholder="Filter requests..."
250
+ value="${panelState.requestFilter}"
251
+ data-action="filter-requests"
252
+ />
253
+ </div>
254
+ <div class="flight-devtools-toolbar">
255
+ <span class="flight-devtools-info">${filteredRequests.length}/${requests.length} requests</span>
256
+ <div class="flight-devtools-btn-group">
257
+ <button data-action="export-requests" class="flight-devtools-btn-small">Export</button>
258
+ <button data-action="clear-requests" class="flight-devtools-btn-small">Clear</button>
259
+ </div>
260
+ </div>
261
+ <div class="flight-devtools-list">
262
+ ${filteredRequests.slice(0, 50).map(req => renderRequestItem(req)).join('')}
263
+ </div>
250
264
  `;
251
265
  }
252
266
  function renderRequestItem(req) {
253
267
  const isExpanded = panelState.expandedItems.has(req.id);
254
268
  const statusClass = `status-${Math.floor(req.status / 100)}xx`;
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>
269
+ return `
270
+ <div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${req.id}">
271
+ <div class="flight-devtools-item-header">
272
+ <span class="method ${req.method.toLowerCase()}">${req.method}</span>
273
+ <span class="path">${req.path}</span>
274
+ <span class="status ${statusClass}">${req.status}</span>
275
+ <span class="duration">${formatDuration(req.duration)}</span>
276
+ <span class="time">${formatRelativeTime(req.timestamp)}</span>
277
+ </div>
278
+ ${isExpanded ? `
279
+ <div class="flight-devtools-item-details">
280
+ <div class="detail-row">
281
+ <span class="detail-label">Duration:</span>
282
+ <span class="detail-value">${formatDuration(req.duration)}</span>
283
+ </div>
284
+ ${req.size !== undefined ? `
285
+ <div class="detail-row">
286
+ <span class="detail-label">Size:</span>
287
+ <span class="detail-value">${formatBytes(req.size)}</span>
288
+ </div>
289
+ ` : ''}
290
+ ${req.type ? `
291
+ <div class="detail-row">
292
+ <span class="detail-label">Type:</span>
293
+ <span class="detail-value">${req.type}</span>
294
+ </div>
295
+ ` : ''}
296
+ <div class="detail-row">
297
+ <span class="detail-label">Time:</span>
298
+ <span class="detail-value">${new Date(req.timestamp).toLocaleTimeString()}</span>
299
+ </div>
300
+ </div>
301
+ ` : ''}
302
+ </div>
289
303
  `;
290
304
  }
291
305
  function renderAdapters(state) {
292
306
  if (state.adapters.length === 0) {
293
307
  return '<div class="flight-devtools-empty">No adapters registered</div>';
294
308
  }
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>
309
+ return `
310
+ <div class="flight-devtools-list">
311
+ ${state.adapters.map(adapter => `
312
+ <div class="flight-devtools-item">
313
+ <div class="flight-devtools-item-header">
314
+ <span class="adapter-icon">${getAdapterIcon(adapter.type)}</span>
315
+ <span class="adapter-name">${adapter.name}</span>
316
+ <span class="adapter-type">${adapter.type}</span>
317
+ <span class="adapter-status status-${adapter.status}">${adapter.status}</span>
318
+ </div>
319
+ </div>
320
+ `).join('')}
321
+ </div>
308
322
  `;
309
323
  }
310
324
  function getAdapterIcon(type) {
@@ -332,22 +346,22 @@ export function injectDevToolsPanel() {
332
346
  { label: 'FCP', value: perf.fcp, unit: 'ms', good: 1800, desc: 'First Contentful Paint' },
333
347
  { label: 'TTFB', value: perf.ttfb, unit: 'ms', good: 800, desc: 'Time to First Byte' },
334
348
  ].filter(m => m.value !== undefined);
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">
349
+ return `
350
+ <div class="flight-devtools-list">
351
+ <div class="flight-devtools-section-title">Core Web Vitals</div>
352
+ <div class="flight-devtools-metrics">
339
353
  ${metrics.map(m => {
340
354
  const status = m.value <= m.good ? 'good' : m.value <= m.good * 2 ? 'needs-improvement' : 'poor';
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>
355
+ return `
356
+ <div class="flight-devtools-metric ${status}">
357
+ <div class="metric-label">${m.label}</div>
358
+ <div class="metric-value">${m.unit ? formatDuration(m.value) : m.value.toFixed(3)}</div>
359
+ <div class="metric-desc">${m.desc}</div>
360
+ </div>
347
361
  `;
348
- }).join('')}
349
- </div>
350
- </div>
362
+ }).join('')}
363
+ </div>
364
+ </div>
351
365
  `;
352
366
  }
353
367
  function renderConsole(state) {
@@ -358,30 +372,30 @@ export function injectDevToolsPanel() {
358
372
  const filtered = panelState.consoleFilter === 'all'
359
373
  ? entries
360
374
  : entries.filter(e => e.level === panelState.consoleFilter);
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">
375
+ return `
376
+ <div class="flight-devtools-toolbar">
377
+ <div class="flight-devtools-filter-group">
378
+ ${['all', 'log', 'warn', 'error'].map(f => `
379
+ <button
380
+ class="flight-devtools-filter-btn ${panelState.consoleFilter === f ? 'active' : ''}"
381
+ data-action="filter-console"
382
+ data-filter="${f}"
383
+ >${f}</button>
384
+ `).join('')}
385
+ </div>
386
+ <button data-action="clear-console" class="flight-devtools-btn-small">Clear</button>
387
+ </div>
388
+ <div class="flight-devtools-list console-list">
375
389
  ${filtered.length === 0
376
390
  ? '<div class="flight-devtools-empty">No console entries</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>
391
+ : filtered.slice(0, 100).map(entry => `
392
+ <div class="flight-devtools-console-item level-${entry.level}">
393
+ <span class="console-time">${formatRelativeTime(entry.timestamp)}</span>
394
+ <span class="console-level">${entry.level}</span>
395
+ <span class="console-message">${escapeHtml(entry.message)}</span>
396
+ </div>
397
+ `).join('')}
398
+ </div>
385
399
  `;
386
400
  }
387
401
  function escapeHtml(text) {
@@ -570,551 +584,555 @@ export function injectDevToolsPanel() {
570
584
  function getFlightLogo() {
571
585
  // Flight logo SVG - exact copy from favicon.svg with optimized viewBox for button display
572
586
  // Following SVGO best practices: proper viewBox, inlined transforms, appropriate sizing
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>
587
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="40" height="40" fill="currentColor">
588
+ <g transform="translate(0,800) scale(0.1,-0.1)">
589
+ <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"/>
590
+ <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"/>
591
+ <path d="M3923 2465 c-10 -10 -9 -16 1 -24 10 -8 16 -8 25 3 16 19 -9 40 -26 21z"/>
592
+ <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"/>
593
+ <path d="M3851 2307 c-9 -11 -8 -17 3 -26 11 -9 15 -8 20 5 9 23 -8 38 -23 21z"/>
594
+ <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"/>
595
+ </g>
582
596
  </svg>`;
583
597
  }
584
598
  function getFlightLogoSmall() {
585
599
  // Small Flight logo for the header (16x16)
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>
600
+ 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;">
601
+ <g transform="translate(0,800) scale(0.1,-0.1)">
602
+ <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"/>
603
+ <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"/>
604
+ </g>
591
605
  </svg>`;
592
606
  }
593
607
  // ============================================================================
594
608
  // Styles
595
609
  // ============================================================================
596
610
  function getDevToolsStyles() {
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
- }
611
+ return `
612
+ #flight-devtools {
613
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
614
+ font-size: 12px;
615
+ line-height: 1.4;
616
+ --green: #22c55e;
617
+ --green-dark: #16a34a;
618
+ --bg: #0f0f0f;
619
+ --bg-2: #1a1a1a;
620
+ --bg-3: #252525;
621
+ --border: #333;
622
+ --text: #e5e5e5;
623
+ --text-muted: #888;
624
+ }
625
+
626
+ .flight-devtools {
627
+ position: fixed;
628
+ z-index: 999999;
629
+ }
630
+
631
+ .flight-devtools.bottom-right { bottom: 16px; right: 16px; }
632
+ .flight-devtools.bottom-left { bottom: 16px; left: 16px; }
633
+ .flight-devtools.top-right { top: 16px; right: 16px; }
634
+ .flight-devtools.top-left { top: 16px; left: 16px; }
635
+
636
+ .flight-devtools-toggle {
637
+ width: 48px;
638
+ height: 48px;
639
+ border-radius: 50%;
640
+ border: none;
641
+ background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
642
+ color: white;
643
+ font-size: 20px;
644
+ cursor: pointer;
645
+ box-shadow: 0 4px 20px rgba(34, 197, 94, 0.4);
646
+ transition: transform 0.2s, box-shadow 0.2s;
647
+ display: flex;
648
+ align-items: center;
649
+ justify-content: center;
650
+ padding: 0;
651
+ }
652
+
653
+ .flight-devtools-toggle:hover {
654
+ transform: scale(1.05);
655
+ box-shadow: 0 6px 24px rgba(34, 197, 94, 0.5);
656
+ }
657
+
658
+ .flight-devtools.open .flight-devtools-toggle {
659
+ position: absolute;
660
+ top: 8px;
661
+ right: 8px;
662
+ width: 28px;
663
+ height: 28px;
664
+ font-size: 14px;
665
+ background: rgba(0,0,0,0.3);
666
+ box-shadow: none;
667
+ z-index: 10;
668
+ }
669
+
670
+ .flight-devtools-badge {
671
+ position: absolute;
672
+ top: -4px;
673
+ right: -4px;
674
+ width: 18px;
675
+ height: 18px;
676
+ border-radius: 50%;
677
+ background: #ef4444;
678
+ color: white;
679
+ font-size: 10px;
680
+ font-weight: 600;
681
+ display: flex;
682
+ align-items: center;
683
+ justify-content: center;
684
+ }
685
+
686
+ .flight-devtools-panel {
687
+ position: absolute;
688
+ bottom: 0;
689
+ right: 0;
690
+ width: 480px;
691
+ height: 500px;
692
+ max-height: 80vh;
693
+ background: var(--bg);
694
+ border: 1px solid var(--border);
695
+ border-radius: 12px;
696
+ overflow: hidden;
697
+ box-shadow: 0 8px 40px rgba(0,0,0,0.5);
698
+ display: flex;
699
+ flex-direction: column;
700
+ }
701
+
702
+ .flight-devtools-resize-handle-top {
703
+ position: absolute;
704
+ top: 0;
705
+ left: 20px;
706
+ right: 20px;
707
+ height: 6px;
708
+ cursor: ns-resize;
709
+ z-index: 10;
710
+ }
711
+
712
+ .flight-devtools-resize-handle-left {
713
+ position: absolute;
714
+ left: 0;
715
+ top: 20px;
716
+ bottom: 20px;
717
+ width: 6px;
718
+ cursor: ew-resize;
719
+ z-index: 10;
720
+ }
721
+
722
+ .flight-devtools-resize-handle-corner {
723
+ position: absolute;
724
+ top: 0;
725
+ left: 0;
726
+ width: 16px;
727
+ height: 16px;
728
+ cursor: nwse-resize;
729
+ z-index: 11;
730
+ }
731
+
732
+ .flight-devtools-header {
733
+ padding: 12px 16px;
734
+ background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
735
+ color: white;
736
+ display: flex;
737
+ align-items: center;
738
+ justify-content: space-between;
739
+ gap: 12px;
740
+ cursor: move;
741
+ user-select: none;
742
+ }
743
+
744
+ .flight-devtools-header-title {
745
+ display: flex;
746
+ align-items: center;
747
+ gap: 8px;
748
+ }
749
+
750
+ .flight-devtools-logo {
751
+ font-weight: 600;
752
+ font-size: 14px;
753
+ }
754
+
755
+ .flight-devtools-version {
756
+ font-size: 10px;
757
+ opacity: 0.7;
758
+ background: rgba(0,0,0,0.15);
759
+ padding: 2px 6px;
760
+ border-radius: 4px;
761
+ }
762
+
763
+ .flight-devtools-close {
764
+ width: 28px;
765
+ height: 28px;
766
+ border: none;
767
+ background: rgba(0,0,0,0.2);
768
+ color: white;
769
+ border-radius: 6px;
770
+ cursor: pointer;
771
+ font-size: 16px;
772
+ line-height: 1;
773
+ display: flex;
774
+ align-items: center;
775
+ justify-content: center;
776
+ transition: background 0.2s;
777
+ flex-shrink: 0;
778
+ }
779
+
780
+ .flight-devtools-close:hover {
781
+ background: rgba(0,0,0,0.4);
782
+ }
783
+
784
+ .flight-devtools-tabs {
785
+ display: flex;
786
+ background: var(--bg-2);
787
+ border-bottom: 1px solid var(--border);
788
+ }
789
+
790
+ .flight-devtools-tabs button {
791
+ flex: 1;
792
+ padding: 10px 8px;
793
+ border: none;
794
+ background: transparent;
795
+ color: var(--text-muted);
796
+ font-size: 11px;
797
+ cursor: pointer;
798
+ border-bottom: 2px solid transparent;
799
+ transition: all 0.2s;
800
+ }
801
+
802
+ .flight-devtools-tabs button:hover {
803
+ color: var(--text);
804
+ background: var(--bg-3);
805
+ }
806
+
807
+ .flight-devtools-tabs button.active {
808
+ color: var(--green);
809
+ border-bottom-color: var(--green);
810
+ }
811
+
812
+ .flight-devtools-tabs .count {
813
+ display: inline-block;
814
+ min-width: 18px;
815
+ padding: 1px 5px;
816
+ margin-left: 4px;
817
+ background: var(--bg-3);
818
+ border-radius: 10px;
819
+ font-size: 10px;
820
+ }
821
+
822
+ .flight-devtools-content {
823
+ flex: 1;
824
+ overflow-y: auto;
825
+ min-height: 0;
826
+ }
827
+
828
+ .flight-devtools-toolbar {
829
+ display: flex;
830
+ align-items: center;
831
+ justify-content: space-between;
832
+ padding: 8px 12px;
833
+ background: var(--bg-2);
834
+ border-bottom: 1px solid var(--border);
835
+ }
836
+
837
+ .flight-devtools-search {
838
+ flex: 1;
839
+ padding: 6px 10px;
840
+ background: var(--bg-3);
841
+ border: 1px solid var(--border);
842
+ border-radius: 6px;
843
+ color: var(--text);
844
+ font-size: 12px;
845
+ }
846
+
847
+ .flight-devtools-search:focus {
848
+ outline: none;
849
+ border-color: var(--green);
850
+ }
851
+
852
+ .flight-devtools-btn-small {
853
+ padding: 4px 10px;
854
+ background: var(--bg-3);
855
+ border: 1px solid var(--border);
856
+ border-radius: 4px;
857
+ color: var(--text-muted);
858
+ font-size: 11px;
859
+ cursor: pointer;
860
+ }
861
+
862
+ .flight-devtools-btn-small:hover {
863
+ background: var(--border);
864
+ color: var(--text);
865
+ }
866
+
867
+ .flight-devtools-btn-group {
868
+ display: flex;
869
+ gap: 6px;
870
+ }
871
+
872
+ .flight-devtools-info {
873
+ color: var(--text-muted);
874
+ font-size: 11px;
875
+ }
876
+
877
+ .flight-devtools-list {
878
+ padding: 8px;
879
+ }
880
+
881
+ .flight-devtools-section {
882
+ margin-bottom: 16px;
883
+ }
884
+
885
+ .flight-devtools-section-title {
886
+ padding: 8px 8px 4px;
887
+ color: var(--text-muted);
888
+ font-size: 10px;
889
+ font-weight: 600;
890
+ text-transform: uppercase;
891
+ letter-spacing: 0.5px;
892
+ }
893
+
894
+ .flight-devtools-item {
895
+ padding: 8px 10px;
896
+ border-radius: 6px;
897
+ cursor: pointer;
898
+ transition: background 0.15s;
899
+ }
900
+
901
+ .flight-devtools-item:hover {
902
+ background: var(--bg-2);
903
+ }
904
+
905
+ .flight-devtools-item.expanded {
906
+ background: var(--bg-2);
907
+ }
908
+
909
+ .flight-devtools-item-header {
910
+ display: flex;
911
+ align-items: center;
912
+ gap: 8px;
913
+ }
914
+
915
+ .flight-devtools-item-details {
916
+ margin-top: 8px;
917
+ padding: 8px;
918
+ background: var(--bg-3);
919
+ border-radius: 4px;
920
+ }
921
+
922
+ .detail-row {
923
+ display: flex;
924
+ padding: 3px 0;
925
+ }
926
+
927
+ .detail-label {
928
+ width: 80px;
929
+ color: var(--text-muted);
930
+ }
931
+
932
+ .detail-value {
933
+ flex: 1;
934
+ color: var(--text);
935
+ }
936
+
937
+ .detail-value.mono {
938
+ font-family: 'SF Mono', Monaco, monospace;
939
+ font-size: 11px;
940
+ }
941
+
942
+ .method {
943
+ font-weight: 600;
944
+ font-size: 10px;
945
+ padding: 2px 6px;
946
+ border-radius: 3px;
947
+ background: var(--bg-3);
948
+ }
949
+
950
+ .method.get { color: #22c55e; }
951
+ .method.post { color: #3b82f6; }
952
+ .method.put { color: #f59e0b; }
953
+ .method.patch { color: #f59e0b; }
954
+ .method.delete { color: #ef4444; }
955
+
956
+ .path {
957
+ flex: 1;
958
+ font-family: 'SF Mono', Monaco, monospace;
959
+ font-size: 11px;
960
+ color: var(--text);
961
+ overflow: hidden;
962
+ text-overflow: ellipsis;
963
+ white-space: nowrap;
964
+ }
965
+
966
+ .badge {
967
+ font-size: 9px;
968
+ padding: 2px 5px;
969
+ border-radius: 3px;
970
+ background: var(--bg-3);
971
+ color: var(--text-muted);
972
+ }
973
+
974
+ .badge.dynamic { color: #a78bfa; }
975
+ .badge.slot { color: #60a5fa; }
976
+
977
+ .expand-icon {
978
+ color: var(--text-muted);
979
+ font-size: 8px;
980
+ }
981
+
982
+ .status { font-weight: 600; }
983
+ .status.status-2xx { color: #22c55e; }
984
+ .status.status-3xx { color: #f59e0b; }
985
+ .status.status-4xx { color: #ef4444; }
986
+ .status.status-5xx { color: #a855f7; }
987
+
988
+ .duration, .time {
989
+ color: var(--text-muted);
990
+ font-size: 11px;
991
+ }
992
+
993
+ .adapter-icon {
994
+ font-size: 16px;
995
+ }
996
+
997
+ .adapter-name {
998
+ flex: 1;
999
+ font-weight: 500;
1000
+ }
1001
+
1002
+ .adapter-type {
1003
+ color: var(--text-muted);
1004
+ font-size: 10px;
1005
+ }
1006
+
1007
+ .adapter-status {
1008
+ font-size: 10px;
1009
+ padding: 2px 6px;
1010
+ border-radius: 3px;
1011
+ }
1012
+
1013
+ .adapter-status.status-connected { background: rgba(34, 197, 94, 0.2); color: #22c55e; }
1014
+ .adapter-status.status-disconnected { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
1015
+ .adapter-status.status-error { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
1016
+
1017
+ .flight-devtools-metrics {
1018
+ display: grid;
1019
+ grid-template-columns: repeat(3, 1fr);
1020
+ gap: 8px;
1021
+ padding: 8px;
1022
+ }
1023
+
1024
+ .flight-devtools-metric {
1025
+ padding: 12px;
1026
+ background: var(--bg-2);
1027
+ border-radius: 8px;
1028
+ text-align: center;
1029
+ }
1030
+
1031
+ .flight-devtools-metric.good { border-left: 3px solid #22c55e; }
1032
+ .flight-devtools-metric.needs-improvement { border-left: 3px solid #f59e0b; }
1033
+ .flight-devtools-metric.poor { border-left: 3px solid #ef4444; }
1034
+
1035
+ .metric-label {
1036
+ font-weight: 600;
1037
+ font-size: 11px;
1038
+ color: var(--text);
1039
+ }
1040
+
1041
+ .metric-value {
1042
+ font-size: 18px;
1043
+ font-weight: 600;
1044
+ margin: 4px 0;
1045
+ }
1046
+
1047
+ .flight-devtools-metric.good .metric-value { color: #22c55e; }
1048
+ .flight-devtools-metric.needs-improvement .metric-value { color: #f59e0b; }
1049
+ .flight-devtools-metric.poor .metric-value { color: #ef4444; }
1050
+
1051
+ .metric-desc {
1052
+ font-size: 9px;
1053
+ color: var(--text-muted);
1054
+ }
1055
+
1056
+ .flight-devtools-filter-group {
1057
+ display: flex;
1058
+ gap: 4px;
1059
+ }
1060
+
1061
+ .flight-devtools-filter-btn {
1062
+ padding: 4px 8px;
1063
+ background: transparent;
1064
+ border: 1px solid var(--border);
1065
+ border-radius: 4px;
1066
+ color: var(--text-muted);
1067
+ font-size: 10px;
1068
+ cursor: pointer;
1069
+ text-transform: capitalize;
1070
+ }
1071
+
1072
+ .flight-devtools-filter-btn.active {
1073
+ background: var(--green);
1074
+ border-color: var(--green);
1075
+ color: white;
1076
+ }
1077
+
1078
+ .console-list {
1079
+ font-family: 'SF Mono', Monaco, monospace;
1080
+ font-size: 11px;
1081
+ }
1082
+
1083
+ .flight-devtools-console-item {
1084
+ display: flex;
1085
+ gap: 8px;
1086
+ padding: 6px 8px;
1087
+ border-bottom: 1px solid var(--border);
1088
+ }
1089
+
1090
+ .flight-devtools-console-item.level-error {
1091
+ background: rgba(239, 68, 68, 0.1);
1092
+ }
1093
+
1094
+ .flight-devtools-console-item.level-warn {
1095
+ background: rgba(245, 158, 11, 0.1);
1096
+ }
1097
+
1098
+ .console-time {
1099
+ color: var(--text-muted);
1100
+ flex-shrink: 0;
1101
+ }
1102
+
1103
+ .console-level {
1104
+ flex-shrink: 0;
1105
+ width: 40px;
1106
+ font-weight: 600;
1107
+ }
1108
+
1109
+ .level-log .console-level { color: #888; }
1110
+ .level-info .console-level { color: #3b82f6; }
1111
+ .level-warn .console-level { color: #f59e0b; }
1112
+ .level-error .console-level { color: #ef4444; }
1113
+
1114
+ .console-message {
1115
+ flex: 1;
1116
+ word-break: break-word;
1117
+ color: var(--text);
1118
+ }
1119
+
1120
+ .flight-devtools-empty {
1121
+ padding: 40px 20px;
1122
+ text-align: center;
1123
+ color: var(--text-muted);
1124
+ }
1125
+
1126
+ .flight-devtools-empty small {
1127
+ display: block;
1128
+ margin-top: 8px;
1129
+ font-size: 11px;
1130
+ opacity: 0.7;
1131
+ }
1132
+
1133
+ ${hydrationPanelStyles}
1134
+
1135
+ ${bundlePanelStyles}
1118
1136
  `;
1119
1137
  }
1120
1138
  //# sourceMappingURL=panel.js.map