Skip to content

Commit eff4a40

Browse files
committed
dimming
1 parent 33de9cd commit eff4a40

File tree

1 file changed

+172
-51
lines changed

1 file changed

+172
-51
lines changed

src/main.rs

Lines changed: 172 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,49 @@ fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
5858
)
5959
}
6060

61+
// Color constants for consistent theming
62+
mod colors {
63+
pub const GREEN: (u8, u8, u8) = (142, 192, 124);
64+
pub const RED: (u8, u8, u8) = (204, 36, 29);
65+
pub const GRAY: (u8, u8, u8) = (128, 128, 128);
66+
pub const GOLD: (u8, u8, u8) = (215, 153, 33);
67+
pub const TREE: (u8, u8, u8) = (55, 55, 50);
68+
pub const YELLOW: (u8, u8, u8) = (250, 189, 47);
69+
pub const PURPLE: (u8, u8, u8) = (180, 142, 173);
70+
pub const MUTED: (u8, u8, u8) = (90, 90, 90);
71+
pub const PR_NUMBER: (u8, u8, u8) = (90, 78, 98);
72+
pub const PR_ARROW: (u8, u8, u8) = (100, 105, 105);
73+
pub const UPSTREAM: (u8, u8, u8) = (88, 88, 88);
74+
pub const STACKED_ON: (u8, u8, u8) = (90, 120, 87);
75+
}
76+
77+
/// Dimming factor for display. Wraps RGB values and applies a multiplier.
78+
#[derive(Clone, Copy)]
79+
struct Dim(f32);
80+
81+
impl Dim {
82+
fn full() -> Self {
83+
Dim(1.0)
84+
}
85+
fn dimmed() -> Self {
86+
Dim(0.75)
87+
}
88+
89+
/// Apply dimming to RGB values
90+
fn rgb(&self, r: u8, g: u8, b: u8) -> (u8, u8, u8) {
91+
(
92+
(r as f32 * self.0) as u8,
93+
(g as f32 * self.0) as u8,
94+
(b as f32 * self.0) as u8,
95+
)
96+
}
97+
98+
/// Apply dimming to a color tuple
99+
fn apply(&self, color: (u8, u8, u8)) -> (u8, u8, u8) {
100+
self.rgb(color.0, color.1, color.2)
101+
}
102+
}
103+
61104
// This is an important refactoring.
62105
#[derive(Parser)]
63106
#[command(author, version, about)]
@@ -490,6 +533,11 @@ fn recur_tree(
490533
} else {
491534
pr_author.is_some_and(|author| !display_authors.contains(&author.to_string()))
492535
};
536+
let dim = if is_dimmed {
537+
Dim::dimmed()
538+
} else {
539+
Dim::full()
540+
};
493541

494542
// Check if branch is remote-only (not local)
495543
let is_remote_only = !git_repo.branch_exists(&branch.name);
@@ -512,18 +560,21 @@ fn recur_tree(
512560
} else {
513561
print!(" ");
514562
}
563+
// Tree lines stay at full color regardless of dimming
515564
for _ in 0..depth {
516-
print!("{}", "┃ ".truecolor(55, 55, 50));
565+
print!(
566+
"{}",
567+
"┃ ".truecolor(colors::TREE.0, colors::TREE.1, colors::TREE.2)
568+
);
517569
}
518-
let branch_name_colored = if is_dimmed {
519-
branch.name.truecolor(90, 90, 90)
520-
} else {
521-
branch.name.truecolor(128, 128, 128)
522-
};
570+
let branch_color = dim.apply(colors::GRAY);
571+
let muted_color = dim.apply(colors::MUTED);
523572
println!(
524573
"{} {}",
525-
branch_name_colored,
526-
"(remote)".truecolor(90, 90, 90)
574+
branch
575+
.name
576+
.truecolor(branch_color.0, branch_color.1, branch_color.2),
577+
"(remote)".truecolor(muted_color.0, muted_color.1, muted_color.2)
527578
);
528579

529580
// Recurse into children
@@ -552,38 +603,51 @@ fn recur_tree(
552603
false
553604
};
554605

606+
// Tree lines stay at full color regardless of dimming
555607
for _ in 0..depth {
556-
print!("{}", "┃ ".truecolor(55, 55, 50));
608+
print!(
609+
"{}",
610+
"┃ ".truecolor(colors::TREE.0, colors::TREE.1, colors::TREE.2)
611+
);
557612
}
558613

