@flight-framework/devtools 0.0.3 → 0.0.5
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/index.d.ts +80 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +146 -12
- package/dist/index.js.map +1 -1
- package/dist/instrumentation.d.ts +13 -22
- package/dist/instrumentation.d.ts.map +1 -1
- package/dist/instrumentation.js +250 -89
- package/dist/instrumentation.js.map +1 -1
- package/dist/panel.d.ts +2 -6
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +655 -108
- package/dist/panel.js.map +1 -1
- package/package.json +1 -1
package/dist/panel.js
CHANGED
|
@@ -1,103 +1,214 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @flight-framework/devtools/panel - DevTools Panel UI
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Enhanced floating panel with collapsible sections, real-time updates,
|
|
5
|
+
* and improved data visualization.
|
|
6
6
|
*/
|
|
7
|
-
import { getDevTools } from './index.js';
|
|
7
|
+
import { getDevTools, formatBytes, formatDuration, formatRelativeTime } from './index.js';
|
|
8
8
|
// ============================================================================
|
|
9
|
-
// Panel Component
|
|
9
|
+
// Panel Component
|
|
10
10
|
// ============================================================================
|
|
11
|
-
/**
|
|
12
|
-
* Inject the DevTools panel into the page
|
|
13
|
-
* Call this in your app's entry point during development
|
|
14
|
-
*/
|
|
15
11
|
export function injectDevToolsPanel() {
|
|
16
12
|
if (typeof document === 'undefined')
|
|
17
13
|
return;
|
|
18
14
|
const devTools = getDevTools();
|
|
19
15
|
if (!devTools)
|
|
20
16
|
return;
|
|
17
|
+
// Remove existing panel if any
|
|
18
|
+
const existing = document.getElementById('flight-devtools');
|
|
19
|
+
if (existing)
|
|
20
|
+
existing.remove();
|
|
21
21
|
// Create container
|
|
22
22
|
const container = document.createElement('div');
|
|
23
23
|
container.id = 'flight-devtools';
|
|
24
24
|
document.body.appendChild(container);
|
|
25
25
|
// Inject styles
|
|
26
26
|
const styles = document.createElement('style');
|
|
27
|
+
styles.id = 'flight-devtools-styles';
|
|
27
28
|
styles.textContent = getDevToolsStyles();
|
|
28
29
|
document.head.appendChild(styles);
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// Panel state
|
|
31
|
+
const panelState = {
|
|
32
|
+
isOpen: false,
|
|
33
|
+
currentTab: 'routes',
|
|
34
|
+
routeFilter: '',
|
|
35
|
+
consoleFilter: 'all',
|
|
36
|
+
expandedItems: new Set(),
|
|
37
|
+
};
|
|
32
38
|
function render(state) {
|
|
33
39
|
const options = devTools.getOptions();
|
|
34
40
|
container.innerHTML = `
|
|
35
|
-
<div class="flight-devtools ${options.position} ${isOpen ? 'open' : ''}">
|
|
36
|
-
<button class="flight-devtools-toggle"
|
|
37
|
-
${isOpen ? '
|
|
41
|
+
<div class="flight-devtools ${options.position} ${panelState.isOpen ? 'open' : ''}">
|
|
42
|
+
<button class="flight-devtools-toggle" data-action="toggle">
|
|
43
|
+
${panelState.isOpen ? '✕' : '✈'}
|
|
38
44
|
</button>
|
|
39
|
-
${isOpen ? renderPanel(state
|
|
45
|
+
${panelState.isOpen ? renderPanel(state) : renderBadge(state)}
|
|
40
46
|
</div>
|
|
41
47
|
`;
|
|
48
|
+
// Attach event listeners
|
|
49
|
+
attachEventListeners(state);
|
|
42
50
|
}
|
|
43
|
-
function
|
|
51
|
+
function renderBadge(state) {
|
|
52
|
+
const errorCount = state.errors.length;
|
|
53
|
+
if (errorCount === 0)
|
|
54
|
+
return '';
|
|
55
|
+
return `<span class="flight-devtools-badge">${errorCount}</span>`;
|
|
56
|
+
}
|
|
57
|
+
function renderPanel(state) {
|
|
58
|
+
const tabs = [
|
|
59
|
+
{ id: 'routes', label: 'Routes', count: state.routes.length },
|
|
60
|
+
{ id: 'requests', label: 'Requests', count: state.requests.length },
|
|
61
|
+
{ id: 'adapters', label: 'Adapters', count: state.adapters.length },
|
|
62
|
+
{ id: 'performance', label: 'Perf', count: null },
|
|
63
|
+
{ id: 'console', label: 'Console', count: state.console.length + state.errors.length },
|
|
64
|
+
];
|
|
44
65
|
return `
|
|
45
66
|
<div class="flight-devtools-panel">
|
|
46
67
|
<div class="flight-devtools-header">
|
|
47
|
-
<span
|
|
68
|
+
<span class="flight-devtools-logo">✈ Flight DevTools</span>
|
|
69
|
+
<span class="flight-devtools-version">v0.0.5</span>
|
|
48
70
|
</div>
|
|
49
71
|
<div class="flight-devtools-tabs">
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
${tabs.map(tab => `
|
|
73
|
+
<button
|
|
74
|
+
class="${panelState.currentTab === tab.id ? 'active' : ''}"
|
|
75
|
+
data-action="tab"
|
|
76
|
+
data-tab="${tab.id}"
|
|
77
|
+
>
|
|
78
|
+
${tab.label}${tab.count !== null ? ` <span class="count">${tab.count}</span>` : ''}
|
|
79
|
+
</button>
|
|
80
|
+
`).join('')}
|
|
59
81
|
</div>
|
|
60
82
|
<div class="flight-devtools-content">
|
|
61
|
-
${
|
|
62
|
-
${tab === 'requests' ? renderRequests(state.requests) : ''}
|
|
63
|
-
${tab === 'adapters' ? renderAdapters(state) : ''}
|
|
83
|
+
${renderTabContent(state)}
|
|
64
84
|
</div>
|
|
65
85
|
</div>
|
|
66
86
|
`;
|
|
67
87
|
}
|
|
88
|
+
function renderTabContent(state) {
|
|
89
|
+
switch (panelState.currentTab) {
|
|
90
|
+
case 'routes': return renderRoutes(state.routes);
|
|
91
|
+
case 'requests': return renderRequests(state.requests);
|
|
92
|
+
case 'adapters': return renderAdapters(state);
|
|
93
|
+
case 'performance': return renderPerformance(state);
|
|
94
|
+
case 'console': return renderConsole(state);
|
|
95
|
+
default: return '';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
68
98
|
function renderRoutes(routes) {
|
|
69
99
|
if (routes.length === 0) {
|
|
70
100
|
return '<div class="flight-devtools-empty">No routes registered</div>';
|
|
71
101
|
}
|
|
72
|
-
const
|
|
102
|
+
const filtered = panelState.routeFilter
|
|
103
|
+
? routes.filter(r => r.path.includes(panelState.routeFilter))
|
|
104
|
+
: routes;
|
|
105
|
+
const pageRoutes = filtered.filter(r => r.type === 'page');
|
|
106
|
+
const apiRoutes = filtered.filter(r => r.type === 'api');
|
|
73
107
|
return `
|
|
108
|
+
<div class="flight-devtools-toolbar">
|
|
109
|
+
<input
|
|
110
|
+
type="text"
|
|
111
|
+
placeholder="Filter routes..."
|
|
112
|
+
value="${panelState.routeFilter}"
|
|
113
|
+
data-action="filter-routes"
|
|
114
|
+
class="flight-devtools-search"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
74
117
|
<div class="flight-devtools-list">
|
|
75
|
-
${
|
|
76
|
-
<div class="flight-devtools-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
<span class="type">${route.type}</span>
|
|
80
|
-
${route.slot ? `<span class="slot">@${route.slot}</span>` : ''}
|
|
81
|
-
${route.interceptInfo ? `<span class="intercept">(${dots(route.interceptInfo.level)})</span>` : ''}
|
|
118
|
+
${pageRoutes.length > 0 ? `
|
|
119
|
+
<div class="flight-devtools-section">
|
|
120
|
+
<div class="flight-devtools-section-title">Pages (${pageRoutes.length})</div>
|
|
121
|
+
${pageRoutes.map(route => renderRouteItem(route)).join('')}
|
|
82
122
|
</div>
|
|
83
|
-
`
|
|
123
|
+
` : ''}
|
|
124
|
+
${apiRoutes.length > 0 ? `
|
|
125
|
+
<div class="flight-devtools-section">
|
|
126
|
+
<div class="flight-devtools-section-title">API Routes (${apiRoutes.length})</div>
|
|
127
|
+
${apiRoutes.map(route => renderRouteItem(route)).join('')}
|
|
128
|
+
</div>
|
|
129
|
+
` : ''}
|
|
130
|
+
</div>
|
|
131
|
+
`;
|
|
132
|
+
}
|
|
133
|
+
function renderRouteItem(route) {
|
|
134
|
+
const isExpanded = panelState.expandedItems.has(route.path);
|
|
135
|
+
const hasParams = route.path.includes(':') || route.path.includes('[');
|
|
136
|
+
return `
|
|
137
|
+
<div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${route.path}">
|
|
138
|
+
<div class="flight-devtools-item-header">
|
|
139
|
+
<span class="method ${route.method.toLowerCase()}">${route.method}</span>
|
|
140
|
+
<span class="path">${route.path}</span>
|
|
141
|
+
${hasParams ? '<span class="badge dynamic">Dynamic</span>' : ''}
|
|
142
|
+
${route.slot ? `<span class="badge slot">@${route.slot}</span>` : ''}
|
|
143
|
+
<span class="expand-icon">${isExpanded ? '▼' : '▶'}</span>
|
|
144
|
+
</div>
|
|
145
|
+
${isExpanded ? `
|
|
146
|
+
<div class="flight-devtools-item-details">
|
|
147
|
+
<div class="detail-row">
|
|
148
|
+
<span class="detail-label">File:</span>
|
|
149
|
+
<span class="detail-value mono">${route.filePath}</span>
|
|
150
|
+
</div>
|
|
151
|
+
${route.params ? `
|
|
152
|
+
<div class="detail-row">
|
|
153
|
+
<span class="detail-label">Params:</span>
|
|
154
|
+
<span class="detail-value">${route.params.join(', ')}</span>
|
|
155
|
+
</div>
|
|
156
|
+
` : ''}
|
|
157
|
+
</div>
|
|
158
|
+
` : ''}
|
|
84
159
|
</div>
|
|
85
160
|
`;
|
|
86
161
|
}
|
|
87
162
|
function renderRequests(requests) {
|
|
88
163
|
if (requests.length === 0) {
|
|
89
|
-
return '<div class="flight-devtools-empty">No requests yet</div>';
|
|
164
|
+
return '<div class="flight-devtools-empty">No requests captured yet<br/><small>Make some fetch requests to see them here</small></div>';
|
|
90
165
|
}
|
|
91
166
|
return `
|
|
167
|
+
<div class="flight-devtools-toolbar">
|
|
168
|
+
<span class="flight-devtools-info">${requests.length} requests</span>
|
|
169
|
+
<button data-action="clear-requests" class="flight-devtools-btn-small">Clear</button>
|
|
170
|
+
</div>
|
|
92
171
|
<div class="flight-devtools-list">
|
|
93
|
-
${requests.slice(0,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
172
|
+
${requests.slice(0, 50).map(req => renderRequestItem(req)).join('')}
|
|
173
|
+
</div>
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
function renderRequestItem(req) {
|
|
177
|
+
const isExpanded = panelState.expandedItems.has(req.id);
|
|
178
|
+
const statusClass = `status-${Math.floor(req.status / 100)}xx`;
|
|
179
|
+
return `
|
|
180
|
+
<div class="flight-devtools-item ${isExpanded ? 'expanded' : ''}" data-action="toggle-item" data-id="${req.id}">
|
|
181
|
+
<div class="flight-devtools-item-header">
|
|
182
|
+
<span class="method ${req.method.toLowerCase()}">${req.method}</span>
|
|
183
|
+
<span class="path">${req.path}</span>
|
|
184
|
+
<span class="status ${statusClass}">${req.status}</span>
|
|
185
|
+
<span class="duration">${formatDuration(req.duration)}</span>
|
|
186
|
+
<span class="time">${formatRelativeTime(req.timestamp)}</span>
|
|
187
|
+
</div>
|
|
188
|
+
${isExpanded ? `
|
|
189
|
+
<div class="flight-devtools-item-details">
|
|
190
|
+
<div class="detail-row">
|
|
191
|
+
<span class="detail-label">Duration:</span>
|
|
192
|
+
<span class="detail-value">${formatDuration(req.duration)}</span>
|
|
193
|
+
</div>
|
|
194
|
+
${req.size !== undefined ? `
|
|
195
|
+
<div class="detail-row">
|
|
196
|
+
<span class="detail-label">Size:</span>
|
|
197
|
+
<span class="detail-value">${formatBytes(req.size)}</span>
|
|
198
|
+
</div>
|
|
199
|
+
` : ''}
|
|
200
|
+
${req.type ? `
|
|
201
|
+
<div class="detail-row">
|
|
202
|
+
<span class="detail-label">Type:</span>
|
|
203
|
+
<span class="detail-value">${req.type}</span>
|
|
204
|
+
</div>
|
|
205
|
+
` : ''}
|
|
206
|
+
<div class="detail-row">
|
|
207
|
+
<span class="detail-label">Time:</span>
|
|
208
|
+
<span class="detail-value">${new Date(req.timestamp).toLocaleTimeString()}</span>
|
|
209
|
+
</div>
|
|
99
210
|
</div>
|
|
100
|
-
`
|
|
211
|
+
` : ''}
|
|
101
212
|
</div>
|
|
102
213
|
`;
|
|
103
214
|
}
|
|
@@ -109,23 +220,143 @@ export function injectDevToolsPanel() {
|
|
|
109
220
|
<div class="flight-devtools-list">
|
|
110
221
|
${state.adapters.map(adapter => `
|
|
111
222
|
<div class="flight-devtools-item">
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
223
|
+
<div class="flight-devtools-item-header">
|
|
224
|
+
<span class="adapter-icon">${getAdapterIcon(adapter.type)}</span>
|
|
225
|
+
<span class="adapter-name">${adapter.name}</span>
|
|
226
|
+
<span class="adapter-type">${adapter.type}</span>
|
|
227
|
+
<span class="adapter-status status-${adapter.status}">${adapter.status}</span>
|
|
228
|
+
</div>
|
|
115
229
|
</div>
|
|
116
230
|
`).join('')}
|
|
117
231
|
</div>
|
|
118
232
|
`;
|
|
119
233
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
234
|
+
function getAdapterIcon(type) {
|
|
235
|
+
const icons = {
|
|
236
|
+
db: '🗄️',
|
|
237
|
+
auth: '🔐',
|
|
238
|
+
storage: '📦',
|
|
239
|
+
email: '✉️',
|
|
240
|
+
payments: '💳',
|
|
241
|
+
realtime: '⚡',
|
|
242
|
+
other: '🔌',
|
|
243
|
+
};
|
|
244
|
+
return icons[type] || '🔌';
|
|
245
|
+
}
|
|
246
|
+
function renderPerformance(state) {
|
|
247
|
+
const perf = state.performance;
|
|
248
|
+
const hasMetrics = Object.keys(perf).some(k => perf[k] !== undefined);
|
|
249
|
+
if (!hasMetrics) {
|
|
250
|
+
return '<div class="flight-devtools-empty">Performance metrics will appear after page load</div>';
|
|
251
|
+
}
|
|
252
|
+
const metrics = [
|
|
253
|
+
{ label: 'LCP', value: perf.lcp, unit: 'ms', good: 2500, desc: 'Largest Contentful Paint' },
|
|
254
|
+
{ label: 'FID', value: perf.fid, unit: 'ms', good: 100, desc: 'First Input Delay' },
|
|
255
|
+
{ label: 'CLS', value: perf.cls, unit: '', good: 0.1, desc: 'Cumulative Layout Shift' },
|
|
256
|
+
{ label: 'FCP', value: perf.fcp, unit: 'ms', good: 1800, desc: 'First Contentful Paint' },
|
|
257
|
+
{ label: 'TTFB', value: perf.ttfb, unit: 'ms', good: 800, desc: 'Time to First Byte' },
|
|
258
|
+
].filter(m => m.value !== undefined);
|
|
259
|
+
return `
|
|
260
|
+
<div class="flight-devtools-list">
|
|
261
|
+
<div class="flight-devtools-section-title">Core Web Vitals</div>
|
|
262
|
+
<div class="flight-devtools-metrics">
|
|
263
|
+
${metrics.map(m => {
|
|
264
|
+
const status = m.value <= m.good ? 'good' : m.value <= m.good * 2 ? 'needs-improvement' : 'poor';
|
|
265
|
+
return `
|
|
266
|
+
<div class="flight-devtools-metric ${status}">
|
|
267
|
+
<div class="metric-label">${m.label}</div>
|
|
268
|
+
<div class="metric-value">${m.unit ? formatDuration(m.value) : m.value.toFixed(3)}</div>
|
|
269
|
+
<div class="metric-desc">${m.desc}</div>
|
|
270
|
+
</div>
|
|
271
|
+
`;
|
|
272
|
+
}).join('')}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
`;
|
|
276
|
+
}
|
|
277
|
+
function renderConsole(state) {
|
|
278
|
+
const entries = [
|
|
279
|
+
...state.errors.map(e => ({ id: e.id, level: 'error', message: e.message, timestamp: e.timestamp, stack: e.stack })),
|
|
280
|
+
...state.console.map(c => ({ id: c.id, level: c.level, message: c.message, timestamp: c.timestamp, stack: c.stack })),
|
|
281
|
+
].sort((a, b) => b.timestamp - a.timestamp);
|
|
282
|
+
const filtered = panelState.consoleFilter === 'all'
|
|
283
|
+
? entries
|
|
284
|
+
: entries.filter(e => e.level === panelState.consoleFilter);
|
|
285
|
+
return `
|
|
286
|
+
<div class="flight-devtools-toolbar">
|
|
287
|
+
<div class="flight-devtools-filter-group">
|
|
288
|
+
${['all', 'log', 'warn', 'error'].map(f => `
|
|
289
|
+
<button
|
|
290
|
+
class="flight-devtools-filter-btn ${panelState.consoleFilter === f ? 'active' : ''}"
|
|
291
|
+
data-action="filter-console"
|
|
292
|
+
data-filter="${f}"
|
|
293
|
+
>${f}</button>
|
|
294
|
+
`).join('')}
|
|
295
|
+
</div>
|
|
296
|
+
<button data-action="clear-console" class="flight-devtools-btn-small">Clear</button>
|
|
297
|
+
</div>
|
|
298
|
+
<div class="flight-devtools-list console-list">
|
|
299
|
+
${filtered.length === 0
|
|
300
|
+
? '<div class="flight-devtools-empty">No console entries</div>'
|
|
301
|
+
: filtered.slice(0, 100).map(entry => `
|
|
302
|
+
<div class="flight-devtools-console-item level-${entry.level}">
|
|
303
|
+
<span class="console-time">${formatRelativeTime(entry.timestamp)}</span>
|
|
304
|
+
<span class="console-level">${entry.level}</span>
|
|
305
|
+
<span class="console-message">${escapeHtml(entry.message)}</span>
|
|
306
|
+
</div>
|
|
307
|
+
`).join('')}
|
|
308
|
+
</div>
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
function escapeHtml(text) {
|
|
312
|
+
const div = document.createElement('div');
|
|
313
|
+
div.textContent = text;
|
|
314
|
+
return div.innerHTML;
|
|
315
|
+
}
|
|
316
|
+
function attachEventListeners(state) {
|
|
317
|
+
container.querySelectorAll('[data-action]').forEach(el => {
|
|
318
|
+
const action = el.getAttribute('data-action');
|
|
319
|
+
el.addEventListener('click', (e) => {
|
|
320
|
+
e.stopPropagation();
|
|
321
|
+
switch (action) {
|
|
322
|
+
case 'toggle':
|
|
323
|
+
panelState.isOpen = !panelState.isOpen;
|
|
324
|
+
break;
|
|
325
|
+
case 'tab':
|
|
326
|
+
panelState.currentTab = el.getAttribute('data-tab');
|
|
327
|
+
break;
|
|
328
|
+
case 'toggle-item':
|
|
329
|
+
const id = el.getAttribute('data-id');
|
|
330
|
+
if (panelState.expandedItems.has(id)) {
|
|
331
|
+
panelState.expandedItems.delete(id);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
panelState.expandedItems.add(id);
|
|
335
|
+
}
|
|
336
|
+
break;
|
|
337
|
+
case 'clear-requests':
|
|
338
|
+
devTools.clearRequests();
|
|
339
|
+
break;
|
|
340
|
+
case 'clear-console':
|
|
341
|
+
devTools.clearConsole();
|
|
342
|
+
devTools.clearErrors();
|
|
343
|
+
break;
|
|
344
|
+
case 'filter-console':
|
|
345
|
+
panelState.consoleFilter = el.getAttribute('data-filter');
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
render(devTools.getState());
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
// Route filter input
|
|
352
|
+
const filterInput = container.querySelector('[data-action="filter-routes"]');
|
|
353
|
+
if (filterInput) {
|
|
354
|
+
filterInput.addEventListener('input', (e) => {
|
|
355
|
+
panelState.routeFilter = e.target.value;
|
|
356
|
+
render(devTools.getState());
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
129
360
|
// Subscribe to state changes
|
|
130
361
|
devTools.subscribe(render);
|
|
131
362
|
}
|
|
@@ -135,134 +366,450 @@ export function injectDevToolsPanel() {
|
|
|
135
366
|
function getDevToolsStyles() {
|
|
136
367
|
return `
|
|
137
368
|
#flight-devtools {
|
|
138
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
369
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
139
370
|
font-size: 12px;
|
|
371
|
+
line-height: 1.4;
|
|
372
|
+
--green: #22c55e;
|
|
373
|
+
--green-dark: #16a34a;
|
|
374
|
+
--bg: #0f0f0f;
|
|
375
|
+
--bg-2: #1a1a1a;
|
|
376
|
+
--bg-3: #252525;
|
|
377
|
+
--border: #333;
|
|
378
|
+
--text: #e5e5e5;
|
|
379
|
+
--text-muted: #888;
|
|
140
380
|
}
|
|
141
381
|
|
|
142
382
|
.flight-devtools {
|
|
143
383
|
position: fixed;
|
|
144
|
-
z-index:
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.flight-devtools.bottom-right {
|
|
148
|
-
bottom: 16px;
|
|
149
|
-
right: 16px;
|
|
384
|
+
z-index: 999999;
|
|
150
385
|
}
|
|
151
386
|
|
|
152
|
-
.flight-devtools.bottom-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
387
|
+
.flight-devtools.bottom-right { bottom: 16px; right: 16px; }
|
|
388
|
+
.flight-devtools.bottom-left { bottom: 16px; left: 16px; }
|
|
389
|
+
.flight-devtools.top-right { top: 16px; right: 16px; }
|
|
390
|
+
.flight-devtools.top-left { top: 16px; left: 16px; }
|
|
156
391
|
|
|
157
392
|
.flight-devtools-toggle {
|
|
158
393
|
width: 48px;
|
|
159
394
|
height: 48px;
|
|
160
395
|
border-radius: 50%;
|
|
161
396
|
border: none;
|
|
162
|
-
background:
|
|
397
|
+
background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
|
|
163
398
|
color: white;
|
|
164
|
-
font-
|
|
399
|
+
font-size: 20px;
|
|
165
400
|
cursor: pointer;
|
|
166
|
-
box-shadow: 0 4px
|
|
401
|
+
box-shadow: 0 4px 20px rgba(34, 197, 94, 0.4);
|
|
402
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.flight-devtools-toggle:hover {
|
|
406
|
+
transform: scale(1.05);
|
|
407
|
+
box-shadow: 0 6px 24px rgba(34, 197, 94, 0.5);
|
|
167
408
|
}
|
|
168
409
|
|
|
169
410
|
.flight-devtools.open .flight-devtools-toggle {
|
|
170
|
-
border-radius: 50%;
|
|
171
411
|
position: absolute;
|
|
172
|
-
top:
|
|
173
|
-
right:
|
|
174
|
-
width:
|
|
175
|
-
height:
|
|
412
|
+
top: 12px;
|
|
413
|
+
right: 12px;
|
|
414
|
+
width: 28px;
|
|
415
|
+
height: 28px;
|
|
176
416
|
font-size: 14px;
|
|
417
|
+
background: var(--bg-3);
|
|
418
|
+
box-shadow: none;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.flight-devtools-badge {
|
|
422
|
+
position: absolute;
|
|
423
|
+
top: -4px;
|
|
424
|
+
right: -4px;
|
|
425
|
+
width: 18px;
|
|
426
|
+
height: 18px;
|
|
427
|
+
border-radius: 50%;
|
|
428
|
+
background: #ef4444;
|
|
429
|
+
color: white;
|
|
430
|
+
font-size: 10px;
|
|
431
|
+
font-weight: 600;
|
|
432
|
+
display: flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
justify-content: center;
|
|
177
435
|
}
|
|
178
436
|
|
|
179
437
|
.flight-devtools-panel {
|
|
180
438
|
position: absolute;
|
|
181
439
|
bottom: 60px;
|
|
182
440
|
right: 0;
|
|
183
|
-
width:
|
|
184
|
-
max-height:
|
|
185
|
-
background:
|
|
441
|
+
width: 480px;
|
|
442
|
+
max-height: 600px;
|
|
443
|
+
background: var(--bg);
|
|
444
|
+
border: 1px solid var(--border);
|
|
186
445
|
border-radius: 12px;
|
|
187
446
|
overflow: hidden;
|
|
188
|
-
box-shadow: 0 8px
|
|
447
|
+
box-shadow: 0 8px 40px rgba(0,0,0,0.5);
|
|
448
|
+
display: flex;
|
|
449
|
+
flex-direction: column;
|
|
189
450
|
}
|
|
190
451
|
|
|
191
452
|
.flight-devtools-header {
|
|
192
|
-
padding: 12px 16px;
|
|
193
|
-
background:
|
|
453
|
+
padding: 12px 48px 12px 16px;
|
|
454
|
+
background: linear-gradient(135deg, var(--green) 0%, var(--green-dark) 100%);
|
|
194
455
|
color: white;
|
|
456
|
+
display: flex;
|
|
457
|
+
align-items: center;
|
|
458
|
+
justify-content: space-between;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.flight-devtools-logo {
|
|
195
462
|
font-weight: 600;
|
|
463
|
+
font-size: 14px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.flight-devtools-version {
|
|
467
|
+
font-size: 10px;
|
|
468
|
+
opacity: 0.8;
|
|
196
469
|
}
|
|
197
470
|
|
|
198
471
|
.flight-devtools-tabs {
|
|
199
472
|
display: flex;
|
|
200
|
-
|
|
473
|
+
background: var(--bg-2);
|
|
474
|
+
border-bottom: 1px solid var(--border);
|
|
201
475
|
}
|
|
202
476
|
|
|
203
477
|
.flight-devtools-tabs button {
|
|
204
478
|
flex: 1;
|
|
205
|
-
padding: 8px;
|
|
479
|
+
padding: 10px 8px;
|
|
206
480
|
border: none;
|
|
207
481
|
background: transparent;
|
|
208
|
-
color:
|
|
482
|
+
color: var(--text-muted);
|
|
483
|
+
font-size: 11px;
|
|
209
484
|
cursor: pointer;
|
|
485
|
+
border-bottom: 2px solid transparent;
|
|
486
|
+
transition: all 0.2s;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.flight-devtools-tabs button:hover {
|
|
490
|
+
color: var(--text);
|
|
491
|
+
background: var(--bg-3);
|
|
210
492
|
}
|
|
211
493
|
|
|
212
494
|
.flight-devtools-tabs button.active {
|
|
213
|
-
color:
|
|
214
|
-
border-bottom:
|
|
495
|
+
color: var(--green);
|
|
496
|
+
border-bottom-color: var(--green);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.flight-devtools-tabs .count {
|
|
500
|
+
display: inline-block;
|
|
501
|
+
min-width: 18px;
|
|
502
|
+
padding: 1px 5px;
|
|
503
|
+
margin-left: 4px;
|
|
504
|
+
background: var(--bg-3);
|
|
505
|
+
border-radius: 10px;
|
|
506
|
+
font-size: 10px;
|
|
215
507
|
}
|
|
216
508
|
|
|
217
509
|
.flight-devtools-content {
|
|
218
|
-
|
|
510
|
+
flex: 1;
|
|
219
511
|
overflow-y: auto;
|
|
512
|
+
max-height: 480px;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.flight-devtools-toolbar {
|
|
516
|
+
display: flex;
|
|
517
|
+
align-items: center;
|
|
518
|
+
justify-content: space-between;
|
|
519
|
+
padding: 8px 12px;
|
|
520
|
+
background: var(--bg-2);
|
|
521
|
+
border-bottom: 1px solid var(--border);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.flight-devtools-search {
|
|
525
|
+
flex: 1;
|
|
526
|
+
padding: 6px 10px;
|
|
527
|
+
background: var(--bg-3);
|
|
528
|
+
border: 1px solid var(--border);
|
|
529
|
+
border-radius: 6px;
|
|
530
|
+
color: var(--text);
|
|
531
|
+
font-size: 12px;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.flight-devtools-search:focus {
|
|
535
|
+
outline: none;
|
|
536
|
+
border-color: var(--green);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.flight-devtools-btn-small {
|
|
540
|
+
padding: 4px 10px;
|
|
541
|
+
background: var(--bg-3);
|
|
542
|
+
border: 1px solid var(--border);
|
|
543
|
+
border-radius: 4px;
|
|
544
|
+
color: var(--text-muted);
|
|
545
|
+
font-size: 11px;
|
|
546
|
+
cursor: pointer;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.flight-devtools-btn-small:hover {
|
|
550
|
+
background: var(--border);
|
|
551
|
+
color: var(--text);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.flight-devtools-info {
|
|
555
|
+
color: var(--text-muted);
|
|
556
|
+
font-size: 11px;
|
|
220
557
|
}
|
|
221
558
|
|
|
222
559
|
.flight-devtools-list {
|
|
223
560
|
padding: 8px;
|
|
224
561
|
}
|
|
225
562
|
|
|
563
|
+
.flight-devtools-section {
|
|
564
|
+
margin-bottom: 16px;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.flight-devtools-section-title {
|
|
568
|
+
padding: 8px 8px 4px;
|
|
569
|
+
color: var(--text-muted);
|
|
570
|
+
font-size: 10px;
|
|
571
|
+
font-weight: 600;
|
|
572
|
+
text-transform: uppercase;
|
|
573
|
+
letter-spacing: 0.5px;
|
|
574
|
+
}
|
|
575
|
+
|
|
226
576
|
.flight-devtools-item {
|
|
577
|
+
padding: 8px 10px;
|
|
578
|
+
border-radius: 6px;
|
|
579
|
+
cursor: pointer;
|
|
580
|
+
transition: background 0.15s;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.flight-devtools-item:hover {
|
|
584
|
+
background: var(--bg-2);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.flight-devtools-item.expanded {
|
|
588
|
+
background: var(--bg-2);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.flight-devtools-item-header {
|
|
227
592
|
display: flex;
|
|
593
|
+
align-items: center;
|
|
228
594
|
gap: 8px;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.flight-devtools-item-details {
|
|
598
|
+
margin-top: 8px;
|
|
229
599
|
padding: 8px;
|
|
600
|
+
background: var(--bg-3);
|
|
230
601
|
border-radius: 4px;
|
|
231
|
-
color: #ccc;
|
|
232
602
|
}
|
|
233
603
|
|
|
234
|
-
.
|
|
235
|
-
|
|
604
|
+
.detail-row {
|
|
605
|
+
display: flex;
|
|
606
|
+
padding: 3px 0;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.detail-label {
|
|
610
|
+
width: 80px;
|
|
611
|
+
color: var(--text-muted);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.detail-value {
|
|
615
|
+
flex: 1;
|
|
616
|
+
color: var(--text);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.detail-value.mono {
|
|
620
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
621
|
+
font-size: 11px;
|
|
236
622
|
}
|
|
237
623
|
|
|
238
|
-
.
|
|
239
|
-
width: 50px;
|
|
624
|
+
.method {
|
|
240
625
|
font-weight: 600;
|
|
626
|
+
font-size: 10px;
|
|
627
|
+
padding: 2px 6px;
|
|
628
|
+
border-radius: 3px;
|
|
629
|
+
background: var(--bg-3);
|
|
241
630
|
}
|
|
242
631
|
|
|
243
|
-
.
|
|
244
|
-
.
|
|
245
|
-
.
|
|
246
|
-
.
|
|
632
|
+
.method.get { color: #22c55e; }
|
|
633
|
+
.method.post { color: #3b82f6; }
|
|
634
|
+
.method.put { color: #f59e0b; }
|
|
635
|
+
.method.patch { color: #f59e0b; }
|
|
636
|
+
.method.delete { color: #ef4444; }
|
|
247
637
|
|
|
248
|
-
.
|
|
638
|
+
.path {
|
|
249
639
|
flex: 1;
|
|
250
|
-
font-family: monospace;
|
|
640
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
641
|
+
font-size: 11px;
|
|
642
|
+
color: var(--text);
|
|
643
|
+
overflow: hidden;
|
|
644
|
+
text-overflow: ellipsis;
|
|
645
|
+
white-space: nowrap;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.badge {
|
|
649
|
+
font-size: 9px;
|
|
650
|
+
padding: 2px 5px;
|
|
651
|
+
border-radius: 3px;
|
|
652
|
+
background: var(--bg-3);
|
|
653
|
+
color: var(--text-muted);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.badge.dynamic { color: #a78bfa; }
|
|
657
|
+
.badge.slot { color: #60a5fa; }
|
|
658
|
+
|
|
659
|
+
.expand-icon {
|
|
660
|
+
color: var(--text-muted);
|
|
661
|
+
font-size: 8px;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.status { font-weight: 600; }
|
|
665
|
+
.status.status-2xx { color: #22c55e; }
|
|
666
|
+
.status.status-3xx { color: #f59e0b; }
|
|
667
|
+
.status.status-4xx { color: #ef4444; }
|
|
668
|
+
.status.status-5xx { color: #a855f7; }
|
|
669
|
+
|
|
670
|
+
.duration, .time {
|
|
671
|
+
color: var(--text-muted);
|
|
672
|
+
font-size: 11px;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.adapter-icon {
|
|
676
|
+
font-size: 16px;
|
|
251
677
|
}
|
|
252
678
|
|
|
253
|
-
.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
679
|
+
.adapter-name {
|
|
680
|
+
flex: 1;
|
|
681
|
+
font-weight: 500;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.adapter-type {
|
|
685
|
+
color: var(--text-muted);
|
|
686
|
+
font-size: 10px;
|
|
687
|
+
}
|
|
257
688
|
|
|
258
|
-
.
|
|
259
|
-
|
|
689
|
+
.adapter-status {
|
|
690
|
+
font-size: 10px;
|
|
691
|
+
padding: 2px 6px;
|
|
692
|
+
border-radius: 3px;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.adapter-status.status-connected { background: rgba(34, 197, 94, 0.2); color: #22c55e; }
|
|
696
|
+
.adapter-status.status-disconnected { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
|
|
697
|
+
.adapter-status.status-error { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
|
|
698
|
+
|
|
699
|
+
.flight-devtools-metrics {
|
|
700
|
+
display: grid;
|
|
701
|
+
grid-template-columns: repeat(3, 1fr);
|
|
702
|
+
gap: 8px;
|
|
703
|
+
padding: 8px;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.flight-devtools-metric {
|
|
707
|
+
padding: 12px;
|
|
708
|
+
background: var(--bg-2);
|
|
709
|
+
border-radius: 8px;
|
|
710
|
+
text-align: center;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.flight-devtools-metric.good { border-left: 3px solid #22c55e; }
|
|
714
|
+
.flight-devtools-metric.needs-improvement { border-left: 3px solid #f59e0b; }
|
|
715
|
+
.flight-devtools-metric.poor { border-left: 3px solid #ef4444; }
|
|
716
|
+
|
|
717
|
+
.metric-label {
|
|
718
|
+
font-weight: 600;
|
|
719
|
+
font-size: 11px;
|
|
720
|
+
color: var(--text);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.metric-value {
|
|
724
|
+
font-size: 18px;
|
|
725
|
+
font-weight: 600;
|
|
726
|
+
margin: 4px 0;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
.flight-devtools-metric.good .metric-value { color: #22c55e; }
|
|
730
|
+
.flight-devtools-metric.needs-improvement .metric-value { color: #f59e0b; }
|
|
731
|
+
.flight-devtools-metric.poor .metric-value { color: #ef4444; }
|
|
732
|
+
|
|
733
|
+
.metric-desc {
|
|
734
|
+
font-size: 9px;
|
|
735
|
+
color: var(--text-muted);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.flight-devtools-filter-group {
|
|
739
|
+
display: flex;
|
|
740
|
+
gap: 4px;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.flight-devtools-filter-btn {
|
|
744
|
+
padding: 4px 8px;
|
|
745
|
+
background: transparent;
|
|
746
|
+
border: 1px solid var(--border);
|
|
747
|
+
border-radius: 4px;
|
|
748
|
+
color: var(--text-muted);
|
|
749
|
+
font-size: 10px;
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
text-transform: capitalize;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.flight-devtools-filter-btn.active {
|
|
755
|
+
background: var(--green);
|
|
756
|
+
border-color: var(--green);
|
|
757
|
+
color: white;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.console-list {
|
|
761
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
762
|
+
font-size: 11px;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.flight-devtools-console-item {
|
|
766
|
+
display: flex;
|
|
767
|
+
gap: 8px;
|
|
768
|
+
padding: 6px 8px;
|
|
769
|
+
border-bottom: 1px solid var(--border);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.flight-devtools-console-item.level-error {
|
|
773
|
+
background: rgba(239, 68, 68, 0.1);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.flight-devtools-console-item.level-warn {
|
|
777
|
+
background: rgba(245, 158, 11, 0.1);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.console-time {
|
|
781
|
+
color: var(--text-muted);
|
|
782
|
+
flex-shrink: 0;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.console-level {
|
|
786
|
+
flex-shrink: 0;
|
|
787
|
+
width: 40px;
|
|
788
|
+
font-weight: 600;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.level-log .console-level { color: #888; }
|
|
792
|
+
.level-info .console-level { color: #3b82f6; }
|
|
793
|
+
.level-warn .console-level { color: #f59e0b; }
|
|
794
|
+
.level-error .console-level { color: #ef4444; }
|
|
795
|
+
|
|
796
|
+
.console-message {
|
|
797
|
+
flex: 1;
|
|
798
|
+
word-break: break-word;
|
|
799
|
+
color: var(--text);
|
|
260
800
|
}
|
|
261
801
|
|
|
262
802
|
.flight-devtools-empty {
|
|
263
|
-
padding:
|
|
803
|
+
padding: 40px 20px;
|
|
264
804
|
text-align: center;
|
|
265
|
-
color:
|
|
805
|
+
color: var(--text-muted);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.flight-devtools-empty small {
|
|
809
|
+
display: block;
|
|
810
|
+
margin-top: 8px;
|
|
811
|
+
font-size: 11px;
|
|
812
|
+
opacity: 0.7;
|
|
266
813
|
}
|
|
267
814
|
`;
|
|
268
815
|
}
|