@@ -19,7 +19,8 @@ pub fn draw_config(
1919) {
2020 let full = f. area ( ) ;
2121
22- // Dynamic footer height based on the footer content and available width (generic estimator)
22+ // Compute footer height from hotkey segments and terminal width (cells).
23+ // This prevents the footer from wrapping unexpectedly when the screen is narrow.
2324 let segs = if app
2425 . overlays
2526 . expert
@@ -37,6 +38,8 @@ pub fn draw_config(
3738
3839 let layout = Layout :: default ( )
3940 . direction ( Direction :: Vertical )
41+ // Reserve at least 10 rows for content; footer is exact length computed above.
42+ // Using fixed footer length avoids layout jitter across frames.
4043 . constraints ( [ Constraint :: Min ( 10 ) , Constraint :: Length ( lines_needed) ] )
4144 . split ( full) ;
4245
@@ -51,27 +54,33 @@ pub fn draw_config(
5154 main_outer,
5255 ) ;
5356 let mut content = main_outer;
57+ // Shrink to inner content area (1-cell border on all sides).
58+ // Use saturating ops to avoid underflow on very small terminals.
5459 content. x += 1 ;
5560 content. y += 1 ;
5661 content. width = content. width . saturating_sub ( 2 ) ;
5762 content. height = content. height . saturating_sub ( 2 ) ;
5863
64+ // Heuristic height for sinks panel: items + 2 (title/padding), capped at 6 rows.
65+ // Small cap keeps the top area from starving models/languages.
5966 let sink_height: u16 = ( ( sinks_list. len ( ) as u16 ) + 2 ) . min ( 6 ) ;
6067
6168 let chunks = Layout :: default ( )
6269 . direction ( Direction :: Vertical )
6370 . constraints ( [
6471 Constraint :: Length ( 3 ) , // Top info (metrics + warnings)
65- Constraint :: Length ( sink_height. max ( 5 ) ) , // Sinks
72+ Constraint :: Length ( sink_height. max ( 5 ) ) , // Sinks (min 5 rows for readability)
6673 Constraint :: Min ( 10 ) , // Models + (Languages/Devices)
67- Constraint :: Length ( 7 ) , // VAD + Logs
74+ Constraint :: Length ( 7 ) , // VAD + Logs (fixed to avoid flicker)
6875 ] )
6976 . split ( content) ;
7077 // Brand overlay: render title intersecting the top frame border
7178 {
7279 use crate :: tui:: widgets:: brand:: { BrandOptions , render_brand} ;
7380 let brand_area = ratatui:: layout:: Rect {
7481 x : main_outer. x ,
82+ // Lift the brand one cell above to overlap the outer border.
83+ // `saturating_sub` prevents underflow when y == 0.
7584 y : main_outer. y . saturating_sub ( 1 ) ,
7685 width : main_outer. width ,
7786 height : 1 ,
@@ -100,6 +109,7 @@ pub fn draw_config(
100109 Span :: raw( " " ) ,
101110 Span :: styled( "time: " , Theme :: label( ) ) ,
102111 Span :: styled(
112+ // Convert milliseconds to HH:MM:SS string; assumes ms are monotonic counters.
103113 keyless_core:: utils:: fmt_hms( app. lifetime_talk_ms) ,
104114 Theme :: text_primary( ) ,
105115 ) ,
@@ -110,6 +120,8 @@ pub fn draw_config(
110120 // Warnings / file path (rendered below metrics)
111121 let mut warn_lines: Vec < Line > = Vec :: new ( ) ;
112122 if matches ! ( app. selections. sink_choice, SinkChoice :: File ) {
123+ // File sink: show current path and edit state. `file_path` is cloned to avoid
124+ // borrowing across UI composition. Encoding assumed UTF-8 for display.
113125 let mut spans = vec ! [
114126 Span :: styled( "file: " , Theme :: label( ) ) ,
115127 Span :: styled( app. selections. file_path. clone( ) , Colors :: text_primary( ) ) ,
@@ -130,6 +142,7 @@ pub fn draw_config(
130142 ) ) ;
131143 warn_lines. push ( Line :: from ( spans) ) ;
132144 } else {
145+ // Show the most recent error (if any) and permission warnings gathered at startup.
133146 if let Some ( err) = & app. overlays . error_message {
134147 warn_lines. push ( Line :: from ( vec ! [
135148 Span :: styled( "error: " , Theme :: warn( ) ) ,
@@ -146,6 +159,7 @@ pub fn draw_config(
146159 }
147160 }
148161 if !warn_lines. is_empty ( ) {
162+ // Only render the warnings box when there is content to avoid consuming vertical space.
149163 let warning_p = Paragraph :: new ( warn_lines) ;
150164 f. render_widget ( warning_p, extra_rows[ 1 ] ) ;
151165 }
@@ -160,6 +174,7 @@ pub fn draw_config(
160174 . split ( chunks[ 2 ] ) ;
161175
162176 // Left: Models
177+ // Build an O(1) membership set for installed models; allocates proportional to count.
163178 let installed_set: std:: collections:: HashSet < String > =
164179 app. models . discovered . iter ( ) . cloned ( ) . collect ( ) ;
165180 models:: render_models (
@@ -213,11 +228,13 @@ pub fn draw_config(
213228
214229 // Expert overlay (centered)
215230 if app. expert_mode ( ) {
231+ // Render after footer so overlay sits on top of lower widgets.
216232 expert:: render_expert ( f, full, app) ;
217233 }
218234
219235 // Download overlay (centered, modal)
220236 if app. is_downloading ( ) {
237+ // Two states: transient "loading" (no progress) vs active download.
221238 let is_loading = app
222239 . overlays
223240 . download
@@ -257,7 +274,7 @@ pub fn draw_config(
257274 ] )
258275 . alignment ( Alignment :: Center )
259276 } ) ;
260- // center a box for message + gauge
277+ // Center a box for message + gauge. The middle 4 rows are dedicated to content.
261278 let outer = Layout :: default ( )
262279 . direction ( Direction :: Vertical )
263280 . constraints ( [
@@ -275,10 +292,11 @@ pub fn draw_config(
275292 ] )
276293 . split ( outer) ;
277294 let area = inner_cols[ 1 ] ;
278- // Clear the background to make overlay opaque
295+ // Clear the background to make overlay opaque and avoid bleed-through.
279296 f. render_widget ( Clear , area) ;
280297 f. render_widget ( overlay, area) ;
281298 let mut msg_area = area;
299+ // Inset by 2 cells horizontally to avoid text hugging the border.
282300 msg_area. x += 2 ;
283301 msg_area. y += 1 ;
284302 msg_area. width = msg_area. width . saturating_sub ( 4 ) ;
@@ -289,11 +307,11 @@ pub fn draw_config(
289307 . as_ref ( )
290308 . map ( |d| d. message . as_str ( ) )
291309 . unwrap_or ( "this may take a few minutes on first run..." ) ;
292- // If text starts with percentage (e.g., "75% model..."), hide it from display
310+ // If text starts with a percentage (e.g., "75% model..."), hide it to reduce noise.
293311 let display_text = if let Some ( pct_end) = text. find ( '%' ) {
294312 let before_pct = & text[ ..pct_end] ;
295313 if before_pct. trim ( ) . parse :: < u16 > ( ) . is_ok ( ) {
296- // Skip past "NN% " to hide the percentage from user
314+ // Skip past "NN% ". Assumes percentage fits in u16 and precedes '%'.
297315 text. get ( pct_end + 1 ..) . unwrap_or ( text) . trim_start ( )
298316 } else {
299317 text
@@ -309,11 +327,11 @@ pub fn draw_config(
309327 } else {
310328 Theme :: text_primary ( )
311329 } ;
312- // render primary line
330+ // Render primary line; keep same bg as overlay for a seamless modal look.
313331 let p = Paragraph :: new ( Line :: from ( vec ! [ Span :: styled( display_text, stage_style) ] ) )
314332 . style ( Style :: default ( ) . bg ( Colors :: bg_selected ( ) ) ) ;
315333 f. render_widget ( p, msg_area) ;
316- // render gauge on second line if percent known
334+ // Render gauge on the second line if a percentage can be parsed.
317335 let mut gauge_area = msg_area;
318336 gauge_area. y = gauge_area. y . saturating_add ( 1 ) ;
319337 if let Some ( pct_end) = text. find ( '%' ) {
@@ -323,6 +341,7 @@ pub fn draw_config(
323341 . gauge_style ( Theme :: selected ( ) )
324342 . style ( Style :: default ( ) . bg ( Colors :: bg_selected ( ) ) )
325343 . ratio ( {
344+ // Clamp to [0,1]. Use 1.0 when nearing completion and ETA reports 0s.
326345 let r = val as f64 / 100.0 ;
327346 if val >= 99 && text. contains ( "ETA 0s" ) {
328347 1.0
@@ -360,6 +379,7 @@ pub fn draw_config(
360379 let area = cols[ 1 ] ;
361380 f. render_widget ( toast, area) ;
362381 let mut msg = area;
382+ // One-line message centered horizontally; trim margins to avoid wrapping.
363383 msg. x += 2 ;
364384 msg. y += 1 ;
365385 msg. width = msg. width . saturating_sub ( 4 ) ;
0 commit comments