@@ -83,6 +83,7 @@ const charts: echarts.ECharts[] = [];
8383const trafficChart = initChart ( "trafficChart" ) ;
8484const requestChart = initChart ( "requestChart" ) ;
8585const diskChart = initChart ( "diskChart" ) ;
86+ const colorSchemeMedia = window . matchMedia ( "(prefers-color-scheme: light)" ) ;
8687
8788setHistoryWindow ( historyMinutes ) ;
8889
@@ -105,6 +106,13 @@ window.addEventListener("resize", () => {
105106 charts . forEach ( ( chart ) => chart . resize ( ) ) ;
106107} ) ;
107108
109+ colorSchemeMedia . addEventListener ( "change" , ( ) => {
110+ charts . forEach ( ( chart ) => chart . resize ( ) ) ;
111+ if ( lastContext ) {
112+ renderFromCache ( ) ;
113+ }
114+ } ) ;
115+
108116function initChart ( elementId : string ) : echarts . ECharts {
109117 const el = document . getElementById ( elementId ) as HTMLDivElement | null ;
110118 if ( ! el ) {
@@ -252,43 +260,75 @@ function updateTrafficChart() {
252260
253261 const ingressSeries = trafficHistory . map ( ( p ) => [ p . time , p . ingress ] ) ;
254262 const egressSeries = trafficHistory . map ( ( p ) => [ p . time , p . egress ] ) ;
263+ const palette = getChartPalette ( ) ;
255264
256265 const option : echarts . EChartsOption = {
266+ backgroundColor : "transparent" ,
267+ textStyle : { color : palette . text } ,
257268 color : [ "#4ade80" , "#60a5fa" ] ,
258269 tooltip : {
259270 trigger : "axis" ,
260271 axisPointer : { type : "cross" } ,
272+ backgroundColor : palette . tooltipBg ,
273+ borderColor : palette . tooltipBg ,
274+ textStyle : { color : palette . tooltipText } ,
261275 valueFormatter : ( value ) => ( value ? formatBytesPerSecond ( Number ( value ) ) : "--" )
262276 } ,
263277 legend : {
264278 data : [ "Ingress" , "Egress" ] ,
265- top : 0
279+ top : 0 ,
280+ textStyle : { color : palette . muted }
281+ } ,
282+ grid : { left : 48 , right : 24 , top : 40 , bottom : 70 } ,
283+ xAxis : {
284+ type : "time" ,
285+ boundaryGap : false ,
286+ axisLabel : { color : palette . muted } ,
287+ axisLine : { lineStyle : { color : palette . axis } } ,
288+ splitLine : { show : true , lineStyle : { color : palette . splitLine } }
266289 } ,
267- grid : { left : 40 , right : 20 , top : 30 , bottom : 40 } ,
268- xAxis : { type : "time" , boundaryGap : false } ,
269290 yAxis : {
270291 type : "value" ,
271292 axisLabel : {
293+ color : palette . muted ,
272294 formatter : ( val : number ) => formatBytesPerSecond ( val as number )
273- }
295+ } ,
296+ axisLine : { lineStyle : { color : palette . axis } } ,
297+ splitLine : { lineStyle : { color : palette . splitLine } }
274298 } ,
275299 dataZoom : [
276300 { type : "inside" , realtime : true } ,
277- { type : "slider" , height : 12 , bottom : 0 }
301+ {
302+ type : "slider" ,
303+ height : 14 ,
304+ bottom : 12 ,
305+ borderColor : "transparent" ,
306+ backgroundColor : "rgba(255,255,255,0.08)" ,
307+ handleIcon :
308+ "M8.2,13.3V86.7h2.6V13.3H8.2z M41.6,13.3V86.7h2.6V13.3H41.6z" ,
309+ handleSize : "80%"
310+ }
278311 ] ,
279- series : [
280312 {
281313 name : "Ingress" ,
282314 type : "line" ,
283315 smooth : true ,
284316 showSymbol : false ,
317+ lineStyle : { width : 2 } ,
318+ areaStyle : {
319+ color : createAreaGradient ( "rgba(74, 222, 128, 0.45)" , "rgba(74, 222, 128, 0.05)" )
320+ } ,
285321 data : ingressSeries
286322 } ,
287323 {
288324 name : "Egress" ,
289325 type : "line" ,
290326 smooth : true ,
291327 showSymbol : false ,
328+ lineStyle : { width : 2 } ,
329+ areaStyle : {
330+ color : createAreaGradient ( "rgba(96, 165, 250, 0.4)" , "rgba(96, 165, 250, 0.05)" )
331+ } ,
292332 data : egressSeries
293333 }
294334 ]
@@ -312,40 +352,72 @@ function updateRequestChart() {
312352
313353 const grpcSeries = requestHistory . map ( ( p ) => [ p . time , p . grpc ] ) ;
314354 const urpcSeries = requestHistory . map ( ( p ) => [ p . time , p . urpc ] ) ;
355+ const palette = getChartPalette ( ) ;
315356
316357 const option : echarts . EChartsOption = {
358+ backgroundColor : "transparent" ,
359+ textStyle : { color : palette . text } ,
317360 color : [ "#f472b6" , "#fbbf24" ] ,
318361 tooltip : {
319362 trigger : "axis" ,
320363 axisPointer : { type : "cross" } ,
364+ backgroundColor : palette . tooltipBg ,
365+ borderColor : palette . tooltipBg ,
366+ textStyle : { color : palette . tooltipText } ,
321367 valueFormatter : ( value ) => ( value ? formatReqPerSecond ( Number ( value ) ) : "--" )
322368 } ,
323- legend : { data : [ "gRPC" , "uRPC" ] , top : 0 } ,
324- grid : { left : 40 , right : 20 , top : 30 , bottom : 40 } ,
325- xAxis : { type : "time" , boundaryGap : false } ,
369+ legend : { data : [ "gRPC" , "uRPC" ] , top : 0 , textStyle : { color : palette . muted } } ,
370+ grid : { left : 48 , right : 24 , top : 40 , bottom : 70 } ,
371+ xAxis : {
372+ type : "time" ,
373+ boundaryGap : false ,
374+ axisLabel : { color : palette . muted } ,
375+ axisLine : { lineStyle : { color : palette . axis } } ,
376+ splitLine : { show : true , lineStyle : { color : palette . splitLine } }
377+ } ,
326378 yAxis : {
327379 type : "value" ,
328380 axisLabel : {
381+ color : palette . muted ,
329382 formatter : ( val : number ) => formatReqPerSecond ( val as number )
330- }
383+ } ,
384+ axisLine : { lineStyle : { color : palette . axis } } ,
385+ splitLine : { lineStyle : { color : palette . splitLine } }
331386 } ,
332387 dataZoom : [
333388 { type : "inside" , realtime : true } ,
334- { type : "slider" , height : 12 , bottom : 0 }
389+ {
390+ type : "slider" ,
391+ height : 14 ,
392+ bottom : 12 ,
393+ borderColor : "transparent" ,
394+ backgroundColor : "rgba(255,255,255,0.08)" ,
395+ handleIcon :
396+ "M8.2,13.3V86.7h2.6V13.3H8.2z M41.6,13.3V86.7h2.6V13.3H41.6z" ,
397+ handleSize : "80%"
398+ }
335399 ] ,
336400 series : [
337401 {
338402 name : "gRPC" ,
339403 type : "line" ,
340404 smooth : true ,
341405 showSymbol : false ,
406+ lineStyle : { width : 2 } ,
407+ areaStyle : {
408+ color : createAreaGradient ( "rgba(244, 114, 182, 0.35)" , "rgba(244, 114, 182, 0.05)" )
409+ } ,
342410 data : grpcSeries
343411 } ,
344412 {
345413 name : "uRPC" ,
346414 type : "line" ,
347415 smooth : true ,
348416 showSymbol : false ,
417+ lineStyle : { width : 2 } ,
418+ areaStyle : {
419+ color : createAreaGradient ( "rgba(251, 191, 36, 0.35)" , "rgba(251, 191, 36, 0.05)" )
420+ } ,
349421 data : urpcSeries
350422 }
351423 ]
@@ -378,18 +450,32 @@ function renderDiskChart() {
378450 return ;
379451 }
380452
381- const series = Array . from ( diskSeriesHistory . entries ( ) ) . map ( ( [ root , data ] ) => ( {
453+ const palette = getChartPalette ( ) ;
454+ const series = Array . from ( diskSeriesHistory . entries ( ) ) . map ( ( [ root , data ] , index ) => ( {
382455 name : root ,
383456 type : "line" ,
384457 smooth : true ,
385458 showSymbol : false ,
459+ lineStyle : { width : 2 } ,
460+ areaStyle : {
461+ opacity : 0.2 ,
462+ color : createAreaGradient (
463+ `rgba(96, 109, 255, ${ 0.35 - index * 0.03 } )` ,
464+ "rgba(96, 109, 255, 0.02)"
465+ )
466+ } ,
386467 data
387468 } ) ) ;
388469
389470 const option : echarts . EChartsOption = {
471+ backgroundColor : "transparent" ,
472+ textStyle : { color : palette . text } ,
390473 tooltip : {
391474 trigger : "axis" ,
392475 axisPointer : { type : "cross" } ,
476+ backgroundColor : palette . tooltipBg ,
477+ borderColor : palette . tooltipBg ,
478+ textStyle : { color : palette . tooltipText } ,
393479 formatter : ( params : any ) => {
394480 if ( ! Array . isArray ( params ) || ! params . length ) {
395481 return "" ;
@@ -410,21 +496,40 @@ function renderDiskChart() {
410496 } ,
411497 legend : {
412498 type : "scroll" ,
413- top : 0
499+ top : 0 ,
500+ textStyle : { color : palette . muted }
501+ } ,
502+ grid : { left : 48 , right : 24 , top : 40 , bottom : 70 } ,
503+ xAxis : {
504+ type : "time" ,
505+ boundaryGap : false ,
506+ axisLabel : { color : palette . muted } ,
507+ axisLine : { lineStyle : { color : palette . axis } } ,
508+ splitLine : { show : true , lineStyle : { color : palette . splitLine } }
414509 } ,
415- grid : { left : 40 , right : 20 , top : 30 , bottom : 40 } ,
416- xAxis : { type : "time" , boundaryGap : false } ,
417510 yAxis : {
418511 type : "value" ,
419512 min : 0 ,
420513 max : 100 ,
421514 axisLabel : {
515+ color : palette . muted ,
422516 formatter : ( val : number ) => `${ val } %`
423- }
517+ } ,
518+ axisLine : { lineStyle : { color : palette . axis } } ,
519+ splitLine : { lineStyle : { color : palette . splitLine } }
424520 } ,
425521 dataZoom : [
426522 { type : "inside" , realtime : true } ,
427- { type : "slider" , height : 12 , bottom : 0 }
523+ {
524+ type : "slider" ,
525+ height : 14 ,
526+ bottom : 12 ,
527+ borderColor : "transparent" ,
528+ backgroundColor : "rgba(255,255,255,0.08)" ,
529+ handleIcon :
530+ "M8.2,13.3V86.7h2.6V13.3H8.2z M41.6,13.3V86.7h2.6V13.3H41.6z" ,
531+ handleSize : "80%"
532+ }
428533 ] ,
429534 series
430535 } ;
@@ -884,9 +989,12 @@ function formatReqPerSecond(value: number): string {
884989}
885990
886991function formatBytes ( value : number | null ) : string {
887- if ( ! Number . isFinite ( value as number ) || ( value as number ) <= 0 ) {
992+ if ( ! Number . isFinite ( value as number ) || value == null ) {
888993 return "--" ;
889994 }
995+ if ( value === 0 ) {
996+ return "0 B" ;
997+ }
890998 const units = [ "B" , "KiB" , "MiB" , "GiB" , "TiB" ] ;
891999 let idx = 0 ;
8921000 let val = value as number ;
@@ -938,3 +1046,22 @@ function formatDuration(ms: number): string {
9381046 }
9391047 return `${ seconds } s` ;
9401048}
1049+
1050+ function getChartPalette ( ) {
1051+ const text = getCssVar ( "--text-primary" , "#f8fafc" ) ;
1052+ const muted = getCssVar ( "--text-muted" , "#94a3b8" ) ;
1053+ const axis = muted || "#94a3b8" ;
1054+ const splitLine = colorSchemeMedia . matches
1055+ ? "rgba(21, 25, 51, 0.12)"
1056+ : "rgba(148, 163, 184, 0.2)" ;
1057+ const tooltipBg = colorSchemeMedia . matches ? "rgba(255,255,255,0.96)" : "rgba(15,23,42,0.95)" ;
1058+ const tooltipText = colorSchemeMedia . matches ? "#151933" : "#f8fafc" ;
1059+ return { text, muted, axis, splitLine, tooltipBg, tooltipText } ;
1060+ }
1061+
1062+ function createAreaGradient ( topColor : string , bottomColor : string ) {
1063+ return new echarts . graphic . LinearGradient ( 0 , 0 , 0 , 1 , [
1064+ { offset : 0 , color : topColor } ,
1065+ { offset : 1 , color : bottomColor }
1066+ ] ) ;
1067+ }
0 commit comments