@rozenite/network-activity-plugin 1.0.0-alpha.1 → 1.0.0-alpha.4
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/README.md +3 -5
- package/dist/assets/panel-C0o5JcM0.js +16664 -0
- package/dist/assets/panel-DXGMsavf.css +555 -0
- package/dist/panel.html +2 -2
- package/dist/react-native.cjs +8 -1
- package/dist/react-native.d.ts +86 -1
- package/dist/react-native.js +6 -171
- package/dist/rozenite.json +1 -1
- package/dist/useNetworkActivityDevTools.js +488 -0
- package/package.json +12 -9
- package/project.json +12 -0
- package/react-native.ts +2 -1
- package/rozenite.config.ts +1 -1
- package/src/css-modules.d.ts +1 -1
- package/src/react-native/network-inspector.ts +391 -0
- package/src/react-native/network-requests-registry.ts +122 -0
- package/src/react-native/useNetworkActivityDevTools.ts +6 -217
- package/src/react-native/xhr-interceptor.ts +211 -0
- package/src/react-native/xml-request.d.ts +23 -0
- package/src/types/client.ts +111 -0
- package/src/types/network.ts +26 -147
- package/src/ui/components.tsx +48 -26
- package/src/ui/network-details.module.css +140 -0
- package/src/ui/network-details.tsx +252 -41
- package/src/ui/network-list.module.css +6 -0
- package/src/ui/network-list.tsx +148 -53
- package/src/ui/network-toolbar.tsx +3 -9
- package/src/ui/panel.module.css +6 -0
- package/src/ui/panel.tsx +158 -40
- package/src/ui/tanstack-query.tsx +83 -76
- package/src/ui/utils.ts +22 -13
- package/tsconfig.json +13 -6
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +1 -1
- package/dist/assets/panel-C5YgUUj5.js +0 -54
- package/dist/assets/panel-NCVczPb1.css +0 -1
|
@@ -1,14 +1,124 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { NetworkEntry } from '../types/network';
|
|
3
3
|
import { formatFileSize, formatDuration, formatLongUrl } from './utils';
|
|
4
4
|
import { Card, EmptyState, Tooltip } from './components';
|
|
5
5
|
import styles from './network-details.module.css';
|
|
6
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
|
+
|
|
7
62
|
interface NetworkDetailsProps {
|
|
8
|
-
entry:
|
|
63
|
+
entry: EnhancedNetworkEntry | null;
|
|
64
|
+
onRequestResponseBody?: (requestId: string) => void;
|
|
9
65
|
}
|
|
10
66
|
|
|
11
|
-
export const NetworkDetails: React.FC<NetworkDetailsProps> = ({
|
|
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
|
+
|
|
12
122
|
if (!entry) {
|
|
13
123
|
return <EmptyState message="Select a request to view details" />;
|
|
14
124
|
}
|
|
@@ -20,25 +130,59 @@ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
|
|
|
20
130
|
<h3 className={styles.cardTitle}>General</h3>
|
|
21
131
|
<div className={styles.infoText}>
|
|
22
132
|
<div className={styles.infoRowUrl}>
|
|
23
|
-
<strong>Request URL:</strong>
|
|
24
|
-
<Tooltip content={entry.
|
|
133
|
+
<strong>Request URL:</strong>
|
|
134
|
+
<Tooltip content={entry.url} showOnlyWhenTruncated>
|
|
25
135
|
<span className={styles.urlText}>
|
|
26
|
-
{formatLongUrl(entry.
|
|
136
|
+
{formatLongUrl(entry.url, 100)}
|
|
27
137
|
</span>
|
|
28
138
|
</Tooltip>
|
|
29
139
|
</div>
|
|
30
140
|
<div className={styles.infoRow}>
|
|
31
|
-
<strong>Request Method:</strong> {entry.
|
|
32
|
-
</div>
|
|
33
|
-
<div className={styles.infoRow}>
|
|
34
|
-
<strong>Status Code:</strong> {entry.response?.response.status || 'Pending'}
|
|
141
|
+
<strong>Request Method:</strong> {entry.method}
|
|
35
142
|
</div>
|
|
36
143
|
<div className={styles.infoRow}>
|
|
37
|
-
<strong>
|
|
144
|
+
<strong>Status Code:</strong> {entry.response?.status || 'Pending'}
|
|
38
145
|
</div>
|
|
39
146
|
<div className={styles.infoRow}>
|
|
40
|
-
<strong>
|
|
147
|
+
<strong>Status:</strong> {entry.status}
|
|
41
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
|
+
)}
|
|
42
186
|
</div>
|
|
43
187
|
</Card>
|
|
44
188
|
|
|
@@ -47,13 +191,11 @@ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
|
|
|
47
191
|
<Card className={styles.card}>
|
|
48
192
|
<h3 className={styles.cardTitle}>Response Headers</h3>
|
|
49
193
|
<div className={styles.headersContainer}>
|
|
50
|
-
{Object.entries(entry.response.
|
|
194
|
+
{Object.entries(entry.response.headers).map(([key, value]) => (
|
|
51
195
|
<div key={key} className={styles.headerRow}>
|
|
52
|
-
<strong>{key}:</strong>
|
|
196
|
+
<strong>{key}:</strong>
|
|
53
197
|
<Tooltip content={value} showOnlyWhenTruncated>
|
|
54
|
-
<span className={styles.headerValue}>
|
|
55
|
-
{value}
|
|
56
|
-
</span>
|
|
198
|
+
<span className={styles.headerValue}>{value}</span>
|
|
57
199
|
</Tooltip>
|
|
58
200
|
</div>
|
|
59
201
|
))}
|
|
@@ -65,13 +207,11 @@ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
|
|
|
65
207
|
<Card className={styles.card}>
|
|
66
208
|
<h3 className={styles.cardTitle}>Request Headers</h3>
|
|
67
209
|
<div className={styles.headersContainer}>
|
|
68
|
-
{Object.entries(entry.
|
|
210
|
+
{Object.entries(entry.headers).map(([key, value]) => (
|
|
69
211
|
<div key={key} className={styles.headerRow}>
|
|
70
|
-
<strong>{key}:</strong>
|
|
212
|
+
<strong>{key}:</strong>
|
|
71
213
|
<Tooltip content={value} showOnlyWhenTruncated>
|
|
72
|
-
<span className={styles.headerValue}>
|
|
73
|
-
{value}
|
|
74
|
-
</span>
|
|
214
|
+
<span className={styles.headerValue}>{value}</span>
|
|
75
215
|
</Tooltip>
|
|
76
216
|
</div>
|
|
77
217
|
))}
|
|
@@ -79,51 +219,122 @@ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
|
|
|
79
219
|
</Card>
|
|
80
220
|
|
|
81
221
|
{/* Size Information */}
|
|
82
|
-
{entry.
|
|
222
|
+
{(entry.encodedDataLength || entry.dataLength) && (
|
|
83
223
|
<Card className={styles.card}>
|
|
84
224
|
<h3 className={styles.cardTitle}>Size Information</h3>
|
|
85
225
|
<div className={styles.infoText}>
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
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
|
+
)}
|
|
89
242
|
</div>
|
|
90
243
|
</Card>
|
|
91
244
|
)}
|
|
92
245
|
|
|
93
246
|
{/* Timing Information */}
|
|
94
|
-
{entry.
|
|
247
|
+
{entry.duration && (
|
|
95
248
|
<Card className={styles.card}>
|
|
96
249
|
<h3 className={styles.cardTitle}>Timing</h3>
|
|
97
250
|
<div className={styles.infoText}>
|
|
98
251
|
<div className={styles.infoRow}>
|
|
99
|
-
<strong>
|
|
252
|
+
<strong>Duration:</strong> {formatDuration(entry.duration)}
|
|
100
253
|
</div>
|
|
101
254
|
<div className={styles.infoRow}>
|
|
102
|
-
<strong>
|
|
255
|
+
<strong>Start Time:</strong>{' '}
|
|
256
|
+
{new Date(entry.startTime).toLocaleTimeString()}
|
|
103
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
|
+
)}
|
|
104
270
|
</div>
|
|
105
271
|
</Card>
|
|
106
272
|
)}
|
|
107
273
|
|
|
108
274
|
{/* Error Information */}
|
|
109
|
-
{entry.
|
|
275
|
+
{entry.status === 'failed' && (
|
|
110
276
|
<Card className={styles.card}>
|
|
111
|
-
<h3 className={styles.
|
|
277
|
+
<h3 className={styles.cardTitle}>Error Information</h3>
|
|
112
278
|
<div className={styles.infoText}>
|
|
113
279
|
<div className={styles.infoRow}>
|
|
114
|
-
<strong>Error
|
|
280
|
+
<strong>Error:</strong> {entry.errorText || 'Unknown error'}
|
|
115
281
|
</div>
|
|
116
282
|
<div className={styles.infoRow}>
|
|
117
|
-
<strong>
|
|
283
|
+
<strong>Canceled:</strong> {entry.canceled ? 'Yes' : 'No'}
|
|
118
284
|
</div>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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 && (
|
|
125
335
|
<div className={styles.infoRow}>
|
|
126
|
-
<strong>
|
|
336
|
+
<strong>Post Data Length:</strong>{' '}
|
|
337
|
+
{entry.request.postData.length} characters
|
|
127
338
|
</div>
|
|
128
339
|
)}
|
|
129
340
|
</div>
|
|
@@ -131,4 +342,4 @@ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
|
|
|
131
342
|
)}
|
|
132
343
|
</div>
|
|
133
344
|
);
|
|
134
|
-
};
|
|
345
|
+
};
|
package/src/ui/network-list.tsx
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
3
|
import { NetworkEntry } from '../types/network';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getStatusColor,
|
|
6
|
+
getMethodColor,
|
|
7
|
+
formatDuration,
|
|
8
|
+
formatFileSize,
|
|
9
|
+
parseUrl,
|
|
10
|
+
} from './utils';
|
|
5
11
|
import { Badge, Tooltip } from './components';
|
|
6
12
|
import styles from './network-list.module.css';
|
|
7
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
|
+
|
|
8
42
|
interface NetworkListProps {
|
|
9
|
-
entries:
|
|
43
|
+
entries: EnhancedNetworkEntry[];
|
|
10
44
|
selectedRequestId: string | null;
|
|
11
45
|
onSelect: (requestId: string) => void;
|
|
12
46
|
height: number;
|
|
@@ -14,6 +48,56 @@ interface NetworkListProps {
|
|
|
14
48
|
|
|
15
49
|
const ITEM_HEIGHT = 60; // Height of each network list item
|
|
16
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
|
+
|
|
17
101
|
export const NetworkList: React.FC<NetworkListProps> = ({
|
|
18
102
|
entries,
|
|
19
103
|
selectedRequestId,
|
|
@@ -29,16 +113,18 @@ export const NetworkList: React.FC<NetworkListProps> = ({
|
|
|
29
113
|
overscan: 5,
|
|
30
114
|
});
|
|
31
115
|
|
|
32
|
-
const NetworkListItem: React.FC<{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
116
|
+
const NetworkListItem: React.FC<{
|
|
117
|
+
entry: EnhancedNetworkEntry;
|
|
118
|
+
index: number;
|
|
119
|
+
}> = ({ entry, index }) => {
|
|
120
|
+
const method = entry.method;
|
|
121
|
+
const url = entry.url;
|
|
36
122
|
const { domain, path } = parseUrl(url);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const
|
|
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);
|
|
42
128
|
|
|
43
129
|
const isSelected = selectedRequestId === entry.requestId;
|
|
44
130
|
|
|
@@ -48,53 +134,70 @@ export const NetworkList: React.FC<NetworkListProps> = ({
|
|
|
48
134
|
onClick={() => onSelect(entry.requestId)}
|
|
49
135
|
>
|
|
50
136
|
<div className={styles.statusColumn}>
|
|
51
|
-
<Tooltip
|
|
52
|
-
content={`Status: ${
|
|
137
|
+
<Tooltip
|
|
138
|
+
content={`Status: ${statusDisplay.text}`}
|
|
53
139
|
showOnlyWhenTruncated
|
|
54
|
-
variant={
|
|
140
|
+
variant={
|
|
141
|
+
statusDisplay.color === '#d32f2f'
|
|
142
|
+
? 'error'
|
|
143
|
+
: statusDisplay.color === '#ff9800'
|
|
144
|
+
? 'warning'
|
|
145
|
+
: 'info'
|
|
146
|
+
}
|
|
55
147
|
>
|
|
56
|
-
<Badge color={
|
|
57
|
-
{status || '...'}
|
|
58
|
-
</Badge>
|
|
148
|
+
<Badge color={statusDisplay.color}>{statusDisplay.text}</Badge>
|
|
59
149
|
</Tooltip>
|
|
60
150
|
</div>
|
|
61
151
|
<div className={styles.methodColumn}>
|
|
62
|
-
<Tooltip
|
|
63
|
-
content={`Method: ${method}`}
|
|
152
|
+
<Tooltip
|
|
153
|
+
content={`Method: ${method}`}
|
|
64
154
|
showOnlyWhenTruncated
|
|
65
155
|
variant="info"
|
|
66
156
|
>
|
|
67
|
-
<Badge color={getMethodColor(method)}>
|
|
68
|
-
{method}
|
|
69
|
-
</Badge>
|
|
157
|
+
<Badge color={getMethodColor(method)}>{method}</Badge>
|
|
70
158
|
</Tooltip>
|
|
71
159
|
</div>
|
|
72
160
|
<div className={styles.urlColumn}>
|
|
73
161
|
<Tooltip content={domain} showOnlyWhenTruncated>
|
|
74
|
-
<div className={styles.domainText}>
|
|
75
|
-
{domain}
|
|
76
|
-
</div>
|
|
162
|
+
<div className={styles.domainText}>{domain}</div>
|
|
77
163
|
</Tooltip>
|
|
78
164
|
<Tooltip content={path} showOnlyWhenTruncated>
|
|
79
|
-
<div className={styles.pathText}>
|
|
80
|
-
{path}
|
|
81
|
-
</div>
|
|
165
|
+
<div className={styles.pathText}>{path}</div>
|
|
82
166
|
</Tooltip>
|
|
83
167
|
<Tooltip content={url} showOnlyWhenTruncated>
|
|
84
|
-
<div className={styles.fullUrlText}>
|
|
85
|
-
|
|
86
|
-
|
|
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>
|
|
87
180
|
</Tooltip>
|
|
88
181
|
</div>
|
|
89
182
|
<div className={styles.durationColumn}>
|
|
90
|
-
<Tooltip
|
|
183
|
+
<Tooltip
|
|
184
|
+
content={`Duration: ${
|
|
185
|
+
entry.duration ? formatDuration(entry.duration) : 'Pending'
|
|
186
|
+
}`}
|
|
187
|
+
showOnlyWhenTruncated
|
|
188
|
+
>
|
|
91
189
|
<span className={styles.columnText}>
|
|
92
190
|
{entry.duration ? formatDuration(entry.duration) : '...'}
|
|
93
191
|
</span>
|
|
94
192
|
</Tooltip>
|
|
95
193
|
</div>
|
|
96
194
|
<div className={styles.sizeColumn}>
|
|
97
|
-
<Tooltip
|
|
195
|
+
<Tooltip
|
|
196
|
+
content={`Size: ${
|
|
197
|
+
displaySize > 0 ? formatFileSize(displaySize) : 'Unknown'
|
|
198
|
+
}`}
|
|
199
|
+
showOnlyWhenTruncated
|
|
200
|
+
>
|
|
98
201
|
<span className={styles.columnText}>
|
|
99
202
|
{displaySize > 0 ? formatFileSize(displaySize) : '...'}
|
|
100
203
|
</span>
|
|
@@ -104,31 +207,23 @@ export const NetworkList: React.FC<NetworkListProps> = ({
|
|
|
104
207
|
);
|
|
105
208
|
};
|
|
106
209
|
|
|
107
|
-
if (entries.length === 0) {
|
|
108
|
-
return (
|
|
109
|
-
<div className={styles.emptyContainer} style={{ height }}>
|
|
110
|
-
<div className={styles.emptyText}>
|
|
111
|
-
No network requests recorded
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
210
|
return (
|
|
118
|
-
<div
|
|
119
|
-
ref={parentRef}
|
|
120
|
-
className={styles.container}
|
|
121
|
-
style={{ height }}
|
|
122
|
-
>
|
|
211
|
+
<div ref={parentRef} className={styles.networkList} style={{ height }}>
|
|
123
212
|
<div
|
|
124
|
-
|
|
125
|
-
|
|
213
|
+
style={{
|
|
214
|
+
height: `${virtualizer.getTotalSize()}px`,
|
|
215
|
+
width: '100%',
|
|
216
|
+
position: 'relative',
|
|
217
|
+
}}
|
|
126
218
|
>
|
|
127
|
-
{virtualizer.getVirtualItems().map((virtualItem
|
|
219
|
+
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
128
220
|
<div
|
|
129
221
|
key={virtualItem.key}
|
|
130
|
-
className={styles.virtualItem}
|
|
131
222
|
style={{
|
|
223
|
+
position: 'absolute',
|
|
224
|
+
top: 0,
|
|
225
|
+
left: 0,
|
|
226
|
+
width: '100%',
|
|
132
227
|
height: `${virtualItem.size}px`,
|
|
133
228
|
transform: `translateY(${virtualItem.start}px)`,
|
|
134
229
|
}}
|
|
@@ -142,4 +237,4 @@ export const NetworkList: React.FC<NetworkListProps> = ({
|
|
|
142
237
|
</div>
|
|
143
238
|
</div>
|
|
144
239
|
);
|
|
145
|
-
};
|
|
240
|
+
};
|
|
@@ -25,16 +25,10 @@ export const NetworkToolbar: React.FC<NetworkToolbarProps> = ({
|
|
|
25
25
|
>
|
|
26
26
|
{isRecording ? 'Stop' : 'Start'} Recording
|
|
27
27
|
</Button>
|
|
28
|
-
<Button
|
|
29
|
-
onClick={onClear}
|
|
30
|
-
variant="secondary"
|
|
31
|
-
size="small"
|
|
32
|
-
>
|
|
28
|
+
<Button onClick={onClear} variant="secondary" size="small">
|
|
33
29
|
Clear
|
|
34
30
|
</Button>
|
|
35
|
-
<div className={styles.requestCount}>
|
|
36
|
-
{requestCount} requests
|
|
37
|
-
</div>
|
|
31
|
+
<div className={styles.requestCount}>{requestCount} requests</div>
|
|
38
32
|
</Toolbar>
|
|
39
33
|
);
|
|
40
|
-
};
|
|
34
|
+
};
|