@rozenite/network-activity-plugin 1.0.0-alpha.5 → 1.0.0-alpha.7
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.html → App.html} +3 -3
- package/dist/assets/App-CIflVb88.js +24164 -0
- package/dist/assets/App-Czu6Vt2P.css +1233 -0
- package/dist/react-native.cjs +1 -1
- package/dist/react-native.d.ts +1 -90
- package/dist/rozenite.config.d.ts +7 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/network-inspector.d.ts +8 -0
- package/dist/src/react-native/network-requests-registry.d.ts +6 -0
- package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -0
- package/dist/src/react-native/xhr-interceptor.d.ts +38 -0
- package/dist/src/shared/client.d.ts +64 -0
- package/dist/src/ui/App.d.ts +1 -0
- package/dist/src/ui/components/Badge.d.ts +9 -0
- package/dist/src/ui/components/Button.d.ts +11 -0
- package/dist/src/ui/components/Input.d.ts +3 -0
- package/dist/src/ui/components/JsonTree.d.ts +5 -0
- package/dist/src/ui/components/RequestList.d.ts +45 -0
- package/dist/src/ui/components/ScrollArea.d.ts +4 -0
- package/dist/src/ui/components/Separator.d.ts +3 -0
- package/dist/src/ui/tabs/CookiesTab.d.ts +8 -0
- package/dist/src/ui/tabs/HeadersTab.d.ts +17 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +10 -0
- package/dist/src/ui/tabs/ResponseTab.d.ts +12 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +7 -0
- package/dist/src/ui/types.d.ts +23 -0
- package/dist/src/ui/utils.d.ts +2 -0
- package/dist/src/ui/views/InspectorView.d.ts +5 -0
- package/dist/src/ui/views/LoadingView.d.ts +1 -0
- package/dist/useNetworkActivityDevTools.cjs +360 -0
- package/dist/useNetworkActivityDevTools.js +108 -236
- package/package.json +23 -16
- package/postcss.config.js +6 -0
- package/rozenite.config.ts +1 -1
- package/src/react-native/network-inspector.ts +113 -260
- package/src/react-native/network-requests-registry.ts +7 -77
- package/src/react-native/useNetworkActivityDevTools.ts +1 -1
- package/src/react-native/xhr-interceptor.ts +2 -2
- package/src/react-native/xml-request.d.ts +11 -1
- package/src/shared/client.ts +80 -0
- package/src/ui/App.tsx +19 -0
- package/src/ui/components/Badge.tsx +36 -0
- package/src/ui/components/Button.tsx +56 -0
- package/src/ui/components/Input.tsx +22 -0
- package/src/ui/components/JsonTree.tsx +37 -0
- package/src/ui/components/RequestList.tsx +376 -0
- package/src/ui/components/ScrollArea.tsx +48 -0
- package/src/ui/components/Separator.tsx +31 -0
- package/src/ui/components/Tabs.tsx +55 -0
- package/src/ui/globals.css +90 -0
- package/src/ui/tabs/CookiesTab.tsx +290 -0
- package/src/ui/tabs/HeadersTab.tsx +117 -0
- package/src/ui/tabs/RequestTab.tsx +72 -0
- package/src/ui/tabs/ResponseTab.tsx +140 -0
- package/src/ui/tabs/TimingTab.tsx +71 -0
- package/src/ui/types.ts +30 -0
- package/src/ui/utils.ts +5 -97
- package/src/ui/views/InspectorView.tsx +349 -0
- package/src/ui/views/LoadingView.tsx +19 -0
- package/tailwind.config.ts +93 -0
- package/dist/assets/panel-BNxB_KsS.js +0 -16663
- package/dist/assets/panel-DXGMsavf.css +0 -555
- package/src/types/client.ts +0 -111
- package/src/types/network.ts +0 -32
- package/src/ui/components.module.css +0 -158
- package/src/ui/components.tsx +0 -241
- package/src/ui/network-details.module.css +0 -197
- package/src/ui/network-details.tsx +0 -345
- package/src/ui/network-list.module.css +0 -128
- package/src/ui/network-list.tsx +0 -240
- package/src/ui/network-toolbar.module.css +0 -9
- package/src/ui/network-toolbar.tsx +0 -34
- package/src/ui/panel.module.css +0 -67
- package/src/ui/panel.tsx +0 -318
- package/src/ui/tanstack-query.tsx +0 -204
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { NetworkEntry } from '../types/network';
|
|
3
|
-
import { formatFileSize, formatDuration, formatLongUrl } from './utils';
|
|
4
|
-
import { Card, EmptyState, Tooltip } from './components';
|
|
5
|
-
import styles from './network-details.module.css';
|
|
6
|
-
|
|
7
|
-
// Enhanced network entry type to match the panel
|
|
8
|
-
type EnhancedNetworkEntry = NetworkEntry & {
|
|
9
|
-
type?: string;
|
|
10
|
-
initiator?: {
|
|
11
|
-
type: string;
|
|
12
|
-
url?: string;
|
|
13
|
-
lineNumber?: number;
|
|
14
|
-
columnNumber?: number;
|
|
15
|
-
};
|
|
16
|
-
request?: {
|
|
17
|
-
url: string;
|
|
18
|
-
method: string;
|
|
19
|
-
headers: Record<string, string>;
|
|
20
|
-
postData?: string;
|
|
21
|
-
hasPostData?: boolean;
|
|
22
|
-
};
|
|
23
|
-
response?: {
|
|
24
|
-
url: string;
|
|
25
|
-
status: number;
|
|
26
|
-
statusText: string;
|
|
27
|
-
headers: Record<string, string>;
|
|
28
|
-
mimeType: string;
|
|
29
|
-
encodedDataLength: number;
|
|
30
|
-
responseTime: number;
|
|
31
|
-
};
|
|
32
|
-
responseBody?: {
|
|
33
|
-
body: string;
|
|
34
|
-
base64Encoded: boolean;
|
|
35
|
-
};
|
|
36
|
-
dataLength?: number;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Symbolication response types
|
|
40
|
-
interface SymbolicatedStackFrame {
|
|
41
|
-
column: number;
|
|
42
|
-
file: string;
|
|
43
|
-
lineNumber: number;
|
|
44
|
-
methodName: string;
|
|
45
|
-
collapse: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface CodeFrame {
|
|
49
|
-
content: string;
|
|
50
|
-
location: {
|
|
51
|
-
row: number;
|
|
52
|
-
column: number;
|
|
53
|
-
};
|
|
54
|
-
fileName: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface SymbolicationResponse {
|
|
58
|
-
codeFrame: CodeFrame;
|
|
59
|
-
stack: SymbolicatedStackFrame[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface NetworkDetailsProps {
|
|
63
|
-
entry: EnhancedNetworkEntry | null;
|
|
64
|
-
onRequestResponseBody?: (requestId: string) => void;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const NetworkDetails: React.FC<NetworkDetailsProps> = ({
|
|
68
|
-
entry,
|
|
69
|
-
onRequestResponseBody,
|
|
70
|
-
}) => {
|
|
71
|
-
const [isSymbolicating, setIsSymbolicating] = useState(false);
|
|
72
|
-
const [, forceUpdate] = useState({});
|
|
73
|
-
|
|
74
|
-
const symbolicateCaller = async () => {
|
|
75
|
-
if (!entry?.initiator) return;
|
|
76
|
-
|
|
77
|
-
setIsSymbolicating(true);
|
|
78
|
-
try {
|
|
79
|
-
const stackData = {
|
|
80
|
-
stack: [
|
|
81
|
-
{
|
|
82
|
-
column: entry.initiator.columnNumber || 0,
|
|
83
|
-
file: entry.initiator.url || '',
|
|
84
|
-
lineNumber: entry.initiator.lineNumber || 0,
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const response = await fetch(`${window.location.origin}/symbolicate`, {
|
|
90
|
-
method: 'POST',
|
|
91
|
-
headers: {
|
|
92
|
-
'Content-Type': 'application/json',
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify(stackData),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (response.ok) {
|
|
98
|
-
const data: SymbolicationResponse = await response.json();
|
|
99
|
-
|
|
100
|
-
// Update the entry.initiator with symbolicated data
|
|
101
|
-
if (data.stack && data.stack.length > 0) {
|
|
102
|
-
const symbolicatedFrame = data.stack[0];
|
|
103
|
-
if (entry.initiator) {
|
|
104
|
-
entry.initiator.url = symbolicatedFrame.file;
|
|
105
|
-
entry.initiator.lineNumber = symbolicatedFrame.lineNumber;
|
|
106
|
-
entry.initiator.columnNumber = symbolicatedFrame.column;
|
|
107
|
-
// Add method name to initiator
|
|
108
|
-
(entry.initiator as any).methodName = symbolicatedFrame.methodName;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
forceUpdate({});
|
|
112
|
-
} else {
|
|
113
|
-
console.error('Symbolication failed:', response.statusText);
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('Symbolication error:', error);
|
|
117
|
-
} finally {
|
|
118
|
-
setIsSymbolicating(false);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
if (!entry) {
|
|
123
|
-
return <EmptyState message="Select a request to view details" />;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<div className={styles.container}>
|
|
128
|
-
{/* General Information */}
|
|
129
|
-
<Card className={styles.card}>
|
|
130
|
-
<h3 className={styles.cardTitle}>General</h3>
|
|
131
|
-
<div className={styles.infoText}>
|
|
132
|
-
<div className={styles.infoRowUrl}>
|
|
133
|
-
<strong>Request URL:</strong>
|
|
134
|
-
<Tooltip content={entry.url} showOnlyWhenTruncated>
|
|
135
|
-
<span className={styles.urlText}>
|
|
136
|
-
{formatLongUrl(entry.url, 100)}
|
|
137
|
-
</span>
|
|
138
|
-
</Tooltip>
|
|
139
|
-
</div>
|
|
140
|
-
<div className={styles.infoRow}>
|
|
141
|
-
<strong>Request Method:</strong> {entry.method}
|
|
142
|
-
</div>
|
|
143
|
-
<div className={styles.infoRow}>
|
|
144
|
-
<strong>Status Code:</strong> {entry.response?.status || 'Pending'}
|
|
145
|
-
</div>
|
|
146
|
-
<div className={styles.infoRow}>
|
|
147
|
-
<strong>Status:</strong> {entry.status}
|
|
148
|
-
</div>
|
|
149
|
-
{entry.type && (
|
|
150
|
-
<div className={styles.infoRow}>
|
|
151
|
-
<strong>Resource Type:</strong> {entry.type}
|
|
152
|
-
</div>
|
|
153
|
-
)}
|
|
154
|
-
{entry.initiator && (
|
|
155
|
-
<div className={styles.infoRow}>
|
|
156
|
-
<strong>Initiator:</strong> {entry.initiator.type}
|
|
157
|
-
{(entry.initiator as any).methodName && (
|
|
158
|
-
<div className={styles.infoRowSub}>
|
|
159
|
-
<strong>Method:</strong> {(entry.initiator as any).methodName}
|
|
160
|
-
</div>
|
|
161
|
-
)}
|
|
162
|
-
{entry.initiator.url && (
|
|
163
|
-
<div className={styles.infoRowSub}>
|
|
164
|
-
<strong>URL:</strong> {entry.initiator.url}
|
|
165
|
-
</div>
|
|
166
|
-
)}
|
|
167
|
-
{entry.initiator.lineNumber && (
|
|
168
|
-
<div className={styles.infoRowSub}>
|
|
169
|
-
<strong>Line:</strong> {entry.initiator.lineNumber}:
|
|
170
|
-
{entry.initiator.columnNumber || 0}
|
|
171
|
-
</div>
|
|
172
|
-
)}
|
|
173
|
-
<div className={styles.infoRowSub}>
|
|
174
|
-
<button
|
|
175
|
-
className={styles.symbolicateButton}
|
|
176
|
-
onClick={symbolicateCaller}
|
|
177
|
-
disabled={isSymbolicating}
|
|
178
|
-
>
|
|
179
|
-
{isSymbolicating
|
|
180
|
-
? 'Symbolicating...'
|
|
181
|
-
: 'Symbolicate initiator'}
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
)}
|
|
186
|
-
</div>
|
|
187
|
-
</Card>
|
|
188
|
-
|
|
189
|
-
{/* Response Headers */}
|
|
190
|
-
{entry.response && (
|
|
191
|
-
<Card className={styles.card}>
|
|
192
|
-
<h3 className={styles.cardTitle}>Response Headers</h3>
|
|
193
|
-
<div className={styles.headersContainer}>
|
|
194
|
-
{Object.entries(entry.response.headers).map(([key, value]) => (
|
|
195
|
-
<div key={key} className={styles.headerRow}>
|
|
196
|
-
<strong>{key}:</strong>
|
|
197
|
-
<Tooltip content={value} showOnlyWhenTruncated>
|
|
198
|
-
<span className={styles.headerValue}>{value}</span>
|
|
199
|
-
</Tooltip>
|
|
200
|
-
</div>
|
|
201
|
-
))}
|
|
202
|
-
</div>
|
|
203
|
-
</Card>
|
|
204
|
-
)}
|
|
205
|
-
|
|
206
|
-
{/* Request Headers */}
|
|
207
|
-
<Card className={styles.card}>
|
|
208
|
-
<h3 className={styles.cardTitle}>Request Headers</h3>
|
|
209
|
-
<div className={styles.headersContainer}>
|
|
210
|
-
{Object.entries(entry.headers).map(([key, value]) => (
|
|
211
|
-
<div key={key} className={styles.headerRow}>
|
|
212
|
-
<strong>{key}:</strong>
|
|
213
|
-
<Tooltip content={value} showOnlyWhenTruncated>
|
|
214
|
-
<span className={styles.headerValue}>{value}</span>
|
|
215
|
-
</Tooltip>
|
|
216
|
-
</div>
|
|
217
|
-
))}
|
|
218
|
-
</div>
|
|
219
|
-
</Card>
|
|
220
|
-
|
|
221
|
-
{/* Size Information */}
|
|
222
|
-
{(entry.encodedDataLength || entry.dataLength) && (
|
|
223
|
-
<Card className={styles.card}>
|
|
224
|
-
<h3 className={styles.cardTitle}>Size Information</h3>
|
|
225
|
-
<div className={styles.infoText}>
|
|
226
|
-
{entry.dataLength && (
|
|
227
|
-
<div className={styles.infoRow}>
|
|
228
|
-
<strong>Data Length:</strong> {formatFileSize(entry.dataLength)}
|
|
229
|
-
</div>
|
|
230
|
-
)}
|
|
231
|
-
{entry.encodedDataLength && (
|
|
232
|
-
<div className={styles.infoRow}>
|
|
233
|
-
<strong>Encoded Data Length:</strong>{' '}
|
|
234
|
-
{formatFileSize(entry.encodedDataLength)}
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
{entry.response?.mimeType && (
|
|
238
|
-
<div className={styles.infoRow}>
|
|
239
|
-
<strong>MIME Type:</strong> {entry.response.mimeType}
|
|
240
|
-
</div>
|
|
241
|
-
)}
|
|
242
|
-
</div>
|
|
243
|
-
</Card>
|
|
244
|
-
)}
|
|
245
|
-
|
|
246
|
-
{/* Timing Information */}
|
|
247
|
-
{entry.duration && (
|
|
248
|
-
<Card className={styles.card}>
|
|
249
|
-
<h3 className={styles.cardTitle}>Timing</h3>
|
|
250
|
-
<div className={styles.infoText}>
|
|
251
|
-
<div className={styles.infoRow}>
|
|
252
|
-
<strong>Duration:</strong> {formatDuration(entry.duration)}
|
|
253
|
-
</div>
|
|
254
|
-
<div className={styles.infoRow}>
|
|
255
|
-
<strong>Start Time:</strong>{' '}
|
|
256
|
-
{new Date(entry.startTime).toLocaleTimeString()}
|
|
257
|
-
</div>
|
|
258
|
-
{entry.endTime && (
|
|
259
|
-
<div className={styles.infoRow}>
|
|
260
|
-
<strong>End Time:</strong>{' '}
|
|
261
|
-
{new Date(entry.endTime).toLocaleTimeString()}
|
|
262
|
-
</div>
|
|
263
|
-
)}
|
|
264
|
-
{entry.response?.responseTime && (
|
|
265
|
-
<div className={styles.infoRow}>
|
|
266
|
-
<strong>Response Time:</strong>{' '}
|
|
267
|
-
{new Date(entry.response.responseTime).toLocaleTimeString()}
|
|
268
|
-
</div>
|
|
269
|
-
)}
|
|
270
|
-
</div>
|
|
271
|
-
</Card>
|
|
272
|
-
)}
|
|
273
|
-
|
|
274
|
-
{/* Error Information */}
|
|
275
|
-
{entry.status === 'failed' && (
|
|
276
|
-
<Card className={styles.card}>
|
|
277
|
-
<h3 className={styles.cardTitle}>Error Information</h3>
|
|
278
|
-
<div className={styles.infoText}>
|
|
279
|
-
<div className={styles.infoRow}>
|
|
280
|
-
<strong>Error:</strong> {entry.errorText || 'Unknown error'}
|
|
281
|
-
</div>
|
|
282
|
-
<div className={styles.infoRow}>
|
|
283
|
-
<strong>Canceled:</strong> {entry.canceled ? 'Yes' : 'No'}
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
</Card>
|
|
287
|
-
)}
|
|
288
|
-
|
|
289
|
-
{/* Post Data */}
|
|
290
|
-
{entry.postData && (
|
|
291
|
-
<Card className={styles.card}>
|
|
292
|
-
<h3 className={styles.cardTitle}>Post Data</h3>
|
|
293
|
-
<div className={styles.postDataContainer}>
|
|
294
|
-
<pre className={styles.postDataText}>{entry.postData}</pre>
|
|
295
|
-
</div>
|
|
296
|
-
</Card>
|
|
297
|
-
)}
|
|
298
|
-
|
|
299
|
-
{/* Response Body */}
|
|
300
|
-
{entry.response && (
|
|
301
|
-
<Card className={styles.card}>
|
|
302
|
-
<h3 className={styles.cardTitle}>Response Body</h3>
|
|
303
|
-
{entry.responseBody ? (
|
|
304
|
-
<div className={styles.responseBodyContainer}>
|
|
305
|
-
<pre className={styles.responseBodyText}>
|
|
306
|
-
{entry.responseBody.base64Encoded
|
|
307
|
-
? atob(entry.responseBody.body)
|
|
308
|
-
: entry.responseBody.body}
|
|
309
|
-
</pre>
|
|
310
|
-
</div>
|
|
311
|
-
) : (
|
|
312
|
-
<div className={styles.responseBodyContainer}>
|
|
313
|
-
<button
|
|
314
|
-
className={styles.loadResponseBodyButton}
|
|
315
|
-
onClick={() => onRequestResponseBody?.(entry.requestId)}
|
|
316
|
-
disabled={!onRequestResponseBody}
|
|
317
|
-
>
|
|
318
|
-
Load Response Body
|
|
319
|
-
</button>
|
|
320
|
-
</div>
|
|
321
|
-
)}
|
|
322
|
-
</Card>
|
|
323
|
-
)}
|
|
324
|
-
|
|
325
|
-
{/* Request Details */}
|
|
326
|
-
{entry.request && (
|
|
327
|
-
<Card className={styles.card}>
|
|
328
|
-
<h3 className={styles.cardTitle}>Request Details</h3>
|
|
329
|
-
<div className={styles.infoText}>
|
|
330
|
-
<div className={styles.infoRow}>
|
|
331
|
-
<strong>Has Post Data:</strong>{' '}
|
|
332
|
-
{entry.request.hasPostData ? 'Yes' : 'No'}
|
|
333
|
-
</div>
|
|
334
|
-
{entry.request.postData && (
|
|
335
|
-
<div className={styles.infoRow}>
|
|
336
|
-
<strong>Post Data Length:</strong>{' '}
|
|
337
|
-
{entry.request.postData.length} characters
|
|
338
|
-
</div>
|
|
339
|
-
)}
|
|
340
|
-
</div>
|
|
341
|
-
</Card>
|
|
342
|
-
)}
|
|
343
|
-
</div>
|
|
344
|
-
);
|
|
345
|
-
};
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
height: 100%;
|
|
3
|
-
overflow: auto;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
.virtualContainer {
|
|
7
|
-
height: 100%;
|
|
8
|
-
width: 100%;
|
|
9
|
-
position: relative;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.virtualItem {
|
|
13
|
-
position: absolute;
|
|
14
|
-
top: 0;
|
|
15
|
-
left: 0;
|
|
16
|
-
width: 100%;
|
|
17
|
-
height: 60px;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.listItem {
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
padding: 8px 12px;
|
|
24
|
-
border-bottom: 1px solid #e0e0e0;
|
|
25
|
-
background-color: white;
|
|
26
|
-
cursor: pointer;
|
|
27
|
-
font-size: 12px;
|
|
28
|
-
font-family: monospace;
|
|
29
|
-
height: 60px;
|
|
30
|
-
box-sizing: border-box;
|
|
31
|
-
min-width: 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.listItemSelected {
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
padding: 8px 12px;
|
|
38
|
-
border-bottom: 1px solid #e0e0e0;
|
|
39
|
-
background-color: #f5f5f5;
|
|
40
|
-
cursor: pointer;
|
|
41
|
-
font-size: 12px;
|
|
42
|
-
font-family: monospace;
|
|
43
|
-
height: 60px;
|
|
44
|
-
box-sizing: border-box;
|
|
45
|
-
min-width: 0;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.statusColumn {
|
|
49
|
-
width: 60px;
|
|
50
|
-
text-align: center;
|
|
51
|
-
flex-shrink: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.methodColumn {
|
|
55
|
-
width: 80px;
|
|
56
|
-
text-align: center;
|
|
57
|
-
flex-shrink: 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.urlColumn {
|
|
61
|
-
flex: 1;
|
|
62
|
-
overflow: hidden;
|
|
63
|
-
min-width: 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.typeColumn {
|
|
67
|
-
width: 100px;
|
|
68
|
-
text-align: center;
|
|
69
|
-
flex-shrink: 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.domainText {
|
|
73
|
-
font-weight: bold;
|
|
74
|
-
color: #333;
|
|
75
|
-
margin-bottom: 2px;
|
|
76
|
-
white-space: nowrap;
|
|
77
|
-
overflow: hidden;
|
|
78
|
-
text-overflow: ellipsis;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.pathText {
|
|
82
|
-
color: #666;
|
|
83
|
-
font-size: 11px;
|
|
84
|
-
white-space: nowrap;
|
|
85
|
-
overflow: hidden;
|
|
86
|
-
text-overflow: ellipsis;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.fullUrlText {
|
|
90
|
-
color: #999;
|
|
91
|
-
font-size: 10px;
|
|
92
|
-
white-space: nowrap;
|
|
93
|
-
overflow: hidden;
|
|
94
|
-
text-overflow: ellipsis;
|
|
95
|
-
margin-top: 1px;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.durationColumn {
|
|
99
|
-
width: 80px;
|
|
100
|
-
text-align: right;
|
|
101
|
-
margin-right: 8px;
|
|
102
|
-
flex-shrink: 0;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.sizeColumn {
|
|
106
|
-
width: 80px;
|
|
107
|
-
text-align: right;
|
|
108
|
-
flex-shrink: 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.columnText {
|
|
112
|
-
white-space: nowrap;
|
|
113
|
-
overflow: hidden;
|
|
114
|
-
text-overflow: ellipsis;
|
|
115
|
-
display: block;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.emptyContainer {
|
|
119
|
-
height: 100%;
|
|
120
|
-
display: flex;
|
|
121
|
-
align-items: center;
|
|
122
|
-
justify-content: center;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.emptyText {
|
|
126
|
-
color: #666;
|
|
127
|
-
font-size: 14px;
|
|
128
|
-
}
|
package/src/ui/network-list.tsx
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
-
import { NetworkEntry } from '../types/network';
|
|
4
|
-
import {
|
|
5
|
-
getStatusColor,
|
|
6
|
-
getMethodColor,
|
|
7
|
-
formatDuration,
|
|
8
|
-
formatFileSize,
|
|
9
|
-
parseUrl,
|
|
10
|
-
} from './utils';
|
|
11
|
-
import { Badge, Tooltip } from './components';
|
|
12
|
-
import styles from './network-list.module.css';
|
|
13
|
-
|
|
14
|
-
// Enhanced network entry type to match the panel
|
|
15
|
-
type EnhancedNetworkEntry = NetworkEntry & {
|
|
16
|
-
type?: string;
|
|
17
|
-
initiator?: {
|
|
18
|
-
type: string;
|
|
19
|
-
url?: string;
|
|
20
|
-
lineNumber?: number;
|
|
21
|
-
columnNumber?: number;
|
|
22
|
-
};
|
|
23
|
-
request?: {
|
|
24
|
-
url: string;
|
|
25
|
-
method: string;
|
|
26
|
-
headers: Record<string, string>;
|
|
27
|
-
postData?: string;
|
|
28
|
-
hasPostData?: boolean;
|
|
29
|
-
};
|
|
30
|
-
response?: {
|
|
31
|
-
url: string;
|
|
32
|
-
status: number;
|
|
33
|
-
statusText: string;
|
|
34
|
-
headers: Record<string, string>;
|
|
35
|
-
mimeType: string;
|
|
36
|
-
encodedDataLength: number;
|
|
37
|
-
responseTime: number;
|
|
38
|
-
};
|
|
39
|
-
dataLength?: number;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
interface NetworkListProps {
|
|
43
|
-
entries: EnhancedNetworkEntry[];
|
|
44
|
-
selectedRequestId: string | null;
|
|
45
|
-
onSelect: (requestId: string) => void;
|
|
46
|
-
height: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const ITEM_HEIGHT = 60; // Height of each network list item
|
|
50
|
-
|
|
51
|
-
const getResourceTypeColor = (type: string): string => {
|
|
52
|
-
switch (type) {
|
|
53
|
-
case 'Document':
|
|
54
|
-
return '#4285f4';
|
|
55
|
-
case 'Stylesheet':
|
|
56
|
-
return '#34a853';
|
|
57
|
-
case 'Script':
|
|
58
|
-
return '#fbbc04';
|
|
59
|
-
case 'Image':
|
|
60
|
-
return '#ea4335';
|
|
61
|
-
case 'Font':
|
|
62
|
-
return '#9c27b0';
|
|
63
|
-
case 'XHR':
|
|
64
|
-
return '#ff9800';
|
|
65
|
-
case 'Fetch':
|
|
66
|
-
return '#2196f3';
|
|
67
|
-
case 'WebSocket':
|
|
68
|
-
return '#00bcd4';
|
|
69
|
-
case 'Media':
|
|
70
|
-
return '#e91e63';
|
|
71
|
-
default:
|
|
72
|
-
return '#757575';
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const getStatusDisplay = (
|
|
77
|
-
entry: EnhancedNetworkEntry
|
|
78
|
-
): { text: string; color: string } => {
|
|
79
|
-
if (entry.status === 'failed') {
|
|
80
|
-
return { text: 'Failed', color: '#d32f2f' };
|
|
81
|
-
}
|
|
82
|
-
if (entry.status === 'pending') {
|
|
83
|
-
return { text: 'Pending', color: '#ff9800' };
|
|
84
|
-
}
|
|
85
|
-
if (entry.status === 'loading') {
|
|
86
|
-
return { text: 'Loading', color: '#2196f3' };
|
|
87
|
-
}
|
|
88
|
-
if (entry.status === 'finished') {
|
|
89
|
-
const status = entry.response?.status || 0;
|
|
90
|
-
if (status >= 400) {
|
|
91
|
-
return { text: status.toString(), color: '#d32f2f' };
|
|
92
|
-
}
|
|
93
|
-
if (status >= 300) {
|
|
94
|
-
return { text: status.toString(), color: '#ff9800' };
|
|
95
|
-
}
|
|
96
|
-
return { text: status.toString(), color: '#4caf50' };
|
|
97
|
-
}
|
|
98
|
-
return { text: '...', color: '#757575' };
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const NetworkList: React.FC<NetworkListProps> = ({
|
|
102
|
-
entries,
|
|
103
|
-
selectedRequestId,
|
|
104
|
-
onSelect,
|
|
105
|
-
height,
|
|
106
|
-
}) => {
|
|
107
|
-
const parentRef = React.useRef<HTMLDivElement>(null);
|
|
108
|
-
|
|
109
|
-
const virtualizer = useVirtualizer({
|
|
110
|
-
count: entries.length,
|
|
111
|
-
getScrollElement: () => parentRef.current,
|
|
112
|
-
estimateSize: () => ITEM_HEIGHT,
|
|
113
|
-
overscan: 5,
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const NetworkListItem: React.FC<{
|
|
117
|
-
entry: EnhancedNetworkEntry;
|
|
118
|
-
index: number;
|
|
119
|
-
}> = ({ entry, index }) => {
|
|
120
|
-
const method = entry.method;
|
|
121
|
-
const url = entry.url;
|
|
122
|
-
const { domain, path } = parseUrl(url);
|
|
123
|
-
const resourceType = entry.type || 'Other';
|
|
124
|
-
|
|
125
|
-
// Get size information - prefer dataLength over encodedDataLength for display
|
|
126
|
-
const displaySize = entry.dataLength || entry.encodedDataLength || 0;
|
|
127
|
-
const statusDisplay = getStatusDisplay(entry);
|
|
128
|
-
|
|
129
|
-
const isSelected = selectedRequestId === entry.requestId;
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div
|
|
133
|
-
className={isSelected ? styles.listItemSelected : styles.listItem}
|
|
134
|
-
onClick={() => onSelect(entry.requestId)}
|
|
135
|
-
>
|
|
136
|
-
<div className={styles.statusColumn}>
|
|
137
|
-
<Tooltip
|
|
138
|
-
content={`Status: ${statusDisplay.text}`}
|
|
139
|
-
showOnlyWhenTruncated
|
|
140
|
-
variant={
|
|
141
|
-
statusDisplay.color === '#d32f2f'
|
|
142
|
-
? 'error'
|
|
143
|
-
: statusDisplay.color === '#ff9800'
|
|
144
|
-
? 'warning'
|
|
145
|
-
: 'info'
|
|
146
|
-
}
|
|
147
|
-
>
|
|
148
|
-
<Badge color={statusDisplay.color}>{statusDisplay.text}</Badge>
|
|
149
|
-
</Tooltip>
|
|
150
|
-
</div>
|
|
151
|
-
<div className={styles.methodColumn}>
|
|
152
|
-
<Tooltip
|
|
153
|
-
content={`Method: ${method}`}
|
|
154
|
-
showOnlyWhenTruncated
|
|
155
|
-
variant="info"
|
|
156
|
-
>
|
|
157
|
-
<Badge color={getMethodColor(method)}>{method}</Badge>
|
|
158
|
-
</Tooltip>
|
|
159
|
-
</div>
|
|
160
|
-
<div className={styles.urlColumn}>
|
|
161
|
-
<Tooltip content={domain} showOnlyWhenTruncated>
|
|
162
|
-
<div className={styles.domainText}>{domain}</div>
|
|
163
|
-
</Tooltip>
|
|
164
|
-
<Tooltip content={path} showOnlyWhenTruncated>
|
|
165
|
-
<div className={styles.pathText}>{path}</div>
|
|
166
|
-
</Tooltip>
|
|
167
|
-
<Tooltip content={url} showOnlyWhenTruncated>
|
|
168
|
-
<div className={styles.fullUrlText}>{url}</div>
|
|
169
|
-
</Tooltip>
|
|
170
|
-
</div>
|
|
171
|
-
<div className={styles.typeColumn}>
|
|
172
|
-
<Tooltip
|
|
173
|
-
content={`Resource Type: ${resourceType}`}
|
|
174
|
-
showOnlyWhenTruncated
|
|
175
|
-
variant="info"
|
|
176
|
-
>
|
|
177
|
-
<Badge color={getResourceTypeColor(resourceType)}>
|
|
178
|
-
{resourceType}
|
|
179
|
-
</Badge>
|
|
180
|
-
</Tooltip>
|
|
181
|
-
</div>
|
|
182
|
-
<div className={styles.durationColumn}>
|
|
183
|
-
<Tooltip
|
|
184
|
-
content={`Duration: ${
|
|
185
|
-
entry.duration ? formatDuration(entry.duration) : 'Pending'
|
|
186
|
-
}`}
|
|
187
|
-
showOnlyWhenTruncated
|
|
188
|
-
>
|
|
189
|
-
<span className={styles.columnText}>
|
|
190
|
-
{entry.duration ? formatDuration(entry.duration) : '...'}
|
|
191
|
-
</span>
|
|
192
|
-
</Tooltip>
|
|
193
|
-
</div>
|
|
194
|
-
<div className={styles.sizeColumn}>
|
|
195
|
-
<Tooltip
|
|
196
|
-
content={`Size: ${
|
|
197
|
-
displaySize > 0 ? formatFileSize(displaySize) : 'Unknown'
|
|
198
|
-
}`}
|
|
199
|
-
showOnlyWhenTruncated
|
|
200
|
-
>
|
|
201
|
-
<span className={styles.columnText}>
|
|
202
|
-
{displaySize > 0 ? formatFileSize(displaySize) : '...'}
|
|
203
|
-
</span>
|
|
204
|
-
</Tooltip>
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
207
|
-
);
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<div ref={parentRef} className={styles.networkList} style={{ height }}>
|
|
212
|
-
<div
|
|
213
|
-
style={{
|
|
214
|
-
height: `${virtualizer.getTotalSize()}px`,
|
|
215
|
-
width: '100%',
|
|
216
|
-
position: 'relative',
|
|
217
|
-
}}
|
|
218
|
-
>
|
|
219
|
-
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
220
|
-
<div
|
|
221
|
-
key={virtualItem.key}
|
|
222
|
-
style={{
|
|
223
|
-
position: 'absolute',
|
|
224
|
-
top: 0,
|
|
225
|
-
left: 0,
|
|
226
|
-
width: '100%',
|
|
227
|
-
height: `${virtualItem.size}px`,
|
|
228
|
-
transform: `translateY(${virtualItem.start}px)`,
|
|
229
|
-
}}
|
|
230
|
-
>
|
|
231
|
-
<NetworkListItem
|
|
232
|
-
entry={entries[virtualItem.index]}
|
|
233
|
-
index={virtualItem.index}
|
|
234
|
-
/>
|
|
235
|
-
</div>
|
|
236
|
-
))}
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
);
|
|
240
|
-
};
|