@@ -37,7 +37,6 @@ impl StageRenderer {
3737 challenge_text : & str ,
3838 current_position : usize ,
3939 mistakes : usize ,
40- start_time : & std:: time:: Instant ,
4140 line_starts : & [ usize ] ,
4241 comment_ranges : & [ ( usize , usize ) ] ,
4342 challenge : Option < & Challenge > ,
@@ -80,15 +79,17 @@ impl StageRenderer {
8079 comment_ranges,
8180 current_mistake_position,
8281 terminal_size. width ,
82+ challenge,
8383 ) ;
8484
85+ let elapsed_time = scoring_engine. get_elapsed_time ( ) ;
8586 let metrics = crate :: scoring:: engine:: ScoringEngine :: calculate_real_time_result (
8687 current_position,
8788 mistakes,
88- start_time ,
89+ elapsed_time ,
8990 ) ;
9091 let current_line = self . find_line_for_position ( current_position, line_starts) ;
91- let elapsed_secs = scoring_engine . get_elapsed_time ( ) . as_secs ( ) ;
92+ let elapsed_secs = elapsed_time . as_secs ( ) ;
9293
9394 let streak = scoring_engine. get_current_streak ( ) ;
9495 let first_line = format ! (
@@ -99,48 +100,90 @@ impl StageRenderer {
99100 self . terminal . draw ( |f| {
100101 let chunks = Layout :: default ( )
101102 . direction ( Direction :: Vertical )
103+ . margin ( 1 ) // Add margin for better spacing
102104 . constraints (
103105 [
104- Constraint :: Length ( 4 ) , // Header with metrics
105- Constraint :: Min ( 1 ) , // Content
106- Constraint :: Length ( 1 ) , // Progress bar at bottom
106+ Constraint :: Length ( 3 ) , // Header (more compact - only challenge info)
107+ Constraint :: Min ( 3 ) , // Content (minimum height)
108+ Constraint :: Length ( 3 ) , // Metrics section (compact)
109+ Constraint :: Length ( 3 ) , // Progress bar (compact)
107110 ]
108111 . as_ref ( ) ,
109112 )
110113 . split ( f. size ( ) ) ;
111114
112- // Header with consolidated information
113- let header = Paragraph :: new ( vec ! [
114- Line :: from( header_text. clone( ) ) ,
115- Line :: from( vec![ Span :: styled(
116- first_line. clone( ) ,
117- Style :: default ( ) . fg( Color :: White ) ,
118- ) ] ) ,
119- Line :: from( vec![
120- Span :: styled( "[ESC]" , Style :: default ( ) . fg( Color :: Cyan ) ) ,
121- Span :: styled( " Options" , Style :: default ( ) . fg( Color :: White ) ) ,
122- ] ) ,
123- ] )
124- . block ( Block :: default ( ) . borders ( Borders :: BOTTOM ) ) ;
115+ // Header with basic info
116+ let header = Paragraph :: new ( vec ! [ Line :: from( header_text. clone( ) ) ] ) . block (
117+ Block :: default ( )
118+ . borders ( Borders :: ALL )
119+ . border_style ( Style :: default ( ) . fg ( Color :: Cyan ) )
120+ . title ( "Challenge" )
121+ . title_style ( Style :: default ( ) . fg ( Color :: Cyan ) )
122+ . padding ( ratatui:: widgets:: Padding :: horizontal ( 1 ) ) ,
123+ ) ; // Only horizontal padding
125124 f. render_widget ( header, chunks[ 0 ] ) ;
126125
127- // Content with syntax highlighting and cursor
128- let scroll_offset = if current_line > chunks[ 1 ] . height as usize / 2 {
129- ( current_line - chunks[ 1 ] . height as usize / 2 ) as u16
126+ // Content with syntax highlighting and cursor with padding
127+ let scroll_offset = if current_line > chunks[ 1 ] . height . saturating_sub ( 2 ) as usize / 2 {
128+ ( current_line - chunks[ 1 ] . height . saturating_sub ( 2 ) as usize / 2 ) as u16
130129 } else {
131130 0
132131 } ;
133132
134- let content =
135- Paragraph :: new ( Text :: from ( content_spans. clone ( ) ) ) . scroll ( ( scroll_offset, 0 ) ) ;
133+ let content = Paragraph :: new ( Text :: from ( content_spans. clone ( ) ) )
134+ . scroll ( ( scroll_offset, 0 ) )
135+ . block (
136+ Block :: default ( )
137+ . borders ( Borders :: ALL )
138+ . border_style ( Style :: default ( ) . fg ( Color :: Blue ) )
139+ . title ( "Code" )
140+ . title_style ( Style :: default ( ) . fg ( Color :: LightBlue ) )
141+ . padding ( ratatui:: widgets:: Padding :: uniform ( 1 ) ) ,
142+ ) ;
136143 f. render_widget ( content, chunks[ 1 ] ) ;
137144
138- // Progress bar at the bottom, full width
139- let terminal_width = f. size ( ) . width as u8 ;
140- let full_width_progress = Self :: create_progress_bar ( progress_percent, terminal_width) ;
141- let progress_widget =
142- Paragraph :: new ( full_width_progress) . style ( Style :: default ( ) . fg ( Color :: White ) ) ;
143- f. render_widget ( progress_widget, chunks[ 2 ] ) ;
145+ // Metrics section below the code
146+ let metrics_widget = Paragraph :: new ( vec ! [ Line :: from( vec![ Span :: styled(
147+ first_line. clone( ) ,
148+ Style :: default ( ) . fg( Color :: White ) ,
149+ ) ] ) ] )
150+ . block (
151+ Block :: default ( )
152+ . borders ( Borders :: ALL )
153+ . border_style ( Style :: default ( ) . fg ( Color :: Yellow ) )
154+ . title ( "Metrics" )
155+ . title_style ( Style :: default ( ) . fg ( Color :: Yellow ) )
156+ . padding ( ratatui:: widgets:: Padding :: horizontal ( 1 ) ) ,
157+ ) ; // Only horizontal padding
158+ f. render_widget ( metrics_widget, chunks[ 2 ] ) ;
159+
160+ // Progress bar in its own bordered box with different color
161+ let progress_width = chunks[ 3 ] . width . saturating_sub ( 4 ) as u8 ; // Account for borders and padding
162+ let full_width_progress = Self :: create_progress_bar ( progress_percent, progress_width) ;
163+ let progress_widget = Paragraph :: new ( full_width_progress)
164+ . style ( Style :: default ( ) . fg ( Color :: Green ) )
165+ . block (
166+ Block :: default ( )
167+ . borders ( Borders :: ALL )
168+ . border_style ( Style :: default ( ) . fg ( Color :: Green ) )
169+ . title ( "Progress" )
170+ . title_style ( Style :: default ( ) . fg ( Color :: Green ) ) ,
171+ )
172+ . alignment ( ratatui:: layout:: Alignment :: Center ) ;
173+ f. render_widget ( progress_widget, chunks[ 3 ] ) ;
174+
175+ // Render [ESC] Options in bottom left without border
176+ let esc_area = ratatui:: layout:: Rect {
177+ x : 1 , // Left margin
178+ y : f. size ( ) . height . saturating_sub ( 1 ) , // Bottom of screen
179+ width : 15 , // Width for "[ESC] Options"
180+ height : 1 ,
181+ } ;
182+ let esc_text = Paragraph :: new ( vec ! [ Line :: from( vec![
183+ Span :: styled( "[ESC]" , Style :: default ( ) . fg( Color :: LightBlue ) ) ,
184+ Span :: styled( " Options" , Style :: default ( ) . fg( Color :: White ) ) ,
185+ ] ) ] ) ;
186+ f. render_widget ( esc_text, esc_area) ;
144187
145188 // Render dialog if shown
146189 if dialog_shown {
@@ -159,20 +202,44 @@ impl StageRenderer {
159202 comment_ranges : & [ ( usize , usize ) ] ,
160203 current_mistake_position : Option < usize > ,
161204 terminal_width : u16 ,
205+ challenge : Option < & Challenge > ,
162206 ) -> Vec < Line < ' static > > {
163207 let mut lines = Vec :: new ( ) ;
164208 let mut current_line_spans = Vec :: new ( ) ;
165209 let mut current_line_width = 0u16 ;
166- let max_width = terminal_width. saturating_sub ( 1 ) ;
210+
211+ // Reserve space for line numbers (assume max 4 digits + 2 spaces)
212+ let line_number_width = 6u16 ;
213+ let max_width = terminal_width. saturating_sub ( line_number_width + 1 ) ;
167214
168215 // Pre-calculate all character properties to avoid O(n²) complexity
169216 let skip_cache = self . create_skip_cache ( line_starts, comment_ranges) ;
170217 let comment_cache = self . create_comment_cache ( comment_ranges) ;
171218 let current_line_number = self . find_line_for_position ( current_position, line_starts) ;
172219
173220 let mut line_number = 0 ;
221+ let mut line_start = true ;
222+
223+ // Get the starting line number from challenge, default to 1
224+ let start_line_number = challenge. and_then ( |c| c. start_line ) . unwrap_or ( 1 ) ;
174225
175226 for ( i, & ch) in self . chars . iter ( ) . enumerate ( ) {
227+ // Add line number at the start of each line
228+ if line_start {
229+ let actual_line_number = start_line_number + line_number;
230+ let line_num_text = format ! ( "{:>4} │ " , actual_line_number) ;
231+ let line_num_style = if line_number == current_line_number {
232+ Style :: default ( )
233+ . fg ( Color :: Yellow )
234+ . add_modifier ( ratatui:: style:: Modifier :: BOLD )
235+ } else {
236+ Style :: default ( ) . fg ( Color :: DarkGray )
237+ } ;
238+ current_line_spans. push ( Span :: styled ( line_num_text, line_num_style) ) ;
239+ current_line_width += line_number_width;
240+ line_start = false ;
241+ }
242+
176243 // Handle explicit newlines
177244 if ch == '\n' {
178245 // Use cached properties for newline styling
@@ -198,6 +265,7 @@ impl StageRenderer {
198265 current_line_spans = Vec :: new ( ) ;
199266 current_line_width = 0 ;
200267 line_number += 1 ;
268+ line_start = true ; // Next iteration will be start of new line
201269 continue ;
202270 }
203271
@@ -234,10 +302,32 @@ impl StageRenderer {
234302
235303 if !current_line_spans. is_empty ( ) {
236304 lines. push ( Line :: from ( current_line_spans) ) ;
305+ } else if line_start {
306+ // Handle case where file ends without content on last line
307+ let actual_line_number = start_line_number + line_number;
308+ let line_num_text = format ! ( "{:>4} │ " , actual_line_number) ;
309+ let line_num_style = if line_number == current_line_number {
310+ Style :: default ( )
311+ . fg ( Color :: Yellow )
312+ . add_modifier ( ratatui:: style:: Modifier :: BOLD )
313+ } else {
314+ Style :: default ( ) . fg ( Color :: DarkGray )
315+ } ;
316+ lines. push ( Line :: from ( vec ! [ Span :: styled(
317+ line_num_text,
318+ line_num_style,
319+ ) ] ) ) ;
237320 }
238321
239322 if lines. is_empty ( ) {
240- lines. push ( Line :: from ( "" ) ) ;
323+ let line_num_text = format ! ( "{:>4} │ " , start_line_number) ;
324+ let line_num_style = Style :: default ( )
325+ . fg ( Color :: Yellow )
326+ . add_modifier ( ratatui:: style:: Modifier :: BOLD ) ;
327+ lines. push ( Line :: from ( vec ! [ Span :: styled(
328+ line_num_text,
329+ line_num_style,
330+ ) ] ) ) ;
241331 }
242332
243333 lines
@@ -436,7 +526,7 @@ impl StageRenderer {
436526 Span :: styled(
437527 "[S] " ,
438528 Style :: default ( )
439- . fg( Color :: Yellow )
529+ . fg( Color :: Cyan )
440530 . add_modifier( Modifier :: BOLD ) ,
441531 )
442532 } else {
@@ -472,7 +562,7 @@ impl StageRenderer {
472562 Span :: styled(
473563 "[ESC] " ,
474564 Style :: default ( )
475- . fg( Color :: Green )
565+ . fg( Color :: LightBlue )
476566 . add_modifier( Modifier :: BOLD ) ,
477567 ) ,
478568 Span :: styled( "Back to game" , Style :: default ( ) . fg( Color :: White ) ) ,
@@ -487,10 +577,10 @@ impl StageRenderer {
487577 . title ( "Game Options" )
488578 . title_style (
489579 Style :: default ( )
490- . fg ( Color :: Cyan )
580+ . fg ( Color :: LightBlue )
491581 . add_modifier ( Modifier :: BOLD ) ,
492582 )
493- . border_style ( Style :: default ( ) . fg ( Color :: Cyan ) ) ,
583+ . border_style ( Style :: default ( ) . fg ( Color :: Blue ) ) ,
494584 )
495585 . alignment ( ratatui:: layout:: Alignment :: Center ) ;
496586
0 commit comments