@rozenite/network-activity-plugin 1.10.0 → 1.12.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.
@@ -0,0 +1,422 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import type { CSSProperties, PointerEvent } from 'react';
3
+ import { X } from 'lucide-react';
4
+ import type { RequestId } from '../../shared/client';
5
+ import type { ProcessedRequest } from '../state/model';
6
+ import {
7
+ useNetworkActivityActions,
8
+ useSelectedRequestId,
9
+ } from '../state/hooks';
10
+ import {
11
+ formatTimelineOffset,
12
+ getTimelineBarTopOffset,
13
+ getTimelineModel,
14
+ getTimelineTrackTop,
15
+ isRequestActive,
16
+ TIMELINE_LAYOUT,
17
+ } from '../utils/timelineModel';
18
+ import type {
19
+ TimelineModel,
20
+ TimelineRangeSelection,
21
+ TimelineRow,
22
+ TimelineTick,
23
+ } from '../utils/timelineModel';
24
+
25
+ const REQUEST_TIMELINE_COLORS = {
26
+ error: 'bg-red-400',
27
+ primary: 'bg-gray-400',
28
+ active: 'bg-gray-500',
29
+ httpTtfb: 'bg-gray-200',
30
+ } as const;
31
+
32
+ const getPrimaryBarClassName = (request: ProcessedRequest) => {
33
+ if (request.status === 'failed' || request.status === 'error') {
34
+ return REQUEST_TIMELINE_COLORS.error;
35
+ }
36
+
37
+ return REQUEST_TIMELINE_COLORS.primary;
38
+ };
39
+
40
+ const getStyle = (
41
+ offsetPercent: number,
42
+ widthPercent: number,
43
+ ): CSSProperties => ({
44
+ left: `${offsetPercent}%`,
45
+ width: `${widthPercent}%`,
46
+ });
47
+
48
+ const GridLines = ({ ticks }: { ticks: TimelineTick[] }) => {
49
+ return (
50
+ <div className="pointer-events-none absolute inset-0">
51
+ {ticks.map((tick) => (
52
+ <div
53
+ key={`${tick.label}-${tick.offsetPercent}`}
54
+ className="absolute inset-y-0 border-l border-gray-800"
55
+ style={{ left: `${tick.offsetPercent}%` }}
56
+ />
57
+ ))}
58
+ </div>
59
+ );
60
+ };
61
+
62
+ const getTickLabelStyle = (tick: TimelineTick): CSSProperties => {
63
+ if (tick.offsetPercent === 0) {
64
+ return {
65
+ left: 4,
66
+ };
67
+ }
68
+
69
+ if (tick.offsetPercent === 100) {
70
+ return {
71
+ right: 4,
72
+ };
73
+ }
74
+
75
+ return {
76
+ left: `${tick.offsetPercent}%`,
77
+ };
78
+ };
79
+
80
+ const TimelineTrack = ({
81
+ row,
82
+ isSelected,
83
+ onSelect,
84
+ shouldSuppressSelect,
85
+ }: {
86
+ row: TimelineRow;
87
+ isSelected: boolean;
88
+ onSelect: (requestId: RequestId) => void;
89
+ shouldSuppressSelect: () => boolean;
90
+ }) => {
91
+ const primaryBarClassName = row.isActive
92
+ ? REQUEST_TIMELINE_COLORS.active
93
+ : getPrimaryBarClassName(row.request);
94
+ const isSplitHttpBar =
95
+ row.request.type === 'http' &&
96
+ row.ttfbPercent > 0 &&
97
+ row.receivePercent > 0;
98
+ const trackTop = getTimelineTrackTop(row.lane);
99
+ const barTop = getTimelineBarTopOffset();
100
+ const positionStyle = {
101
+ ...getStyle(row.offsetPercent, row.widthPercent),
102
+ top: trackTop,
103
+ };
104
+ const durationLabel = row.isActive
105
+ ? `${formatTimelineOffset(row.duration)}+`
106
+ : formatTimelineOffset(row.duration);
107
+ const label = `${row.request.method} ${row.request.name} - ${durationLabel}`;
108
+
109
+ return (
110
+ <button
111
+ type="button"
112
+ aria-label={label}
113
+ title={label}
114
+ data-timeline-track="true"
115
+ className="absolute rounded-sm text-left transition-opacity hover:opacity-80"
116
+ style={{
117
+ ...positionStyle,
118
+ height: TIMELINE_LAYOUT.laneHitTargetHeightPx,
119
+ }}
120
+ onClick={(event) => {
121
+ if (shouldSuppressSelect()) {
122
+ event.preventDefault();
123
+ event.stopPropagation();
124
+ return;
125
+ }
126
+
127
+ onSelect(row.request.id);
128
+ }}
129
+ >
130
+ {isSplitHttpBar ? (
131
+ <div
132
+ className={`absolute flex w-full overflow-hidden rounded-sm ${
133
+ isSelected
134
+ ? 'ring-1 ring-blue-300 ring-offset-1 ring-offset-gray-950'
135
+ : ''
136
+ }`}
137
+ style={{
138
+ top: barTop,
139
+ height: TIMELINE_LAYOUT.laneHeightPx,
140
+ }}
141
+ >
142
+ <div
143
+ className={`h-full ${REQUEST_TIMELINE_COLORS.httpTtfb}`}
144
+ style={{ width: `${row.ttfbPercent}%` }}
145
+ />
146
+ <div
147
+ className={`h-full ${REQUEST_TIMELINE_COLORS.primary}`}
148
+ style={{ width: `${row.receivePercent}%` }}
149
+ />
150
+ </div>
151
+ ) : (
152
+ <div
153
+ className={`absolute w-full rounded-sm ${primaryBarClassName} ${
154
+ isSelected
155
+ ? 'ring-1 ring-blue-300 ring-offset-1 ring-offset-gray-950'
156
+ : ''
157
+ }`}
158
+ style={{
159
+ top: barTop,
160
+ height: TIMELINE_LAYOUT.laneHeightPx,
161
+ }}
162
+ />
163
+ )}
164
+ </button>
165
+ );
166
+ };
167
+
168
+ type DraftSelection = {
169
+ anchorPercent: number;
170
+ currentPercent: number;
171
+ startedOnTrack: boolean;
172
+ };
173
+
174
+ const clampPercent = (value: number) => Math.min(Math.max(value, 0), 100);
175
+
176
+ const getPointerPercent = (
177
+ event: PointerEvent<HTMLDivElement>,
178
+ element: HTMLDivElement,
179
+ ) => {
180
+ const rect = element.getBoundingClientRect();
181
+
182
+ if (rect.width === 0) {
183
+ return 0;
184
+ }
185
+
186
+ return clampPercent(((event.clientX - rect.left) / rect.width) * 100);
187
+ };
188
+
189
+ const getSelectionStyle = (
190
+ range: TimelineRangeSelection,
191
+ timeline: TimelineModel,
192
+ ): CSSProperties => {
193
+ const startPercent = clampPercent(
194
+ ((range.startTime - timeline.rangeStart) / timeline.rangeDuration) * 100,
195
+ );
196
+ const endPercent = clampPercent(
197
+ ((range.endTime - timeline.rangeStart) / timeline.rangeDuration) * 100,
198
+ );
199
+ const left = Math.min(startPercent, endPercent);
200
+ const width = Math.abs(endPercent - startPercent);
201
+
202
+ return {
203
+ left: `${left}%`,
204
+ width: `${width}%`,
205
+ top: TIMELINE_LAYOUT.rulerHeightPx,
206
+ };
207
+ };
208
+
209
+ const getDraftSelectionStyle = (draft: DraftSelection): CSSProperties => {
210
+ const left = Math.min(draft.anchorPercent, draft.currentPercent);
211
+ const width = Math.abs(draft.currentPercent - draft.anchorPercent);
212
+
213
+ return {
214
+ left: `${left}%`,
215
+ width: `${width}%`,
216
+ top: TIMELINE_LAYOUT.rulerHeightPx,
217
+ };
218
+ };
219
+
220
+ export type NetworkTimelineProps = {
221
+ requests: ProcessedRequest[];
222
+ selection: TimelineRangeSelection | null;
223
+ filteredRequestCount: number;
224
+ onSelectionChange: (selection: TimelineRangeSelection | null) => void;
225
+ };
226
+
227
+ export const NetworkTimeline = ({
228
+ requests,
229
+ selection,
230
+ filteredRequestCount,
231
+ onSelectionChange,
232
+ }: NetworkTimelineProps) => {
233
+ const actions = useNetworkActivityActions();
234
+ const selectedRequestId = useSelectedRequestId();
235
+ const [now, setNow] = useState(() => Date.now());
236
+ const [draftSelection, setDraftSelection] = useState<DraftSelection | null>(
237
+ null,
238
+ );
239
+ const chartRef = useRef<HTMLDivElement | null>(null);
240
+ const suppressTrackClickRef = useRef(false);
241
+
242
+ const hasActiveRequests = requests.some(isRequestActive);
243
+
244
+ useEffect(() => {
245
+ if (!hasActiveRequests) {
246
+ return;
247
+ }
248
+
249
+ const interval = window.setInterval(() => {
250
+ setNow(Date.now());
251
+ }, TIMELINE_LAYOUT.liveRefreshMs);
252
+
253
+ return () => window.clearInterval(interval);
254
+ }, [hasActiveRequests]);
255
+
256
+ const timeline = useMemo(() => {
257
+ return getTimelineModel(requests, now);
258
+ }, [requests, now]);
259
+
260
+ const onRequestSelect = (requestId: RequestId) => {
261
+ actions.setSelectedRequest(requestId);
262
+ };
263
+
264
+ const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {
265
+ if (event.button !== 0 || requests.length === 0) {
266
+ return;
267
+ }
268
+
269
+ const chartElement = chartRef.current;
270
+
271
+ if (!chartElement) {
272
+ return;
273
+ }
274
+
275
+ const percent = getPointerPercent(event, chartElement);
276
+ const target = event.target;
277
+ const startedOnTrack =
278
+ target instanceof Element &&
279
+ target.closest('[data-timeline-track="true"]') !== null;
280
+
281
+ setDraftSelection({
282
+ anchorPercent: percent,
283
+ currentPercent: percent,
284
+ startedOnTrack,
285
+ });
286
+ chartElement.setPointerCapture(event.pointerId);
287
+ };
288
+
289
+ const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {
290
+ if (!draftSelection) {
291
+ return;
292
+ }
293
+
294
+ const chartElement = chartRef.current;
295
+
296
+ if (!chartElement) {
297
+ return;
298
+ }
299
+
300
+ event.preventDefault();
301
+ const percent = getPointerPercent(event, chartElement);
302
+
303
+ setDraftSelection((current) =>
304
+ current ? { ...current, currentPercent: percent } : current,
305
+ );
306
+ };
307
+
308
+ const onPointerUp = (event: PointerEvent<HTMLDivElement>) => {
309
+ if (!draftSelection) {
310
+ return;
311
+ }
312
+
313
+ const chartElement = chartRef.current;
314
+ const currentPercent = chartElement
315
+ ? getPointerPercent(event, chartElement)
316
+ : draftSelection.currentPercent;
317
+ const distance = Math.abs(currentPercent - draftSelection.anchorPercent);
318
+
319
+ if (distance > 1) {
320
+ const startOffset =
321
+ (Math.min(draftSelection.anchorPercent, currentPercent) / 100) *
322
+ timeline.rangeDuration;
323
+ const endOffset =
324
+ (Math.max(draftSelection.anchorPercent, currentPercent) / 100) *
325
+ timeline.rangeDuration;
326
+
327
+ onSelectionChange({
328
+ startTime: timeline.rangeStart + startOffset,
329
+ endTime: timeline.rangeStart + endOffset,
330
+ });
331
+
332
+ suppressTrackClickRef.current = true;
333
+ window.setTimeout(() => {
334
+ suppressTrackClickRef.current = false;
335
+ }, 0);
336
+ } else if (!draftSelection.startedOnTrack) {
337
+ onSelectionChange(null);
338
+ }
339
+
340
+ setDraftSelection(null);
341
+
342
+ if (chartElement?.hasPointerCapture(event.pointerId)) {
343
+ chartElement.releasePointerCapture(event.pointerId);
344
+ }
345
+ };
346
+
347
+ return (
348
+ <div className="border-b border-gray-700 bg-gray-900 p-1.5">
349
+ <div
350
+ ref={chartRef}
351
+ className="relative overflow-hidden border border-gray-800 bg-gray-950"
352
+ style={{ height: timeline.chartHeight }}
353
+ onPointerDown={onPointerDown}
354
+ onPointerMove={onPointerMove}
355
+ onPointerUp={onPointerUp}
356
+ >
357
+ <GridLines ticks={timeline.ticks} />
358
+
359
+ <div
360
+ className="pointer-events-none absolute inset-x-0 border-b border-gray-800"
361
+ style={{ top: TIMELINE_LAYOUT.rulerHeightPx }}
362
+ />
363
+
364
+ {timeline.ticks.map((tick) => (
365
+ <div
366
+ key={`${tick.label}-${tick.offsetPercent}`}
367
+ className="absolute top-1 whitespace-nowrap tabular-nums text-xs text-gray-200"
368
+ style={getTickLabelStyle(tick)}
369
+ >
370
+ {tick.label}
371
+ </div>
372
+ ))}
373
+
374
+ {selection && (
375
+ <div
376
+ className="pointer-events-none absolute bottom-0 border-x border-blue-300/70 bg-blue-400/10"
377
+ style={getSelectionStyle(selection, timeline)}
378
+ />
379
+ )}
380
+
381
+ {draftSelection && (
382
+ <div
383
+ className="pointer-events-none absolute bottom-0 border-x border-blue-300/70 bg-blue-400/15"
384
+ style={getDraftSelectionStyle(draftSelection)}
385
+ />
386
+ )}
387
+
388
+ {timeline.rows.map((row) => (
389
+ <TimelineTrack
390
+ key={row.request.id}
391
+ row={row}
392
+ isSelected={selectedRequestId === row.request.id}
393
+ onSelect={onRequestSelect}
394
+ shouldSuppressSelect={() => suppressTrackClickRef.current}
395
+ />
396
+ ))}
397
+
398
+ {selection && (
399
+ <div className="absolute bottom-1 right-1 flex items-center gap-1 rounded border border-gray-700 bg-gray-900/95 px-1.5 py-0.5 text-xs text-gray-400">
400
+ <span>{filteredRequestCount} in range</span>
401
+ <button
402
+ type="button"
403
+ title="Clear timeline selection"
404
+ aria-label="Clear timeline selection"
405
+ className="rounded p-0.5 text-gray-400 hover:bg-gray-800 hover:text-gray-100"
406
+ onClick={() => onSelectionChange(null)}
407
+ >
408
+ <X className="h-3 w-3" />
409
+ </button>
410
+ </div>
411
+ )}
412
+
413
+ {timeline.hiddenRequestCount > 0 && (
414
+ <div className="absolute bottom-1 left-1 rounded border border-gray-700 bg-gray-900/95 px-1.5 py-0.5 text-xs text-gray-400">
415
+ Showing latest {timeline.rows.length} of{' '}
416
+ {timeline.totalRequestCount}
417
+ </div>
418
+ )}
419
+ </div>
420
+ </div>
421
+ );
422
+ };
@@ -10,7 +10,6 @@ import {
10
10
  } from '@tanstack/react-table';
