@@ -3,9 +3,9 @@ import { konsola } from '../../../utils/konsola';
33
44export type ProcessingEvent =
55 | { type : 'start' ; total : number }
6- | { type : 'success' ; name : string ; resourceType : string ; color : string }
7- | { type : 'skip' ; name : string ; resourceType : string }
8- | { type : 'error' ; name : string ; resourceType : string ; error : unknown }
6+ | { type : 'success' ; name : string ; resourceType : string ; color : string ; elapsedMs ?: number }
7+ | { type : 'skip' ; name : string ; resourceType : string ; elapsedMs ?: number }
8+ | { type : 'error' ; name : string ; resourceType : string ; error : unknown ; elapsedMs ?: number }
99 | { type : 'complete' ; summary : { updated : number ; unchanged : number ; failed : number } } ;
1010
1111export class ProgressDisplay {
@@ -15,13 +15,18 @@ export class ProgressDisplay {
1515 private unchanged = 0 ;
1616 private failed = 0 ;
1717 private currentProgressLine = '' ;
18+ // Track start time for calculating elapsed time on completion
19+ private startTime : number | null = null ;
1820
1921 start ( total : number ) {
2022 this . total = total ;
2123 this . processed = 0 ;
2224 this . updated = 0 ;
2325 this . unchanged = 0 ;
2426 this . failed = 0 ;
27+ // Record the start time when processing begins
28+ this . startTime = Date . now ( ) ;
29+ konsola . br ( ) ;
2530 console . log ( `Processing ${ total } resources...` ) ;
2631 this . updateProgress ( ) ;
2732 }
@@ -32,33 +37,49 @@ export class ProgressDisplay {
3237 this . start ( event . total ) ;
3338 break ;
3439
35- case 'success' :
40+ case 'success' : {
3641 this . processed ++ ;
3742 this . updated ++ ;
3843 this . clearProgress ( ) ;
39- console . log ( `${ chalk . green ( '✓' ) } ${ this . capitalize ( event . resourceType ) } → ${ chalk . hex ( event . color ) ( event . name ) } - Updated` ) ;
44+ const successTimeString = event . elapsedMs ? chalk . dim ( ` (${ this . formatElapsedTime ( event . elapsedMs ) } )` ) : '' ;
45+ console . log ( `${ chalk . green ( '✓' ) } ${ this . capitalize ( event . resourceType ) } → ${ chalk . hex ( event . color ) ( event . name ) } - Updated${ successTimeString } ` ) ;
4046 this . updateProgress ( ) ;
4147 break ;
48+ }
4249
43- case 'skip' :
50+ case 'skip' : {
4451 this . processed ++ ;
4552 this . unchanged ++ ;
53+ // Optionally show timing for skipped items if they take longer than expected
54+ const skipTimeString = event . elapsedMs && event . elapsedMs > 10 ? chalk . dim ( ` (${ this . formatElapsedTime ( event . elapsedMs ) } )` ) : '' ;
55+ if ( skipTimeString ) {
56+ this . clearProgress ( ) ;
57+ console . log ( `${ chalk . dim ( '—' ) } ${ this . capitalize ( event . resourceType ) } → ${ chalk . dim ( event . name ) } - Skipped${ skipTimeString } ` ) ;
58+ }
4659 this . updateProgress ( ) ;
4760 break ;
61+ }
4862
49- case 'error' :
63+ case 'error' : {
5064 this . processed ++ ;
5165 this . failed ++ ;
5266 this . clearProgress ( ) ;
53- console . log ( `${ chalk . red ( '✗' ) } ${ this . capitalize ( event . resourceType ) } → ${ chalk . red ( event . name ) } - Failed` ) ;
67+ const errorTimeString = event . elapsedMs ? chalk . dim ( ` (${ this . formatElapsedTime ( event . elapsedMs ) } )` ) : '' ;
68+ console . log ( `${ chalk . red ( '✗' ) } ${ this . capitalize ( event . resourceType ) } → ${ chalk . red ( event . name ) } - Failed${ errorTimeString } ` ) ;
5469 this . updateProgress ( ) ;
5570 break ;
71+ }
5672
5773 case 'complete' : {
5874 this . clearProgress ( ) ;
5975 const { updated, unchanged, failed } = event . summary ;
60- konsola . ok ( `Completed: ${ updated } updated, ${ unchanged } unchanged, ${ failed } failed` ) ;
6176
77+ // Calculate elapsed time if startTime was recorded
78+ const elapsedTime = this . startTime ? Date . now ( ) - this . startTime : null ;
79+ const timeString = elapsedTime ? this . formatElapsedTime ( elapsedTime ) : '' ;
80+
81+ konsola . ok ( `Completed: ${ updated } updated, ${ unchanged } unchanged, ${ failed } failed${ timeString ? ` in ${ timeString } ` : '' } ` , true ) ;
82+ konsola . br ( ) ;
6283 // Show summary of skipped items when there are many
6384 if ( unchanged > 5 ) {
6485 console . log ( chalk . dim ( ` (${ unchanged } resources were already up-to-date)` ) ) ;
@@ -82,6 +103,35 @@ export class ProgressDisplay {
82103 private capitalize ( str : string ) : string {
83104 return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) ;
84105 }
106+
107+ /**
108+ * Formats elapsed time in milliseconds to a human-readable string
109+ * @param ms - Time in milliseconds
110+ * @returns Formatted time string (e.g., "1.2s", "2m 30s", "1h 5m")
111+ */
112+ private formatElapsedTime ( ms : number ) : string {
113+ if ( ms < 1000 ) {
114+ return `${ ms } ms` ;
115+ }
116+
117+ const seconds = Math . floor ( ms / 1000 ) ;
118+ const minutes = Math . floor ( seconds / 60 ) ;
119+ const hours = Math . floor ( minutes / 60 ) ;
120+
121+ if ( hours > 0 ) {
122+ const remainingMinutes = minutes % 60 ;
123+ return remainingMinutes > 0 ? `${ hours } h ${ remainingMinutes } m` : `${ hours } h` ;
124+ }
125+
126+ if ( minutes > 0 ) {
127+ const remainingSeconds = seconds % 60 ;
128+ return remainingSeconds > 0 ? `${ minutes } m ${ remainingSeconds } s` : `${ minutes } m` ;
129+ }
130+
131+ // For times under a minute, show decimal precision for seconds
132+ const preciseSeconds = ( ms / 1000 ) . toFixed ( 1 ) ;
133+ return `${ preciseSeconds } s` ;
134+ }
85135}
86136
87137// Global display instance - single source of truth for terminal output
0 commit comments