@@ -794,6 +794,80 @@ async fn run_list(args: ListArgs) -> Result<()> {
794794 Ok ( ( ) )
795795}
796796
797+ /// Parse a hex color string (e.g., "#22c55e") into RGB components.
798+ fn parse_hex_color ( hex : & str ) -> Option < ( u8 , u8 , u8 ) > {
799+ let hex = hex. trim_start_matches ( '#' ) ;
800+ if hex. len ( ) != 6 {
801+ return None ;
802+ }
803+ let r = u8:: from_str_radix ( & hex[ 0 ..2 ] , 16 ) . ok ( ) ?;
804+ let g = u8:: from_str_radix ( & hex[ 2 ..4 ] , 16 ) . ok ( ) ?;
805+ let b = u8:: from_str_radix ( & hex[ 4 ..6 ] , 16 ) . ok ( ) ?;
806+ Some ( ( r, g, b) )
807+ }
808+
809+ /// Get a human-readable approximate color name from RGB values.
810+ fn approximate_color_name ( r : u8 , g : u8 , b : u8 ) -> & ' static str {
811+ // Convert to HSL-like heuristics for color naming
812+ let max = r. max ( g) . max ( b) ;
813+ let min = r. min ( g) . min ( b) ;
814+ let lightness = ( max as u16 + min as u16 ) / 2 ;
815+
816+ // Check for grayscale colors
817+ if max - min < 20 {
818+ return if lightness < 50 {
819+ "black"
820+ } else if lightness > 200 {
821+ "white"
822+ } else {
823+ "gray"
824+ } ;
825+ }
826+
827+ // Determine dominant color
828+ if r >= g && r >= b {
829+ if r > 200 && g < 100 && b < 100 {
830+ "red"
831+ } else if g > 100 {
832+ "orange"
833+ } else if b > 100 {
834+ "pink"
835+ } else {
836+ "red"
837+ }
838+ } else if g >= r && g >= b {
839+ if b > 150 {
840+ "cyan"
841+ } else if r > 150 {
842+ "yellow"
843+ } else {
844+ "green"
845+ }
846+ } else {
847+ // Blue dominant
848+ if r > 150 {
849+ "purple"
850+ } else if g > 150 {
851+ "cyan"
852+ } else {
853+ "blue"
854+ }
855+ }
856+ }
857+
858+ /// Format a hex color with a visual preview and approximate color name.
859+ fn format_color_with_preview ( hex : & str ) -> String {
860+ if let Some ( ( r, g, b) ) = parse_hex_color ( hex) {
861+ let color_name = approximate_color_name ( r, g, b) ;
862+ // Use ANSI true color escape codes: \x1b[48;2;R;G;Bm for background
863+ // Display a colored block followed by the hex code and color name
864+ format ! ( "\x1b [48;2;{r};{g};{b}m \x1b [0m {hex} ({color_name})" )
865+ } else {
866+ // If parsing fails, just return the hex string
867+ hex. to_string ( )
868+ }
869+ }
870+
797871/// Show agent details command.
798872async fn run_show ( args : ShowArgs ) -> Result < ( ) > {
799873 let agents = load_all_agents ( ) ?;
@@ -846,7 +920,7 @@ async fn run_show(args: ShowArgs) -> Result<()> {
846920 }
847921
848922 if let Some ( ref color) = agent. color {
849- println ! ( "Color: {color}" ) ;
923+ println ! ( "Color: {}" , format_color_with_preview ( color ) ) ;
850924 }
851925
852926 if !agent. tags . is_empty ( ) {
@@ -1547,4 +1621,48 @@ This is the system prompt.
15471621 assert ! ( agents. iter( ) . any( |a| a. name == "plan" ) ) ;
15481622 assert ! ( agents. iter( ) . any( |a| a. name == "explore" ) ) ;
15491623 }
1624+
1625+ #[ test]
1626+ fn test_parse_hex_color ( ) {
1627+ // Valid hex colors
1628+ assert_eq ! ( parse_hex_color( "#22c55e" ) , Some ( ( 0x22 , 0xc5 , 0x5e ) ) ) ;
1629+ assert_eq ! ( parse_hex_color( "#3b82f6" ) , Some ( ( 0x3b , 0x82 , 0xf6 ) ) ) ;
1630+ assert_eq ! ( parse_hex_color( "#ffffff" ) , Some ( ( 255 , 255 , 255 ) ) ) ;
1631+ assert_eq ! ( parse_hex_color( "#000000" ) , Some ( ( 0 , 0 , 0 ) ) ) ;
1632+ assert_eq ! ( parse_hex_color( "ff0000" ) , Some ( ( 255 , 0 , 0 ) ) ) ; // Without #
1633+
1634+ // Invalid hex colors
1635+ assert_eq ! ( parse_hex_color( "#fff" ) , None ) ; // Too short
1636+ assert_eq ! ( parse_hex_color( "#fffffff" ) , None ) ; // Too long
1637+ assert_eq ! ( parse_hex_color( "" ) , None ) ;
1638+ }
1639+
1640+ #[ test]
1641+ fn test_approximate_color_name ( ) {
1642+ // Green color (#22c55e -> rgb(34, 197, 94))
1643+ assert_eq ! ( approximate_color_name( 34 , 197 , 94 ) , "green" ) ;
1644+ // Blue color (#3b82f6 -> rgb(59, 130, 246))
1645+ assert_eq ! ( approximate_color_name( 59 , 130 , 246 ) , "blue" ) ;
1646+ // Red color
1647+ assert_eq ! ( approximate_color_name( 255 , 50 , 50 ) , "red" ) ;
1648+ // White
1649+ assert_eq ! ( approximate_color_name( 255 , 255 , 255 ) , "white" ) ;
1650+ // Black
1651+ assert_eq ! ( approximate_color_name( 10 , 10 , 10 ) , "black" ) ;
1652+ // Gray
1653+ assert_eq ! ( approximate_color_name( 128 , 128 , 128 ) , "gray" ) ;
1654+ }
1655+
1656+ #[ test]
1657+ fn test_format_color_with_preview ( ) {
1658+ // Test with valid color
1659+ let result = format_color_with_preview ( "#22c55e" ) ;
1660+ assert ! ( result. contains( "#22c55e" ) ) ;
1661+ assert ! ( result. contains( "green" ) ) ;
1662+ assert ! ( result. contains( "\x1b [" ) ) ; // Contains ANSI escape codes
1663+
1664+ // Test with invalid color (should return as-is)
1665+ let result = format_color_with_preview ( "invalid" ) ;
1666+ assert_eq ! ( result, "invalid" ) ;
1667+ }
15501668}
0 commit comments