From f49a6de4db682e3506657311bd43987ee9dd3be3 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Mon, 11 Aug 2025 12:57:30 -0500 Subject: [PATCH] Add Data Rate column with color coding and content-length prioritization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Data Rate column between Duration and Size in HTTP requests table - Implement formatDataRate function that prefers content-length over transfer size - Add color coding for data rate: green (≥1 MB/s), yellow (100 KB/s - 1 MB/s), red (<100 KB/s) - Add comprehensive tooltip definition for data rate column - Reorder columns: Server Latency → Duration → Total Response Time → Data Rate → Size 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../httprequestviewer/RequestRowSummary.tsx | 9 ++++++- .../httprequestviewer/RequestsTable.tsx | 5 ++++ .../httprequestviewer/lib/colorUtils.ts | 26 +++++++++++++++++++ .../httprequestviewer/lib/formatUtils.ts | 18 +++++++++++++ src/components/shared/tooltipDefinitions.ts | 12 +++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/components/httprequestviewer/RequestRowSummary.tsx b/src/components/httprequestviewer/RequestRowSummary.tsx index 3f6013c..5818dee 100644 --- a/src/components/httprequestviewer/RequestRowSummary.tsx +++ b/src/components/httprequestviewer/RequestRowSummary.tsx @@ -4,7 +4,7 @@ import styles from './HTTPRequestViewer.module.css' import type { HTTPRequest } from './types/httpRequest' // Import utility functions -import { formatDuration, formatSize } from './lib/formatUtils' +import { formatDuration, formatSize, formatDataRate } from './lib/formatUtils' import { getStatusColor, getProtocolColor, @@ -13,6 +13,7 @@ import { getDurationColor, getTotalResponseTimeColor, getServerLatencyColor, + getDataRateColor, getQueueAnalysisIcon, getCDNIcon, getCDNDisplayName @@ -100,6 +101,7 @@ const RequestRowSummary: React.FC = ({ {formatDuration(request.timing.serverLatency)} + {formatDuration(request.timing.duration)} @@ -109,6 +111,11 @@ const RequestRowSummary: React.FC = ({ {formatDuration(request.timing.totalResponseTime)} + {/* Data Rate */} + + {formatDataRate(request.encodedDataLength, request.contentLength, request.timing.duration)} + + {formatSize(request.encodedDataLength)} diff --git a/src/components/httprequestviewer/RequestsTable.tsx b/src/components/httprequestviewer/RequestsTable.tsx index 781dc25..21f2117 100644 --- a/src/components/httprequestviewer/RequestsTable.tsx +++ b/src/components/httprequestviewer/RequestsTable.tsx @@ -106,6 +106,11 @@ const RequestsTable: React.FC = ({ Total Response Time + + + Data Rate + + Size diff --git a/src/components/httprequestviewer/lib/colorUtils.ts b/src/components/httprequestviewer/lib/colorUtils.ts index b7f1a94..ad891e8 100644 --- a/src/components/httprequestviewer/lib/colorUtils.ts +++ b/src/components/httprequestviewer/lib/colorUtils.ts @@ -88,6 +88,32 @@ export const getServerLatencyColor = (microseconds?: number) => { return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.DEFAULT } +export const getDataRateColor = (transferSize?: number, contentLength?: number, durationMicroseconds?: number) => { + if (!durationMicroseconds || durationMicroseconds <= 0) return {} + + // Prefer content-length over transfer size for more accurate data rate calculation + const bytes = contentLength && contentLength > 0 ? contentLength : transferSize + if (!bytes) return {} + + // Convert duration from microseconds to seconds + const durationSeconds = durationMicroseconds / 1000000 + + // Calculate bytes per second + const bytesPerSecond = bytes / durationSeconds + + // Convert to standard units for comparison + const mbps = bytesPerSecond / (1024 * 1024) // MB/s + const kbps = bytesPerSecond / 1024 // KB/s + + if (mbps >= 1) { + return { backgroundColor: '#e8f5e8', color: '#2e7d32' } // Green for >= 1 MB/s + } else if (kbps >= 100) { + return { backgroundColor: '#fff8e1', color: '#f57c00' } // Yellow for 100 KB/s - 1 MB/s + } else { + return { backgroundColor: '#ffebee', color: '#c62828' } // Red for < 100 KB/s + } +} + export const getQueueAnalysisIcon = (analysis?: QueueAnalysis): string => { if (!analysis) return '' diff --git a/src/components/httprequestviewer/lib/formatUtils.ts b/src/components/httprequestviewer/lib/formatUtils.ts index d675193..e7e2dff 100644 --- a/src/components/httprequestviewer/lib/formatUtils.ts +++ b/src/components/httprequestviewer/lib/formatUtils.ts @@ -10,4 +10,22 @@ export const formatSize = (bytes?: number): string => { if (bytes < 1024) return `${bytes}B` if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB` return `${(bytes / 1024 / 1024).toFixed(1)}MB` +} + +export const formatDataRate = (transferSize?: number, contentLength?: number, durationMicroseconds?: number): string => { + if (!durationMicroseconds || durationMicroseconds <= 0) return 'N/A' + + // Prefer content-length over transfer size for more accurate data rate calculation + const bytes = contentLength && contentLength > 0 ? contentLength : transferSize + if (!bytes) return 'N/A' + + // Convert duration from microseconds to seconds + const durationSeconds = durationMicroseconds / 1000000 + + // Calculate bytes per second + const bytesPerSecond = bytes / durationSeconds + + if (bytesPerSecond < 1024) return `${bytesPerSecond.toFixed(0)} B/s` + if (bytesPerSecond < 1024 * 1024) return `${(bytesPerSecond / 1024).toFixed(1)} KB/s` + return `${(bytesPerSecond / 1024 / 1024).toFixed(1)} MB/s` } \ No newline at end of file diff --git a/src/components/shared/tooltipDefinitions.ts b/src/components/shared/tooltipDefinitions.ts index 1d00109..4fc5071 100644 --- a/src/components/shared/tooltipDefinitions.ts +++ b/src/components/shared/tooltipDefinitions.ts @@ -13,6 +13,7 @@ export const TooltipType = { SERVER_LATENCY: 'SERVER_LATENCY', REQUEST_URL: 'REQUEST_URL', REQUEST_DURATION: 'REQUEST_DURATION', + DATA_RATE: 'DATA_RATE', TOTAL_RESPONSE_TIME: 'TOTAL_RESPONSE_TIME', TRANSFER_SIZE: 'TRANSFER_SIZE', CONTENT_LENGTH: 'CONTENT_LENGTH', @@ -164,6 +165,17 @@ export const TOOLTIP_DEFINITIONS: Record = ] }, + [TooltipType.DATA_RATE]: { + title: "Data Rate", + description: "Download speed calculated as transfer size divided by request duration. Indicates network throughput for this resource.", + lighthouseRelation: "Low data rates may indicate network bottlenecks affecting resource loading speed, impacting LCP and overall performance scores.", + calculation: "Transfer Size ÷ Request Duration. Shows effective throughput in KB/s or MB/s.", + links: [ + { text: "Network Performance", url: "https://web.dev/articles/fast" }, + { text: "Optimize Resource Loading", url: "https://web.dev/articles/critical-rendering-path" } + ] + }, + [TooltipType.TOTAL_RESPONSE_TIME]: { title: "Total Response Time", description: "Complete time from navigation start to request completion. Shows request timing in context of page load.",