@@ -9,14 +9,17 @@ use std::{
9
9
10
10
use async_std:: sync:: Arc ;
11
11
use clap:: { Args , CommandFactory , Parser , Subcommand } ;
12
- use color_eyre:: { eyre, eyre:: Context } ;
12
+ use color_eyre:: {
13
+ eyre:: { self , Context } ,
14
+ owo_colors:: OwoColorize ,
15
+ } ;
13
16
use completer:: enter_code;
14
17
use console:: { style, Term } ;
15
18
use futures:: { future:: Either , Future , FutureExt } ;
16
19
use indicatif:: { MultiProgress , ProgressBar } ;
17
20
use magic_wormhole:: {
18
21
forwarding, transfer,
19
- transit:: { self , TransitInfo } ,
22
+ transit:: { self , ConnectionType , TransitInfo } ,
20
23
MailboxConnection , ParseCodeError , ParsePasswordError , Wormhole ,
21
24
} ;
22
25
use std:: { io:: Write , path:: PathBuf } ;
@@ -261,8 +264,18 @@ struct WormholeCli {
261
264
display_order = 100
262
265
) ]
263
266
log : bool ,
267
+
264
268
#[ clap( subcommand) ]
265
269
command : WormholeCommand ,
270
+
271
+ /// Disable color output
272
+ #[ arg(
273
+ long = "no-color" ,
274
+ global = true ,
275
+ help = "Disable color output" ,
276
+ display_order = 101
277
+ ) ]
278
+ no_color : bool ,
266
279
}
267
280
268
281
#[ async_std:: main]
@@ -274,6 +287,11 @@ async fn main() -> eyre::Result<()> {
274
287
275
288
let mut term = Term :: stdout ( ) ;
276
289
290
+ // Set NO_COLOR environment variable if --no-color flag is used
291
+ if app. no_color {
292
+ std:: env:: set_var ( "NO_COLOR" , "1" ) ;
293
+ }
294
+
277
295
if app. log {
278
296
tracing_subscriber:: fmt ( )
279
297
. with_max_level ( tracing:: Level :: TRACE )
@@ -760,11 +778,16 @@ async fn make_send_offer(
760
778
fn create_progress_bar ( file_size : u64 ) -> ProgressBar {
761
779
use indicatif:: ProgressStyle ;
762
780
781
+ let template = if should_use_color ( ) {
782
+ "[{elapsed_precise:.yellow.bold}] [{wide_bar}] {bytes:.blue.bold}/{total_bytes:.blue.bold} | {decimal_bytes_per_sec:.green.bold} | ETA: {eta:.yellow.bold}"
783
+ } else {
784
+ "[{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} | {decimal_bytes_per_sec} | ETA: {eta}"
785
+ } ;
786
+
763
787
let pb = ProgressBar :: new ( file_size) ;
764
788
pb. set_style (
765
789
ProgressStyle :: default_bar ( )
766
- // .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
767
- . template ( "[{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} ({eta})" )
790
+ . template ( template)
768
791
. unwrap ( )
769
792
. progress_chars ( "#>-" ) ,
770
793
) ;
@@ -778,6 +801,7 @@ fn create_progress_handler(pb: ProgressBar) -> impl FnMut(u64, u64) {
778
801
pb. set_length ( total) ;
779
802
pb. enable_steady_tick ( std:: time:: Duration :: from_millis ( 250 ) ) ;
780
803
}
804
+
781
805
pb. set_position ( sent) ;
782
806
}
783
807
}
@@ -1054,15 +1078,28 @@ async fn receive_inner_v1(
1054
1078
use number_prefix:: NumberPrefix ;
1055
1079
if !( noconfirm
1056
1080
|| util:: ask_user (
1057
- format ! (
1058
- "Receive file '{}' ({})?" ,
1059
- req. file_name( ) ,
1060
- match NumberPrefix :: binary( req. file_size( ) as f64 ) {
1061
- NumberPrefix :: Standalone ( bytes) => format!( "{} bytes" , bytes) ,
1062
- NumberPrefix :: Prefixed ( prefix, n) =>
1063
- format!( "{:.1} {}B in size" , n, prefix. symbol( ) ) ,
1064
- } ,
1065
- ) ,
1081
+ match should_use_color ( ) {
1082
+ true => format ! (
1083
+ "Receive file '{}' ({})?" ,
1084
+ req. file_name( ) . green( ) . bold( ) ,
1085
+ match NumberPrefix :: binary( req. file_size( ) as f64 ) {
1086
+ NumberPrefix :: Standalone ( bytes) => format!( "{} bytes" , bytes) ,
1087
+ NumberPrefix :: Prefixed ( prefix, n) =>
1088
+ format!( "{:.1} {}B" , n, prefix. symbol( ) ) ,
1089
+ }
1090
+ . blue( )
1091
+ . bold( ) ,
1092
+ ) ,
1093
+ false => format ! (
1094
+ "Receive file '{}' ({})?" ,
1095
+ req. file_name( ) ,
1096
+ match NumberPrefix :: binary( req. file_size( ) as f64 ) {
1097
+ NumberPrefix :: Standalone ( bytes) => format!( "{} bytes" , bytes) ,
1098
+ NumberPrefix :: Prefixed ( prefix, n) =>
1099
+ format!( "{:.1} {}B" , n, prefix. symbol( ) ) ,
1100
+ } ,
1101
+ ) ,
1102
+ } ,
1066
1103
true ,
1067
1104
)
1068
1105
. await )
@@ -1096,7 +1133,14 @@ async fn receive_inner_v1(
1096
1133
1097
1134
/* If there is a collision, ask whether to overwrite */
1098
1135
if !util:: ask_user (
1099
- format ! ( "Override existing file {}?" , file_path. display( ) ) ,
1136
+ if should_use_color ( ) {
1137
+ format ! (
1138
+ "Override existing file {}?" ,
1139
+ file_path. display( ) . red( ) . bold( )
1140
+ )
1141
+ } else {
1142
+ format ! ( "Override existing file {}?" , file_path. display( ) )
1143
+ } ,
1100
1144
false ,
1101
1145
)
1102
1146
. await
@@ -1220,6 +1264,46 @@ async fn receive_inner_v2(
1220
1264
1221
1265
fn transit_handler ( info : TransitInfo ) {
1222
1266
tracing:: info!( "{info}" ) ;
1267
+ let mut term = Term :: stderr ( ) ;
1268
+ let use_color = should_use_color ( ) ;
1269
+
1270
+ let conn_type = if use_color {
1271
+ info. conn_type . bright_magenta ( ) . bold ( ) . to_string ( )
1272
+ } else {
1273
+ info. conn_type . to_string ( )
1274
+ } ;
1275
+
1276
+ let peer_addr = if use_color {
1277
+ info. peer_addr . cyan ( ) . bold ( ) . to_string ( )
1278
+ } else {
1279
+ info. peer_addr . to_string ( )
1280
+ } ;
1281
+
1282
+ if info. conn_type == ConnectionType :: Direct {
1283
+ let _ = writeln ! ( term, "Connecting {} to {}" , conn_type, peer_addr) ;
1284
+ } else {
1285
+ let _ = writeln ! ( term, "Connecting {}" , conn_type) ;
1286
+ } ;
1287
+ }
1288
+
1289
+ fn should_use_color ( ) -> bool {
1290
+ // Check NO_COLOR first - if it exists with any value, disable colors
1291
+ if std:: env:: var_os ( "NO_COLOR" ) . is_some ( ) {
1292
+ return false ;
1293
+ }
1294
+
1295
+ // Then check CLICOLOR_FORCE - if set and not empty/"0", enable colors regardless of terminal
1296
+ if std:: env:: var_os ( "CLICOLOR_FORCE" ) . is_some_and ( |e| !e. is_empty ( ) && e != "0" ) {
1297
+ return true ;
1298
+ }
1299
+
1300
+ // Check CLICOLOR - if set and not empty/"0", use colors only when writing to a terminal
1301
+ if std:: env:: var_os ( "CLICOLOR" ) . is_some_and ( |e| !e. is_empty ( ) && e != "0" ) {
1302
+ return std:: io:: stdout ( ) . is_terminal ( ) ;
1303
+ }
1304
+
1305
+ // Modern default (acting as if CLICOLOR is set)
1306
+ std:: io:: stdout ( ) . is_terminal ( )
1223
1307
}
1224
1308
1225
1309
#[ cfg( test) ]
0 commit comments