Implement standardized CSS status classes and improve header rendering

- Add dark theme variables and standardized status classes to App.module.css
- Convert RequestRowSummary to use CSS modules with success/warning/danger classes
- Replace inline styles with class-based styling for better theme compatibility
- Enhance RequestRowDetails header display with monospace font and hover tooltips
- Implement proper header name/value layout with grid system and overflow handling
- Remove deprecated CSS files and consolidate styling approach

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-08-11 19:06:12 -05:00
parent f49a6de4db
commit 2ee9c3fc28
16 changed files with 384 additions and 1658 deletions

View File

@ -1,707 +0,0 @@
/* CSS Variables for consistent styling */
:root {
/* Colors */
--color-primary: #007bff;
--color-success: #28a745;
--color-danger: #721c24;
--color-text: #495057;
--color-text-muted: #6c757d;
--color-bg-white: #fff;
--color-bg-light: #f8f9fa;
--color-bg-danger: #f8d7da;
--color-bg-warning: #fff3cd;
--color-bg-info: #f0f8ff;
--color-bg-hover: #e3f2fd;
--color-border: #dee2e6;
--color-border-light: #f1f3f4;
--color-border-danger: #f5c6cb;
--color-border-warning: #ffeaa7;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 15px;
--spacing-lg: 20px;
/* Typography */
--font-family-base: system-ui, sans-serif;
--font-family-mono: monospace;
--font-size-xs: 9px;
--font-size-sm: 10px;
--font-size-md: 11px;
--font-size-base: 12px;
--font-size-lg: 14px;
--font-size-xl: 16px;
--font-size-xxl: 18px;
/* Border radius */
--radius-sm: 3px;
--radius-md: 4px;
--radius-lg: 6px;
--radius-xl: 8px;
--radius-xxl: 12px;
/* Z-index */
--z-modal: 1000;
}
/* Base styles */
.base-container {
padding: var(--spacing-lg);
}
.base-font-mono {
font-family: var(--font-family-mono);
}
.base-font-bold {
font-weight: bold;
}
.base-font-italic {
font-style: italic;
}
.base-cursor-pointer {
cursor: pointer;
}
.base-cursor-help {
cursor: help;
}
.base-cursor-disabled {
cursor: not-allowed;
}
.base-user-select-none {
user-select: none;
}
.base-text-center {
text-align: center;
}
.base-text-left {
text-align: left;
}
.base-text-right {
text-align: right;
}
.base-flex {
display: flex;
}
.base-flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.base-grid-auto {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.base-margin-reset {
margin: 0;
}
.base-border-radius {
border-radius: var(--radius-md);
}
/* Color utility classes */
.color-primary {
color: var(--color-primary);
}
.color-success {
color: var(--color-success);
}
.color-text {
color: var(--color-text);
}
.color-text-muted {
color: var(--color-text-muted);
}
.bg-light {
background: var(--color-bg-light);
}
.bg-white {
background: var(--color-bg-white);
}
.bg-info {
background-color: var(--color-bg-info);
}
.bg-hover:hover {
background-color: var(--color-bg-hover);
}
.border-standard {
border: 1px solid var(--color-border);
}
.border-bottom {
border-bottom: 1px solid var(--color-border);
}
.border-bottom-light {
border-bottom: 1px solid var(--color-border-light);
}
/* Button base styles */
.btn-base {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
font-size: var(--font-size-lg);
cursor: pointer;
border: 1px solid var(--color-border);
}
.btn-disabled:disabled {
background: #e9ecef;
cursor: not-allowed;
}
/* Main container */
.container {
composes: base-container;
font-family: var(--font-family-base);
}
/* Error states */
.errorContainer {
composes: base-container base-text-center;
}
.errorMessage {
background: var(--color-bg-danger);
color: var(--color-danger);
padding: var(--spacing-md);
border-radius: var(--radius-xl);
border: 1px solid var(--color-border-danger);
}
.errorMessage h3 {
margin: 0 0 10px 0;
}
.errorMessage p {
composes: base-margin-reset;
}
/* Pagination controls */
.paginationControls {
composes: base-flex-center;
gap: 10px;
margin-bottom: var(--spacing-lg);
}
.paginationButton {
composes: btn-base bg-white;
}
.paginationButton:disabled {
composes: btn-disabled;
}
.paginationInfo {
margin: 0 var(--spacing-md);
font-size: var(--font-size-lg);
}
/* Modal styles */
.modalOverlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: var(--z-modal);
}
.modalContainer {
width: 90vw;
height: 90vh;
background: var(--color-bg-white);
border-radius: var(--radius-xxl);
display: flex;
flex-direction: column;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.modalHeader {
composes: base-flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md) var(--spacing-lg);
border-bottom: 1px solid var(--color-border);
border-radius: var(--radius-xxl) var(--radius-xxl) 0 0;
}
.modalTitle {
composes: base-margin-reset color-text;
font-size: var(--font-size-xxl);
}
.modalCloseButton {
composes: btn-base;
background: transparent;
color: var(--color-text-muted);
border-color: var(--color-text-muted);
border-radius: var(--radius-lg);
}
.modalCloseButton:hover {
composes: bg-light;
}
.modalContent {
flex: 1;
padding: 10px;
}
.modalLegend {
composes: base-grid-auto bg-light;
padding: var(--spacing-md) var(--spacing-lg);
font-size: var(--font-size-base);
color: var(--color-text-muted);
gap: 10px;
border-top: 1px solid var(--color-border);
border-radius: 0 0 var(--radius-xxl) var(--radius-xxl);
}
.modalLegend div {
margin: 2px 0;
}
.modalLegend strong {
composes: base-font-bold;
}
/* Table styles */
.tableContainer {
composes: bg-white border-standard;
border-radius: var(--radius-xl);
overflow: hidden;
}
.table {
width: 100%;
border-collapse: collapse;
}
.tableHeader {
composes: bg-light;
}
.tableHeaderCell {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-base);
font-weight: bold;
}
.tableHeaderCell.center {
composes: base-text-center;
}
.tableHeaderCell.left {
composes: base-text-left;
}
.tableHeaderCell.right {
composes: base-text-right;
}
.tableHeaderCell.expandColumn {
width: 30px;
}
/* Table rows */
.tableRow {
composes: border-bottom-light base-cursor-pointer;
}
.tableRow:hover {
composes: bg-light;
}
.screenshotRow {
composes: bg-info;
border-bottom: 2px solid var(--color-primary);
}
.screenshotRow:hover {
composes: bg-hover;
}
/* Screenshot components */
.screenshotContainer {
composes: base-flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-md);
}
.screenshotLabel {
composes: base-font-bold color-primary;
font-size: var(--font-size-lg);
min-width: 120px;
}
.screenshotTime {
composes: base-font-mono color-text;
font-size: var(--font-size-base);
}
.screenshotImage {
max-width: 200px;
max-height: 150px;
border: 2px solid var(--color-primary);
border-radius: var(--radius-md);
cursor: pointer;
transition: transform 0.2s ease;
}
.screenshotImage:hover {
transform: scale(1.05);
}
.screenshotHint {
composes: base-font-italic color-text-muted;
font-size: var(--font-size-md);
}
/* Table cells base */
.tableCell {
padding: var(--spacing-sm);
font-size: var(--font-size-base);
vertical-align: middle;
}
.tableCell.center {
composes: base-text-center;
}
.tableCell.right {
composes: base-text-right;
}
.tableCell.monospace {
composes: base-font-mono;
}
.tableCell.bold {
composes: base-font-bold;
}
.tableCell.expandCell {
composes: color-primary base-font-bold base-user-select-none;
font-size: var(--font-size-xl);
}
/* Method styles */
.methodGet {
composes: color-success;
}
.methodOther {
composes: color-primary;
}
/* URL cell */
.urlCell {
font-size: var(--font-size-md);
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
}
.urlLink {
composes: color-primary;
text-decoration: none;
}
.urlLink:hover {
text-decoration: underline;
}
/* Queue time container */
.queueTimeContainer {
composes: base-flex;
align-items: center;
justify-content: flex-end;
gap: var(--spacing-xs);
}
.queueAnalysisIcon {
composes: base-cursor-help;
font-size: var(--font-size-lg);
}
/* Connection status indicators */
.connectionCached,
.connectionReused {
composes: base-font-italic;
color: #666;
}
/* Cache indicators with consistent spacing */
.cacheFromCache::before {
content: '💾';
margin-right: var(--spacing-xs);
}
.cacheConnectionReused::before {
content: '🔄';
margin-right: var(--spacing-xs);
}
.cacheNetwork::before {
content: '🌐';
margin-right: var(--spacing-xs);
}
/* Expanded content */
.expandedRow {
composes: bg-light;
border: 1px solid #e9ecef;
}
.expandedContent {
padding: var(--spacing-md);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-md);
}
.detailCard {
composes: bg-white border-standard base-border-radius;
padding: 10px;
}
.detailCard.fullWidth {
grid-column: 1 / -1;
}
.detailCardTitle {
composes: base-margin-reset color-text base-font-bold;
margin-bottom: var(--spacing-sm);
font-size: var(--font-size-lg);
}
.detailList {
font-size: var(--font-size-base);
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.detailListItem {
composes: base-margin-reset;
}
.detailListItem strong {
composes: base-font-bold;
margin-right: var(--spacing-sm);
}
/* Timing styles */
.timingHighlighted {
composes: base-font-bold base-border-radius;
padding: var(--spacing-xs) var(--spacing-sm);
border: 1px solid #e9ecef;
}
/* Analysis card titles */
.queueAnalysisCard .detailCardTitle,
.cdnAnalysisCard .detailCardTitle {
composes: base-flex;
align-items: center;
gap: var(--spacing-sm);
}
.relatedRequestIds {
composes: base-font-mono color-text;
font-size: var(--font-size-md);
word-break: break-all;
}
/* Debug section */
.debugSection {
margin-top: var(--spacing-sm);
padding: var(--spacing-sm);
background: var(--color-bg-warning);
border: 1px solid var(--color-border-warning);
border-radius: var(--radius-md);
}
.debugTitle {
composes: base-font-bold;
margin-bottom: var(--spacing-xs);
font-size: var(--font-size-md);
}
.debugInfo {
margin-bottom: 6px;
font-size: var(--font-size-md);
}
.debugHeaders {
composes: base-font-bold;
font-size: var(--font-size-sm);
margin-bottom: 2px;
}
.headerLine {
composes: base-font-mono;
font-size: var(--font-size-xs);
margin-bottom: 1px;
padding: 1px 3px;
border-radius: 2px;
}
.headerLine.akamaiIndicator {
background-color: #d1ecf1;
color: #0c5460;
}
.headerName {
composes: base-font-bold;
margin-right: 5px;
}
.headerName.akamaiIndicator {
color: #0c5460;
}
.akamaiLabel {
composes: base-font-bold;
color: #0c5460;
margin-left: 5px;
}
/* Headers container */
.headersContainer {
composes: bg-light base-font-mono;
max-height: 150px;
overflow-y: auto;
font-size: var(--font-size-md);
padding: var(--spacing-sm);
border-radius: var(--radius-sm);
}
.headerItem {
margin-bottom: 2px;
display: grid;
grid-template-columns: 150px 1fr;
gap: 10px;
}
.headerItemName {
composes: color-primary base-font-bold;
}
.headerItemValue {
composes: color-text;
word-break: break-all;
}
/* No results */
.noResults {
composes: base-text-center color-text-muted;
padding: 40px;
font-size: var(--font-size-xl);
}
/* Utility classes */
.coloredBackground {
composes: base-border-radius;
padding: var(--spacing-xs) 6px;
}
.highlighted {
composes: base-font-bold;
}
/* Specialized table cell styles */
.tableCellMonoBold {
composes: tableCell base-font-mono base-font-bold;
}
.tableCellCenter {
composes: tableCellMonoBold base-text-center;
}
.tableCellRight {
composes: tableCellMonoBold base-text-right;
}
.tableCell.statusCell {
composes: tableCellCenter;
}
.tableCell.methodCell {
composes: tableCellMonoBold;
}
.tableCell.priorityCell {
composes: tableCellCenter;
}
.tableCell.timeCell {
composes: tableCellRight color-text;
}
.tableCell.gray {
composes: color-text-muted;
}
/* Response time cells with consistent styling */
.responseTimeCell {
composes: tableCellRight base-border-radius;
padding: var(--spacing-sm);
font-weight: bold;
}
.totalResponseTimeCell {
composes: responseTimeCell;
border: 1px solid #e9ecef;
}
.durationCell,
.sizeCell,
.serverLatencyCell {
composes: responseTimeCell;
}
.protocolCell {
composes: tableCellCenter;
}
.cdnCell,
.cacheCell {
composes: tableCell base-text-center;
}
.cdnCell {
composes: base-cursor-help;
}
.cdnCell.default {
cursor: default;
}

View File

@ -1 +0,0 @@

58
src/App.module.css Normal file
View File

@ -0,0 +1,58 @@
:root {
/* Colors */
--color-primary: #007bff;
--color-success: #287745;
--color-warning: #f57c00;
--color-danger: #ffcccc;
--color-text: #8899aa;
--color-text-muted: #6c757d;
--color-text-highlight: #ccddee;
--color-bg-light: #f8f9fa;
--color-bg-success: #222222;
--color-bg-warning: #ffff00;
--color-bg-danger: #cc3344;
--color-bg-info: #f0f8ff;
--color-bg-hover: #e3f2fd;
--color-border: #dee2e6;
--color-border-light: #f1f3f4;
--color-border-success: #c8e6c9;
--color-border-warning: #ffcc02;
--color-border-danger: #f5c6cb;
--color-bg: #000000;
--color-bg-secondary: #2a2a2a;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 15px;
--spacing-lg: 20px;
/* Typography */
--font-family-base: sans-serif;
--font-family-mono: monospace;
--font-size-xs: 9px;
--font-size-sm: 10px;
--font-size-md: 11px;
--font-size-base: 12px;
--font-size-lg: 14px;
--font-size-xl: 16px;
--font-size-xxl: 18px;
/* Border radius */
--radius-sm: 3px;
--radius-md: 4px;
--radius-lg: 6px;
--radius-xl: 8px;
--radius-xxl: 12px;
/* Z-index */
--z-modal: 1000;
}
body {
background-color: var(--color-bg);
color: var(--color-text);
font-family: system-ui, var(--font-family-base);
}

View File

@ -1,5 +1,5 @@
import { useState, useEffect, useCallback } from 'react'
import './App.css'
import styles from './App.module.css'
import TraceViewer from './components/TraceViewer'
import PhaseViewer from './components/PhaseViewer'
import HTTPRequestViewer from './components/httprequestviewer/HTTPRequestViewer'
@ -180,48 +180,23 @@ function App() {
// Analysis mode - show the main interface
return (
<>
<div>
<div style={{
padding: '10px 20px',
background: '#f8f9fa',
borderBottom: '1px solid #dee2e6',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
<div className={styles.mainApp}>
<div>
<div>
<button
onClick={handleBackToSelector}
style={{
background: 'transparent',
border: '1px solid #6c757d',
color: '#6c757d',
padding: '6px 12px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px'
}}
>
Back to Traces
</button>
<h1 style={{ margin: '0', color: '#495057' }}>Perf Viz</h1>
<h1>Perf Viz</h1>
</div>
<nav style={{ display: 'flex', gap: '10px' }}>
<nav>
<button
onClick={() => {
setCurrentView('trace')
updateUrlWithTraceId(selectedTraceId, 'trace', null)
}}
style={{
background: currentView === 'trace' ? '#007bff' : '#6c757d',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
Trace Stats
</button>
@ -230,15 +205,6 @@ function App() {
setCurrentView('phases')
updateUrlWithTraceId(selectedTraceId, 'phases', null)
}}
style={{
background: currentView === 'phases' ? '#007bff' : '#6c757d',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
Phase Events
</button>
@ -247,15 +213,6 @@ function App() {
setCurrentView('http')
updateUrlWithTraceId(selectedTraceId, 'http', null)
}}
style={{
background: currentView === 'http' ? '#007bff' : '#6c757d',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
HTTP Requests
</button>
@ -264,15 +221,6 @@ function App() {
setCurrentView('debug')
updateUrlWithTraceId(selectedTraceId, 'debug', null)
}}
style={{
background: currentView === 'debug' ? '#007bff' : '#6c757d',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
Request Debug
</button>

View File

@ -1,26 +0,0 @@
.header-div {
display: flex;
flexDirection: column;
justifyContent: center;
alignItems: center;
minHeight: 400px;
fontSize: 18px;
color: #6c757d;
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
}
.header-content {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
borderTop: 4px solid #007bff;
borderRadius: 50%;
animation: spin 1s linear infinite;
marginBottom: 20px;
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
}

View File

@ -1,13 +1,7 @@
import styles from './HTTPRequestLoading.module.css';
export default function HTTPRequestLoading() {
return (<>
<div className={styles.headerDiv}>
<div className={styles.headerContent}/>
return (
<div>
<div>Loading HTTP requests...</div>
</div>
<div className={styles.headerDiv}>
<div className={styles.headerContent}></div>
<div>Loading HTTP requests...</div>
</div>
</>);
);
}

View File

@ -1,705 +0,0 @@
/* CSS Custom Properties */
:root {
--color-primary: #007bff;
--color-success: #28a745;
--color-text: #495057;
--color-text-muted: #6c757d;
--color-text-light: #666;
--color-error: #721c24;
--color-error-bg: #f8d7da;
--color-error-border: #f5c6cb;
--color-warning-bg: #fff3cd;
--color-warning-border: #ffeaa7;
--color-info-bg: #f0f8ff;
--color-info-hover: #e3f2fd;
--color-info-bg-dark: #d1ecf1;
--color-info-text: #0c5460;
--color-border: #dee2e6;
--color-border-light: #f1f3f4;
--color-bg-light: #f8f9fa;
--color-bg-white: white;
--color-bg-disabled: #e9ecef;
--font-family-base: system-ui, sans-serif;
--font-family-mono: monospace;
--font-size-xs: 9px;
--font-size-sm: 10px;
--font-size-base: 11px;
--font-size-md: 12px;
--font-size-lg: 14px;
--font-size-xl: 16px;
--font-size-xxl: 18px;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 15px;
--spacing-lg: 20px;
--border-radius: 4px;
--border-radius-lg: 8px;
--border-radius-xl: 12px;
}
/* Combined Base Classes (now expanded inline) */
.tableCellBase {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
}
.tableCellCenter {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: center;
}
.tableCellRight {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: right;
}
.tableCellMonoBold {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
font-family: var(--font-family-mono);
font-weight: bold;
}
.tableCellCenterMonoBold {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: center;
font-family: var(--font-family-mono);
font-weight: bold;
}
.responseTimeCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: right;
font-family: var(--font-family-mono);
font-weight: bold;
border-radius: var(--border-radius);
}
.btnBase {
padding: var(--spacing-sm);
border-radius: var(--border-radius);
cursor: pointer;
border: 1px solid var(--color-border);
font-size: var(--font-size-lg);
}
/* Main container */
.container {
padding: var(--spacing-lg);
font-family: var(--font-family-base);
}
/* Error states */
.errorContainer {
padding: var(--spacing-lg);
text-align: center;
}
.errorMessage {
background: var(--color-error-bg);
color: var(--color-error);
padding: var(--spacing-md);
border-radius: var(--border-radius-lg);
border: 1px solid var(--color-error-border);
}
.errorMessage h3,
.errorMessage p {
margin: 0;
}
.errorMessage h3 {
margin-bottom: 10px;
}
/* Pagination controls */
.paginationControls {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-bottom: var(--spacing-lg);
}
.paginationButton {
padding: var(--spacing-sm) 16px;
border-radius: var(--border-radius);
cursor: pointer;
border: 1px solid var(--color-border);
font-size: var(--font-size-lg);
background: var(--color-bg-white);
}
.paginationButton:disabled {
background: var(--color-bg-disabled);
cursor: not-allowed;
}
.paginationInfo {
margin: 0 var(--spacing-md);
font-size: var(--font-size-lg);
}
/* Modal components */
.modalOverlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modalContainer {
width: 90vw;
height: 90vh;
background: var(--color-bg-white);
border-radius: var(--border-radius-xl);
display: flex;
flex-direction: column;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.modalHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md) var(--spacing-lg);
border-bottom: 1px solid var(--color-border);
border-radius: var(--border-radius-xl) var(--border-radius-xl) 0 0;
}
.modalTitle {
margin: 0;
color: var(--color-text);
font-size: var(--font-size-xxl);
}
.modalCloseButton {
padding: var(--spacing-sm);
border-radius: 6px;
cursor: pointer;
border: 1px solid var(--color-text-muted);
font-size: var(--font-size-lg);
background: transparent;
color: var(--color-text-muted);
}
.modalCloseButton:hover {
background: var(--color-bg-light);
}
.modalContent {
flex: 1;
padding: 10px;
}
.modalLegend {
padding: var(--spacing-md) var(--spacing-lg);
font-size: var(--font-size-md);
color: var(--color-text-muted);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
border-top: 1px solid var(--color-border);
border-radius: 0 0 var(--border-radius-xl) var(--border-radius-xl);
background-color: var(--color-bg-light);
}
.modalLegend div {
margin: 2px 0;
}
.modalLegend strong {
font-weight: bold;
}
/* Table components */
.tableContainer {
background: var(--color-bg-white);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-lg);
overflow: hidden;
}
.table {
width: 100%;
border-collapse: collapse;
}
.tableHeader {
background: var(--color-bg-light);
}
.tableHeaderCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
font-weight: bold;
border-bottom: 1px solid var(--color-border);
}
.tableHeaderCell.center {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-md);
font-weight: bold;
text-align: center;
}
.tableHeaderCell.left {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-md);
font-weight: bold;
text-align: left;
}
.tableHeaderCell.right {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-md);
font-weight: bold;
text-align: right;
}
.tableHeaderCell.expandColumn {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-md);
font-weight: bold;
width: 30px;
}
/* Table rows */
.tableRow {
border-bottom: 1px solid var(--color-border-light);
cursor: pointer;
}
.tableRow:hover {
background-color: var(--color-bg-light);
}
/* Screenshot components */
.screenshotRow {
background-color: var(--color-info-bg);
border-bottom: 2px solid var(--color-primary);
}
.screenshotRow:hover {
background-color: var(--color-info-hover);
}
.screenshotContainer {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-md);
}
.screenshotLabel {
font-weight: bold;
color: var(--color-primary);
font-size: var(--font-size-lg);
min-width: 120px;
}
.screenshotTime {
font-family: var(--font-family-mono);
color: var(--color-text);
font-size: var(--font-size-md);
}
.screenshotImage {
max-width: 200px;
max-height: 150px;
border: 2px solid var(--color-primary);
border-radius: var(--border-radius);
cursor: pointer;
transition: transform 0.2s ease;
}
.screenshotImage:hover {
transform: scale(1.05);
}
.screenshotHint {
font-size: var(--font-size-base);
color: var(--color-text-muted);
font-style: italic;
}
/* Table cell variants */
.tableCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
}
.tableCell.center {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: center;
}
.tableCell.right {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: right;
}
.tableCell.monospace {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
font-family: var(--font-family-mono);
}
.tableCell.bold {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
font-weight: bold;
}
.tableCell.gray {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
color: var(--color-text-muted);
}
.tableCell.expandCell {
padding: var(--spacing-sm);
font-size: var(--font-size-xl);
vertical-align: middle;
color: var(--color-primary);
font-weight: bold;
user-select: none;
}
.tableCell.statusCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: center;
font-family: var(--font-family-mono);
font-weight: bold;
}
.tableCell.methodCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
font-family: var(--font-family-mono);
font-weight: bold;
}
.tableCell.priorityCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: center;
font-family: var(--font-family-mono);
font-weight: bold;
}
.tableCell.timeCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
vertical-align: middle;
text-align: right;
font-family: var(--font-family-mono);
color: var(--color-text);
}
/* Specialized cells with shared base */
.totalResponseTimeCell {
padding: var(--spacing-sm);
text-align: right;
font-size: var(--font-size-md);
font-family: var(--font-family-mono);
font-weight: bold;
border-radius: var(--border-radius);
border: 1px solid var(--color-border-light);
}
.durationCell {
padding: var(--spacing-sm);
text-align: right;
font-size: var(--font-size-md);
font-family: var(--font-family-mono);
font-weight: bold;
border-radius: var(--border-radius);
}
.sizeCell {
padding: var(--spacing-sm);
text-align: right;
font-size: var(--font-size-md);
font-family: var(--font-family-mono);
font-weight: bold;
border-radius: var(--border-radius);
}
.serverLatencyCell {
padding: var(--spacing-sm);
text-align: right;
font-size: var(--font-size-md);
font-family: var(--font-family-mono);
font-weight: bold;
border-radius: var(--border-radius);
}
.protocolCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
text-align: center;
font-family: var(--font-family-mono);
font-weight: bold;
}
.cdnCell, .cacheCell {
padding: var(--spacing-sm);
font-size: var(--font-size-md);
text-align: center;
}
.cdnCell { cursor: help; }
.cdnCell.default { cursor: default; }
/* Method styling */
.methodGet { color: var(--color-success); }
.methodOther { color: var(--color-primary); }
/* URL cell */
.urlCell {
padding: var(--spacing-sm);
font-size: var(--font-size-base);
vertical-align: middle;
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
}
.urlLink {
color: var(--color-primary);
text-decoration: none;
}
.urlLink:hover {
text-decoration: underline;
}
/* Queue time components */
.queueTimeContainer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: var(--spacing-xs);
}
.queueAnalysisIcon {
cursor: help;
font-size: var(--font-size-lg);
}
/* Connection status - consolidated */
.connectionCached,
.connectionReused {
color: var(--color-text-light);
font-style: italic;
}
/* Cache indicators - using ::before for consistency */
.cacheFromCache::before,
.cacheConnectionReused::before,
.cacheNetwork::before {
margin-right: var(--spacing-xs);
}
.cacheFromCache::before { content: '💾'; }
.cacheConnectionReused::before { content: '🔄'; }
.cacheNetwork::before { content: '🌐'; }
/* Expanded row details */
.expandedRow {
background: var(--color-bg-light);
border: 1px solid var(--color-border-light);
}
.expandedContent {
padding: var(--spacing-md);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-md);
}
.detailCard {
border-radius: var(--border-radius);
background: var(--color-bg-white);
padding: 10px;
border: 1px solid var(--color-border);
}
.detailCard.fullWidth {
grid-column: 1 / -1;
}
.detailCardTitle {
margin: 0 0 var(--spacing-sm) 0;
color: var(--color-text);
font-size: var(--font-size-lg);
font-weight: bold;
}
.detailList {
font-size: var(--font-size-md);
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.detailListItem {
margin: 0;
}
.detailListItem strong {
font-weight: bold;
margin-right: var(--spacing-sm);
}
/* Network timing details */
.timingHighlighted {
font-weight: bold;
border-radius: var(--border-radius);
padding: var(--spacing-xs) var(--spacing-sm);
border: 1px solid var(--color-border-light);
}
/* Analysis sections - consolidated */
.queueAnalysisCard .detailCardTitle,
.cdnAnalysisCard .detailCardTitle {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.relatedRequestIds {
font-family: var(--font-family-mono);
font-size: var(--font-size-base);
color: var(--color-text);
word-break: break-all;
}
/* Debug section */
.debugSection {
border-radius: var(--border-radius);
margin-top: var(--spacing-sm);
padding: var(--spacing-sm);
background: var(--color-warning-bg);
border: 1px solid var(--color-warning-border);
}
.debugTitle {
font-weight: bold;
margin-bottom: var(--spacing-xs);
font-size: var(--font-size-base);
}
.debugInfo {
margin-bottom: 6px;
font-size: var(--font-size-base);
}
.debugHeaders {
font-weight: bold;
font-size: var(--font-size-sm);
margin-bottom: 2px;
}
.headerLine {
font-family: var(--font-family-mono);
font-size: var(--font-size-xs);
margin-bottom: 1px;
padding: 1px 3px;
border-radius: 2px;
}
.headerLine.akamaiIndicator {
background-color: var(--color-info-bg-dark);
color: var(--color-info-text);
}
.headerName {
font-weight: bold;
margin-right: 5px;
}
.headerName.akamaiIndicator,
.akamaiLabel {
color: var(--color-info-text);
font-weight: bold;
}
.akamaiLabel {
margin-left: 5px;
}
/* Response headers section */
.headersContainer {
font-family: var(--font-family-mono);
max-height: 150px;
overflow-y: auto;
font-size: var(--font-size-base);
background: var(--color-bg-light);
padding: var(--spacing-sm);
border-radius: 3px;
}
.headerItem {
margin-bottom: 2px;
display: grid;
grid-template-columns: 150px 1fr;
gap: 10px;
}
.headerItemName {
color: var(--color-primary);
font-weight: bold;
}
.headerItemValue {
color: var(--color-text);
word-break: break-all;
}
/* Utility classes */
.noResults {
text-align: center;
color: var(--color-text-muted);
padding: 40px;
font-size: var(--font-size-xl);
}
.coloredBackground {
border-radius: var(--border-radius);
padding: var(--spacing-xs) 6px;
}
.highlighted {
font-weight: bold;
}

View File

@ -55,6 +55,7 @@ import { addRequestPostProcessing } from './lib/requestPostProcessor'
import type { HTTPRequest, ScreenshotEvent } from './types/httpRequest'
import HTTPRequestLoading from "./HTTPRequestLoading.tsx";
import sortRequests from "./lib/sortRequests.ts";
import PaginationControls from "./PaginationControls.tsx";
interface HTTPRequestViewerProps {
@ -342,30 +343,7 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
handleSSIMRecalculate={handleSSIMRecalculate}
/>
{/* Pagination Controls */}
{totalPages > 1 && (
<div className={styles.paginationControls}>
<button
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
className={styles.paginationButton}
>
Previous
</button>
<span className={styles.paginationInfo}>
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
disabled={currentPage === totalPages}
className={styles.paginationButton}
>
Next
</button>
</div>
)}
<PaginationControls currentPage={currentPage} setCurrentPage={setCurrentPage} totalPages={totalPages} />
{/* 3D Network Visualization Modal */}
{show3DViewer && (

View File

@ -0,0 +1,20 @@
.paginationControls {
font-size: xx-large;
display: flex;
justify-content: center;
margin: 16px;
}
.paginationControls button {
margin: 0 17px;
background-color: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
width: 128px;
color: var(--color-text);
}
.paginationControls button:disabled {
opacity: 0.5;
border: none;
color: var(--color-text-muted);
}

View File

@ -0,0 +1,30 @@
import styles from "./PaginationControls.module.css";
interface PaginationControlsProps {
totalPages: number | null;
setCurrentPage: (page: number) => void;
currentPage: number | null;
}
export default function PaginationControls( {totalPages, setCurrentPage, currentPage}: PaginationControlsProps) {
{/* Pagination Controls */}
if (!totalPages || totalPages <= 1) return <></>
return <> {totalPages > 1 && (
<div className={styles.paginationControls}>
<button
onClick={() => setCurrentPage(Math.max(1, currentPage ? currentPage - 1: 0))}
disabled={currentPage === 1}>
Previous
</button>
<span>
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => setCurrentPage(Math.min(totalPages, currentPage ? currentPage + 1 : 1))}
disabled={currentPage === totalPages}>
Next
</button>
</div>
)}
</>
}

View File

@ -0,0 +1,101 @@
tr {
cursor: pointer;
}
.expandedRow {
background-color: var(--color-bg-secondary);
border-radius: 0.25rem;
padding: 0.5rem;
margin-top: 0.5rem;
}
div.expandedContent {
margin: 0 32px;
border: 1px solid #6c757d;
flex-direction: row;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
div.detailCard {
margin: 16px;
width: 640px;
}
.detailCardTitle {
font-size: var(--font-size-xxl);
font-weight: bold;
color: var(--color-text);
margin-bottom: 8px;
}
.detailList {
display: flex;
flex-direction: column;
padding: 0;
margin: 0;
}
.detailListItem {
margin: 4px;
}
.detailListItem strong {
vertical-align: bottom;
text-align: right;
color: var(--color-text-highlight);
font-weight: bold;
width:128px;
display: inline-block;
}
.cdnAnalysisCard {
overflow: scroll;
}
.debugTitle {
color: var(--color-text-highlight);
font-size: var(--font-size-xxl);
}
.headerLine {
font-family: monospace var(--font-family-mono);
}
.headerName {
font-size: var(--font-size-lg);
font-weight: bold;
color: var(--color-text-highlight);
margin-bottom: 8px;
}
div.fullWidth {
width: 100%;
padding: 16px;
}
/* Headers container for better layout */
.headersContainer {
font-family: var(--font-family-mono);
max-height: 400px;
overflow-y: auto;
background-color: var(--color-bg-light);
padding: var(--spacing-md);
border-radius: var(--radius-md);
}
.headerItem {
display: grid;
grid-template-columns: minmax(200px, max-content) 1fr;
gap: var(--spacing-md);
margin-bottom: 4px;
font-family: var(--font-family-mono);
font-size: var(--font-size-base);
}
.headerItemName {
font-weight: bold;
color: var(--color-primary);
white-space: nowrap;
text-align: right;
padding-right: var(--spacing-xs);
}
.headerItemValue {
color: var(--color-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: help;
word-break: break-all;
}

View File

@ -1,6 +1,6 @@
import React from 'react'
import type { HTTPRequest } from './types/httpRequest'
import styles from './HTTPRequestViewer.module.css'
import styles from './RequestRowDetails.module.css'
// Import utility functions
import { formatDuration, formatSize } from './lib/formatUtils'
@ -140,7 +140,7 @@ const RequestRowDetails: React.FC<RequestRowDetailsProps> = ({ request }) => {
<div className={styles.detailListItem}><strong>Detection Method:</strong> {request.cdnAnalysis.detectionMethod}</div>
{/* Debug info for canadiantire.ca requests */}
{request.hostname.includes('canadiantire.ca') && (
{(
<div className={styles.debugSection}>
<div className={styles.debugTitle}>
Debug - CDN Detection Analysis:
@ -206,7 +206,7 @@ const RequestRowDetails: React.FC<RequestRowDetailsProps> = ({ request }) => {
<div className={styles.headerItemName}>
{header.name}:
</div>
<div className={styles.headerItemValue}>
<div className={styles.headerItemValue} title={header.value}>
{header.value}
</div>
</div>

View File

@ -0,0 +1,29 @@
/* Standardized status indicator classes */
.success {
background-color: var(--color-bg-success);
color: var(--color-success);
}
.warning {
background-color: var(--color-bg-warning);
color: var(--color-warning);
}
.danger {
background-color: var(--color-bg-danger);
color: var(--color-danger);
}
td {
padding: 2px 8px;
border-radius: var(--radius-md);
}
tr {
border: 1px solid #ffffff;
}
a {
color: var(--color-text);
text-decoration: none;
}

View File

@ -1,25 +1,21 @@
import React from 'react'
import RequestRowDetails from './RequestRowDetails'
import styles from './HTTPRequestViewer.module.css'
import styles from './RequestRowSummary.module.css'
import type { HTTPRequest } from './types/httpRequest'
// Import utility functions
import { formatDuration, formatSize, formatDataRate } from './lib/formatUtils'
import {
getStatusColor,
getProtocolColor,
getPriorityColor,
getSizeColor,
getDurationColor,
getTotalResponseTimeColor,
getServerLatencyColor,
getDataRateColor,
getTotalResponseTimeClass,
getServerLatencyClass,
getDurationClass,
getDataRateClass,
getSizeClass,
getQueueAnalysisIcon,
getCDNIcon,
getCDNDisplayName
getCDNDisplayName, getProtocolClass, getConnectionClass
} from './lib/colorUtils'
import { truncateUrl } from './lib/urlUtils'
import { HIGHLIGHTING_CONFIG } from './lib/httpRequestConstants'
interface RequestRowSummaryProps {
request: HTTPRequest
@ -36,43 +32,38 @@ const RequestRowSummary: React.FC<RequestRowSummaryProps> = ({
}) => {
return (
<>
<tr key={request.requestId} className={styles.tableRow}
<tr className={styles.default} key={request.requestId}
onClick={() => onToggleRowExpansion(request.requestId)}
>
<td className={`${styles.tableCell} ${styles.expandCell}`}>
<td>
{isExpanded ? '' : '+'}
</td>
<td className={`${styles.tableCell} ${styles.methodCell} ${request.method === 'GET' ? styles.methodGet : styles.methodOther}`}>
<td>
{request.method}
</td>
<td className={`${styles.tableCell} ${styles.statusCell}`} style={{ color: getStatusColor(request.statusCode) }}>
<td>
{request.statusCode || '-'}
</td>
<td className={`${styles.tableCell} ${styles.gray}`}>
<td>
{request.resourceType}
</td>
<td className={`${styles.tableCell} ${styles.priorityCell}`} style={{ color: getPriorityColor(request.priority) }}>
<td>
{request.priority || '-'}
</td>
<td className={`${styles.tableCell} ${styles.urlCell}`}>
<a href={request.url} target="_blank" rel="noopener noreferrer" className={styles.urlLink}>
<td>
<a href={request.url} target="_blank" rel="noopener noreferrer">
{truncateUrl(request.url)}
</a>
</td>
<td className={`${styles.tableCell} ${styles.timeCell}`}>
<td>
{formatDuration(request.timing.startOffset)}
</td>
<td className={`${styles.tableCell} ${styles.timeCell}`}
style={{
color: request.timing.queueTime && request.timing.queueTime > HIGHLIGHTING_CONFIG.QUEUE_TIME.HIGH_THRESHOLD ? HIGHLIGHTING_CONFIG.COLORS.STATUS.SERVER_ERROR : '#495057',
fontWeight: request.timing.queueTime && request.timing.queueTime > HIGHLIGHTING_CONFIG.QUEUE_TIME.HIGH_THRESHOLD ? 'bold' : 'normal'
}}>
<div className={styles.queueTimeContainer}>
<td className={getConnectionClass(request?.timing?.queueTime ||0)}>
<div>
{formatDuration(request.timing.queueTime)}
{showQueueAnalysis && request.queueAnalysis && (
<span
title={request.queueAnalysis.description}
className={styles.queueAnalysisIcon}
>
{getQueueAnalysisIcon(request.queueAnalysis)}
</span>
@ -81,58 +72,60 @@ const RequestRowSummary: React.FC<RequestRowSummaryProps> = ({
</td>
{/* DNS Time */}
<td className={`${styles.tableCell} ${styles.timeCell}`}>
<td >
{request.timing.dnsStart !== undefined && request.timing.dnsEnd !== undefined && request.timing.dnsStart >= 0 && request.timing.dnsEnd >= 0
? formatDuration((request.timing.dnsEnd || 0) - (request.timing.dnsStart || 0))
: <span className={styles.connectionCached}>cached</span>
: <span>cached</span>
}
</td>
{/* Connection Time */}
<td className={`${styles.tableCell} ${styles.timeCell}`}>
{request.timing.connectStart !== undefined && request.timing.connectEnd !== undefined && request.timing.connectStart >= 0 && request.timing.connectEnd >= 0
? formatDuration((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))
: <span className={styles.connectionReused}>
<td className={getConnectionClass((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))}>
{request.timing.connectStart !== undefined && request.timing.connectEnd !== undefined
&& request.timing.connectStart >= 0 && request.timing.connectEnd >= 0
? <span >
{formatDuration((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))}
</span>
: <span>
{request.connectionReused ? 'reused' : 'cached'}
</span>
}
</td>
<td className={styles.serverLatencyCell} style={{ ...getServerLatencyColor(request.timing.serverLatency) }}>
<td className={`${getServerLatencyClass(request.timing.serverLatency)}`}>
{formatDuration(request.timing.serverLatency)}
</td>
<td className={styles.durationCell} style={{ ...getDurationColor(request.timing.duration) }}>
<td className={getDurationClass(request.timing.duration)}>
{formatDuration(request.timing.duration)}
</td>
{/* Total Response Time */}
<td className={styles.totalResponseTimeCell} style={{ ...getTotalResponseTimeColor(request.timing.totalResponseTime) }}>
<td className={`${getTotalResponseTimeClass(request.timing.totalResponseTime)}`}>
{formatDuration(request.timing.totalResponseTime)}
</td>
{/* Data Rate */}
<td className={`${styles.tableCell} ${styles.right} ${styles.monospace}`} style={{ ...getDataRateColor(request.encodedDataLength, request.contentLength, request.timing.duration) }}>
<td className={`${getDataRateClass(request.encodedDataLength, request.contentLength, request.timing.duration)}`}>
{formatDataRate(request.encodedDataLength, request.contentLength, request.timing.duration)}
</td>
<td className={styles.sizeCell} style={{ ...getSizeColor(request.encodedDataLength) }}>
<td className={`${getSizeClass(request.encodedDataLength)}`}>
{formatSize(request.encodedDataLength)}
</td>
<td className={styles.sizeCell} style={{ ...getSizeColor(request.contentLength) }}>
<td className={`${getSizeClass(request.contentLength)}`}>
{request.contentLength ? formatSize(request.contentLength) : '-'}
</td>
<td className={styles.protocolCell} style={{ color: getProtocolColor(request.protocol) }}>
<td className={getProtocolClass(request.protocol)}>
{request.protocol || '-'}
</td>
<td className={`${styles.cdnCell} ${request.cdnAnalysis ? '' : styles.default}`}
<td
title={request.cdnAnalysis ?
`${getCDNDisplayName(request.cdnAnalysis.provider)} ${request.cdnAnalysis.isEdge ? '(Edge)' : '(Origin)'} - ${request.cdnAnalysis.detectionMethod}` :
'No CDN detected'}
>
{request.cdnAnalysis ? getCDNIcon(request.cdnAnalysis) : '-'}
</td>
<td className={`${styles.cacheCell} ${request.fromCache ? styles.cacheFromCache : request.connectionReused ? styles.cacheConnectionReused : styles.cacheNetwork}`}>
<td>
{request.fromCache ? '💾' : request.connectionReused ? '🔄' : '🌐'}
</td>
</tr>

View File

@ -1,64 +1,6 @@
import { HIGHLIGHTING_CONFIG } from './httpRequestConstants'
import type { QueueAnalysis, CDNAnalysis } from '../types/httpRequest'
export const getStatusColor = (status?: number): string => {
if (!status) return HIGHLIGHTING_CONFIG.COLORS.STATUS.UNKNOWN
if (status >= 200 && status < 300) return HIGHLIGHTING_CONFIG.COLORS.STATUS.SUCCESS
if (status >= 300 && status < 400) return HIGHLIGHTING_CONFIG.COLORS.STATUS.REDIRECT
if (status >= 400 && status < 500) return HIGHLIGHTING_CONFIG.COLORS.STATUS.CLIENT_ERROR
if (status >= 500) return HIGHLIGHTING_CONFIG.COLORS.STATUS.SERVER_ERROR
return HIGHLIGHTING_CONFIG.COLORS.STATUS.UNKNOWN
}
export const getProtocolColor = (protocol?: string): string => {
if (!protocol) return HIGHLIGHTING_CONFIG.COLORS.PROTOCOL.UNKNOWN
if (protocol === 'http/1.1') return HIGHLIGHTING_CONFIG.COLORS.PROTOCOL.HTTP1_1
if (protocol === 'h2') return HIGHLIGHTING_CONFIG.COLORS.PROTOCOL.H2
if (protocol === 'h3') return HIGHLIGHTING_CONFIG.COLORS.PROTOCOL.H3
return HIGHLIGHTING_CONFIG.COLORS.PROTOCOL.UNKNOWN
}
export const getPriorityColor = (priority?: string): string => {
if (!priority) return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.UNKNOWN
const upperPriority = priority.toUpperCase()
if (upperPriority === 'VERYHIGH') return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.VERY_HIGH
if (upperPriority === 'HIGH') return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.HIGH
if (upperPriority === 'MEDIUM') return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.MEDIUM
if (upperPriority === 'LOW') return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.LOW
if (upperPriority === 'VERYLOW') return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.VERY_LOW
return HIGHLIGHTING_CONFIG.COLORS.PRIORITY.UNKNOWN
}
export const getSizeColor = (bytes?: number) => {
if (!bytes) return HIGHLIGHTING_CONFIG.COLORS.SIZE.DEFAULT
const kb = bytes / 1024
if (kb >= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.LARGE) {
return HIGHLIGHTING_CONFIG.COLORS.SIZE.LARGE
} else if (kb >= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.MEDIUM) {
return HIGHLIGHTING_CONFIG.COLORS.SIZE.MEDIUM
} else if (kb <= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.SMALL) {
return HIGHLIGHTING_CONFIG.COLORS.SIZE.SMALL
}
return HIGHLIGHTING_CONFIG.COLORS.SIZE.DEFAULT
}
export const getDurationColor = (microseconds?: number) => {
if (!microseconds) return HIGHLIGHTING_CONFIG.COLORS.DURATION.DEFAULT
const durationMs = microseconds / 1000
if (durationMs > HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.SLOW) {
return HIGHLIGHTING_CONFIG.COLORS.DURATION.SLOW
} else if (durationMs >= HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.MEDIUM) {
return HIGHLIGHTING_CONFIG.COLORS.DURATION.MEDIUM
} else if (durationMs < HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.FAST) {
return HIGHLIGHTING_CONFIG.COLORS.DURATION.FAST
}
return HIGHLIGHTING_CONFIG.COLORS.DURATION.DEFAULT
}
import styles from '../RequestRowSummary.module.css'
export const getTotalResponseTimeColor = (microseconds?: number) => {
if (!microseconds) return HIGHLIGHTING_CONFIG.COLORS.DURATION.DEFAULT
@ -74,26 +16,51 @@ export const getTotalResponseTimeColor = (microseconds?: number) => {
}
}
export const getServerLatencyColor = (microseconds?: number) => {
if (!microseconds) return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.DEFAULT
export const getTotalResponseTimeClass = (microseconds?: number): string => {
if (!microseconds) return ''
if (microseconds > HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.SLOW) {
return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.SLOW
} else if (microseconds >= HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.MEDIUM) {
return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.MEDIUM
} else if (microseconds < HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.FAST) {
return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.FAST
const totalResponseTimeMs = microseconds / 1000
if (totalResponseTimeMs > HIGHLIGHTING_CONFIG.TOTAL_RESPONSE_TIME_THRESHOLDS.SLOW) {
return styles.danger // Red for > 200ms
} else if (totalResponseTimeMs >= HIGHLIGHTING_CONFIG.TOTAL_RESPONSE_TIME_THRESHOLDS.FAST) {
return styles.warning // Yellow for 100-200ms
} else {
return styles.success // Green for < 100ms
}
return HIGHLIGHTING_CONFIG.COLORS.SERVER_LATENCY.DEFAULT
}
export const getConnectionClass = (time: number): string => {
if (time<50000) {
return styles.success;
}
if (time<180000) {
return styles.warning;
}
return styles.danger;
}
export const getProtocolClass = (protocol?: string): string => {
if (!protocol) return ''
switch (protocol.toLowerCase()) {
case 'http/2':
case 'h2':
return styles.warning
case 'http/3':
case 'h3':
return styles.success
case 'http/1.1':
return styles.danger
default:
return styles.danger
}
}
export const getDataRateColor = (transferSize?: number, contentLength?: number, durationMicroseconds?: number) => {
if (!durationMicroseconds || durationMicroseconds <= 0) return {}
export const getDataRateClass = (transferSize?: number, contentLength?: number, durationMicroseconds?: number): string => {
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 {}
if (!bytes) return ''
// Convert duration from microseconds to seconds
const durationSeconds = durationMicroseconds / 1000000
@ -106,14 +73,59 @@ export const getDataRateColor = (transferSize?: number, contentLength?: number,
const kbps = bytesPerSecond / 1024 // KB/s
if (mbps >= 1) {
return { backgroundColor: '#e8f5e8', color: '#2e7d32' } // Green for >= 1 MB/s
return styles.success // Green for >= 1 MB/s
} else if (kbps >= 100) {
return { backgroundColor: '#fff8e1', color: '#f57c00' } // Yellow for 100 KB/s - 1 MB/s
return styles.warning // Yellow for 100 KB/s - 1 MB/s
} else {
return { backgroundColor: '#ffebee', color: '#c62828' } // Red for < 100 KB/s
return styles.danger // Red for < 100 KB/s
}
}
export const getServerLatencyClass = (microseconds?: number): string => {
if (!microseconds) return ''
if (microseconds > HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.SLOW) {
return styles.danger
} else if (microseconds >= HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.MEDIUM) {
return styles.warning
} else if (microseconds < HIGHLIGHTING_CONFIG.SERVER_LATENCY_THRESHOLDS.FAST) {
return styles.success
}
return ''
}
export const getDurationClass = (microseconds?: number): string => {
if (!microseconds) return ''
const durationMs = microseconds / 1000
if (durationMs > HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.SLOW) {
return styles.danger
} else if (durationMs >= HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.MEDIUM) {
return styles.warning
} else if (durationMs < HIGHLIGHTING_CONFIG.DURATION_THRESHOLDS.FAST) {
return styles.success
}
return ''
}
export const getSizeClass = (bytes?: number): string => {
if (!bytes) return ''
const kb = bytes / 1024
if (kb >= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.LARGE) {
return styles.danger
} else if (kb >= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.MEDIUM) {
return styles.warning
} else if (kb <= HIGHLIGHTING_CONFIG.SIZE_THRESHOLDS.SMALL) {
return styles.success
}
return ''
}
export const getQueueAnalysisIcon = (analysis?: QueueAnalysis): string => {
if (!analysis) return ''

View File

@ -68,20 +68,22 @@ export const HIGHLIGHTING_CONFIG = {
VERY_LOW: '#6c757d', // Gray
UNKNOWN: '#6c757d'
},
//background:
// color: var(--color-danger);
//
// File size colors
SIZE: {
LARGE: { background: '#dc3545', color: 'white' }, // Red for 500KB+
MEDIUM: { background: '#ffc107', color: 'black' }, // Yellow for 100-500KB
SMALL: { background: '#28a745', color: 'white' }, // Green for under 50KB
LARGE: { background: 'var(--color-bg-danger)', color: 'var(--color-danger)'}, // Red for 500KB+
MEDIUM: { background: 'var(--color-bg-warning)', color: 'var(--color-warning)' }, // Yellow for 100-500KB
SMALL: { background: 'var(--color-bg)', color: 'var(--color-success)' }, // Green for under 50KB
DEFAULT: { background: 'transparent', color: '#495057' } // Default for 50-100KB
},
// Duration colors
DURATION: {
SLOW: { background: '#dc3545', color: 'white' }, // Red for > 150ms
MEDIUM: { background: '#ffc107', color: 'black' }, // Yellow for 50-150ms
FAST: { background: '#28a745', color: 'white' }, // Green for < 50ms
SLOW: { background: 'var(--color-bg-danger)', color: 'var(--color-danger)'}, // Red for > 150ms
MEDIUM: { background: 'var(--color-bg-warning)', color: 'var(--color-warning)' }, // Yellow for 50-150ms
FAST: { background: 'var(--color-bg)', color: 'var(--color-success)' }, // Green for < 50ms
DEFAULT: { background: 'transparent', color: '#495057' } // Default
},