559614
// Branch name coloring: green for synced, red for diverged, bold for current branch
560-
// Dimmed branches (filtered by display_authors) are shown in gray
561-
let branch_name_colored = if is_dimmed {
562-
branch.name.truecolor(90, 90, 90)
615+
let branch_color = if branch_status.is_descendent {
616+
dim.apply(colors::GREEN)
563617
} else {
564-
match (is_current_branch, branch_status.is_descendent) {
565-
(true, true) => branch.name.truecolor(142, 192, 124).bold(),
566-
(true, false) => branch.name.red().bold(),
567-
(false, true) => branch.name.truecolor(142, 192, 124),
568-
(false, false) => branch.name.red(),
569-
}
618+
dim.apply(colors::RED)
619+
};
620+
let branch_name_colored = if is_current_branch {
621+
branch
622+
.name
623+
.truecolor(branch_color.0, branch_color.1, branch_color.2)
624+
.bold()
625+
} else {
626+
branch
627+
.name
628+
.truecolor(branch_color.0, branch_color.1, branch_color.2)
570629
};
571630

572631
// Remote-only indicator
573632
let remote_indicator = if is_remote_only {
574-
" (remote)".truecolor(90, 90, 90).to_string()
633+
let muted = dim.apply(colors::MUTED);
634+
format!(" {}", "(remote)".truecolor(muted.0, muted.1, muted.2))
575635
} else {
576636
String::new()
577637
};
578638

579639
// Get diff stats from LKG ancestor to current branch
580640
let diff_stats = if let Some(lkg_parent) = branch.lkg_parent.as_ref() {
581641
match git_repo.diff_stats(lkg_parent, &branch_status.sha) {
582-
Ok((adds, dels)) => format!(
583-
" {} {}",
584-
format!("+{}", adds).green(),
585-
format!("-{}", dels).red()
586-
),
642+
Ok((adds, dels)) => {
643+
let green = dim.apply(colors::GREEN);
644+
let red = dim.apply(colors::RED);
645+
format!(
646+
" {} {}",
647+
format!("+{}", adds).truecolor(green.0, green.1, green.2),
648+
format!("-{}", dels).truecolor(red.0, red.1, red.2)
649+
)
650+
}
587651
Err(_) => String::new(), // Silently skip on error
588652
}
589653
} else {
@@ -595,16 +659,27 @@ fn recur_tree(
595659
match get_local_status() {
596660
Ok(status) if !status.is_clean() => {
597661
let mut parts = Vec::new();
662+
let green = dim.apply(colors::GREEN);
663+
let yellow = dim.apply(colors::YELLOW);
664+
let gray = dim.apply(colors::GRAY);
598665
if status.staged > 0 {
599-
parts.push(format!("+{}", status.staged).green().to_string());
666+
parts.push(
667+
format!("+{}", status.staged)
668+
.truecolor(green.0, green.1, green.2)
669+
.to_string(),
670+
);
600671
}
601672
if status.unstaged > 0 {
602-
parts.push(format!("~{}", status.unstaged).yellow().to_string());
673+
parts.push(
674+
format!("~{}", status.unstaged)
675+
.truecolor(yellow.0, yellow.1, yellow.2)
676+
.to_string(),
677+
);
603678
}
604679
if status.untracked > 0 {
605680
parts.push(
606681
format!("?{}", status.untracked)
607-
.truecolor(128, 128, 128)
682+
.truecolor(gray.0, gray.1, gray.2)
608683
.to_string(),
609684
);
610685
}
@@ -617,70 +692,100 @@ fn recur_tree(
617692
};
618693

619694
if verbose {
695+
let gold = dim.apply(colors::GOLD);
696+
let stacked_on = dim.apply(colors::STACKED_ON);
697+
let yellow = dim.apply(colors::YELLOW);
698+
let red = dim.apply(colors::RED);
699+
let green = dim.apply(colors::GREEN);
700+
let upstream_color = dim.apply(colors::UPSTREAM);
701+
620702
println!(
621703
"{}{}{}{} ({}) {}{}{}{}",
622704
branch_name_colored,
623705
remote_indicator,
624706
diff_stats,
625707
local_status,
626-
branch_status.sha[..8].truecolor(215, 153, 33),
708+
branch_status.sha[..8].truecolor(gold.0, gold.1, gold.2),
627709
{
628710
let details: String = if branch_status.exists {
629711
if branch_status.is_descendent {
630712
format!(
631713
"{} {}",
632-
"is stacked on".truecolor(90, 120, 87),
633-
branch_status.parent_branch.yellow()
714+
"is stacked on".truecolor(stacked_on.0, stacked_on.1, stacked_on.2),
715+
branch_status
716+
.parent_branch
717+
.truecolor(yellow.0, yellow.1, yellow.2)
634718
)
635719
} else {
636720
format!(
637721
"{} {}",
638-
"diverges from".red(),
639-
branch_status.parent_branch.yellow()
722+
"diverges from".truecolor(red.0, red.1, red.2),
723+
branch_status
724+
.parent_branch
725+
.truecolor(yellow.0, yellow.1, yellow.2)
640726
)
641727
}
642728
} else {
643-
"does not exist!".bright_red().to_string()
729+
"does not exist!".truecolor(red.0, red.1, red.2).to_string()
644730
};
645731
details
646732
},
647733
{
648734
if let Some(upstream_status) = branch_status.upstream_status {
649735
format!(
650736
" (upstream {} is {})",
651-
upstream_status.symbolic_name.truecolor(88, 88, 88),
737+
upstream_status.symbolic_name.truecolor(
738+
upstream_color.0,
739+
upstream_color.1,
740+
upstream_color.2
741+
),
652742
if upstream_status.synced {
653-
"synced".truecolor(142, 192, 124)
743+
"synced".truecolor(green.0, green.1, green.2)
654744
} else {
655-
"not synced".bright_red()
745+
"not synced".truecolor(red.0, red.1, red.2)
656746
}
657747
)
658748
} else {
659-
format!(" ({})", "no upstream".truecolor(215, 153, 33))
749+
format!(" ({})", "no upstream".truecolor(gold.0, gold.1, gold.2))
660750
}
661751
},
662752
{
663753
if let Some(lkg_parent) = branch.lkg_parent.as_ref() {
664-
format!(" (lkg parent {})", lkg_parent[..8].truecolor(215, 153, 33))
754+
format!(
755+
" (lkg parent {})",
756+
lkg_parent[..8].truecolor(gold.0, gold.1, gold.2)
757+
)
665758
} else {
666759
String::new()
667760
}
668761
},
669-
match branch.stack_method {
670-
StackMethod::ApplyMerge => " (apply-merge)".truecolor(142, 192, 124),
671-
StackMethod::Merge => " (merge)".truecolor(142, 192, 124),
762+
{
763+
let method_color = dim.apply(colors::GREEN);
764+
match branch.stack_method {
765+
StackMethod::ApplyMerge => {
766+
" (apply-merge)".truecolor(method_color.0, method_color.1, method_color.2)
767+
}
768+
StackMethod::Merge => {
769+
" (merge)".truecolor(method_color.0, method_color.1, method_color.2)
770+
}
771+
}
672772
},
673773
);
674774
if let Some(note) = &branch.note {
675775
print!(" ");
776+
// Tree lines stay at full color regardless of dimming
676777
for _ in 0..depth {
677-
print!("{}", "┃ ".truecolor(55, 55, 50));
778+
print!(
779+
"{}",
780+
"┃ ".truecolor(colors::TREE.0, colors::TREE.1, colors::TREE.2)
781+
);
678782
}
679783

680784
let first_line = note.lines().next().unwrap_or("");
785+
// Note: keeping blue for notes as it's distinct from status colors
681786
println!(
682787
" {} {}",
683-
"›".truecolor(55, 55, 50),
788+
"›".truecolor(colors::TREE.0, colors::TREE.1, colors::TREE.2),
684789
if is_current_branch {
685790
first_line.bright_blue().bold()
686791
} else {
@@ -693,22 +798,38 @@ fn recur_tree(
693798
let pr_info = if let Some(cache) = pr_cache {
694799
if let Some(pr) = cache.get(&branch.name) {
695800
let state = pr.display_state();
801+
let gray = dim.apply(colors::GRAY);
802+
let green = dim.apply(colors::GREEN);
803+
let purple = dim.apply(colors::PURPLE);
804+
let red = dim.apply(colors::RED);
696805
let state_colored = match state {
697806
github::PrDisplayState::Draft => {
698-
format!("[{}]", state).truecolor(128, 128, 128)
807+
format!("[{}]", state).truecolor(gray.0, gray.1, gray.2)
808+
}
809+
github::PrDisplayState::Open => {
810+
format!("[{}]", state).truecolor(green.0, green.1, green.2)
699811
}
700-
github::PrDisplayState::Open => format!("[{}]", state).truecolor(142, 192, 124),
701812
github::PrDisplayState::Merged => {
702-
format!("[{}]", state).truecolor(180, 142, 173)
813+
format!("[{}]", state).truecolor(purple.0, purple.1, purple.2)
814+
}
815+
github::PrDisplayState::Closed => {
816+
format!("[{}]", state).truecolor(red.0, red.1, red.2)
703817
}
704-
github::PrDisplayState::Closed => format!("[{}]", state).truecolor(204, 36, 29),
705818
};
706-
let (r, g, b) = string_to_rgb(&pr.user.login);
707-
let author_colored = format!("@{}", pr.user.login).truecolor(r, g, b);
708-
let number_colored = format!("#{}", pr.number).truecolor(90, 78, 98);
819+
let author_rgb = string_to_rgb(&pr.user.login);
820+
let author_color = dim.apply(author_rgb);
821+
let author_colored = format!("@{}", pr.user.login).truecolor(
822+
author_color.0,
823+
author_color.1,
824+
author_color.2,
825+
);
826+
let pr_num = dim.apply(colors::PR_NUMBER);
827+
let number_colored =
828+
format!("#{}", pr.number).truecolor(pr_num.0, pr_num.1, pr_num.2);
829+
let arrow = dim.apply(colors::PR_ARROW);
709830
format!(
710831
" {} {} {} {}",
711-
"".truecolor(100, 105, 105),
832+
"".truecolor(arrow.0, arrow.1, arrow.2),
712833
author_colored,
713834
number_colored,
714835
state_colored

0 commit comments

Comments
 (0)