1
1
use crate :: measurements:: Measurement ;
2
2
use crate :: speedtest:: { Metadata , TestType } ;
3
+ use crate :: tui:: theme:: { ThemedStyles , TokyoNight } ;
3
4
use crossbeam_channel:: Receiver ;
4
5
use crossterm:: event:: { self , Event , KeyCode , KeyEventKind } ;
5
6
use ratatui:: {
6
7
backend:: CrosstermBackend ,
7
8
layout:: { Constraint , Direction , Layout , Rect } ,
8
- style:: { Color , Style } ,
9
9
widgets:: { Block , Borders , Paragraph } ,
10
10
Frame , Terminal ,
11
11
} ;
@@ -384,16 +384,20 @@ impl App {
384
384
fn draw_title ( & self , f : & mut Frame , area : Rect ) {
385
385
let title_text = if let Some ( ref metadata) = self . state . metadata {
386
386
format ! (
387
- "Cloudflare Speed Test - {} {} | IP: {} | Colo: {}" ,
387
+ " Cloudflare Speed Test - {} {} | IP: {} | Colo: {}" ,
388
388
metadata. city, metadata. country, metadata. ip, metadata. colo
389
389
)
390
390
} else {
391
- "Cloudflare Speed Test - Loading..." . to_string ( )
391
+ " Cloudflare Speed Test - Loading..." . to_string ( )
392
392
} ;
393
393
394
394
let title = Paragraph :: new ( title_text)
395
- . style ( Style :: default ( ) . fg ( Color :: Cyan ) )
396
- . block ( Block :: default ( ) . borders ( Borders :: ALL ) ) ;
395
+ . style ( ThemedStyles :: title ( ) )
396
+ . block (
397
+ Block :: default ( )
398
+ . borders ( Borders :: ALL )
399
+ . border_style ( ThemedStyles :: title_border ( ) ) ,
400
+ ) ;
397
401
f. render_widget ( title, area) ;
398
402
}
399
403
@@ -412,33 +416,36 @@ impl App {
412
416
let upload_progress = self . calculate_adaptive_progress ( TestType :: Upload ) ;
413
417
414
418
// Download status with progress bar and text
415
- let download_color = match self . state . progress . phase {
416
- TestPhase :: Download => Color :: Green ,
417
- TestPhase :: Completed if download_progress >= 1.0 => Color :: Green ,
418
- _ if download_progress >= 1.0 => Color :: Green ,
419
- _ => Color :: Gray ,
419
+ let download_style = match self . state . progress . phase {
420
+ TestPhase :: Download => ThemedStyles :: progress_download_active ( ) ,
421
+ TestPhase :: Completed if download_progress >= 1.0 => {
422
+ ThemedStyles :: progress_download_complete ( )
423
+ }
424
+ _ if download_progress >= 1.0 => ThemedStyles :: progress_download_complete ( ) ,
425
+ _ => ThemedStyles :: progress_inactive ( ) ,
420
426
} ;
421
427
422
- let download_text = format ! ( "Download: {}" , self . state. progress. download_status) ;
423
- let download_paragraph =
424
- Paragraph :: new ( download_text) . style ( Style :: default ( ) . fg ( download_color) ) ;
428
+ let download_text = format ! ( " Download: {}" , self . state. progress. download_status) ;
429
+ let download_paragraph = Paragraph :: new ( download_text) . style ( download_style) ;
425
430
f. render_widget ( download_paragraph, chunks[ 0 ] ) ;
426
431
427
432
// Upload status with progress bar and text
428
- let upload_color = match self . state . progress . phase {
429
- TestPhase :: Upload => Color :: Blue ,
430
- TestPhase :: Completed if upload_progress >= 1.0 => Color :: Blue ,
431
- _ if upload_progress >= 1.0 => Color :: Blue ,
432
- _ => Color :: Gray ,
433
+ let upload_style = match self . state . progress . phase {
434
+ TestPhase :: Upload => ThemedStyles :: progress_upload_active ( ) ,
435
+ TestPhase :: Completed if upload_progress >= 1.0 => {
436
+ ThemedStyles :: progress_upload_complete ( )
437
+ }
438
+ _ if upload_progress >= 1.0 => ThemedStyles :: progress_upload_complete ( ) ,
439
+ _ => ThemedStyles :: progress_inactive ( ) ,
433
440
} ;
434
441
435
- let upload_text = format ! ( "Upload: {}" , self . state. progress. upload_status) ;
436
- let upload_paragraph = Paragraph :: new ( upload_text) . style ( Style :: default ( ) . fg ( upload_color ) ) ;
442
+ let upload_text = format ! ( " Upload: {}" , self . state. progress. upload_status) ;
443
+ let upload_paragraph = Paragraph :: new ( upload_text) . style ( upload_style ) ;
437
444
f. render_widget ( upload_paragraph, chunks[ 1 ] ) ;
438
445
439
446
// Latency stats in a compact single line
440
447
let latency_text = format ! (
441
- "Latency: Current: {:.1}ms | Average: {:.1}ms | Min/Max: {:.1}ms / {:.1}ms" ,
448
+ " Latency: Current: {:.1}ms | Average: {:.1}ms | Min/Max: {:.1}ms / {:.1}ms" ,
442
449
self . state. current_latency,
443
450
self . state. avg_latency,
444
451
if self . state. min_latency == f64 :: MAX {
@@ -448,8 +455,7 @@ impl App {
448
455
} ,
449
456
self . state. max_latency
450
457
) ;
451
- let latency_paragraph =
452
- Paragraph :: new ( latency_text) . style ( Style :: default ( ) . fg ( Color :: Yellow ) ) ;
458
+ let latency_paragraph = Paragraph :: new ( latency_text) . style ( ThemedStyles :: latency_stats ( ) ) ;
453
459
f. render_widget ( latency_paragraph, chunks[ 2 ] ) ;
454
460
}
455
461
@@ -539,15 +545,19 @@ impl App {
539
545
let block = Block :: default ( )
540
546
. title ( title)
541
547
. borders ( Borders :: ALL )
542
- . border_style ( Style :: default ( ) . fg ( Color :: Green ) ) ;
548
+ . border_style ( ThemedStyles :: download_graph_border ( ) ) ;
543
549
544
550
let inner = block. inner ( area) ;
545
551
f. render_widget ( block, area) ;
546
552
547
553
if !self . state . download_speeds . is_empty ( ) {
548
554
let graph_widget = crate :: tui:: widgets:: LineGraph :: new ( & self . state . download_speeds )
549
- . color ( Color :: Green ) ;
555
+ . color ( TokyoNight :: DOWNLOAD_PRIMARY ) ;
550
556
f. render_widget ( graph_widget, inner) ;
557
+ } else {
558
+ let placeholder =
559
+ Paragraph :: new ( "Waiting for data..." ) . style ( ThemedStyles :: graph_placeholder ( ) ) ;
560
+ f. render_widget ( placeholder, inner) ;
551
561
}
552
562
}
553
563
@@ -556,15 +566,19 @@ impl App {
556
566
let block = Block :: default ( )
557
567
. title ( title)
558
568
. borders ( Borders :: ALL )
559
- . border_style ( Style :: default ( ) . fg ( Color :: Blue ) ) ;
569
+ . border_style ( ThemedStyles :: upload_graph_border ( ) ) ;
560
570
561
571
let inner = block. inner ( area) ;
562
572
f. render_widget ( block, area) ;
563
573
564
574
if !self . state . upload_speeds . is_empty ( ) {
565
- let graph_widget =
566
- crate :: tui :: widgets :: LineGraph :: new ( & self . state . upload_speeds ) . color ( Color :: Blue ) ;
575
+ let graph_widget = crate :: tui :: widgets :: LineGraph :: new ( & self . state . upload_speeds )
576
+ . color ( TokyoNight :: UPLOAD_PRIMARY ) ;
567
577
f. render_widget ( graph_widget, inner) ;
578
+ } else {
579
+ let placeholder =
580
+ Paragraph :: new ( "Waiting for data..." ) . style ( ThemedStyles :: graph_placeholder ( ) ) ;
581
+ f. render_widget ( placeholder, inner) ;
568
582
}
569
583
}
570
584
@@ -574,45 +588,68 @@ impl App {
574
588
}
575
589
576
590
fn draw_status ( & self , f : & mut Frame , area : Rect ) {
577
- let status_text = match self . state . progress . phase {
578
- TestPhase :: Idle => "Ready to start tests. Press 'q' to quit." . to_string ( ) ,
579
- TestPhase :: Latency => "Running latency tests..." . to_string ( ) ,
591
+ let ( status_text, status_style) = match self . state . progress . phase {
592
+ TestPhase :: Idle => (
593
+ " Ready to start tests. Press 'q' to quit." . to_string ( ) ,
594
+ ThemedStyles :: status_idle ( ) ,
595
+ ) ,
596
+ TestPhase :: Latency => (
597
+ " Running latency tests..." . to_string ( ) ,
598
+ ThemedStyles :: status_active ( ) ,
599
+ ) ,
580
600
TestPhase :: Download => {
581
601
if let ( Some ( payload_size) , Some ( _) ) = (
582
602
self . state . progress . current_payload_size ,
583
603
self . state . progress . current_test ,
584
604
) {
585
- format ! (
586
- "Testing Download {}MB [{}/{}]" ,
587
- payload_size / 1_000_000 ,
588
- self . state. progress. current_iteration,
589
- self . state. progress. total_iterations
605
+ (
606
+ format ! (
607
+ " Testing Download {}MB [{}/{}]" ,
608
+ payload_size / 1_000_000 ,
609
+ self . state. progress. current_iteration,
610
+ self . state. progress. total_iterations
611
+ ) ,
612
+ ThemedStyles :: status_active ( ) ,
590
613
)
591
614
} else {
592
- "Testing Download..." . to_string ( )
615
+ (
616
+ " Testing Download..." . to_string ( ) ,
617
+ ThemedStyles :: status_active ( ) ,
618
+ )
593
619
}
594
620
}
595
621
TestPhase :: Upload => {
596
622
if let ( Some ( payload_size) , Some ( _) ) = (
597
623
self . state . progress . current_payload_size ,
598
624
self . state . progress . current_test ,
599
625
) {
600
- format ! (
601
- "Testing Upload {}MB [{}/{}]" ,
602
- payload_size / 1_000_000 ,
603
- self . state. progress. current_iteration,
604
- self . state. progress. total_iterations
626
+ (
627
+ format ! (
628
+ " Testing Upload {}MB [{}/{}]" ,
629
+ payload_size / 1_000_000 ,
630
+ self . state. progress. current_iteration,
631
+ self . state. progress. total_iterations
632
+ ) ,
633
+ ThemedStyles :: status_active ( ) ,
605
634
)
606
635
} else {
607
- "Testing Upload..." . to_string ( )
636
+ (
637
+ " Testing Upload..." . to_string ( ) ,
638
+ ThemedStyles :: status_active ( ) ,
639
+ )
608
640
}
609
641
}
610
- TestPhase :: Completed => "All tests completed! Press 'q' to quit." . to_string ( ) ,
642
+ TestPhase :: Completed => (
643
+ " All tests completed! Press 'q' to quit." . to_string ( ) ,
644
+ ThemedStyles :: status_complete ( ) ,
645
+ ) ,
611
646
} ;
612
647
613
- let paragraph = Paragraph :: new ( status_text)
614
- . style ( Style :: default ( ) . fg ( Color :: White ) )
615
- . block ( Block :: default ( ) . borders ( Borders :: ALL ) ) ;
648
+ let paragraph = Paragraph :: new ( status_text) . style ( status_style) . block (
649
+ Block :: default ( )
650
+ . borders ( Borders :: ALL )
651
+ . border_style ( ThemedStyles :: status_border ( ) ) ,
652
+ ) ;
616
653
f. render_widget ( paragraph, area) ;
617
654
}
618
655
}
0 commit comments