11
11
  import type { ProcessedRequest } from '../state/model';
12
12
  import type {
13
- HttpMethod,
14
13
  NetworkEventSource,
15
14
  RequestId,
16
15
  RequestOverride,
@@ -18,12 +17,10 @@ import type {
18
17
  import {
19
18
  useNetworkActivityActions,
20
19
  useOverrides,
21
- useProcessedRequests,
22
20
  useSelectedRequestId,
23
21
  useClientUISettings,
24
22
  } from '../state/hooks';
25
23
  import { getStatusColor } from '../utils/getStatusColor';
26
- import { FilterState } from './FilterBar';
27
24
  import { isNumber } from '../../utils/typeChecks';
28
25
 
29
26
  type NetworkRequest = {
@@ -31,7 +28,6 @@ type NetworkRequest = {
31
28
  name: string;
32
29
  status: string | number;
33
30
  statusCode?: number;
34
- statusState: ProcessedRequest['status'];
35
31
  method: ProcessedRequest['method'];
36
32
  domain: string;
37
33
  path: string;
@@ -114,7 +110,6 @@ const sortSize: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
114
110
  const a = rowA.getValue(columnId) as string;
115
111
  const b = rowB.getValue(columnId) as string;
116
112
 
117
- // Extract numeric values from formatted strings like "1.2 kB", "500 B", etc.
118
113
  const getNumericValue = (str: string) => {
119
114
  const match = str.match(/^([\d.]+)\s*([KMGT]?B)$/);
120
115
  if (!match) return 0;
@@ -136,7 +131,6 @@ const sortTime: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
136
131
  const a = rowA.getValue(columnId) as string;
137
132
  const b = rowB.getValue(columnId) as string;
138
133
 
139
- // Extract numeric values from formatted strings like "150 ms", "1.2 s", etc.
140
134
  const getNumericValue = (str: string) => {
141
135
  const match = str.match(/^([\d.]+)\s*(ms|s)$/);
142
136
  if (!match) return 0;
@@ -148,175 +142,6 @@ const sortTime: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
148
142
  return getNumericValue(a) - getNumericValue(b);
149
143
  };
150
144
 
151
- const parseThreshold = (value: string): number | null => {
152
- const normalizedValue = value.trim();
153
- if (!normalizedValue) {
154
- return null;
155
- }
156
-
157
- const parsedValue = Number(normalizedValue);
158
- return Number.isFinite(parsedValue) ? parsedValue : null;
159
- };
160
-
161
- const matchesStatusFilter = (
162
- statusCode: number | undefined,
163
- statusFilter: string,
164
- ) => {
165
- const normalizedFilter = statusFilter.trim().toLowerCase();
166
- if (!normalizedFilter) {
167
- return true;
168
- }
169
-
170
- if (statusCode === undefined) {
171
- return false;
172
- }
173
-
174
- const statusRangeMatch = normalizedFilter.match(/^(\d{3})\s*-\s*(\d{3})$/);
175
- if (statusRangeMatch) {
176
- const min = Number(statusRangeMatch[1]);
177
- const max = Number(statusRangeMatch[2]);
178
- return statusCode >= min && statusCode <= max;
179
- }
180
-
181
- const statusClassMatch = normalizedFilter.match(/^([1-5])xx$/);
182
- if (statusClassMatch) {
183
- return Math.floor(statusCode / 100) === Number(statusClassMatch[1]);
184
- }
185
-
186
- const comparisonMatch = normalizedFilter.match(/^(>=|<=|>|<)\s*(\d{3})$/);
187
- if (comparisonMatch) {
188
- const value = Number(comparisonMatch[2]);
189
- switch (comparisonMatch[1]) {
190
- case '>=':
191
- return statusCode >= value;
192
- case '<=':
193
- return statusCode <= value;
194
- case '>':
195
- return statusCode > value;
196
- case '<':
197
- return statusCode < value;
198
- }
199
- }
200
-
201
- return statusCode === Number(normalizedFilter);
202
- };
203
-
204
- const isInFlightStatus = (status: string) => {
205
- return ['pending', 'loading', 'connecting', 'open'].includes(status);
206
- };
207
-
208
- const isFailedStatus = (status: string) => {
209
- return ['failed', 'error'].includes(status);
210
- };
211
-
212
- const isHttpMethod = (method: NetworkRequest['method']): method is HttpMethod =>
213
- method !== 'WS' && method !== 'SSE';
214
-
215
- const filterNetworkRequests = (
216
- requests: NetworkRequest[],
217
- filter: FilterState,
218
- ) => {
219
- const searchText = filter.text.trim().toLowerCase();
220
- const domainFilter = filter.advanced.domain.trim().toLowerCase();
221
- const contentTypeFilter = filter.advanced.contentType.trim().toLowerCase();
222
- const minSize = parseThreshold(filter.advanced.minSize);
223
- const maxSize = parseThreshold(filter.advanced.maxSize);
224
- const minDuration = parseThreshold(filter.advanced.minDuration);
225
- const maxDuration = parseThreshold(filter.advanced.maxDuration);
226
-
227
- return requests.filter((request) => {
228
- if (filter.types.size > 0 && !filter.types.has(request.type)) {
229
- return false;
230
- }
231
-
232
- if (
233
- filter.advanced.methods.size > 0 &&
234
- (!isHttpMethod(request.method) ||
235
- !filter.advanced.methods.has(request.method))
236
- ) {
237
- return false;
238
- }
239
-
240
- if (
241
- filter.advanced.sources.size > 0 &&
242
- (!request.source || !filter.advanced.sources.has(request.source))
243
- ) {
244
- return false;
245
- }
246
-
247
- if (!matchesStatusFilter(request.statusCode, filter.advanced.status)) {
248
- return false;
249
- }
250
-
251
- if (domainFilter && !request.domain.toLowerCase().includes(domainFilter)) {
252
- return false;
253
- }
254
-
255
- if (
256
- contentTypeFilter &&
257
- !request.contentType?.toLowerCase().includes(contentTypeFilter)
258
- ) {
259
- return false;
260
- }
261
-
262
- if (filter.advanced.failedOnly && !isFailedStatus(request.statusState)) {
263
- return false;
264
- }
265
-
266
- if (
267
- filter.advanced.inFlightOnly &&
268
- !isInFlightStatus(request.statusState)
269
- ) {
270
- return false;
271
- }
272
-
273
- if (filter.advanced.overriddenOnly && !request.hasOverride) {
274
- return false;
275
- }
276
-
277
- if (
278
- minSize !== null &&
279
- (request.sizeBytes === null || request.sizeBytes < minSize)
280
- ) {
281
- return false;
282
- }
283
-
284
- if (
285
- maxSize !== null &&
286
- (request.sizeBytes === null || request.sizeBytes > maxSize)
287
- ) {
288
- return false;
289
- }
290
-
291
- if (minDuration !== null && request.durationMs < minDuration) {
292
- return false;
293
- }
294
-
295
- if (maxDuration !== null && request.durationMs > maxDuration) {
296
- return false;
297
- }
298
-
299
- if (searchText) {
300
- const searchableFields = [
301
- request.name,
302
- request.method,
303
- request.status,
304
- request.domain,
305
- request.path,
306
- request.source,
307
- request.type,
308
- request.contentType,
309
- ]
310
- .join(' ')
311
- .toLowerCase();
312
-
313
- return searchableFields.includes(searchText);
314
- }
315
-
316
- return true;
317
- });
318
- };
319
-
320
145
  const processNetworkRequests = (
321
146
  processedRequests: ProcessedRequest[],
322
147
  overrides: Map<string, RequestOverride>,
@@ -340,7 +165,6 @@ const processNetworkRequests = (
340
165
  name: generateName(request.name, showEntirePathAsName),
341
166
  status: statusDisplay,
342
167
  statusCode: request.httpStatus || undefined,
343
- statusState: request.status,
344
168
  method: request.method,
345
169
  domain,
346
170
  path,
@@ -352,7 +176,7 @@ const processNetworkRequests = (
352
176
  type: request.type,
353
177
  source: request.source,
354
178
  startTime: formatStartTime(request.timestamp),
355
- hasOverride: hasOverride,
179
+ hasOverride,
356
180
  };
357
181
  });
358
182
  };
@@ -428,25 +252,23 @@ const columns = [
428
252
  ];
429
253
 
430
254
  export type RequestListProps = {
431
- filter: FilterState;
255
+ requests: ProcessedRequest[];
432
256
  };
433
257
 
434
- export const RequestList = ({ filter }: RequestListProps) => {
258
+ export const RequestList = ({ requests: filteredRequests }: RequestListProps) => {
435
259
  const actions = useNetworkActivityActions();
436
- const processedRequests = useProcessedRequests();
437
260
  const selectedRequestId = useSelectedRequestId();
438
261
  const [sorting, setSorting] = useState<SortingState>([]);
439
262
  const overrides = useOverrides();
440
263
  const clientUISettings = useClientUISettings();
441
264
 
442
265
  const requests = useMemo(() => {
443
- const allRequests = processNetworkRequests(
444
- processedRequests,
266
+ return processNetworkRequests(
267
+ filteredRequests,
445
268
  overrides,
446
269
  clientUISettings?.showUrlAsName,
447
270
  );
448
- return filterNetworkRequests(allRequests, filter);
449
- }, [processedRequests, overrides, clientUISettings?.showUrlAsName, filter]);
271
+ }, [filteredRequests, overrides, clientUISettings?.showUrlAsName]);
450
272
 
451
273
  const table = useReactTable({
452
274
  data: requests,
@@ -527,7 +349,6 @@ export const RequestList = ({ filter }: RequestListProps) => {
527
349
  );
528
350
  };
529
351
 
530
- // Export helper functions for use in other components
531
352
  export {
532
353
  formatSize,
533
354
  formatDuration,