Skip to content

Commit 6dcb8d3

Browse files
fix: theme-aware chart grid colors and add TTFB charts to host/client sections
Make chart grid lines adapt to light/dark theme instead of hardcoded white rgba. Add per-host and per-client TTFB (P50) time series charts for non-write operations.
1 parent e75199e commit 6dcb8d3

1 file changed

Lines changed: 139 additions & 7 deletions

File tree

wui/static/app.js

Lines changed: 139 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ function updateThemeButton(theme) {
5050
}
5151
}
5252

53+
let chartGridColor = 'rgba(255,255,255,0.05)';
54+
5355
function updateChartDefaults(theme) {
5456
if (theme === 'light') {
5557
Chart.defaults.color = 'hsl(0, 0%, 45.1%)';
5658
Chart.defaults.borderColor = 'hsl(0, 0%, 89.8%)';
59+
chartGridColor = 'rgba(0,0,0,0.06)';
5760
} else {
5861
Chart.defaults.color = 'hsl(0, 0%, 64%)';
5962
Chart.defaults.borderColor = 'hsl(0, 0%, 15%)';
63+
chartGridColor = 'rgba(255,255,255,0.05)';
6064
}
6165
}
6266

@@ -594,7 +598,7 @@ function renderOpThroughputChart(opType, idx) {
594598
},
595599
y: {
596600
beginAtZero: true,
597-
grid: { color: 'rgba(255,255,255,0.05)' }
601+
grid: { color: chartGridColor }
598602
}
599603
}
600604
}
@@ -665,7 +669,7 @@ function renderOpLatencyChart(opType, idx) {
665669
y: {
666670
beginAtZero: true,
667671
title: { display: true, text: 'ms', font: { size: 10 } },
668-
grid: { color: 'rgba(255,255,255,0.05)' }
672+
grid: { color: chartGridColor }
669673
}
670674
}
671675
}
@@ -739,7 +743,7 @@ function renderOpTtfbChart(opType, idx) {
739743
y: {
740744
beginAtZero: true,
741745
title: { display: true, text: 'ms', font: { size: 10 } },
742-
grid: { color: 'rgba(255,255,255,0.05)' }
746+
grid: { color: chartGridColor }
743747
}
744748
}
745749
}
@@ -770,6 +774,9 @@ function renderHostsTable() {
770774
<div class="breakdown-chart-container">
771775
<canvas id="host-chart-${currentChartIdx}"></canvas>
772776
</div>
777+
${hasTtfb(opType) ? `<div class="breakdown-chart-container">
778+
<canvas id="host-ttfb-chart-${currentChartIdx}"></canvas>
779+
</div>` : ''}
773780
<table>
774781
<thead>
775782
<tr>
@@ -806,7 +813,9 @@ function renderHostsTable() {
806813
// Render charts for each op type
807814
chartIdx = 0;
808815
opTypes.forEach((opType) => {
809-
renderHostThroughputChart(opType, chartIdx++, hosts);
816+
const ci = chartIdx++;
817+
renderHostThroughputChart(opType, ci, hosts);
818+
renderHostTtfbChart(opType, ci, hosts);
810819
});
811820
}
812821

@@ -834,6 +843,9 @@ function renderClientsTable() {
834843
<div class="breakdown-chart-container">
835844
<canvas id="client-chart-${currentChartIdx}"></canvas>
836845
</div>
846+
${hasTtfb(opType) ? `<div class="breakdown-chart-container">
847+
<canvas id="client-ttfb-chart-${currentChartIdx}"></canvas>
848+
</div>` : ''}
837849
<table>
838850
<thead>
839851
<tr>
@@ -870,7 +882,9 @@ function renderClientsTable() {
870882
// Render charts for each op type
871883
chartIdx = 0;
872884
opTypes.forEach((opType) => {
873-
renderClientThroughputChart(opType, chartIdx++, clients);
885+
const ci = chartIdx++;
886+
renderClientThroughputChart(opType, ci, clients);
887+
renderClientTtfbChart(opType, ci, clients);
874888
});
875889
}
876890

@@ -963,7 +977,7 @@ function renderHostThroughputChart(opType, idx, hosts) {
963977
y: {
964978
beginAtZero: true,
965979
title: { display: true, text: 'MiB/s', font: { size: 10 } },
966-
grid: { color: 'rgba(255,255,255,0.05)' }
980+
grid: { color: chartGridColor }
967981
}
968982
}
969983
}
@@ -1043,7 +1057,125 @@ function renderClientThroughputChart(opType, idx, clients) {
10431057
y: {
10441058
beginAtZero: true,
10451059
title: { display: true, text: 'MiB/s', font: { size: 10 } },
1046-
grid: { color: 'rgba(255,255,255,0.05)' }
1060+
grid: { color: chartGridColor }
1061+
}
1062+
}
1063+
}
1064+
});
1065+
}
1066+
1067+
function renderHostTtfbChart(opType, idx, hosts) {
1068+
if (!hasTtfb(opType)) return;
1069+
1070+
const byHostData = data.by_host || {};
1071+
const canvas = document.getElementById(`host-ttfb-chart-${idx}`);
1072+
if (!canvas) return;
1073+
const ctx = canvas.getContext('2d');
1074+
1075+
const datasets = [];
1076+
hosts.forEach((host, hostIdx) => {
1077+
const hostAgg = byHostData[host];
1078+
const ttfbTs = sampleData(getTtfbTimeSeries(hostAgg));
1079+
if (ttfbTs && ttfbTs.length > 0) {
1080+
datasets.push({
1081+
label: host + ' P50',
1082+
data: ttfbTs.map(s => ({ x: s.time, y: s.p50 })),
1083+
borderColor: getSeriesColor(hostIdx),
1084+
backgroundColor: 'transparent',
1085+
fill: false,
1086+
tension: 0.4,
1087+
pointRadius: 0,
1088+
borderWidth: 2
1089+
});
1090+
}
1091+
});
1092+
1093+
if (datasets.length === 0) return;
1094+
1095+
new Chart(ctx, {
1096+
type: 'line',
1097+
data: { datasets },
1098+
options: {
1099+
responsive: true,
1100+
maintainAspectRatio: false,
1101+
interaction: { mode: 'index', intersect: false },
1102+
plugins: {
1103+
title: { display: true, text: 'TTFB (P50)', font: { size: 11 } },
1104+
legend: {
1105+
display: datasets.length > 1,
1106+
position: 'top',
1107+
labels: { boxWidth: 12, padding: 8, font: { size: 10 } }
1108+
}
1109+
},
1110+
scales: {
1111+
x: {
1112+
type: 'time',
1113+
time: { displayFormats: { second: 'HH:mm:ss' } },
1114+
grid: { display: false }
1115+
},
1116+
y: {
1117+
beginAtZero: true,
1118+
title: { display: true, text: 'ms', font: { size: 10 } },
1119+
grid: { color: chartGridColor }
1120+
}
1121+
}
1122+
}
1123+
});
1124+
}
1125+
1126+
function renderClientTtfbChart(opType, idx, clients) {
1127+
if (!hasTtfb(opType)) return;
1128+
1129+
const byClientData = data.by_client || {};
1130+
const canvas = document.getElementById(`client-ttfb-chart-${idx}`);
1131+
if (!canvas) return;
1132+
const ctx = canvas.getContext('2d');
1133+
1134+
const datasets = [];
1135+
clients.forEach((client, clientIdx) => {
1136+
const clientAgg = byClientData[client];
1137+
const ttfbTs = sampleData(getTtfbTimeSeries(clientAgg));
1138+
if (ttfbTs && ttfbTs.length > 0) {
1139+
datasets.push({
1140+
label: client + ' P50',
1141+
data: ttfbTs.map(s => ({ x: s.time, y: s.p50 })),
1142+
borderColor: getSeriesColor(clientIdx),
1143+
backgroundColor: 'transparent',
1144+
fill: false,
1145+
tension: 0.4,
1146+
pointRadius: 0,
1147+
borderWidth: 2
1148+
});
1149+
}
1150+
});
1151+
1152+
if (datasets.length === 0) return;
1153+
1154+
new Chart(ctx, {
1155+
type: 'line',
1156+
data: { datasets },
1157+
options: {
1158+
responsive: true,
1159+
maintainAspectRatio: false,
1160+
interaction: { mode: 'index', intersect: false },
1161+
plugins: {
1162+
title: { display: true, text: 'TTFB (P50)', font: { size: 11 } },
1163+
legend: {
1164+
display: datasets.length > 1,
1165+
position: 'top',
1166+
labels: { boxWidth: 12, padding: 8, font: { size: 10 } }
1167+
}
1168+
},
1169+
scales: {
1170+
x: {
1171+
type: 'time',
1172+
time: { displayFormats: { second: 'HH:mm:ss' } },
1173+
grid: { display: false }
1174+
},
1175+
y: {
1176+
beginAtZero: true,
1177+
title: { display: true, text: 'ms', font: { size: 10 } },
1178+
grid: { color: chartGridColor }
10471179
}
10481180
}
10491181
}

0 commit comments

Comments
 (0)