@@ -42,6 +42,7 @@ use crate::bottom_pane::scroll_state::ScrollState;
4242use crate :: exec_cell:: spinner;
4343use crate :: onboarding_viewport:: ViewportAnchor ;
4444use crate :: onboarding_viewport:: render_lines_with_anchor;
45+ use crate :: onboarding_viewport:: render_lines_with_fixed_footer;
4546use crate :: render:: renderable:: Renderable ;
4647use crate :: tui:: frame_requester:: FrameRequester ;
4748
@@ -796,6 +797,7 @@ impl OnboardingWidget {
796797 }
797798 let current = state. selected_idx . unwrap_or ( 0 ) ;
798799 state. selected_idx = Some ( if current == 0 { len - 1 } else { current - 1 } ) ;
800+ state. ensure_visible ( len, MAX_POPUP_ROWS . min ( len) ) ;
799801 }
800802
801803 fn model_move_down ( state : & mut ScrollState , filtered_indices : & [ usize ] ) {
@@ -805,6 +807,7 @@ impl OnboardingWidget {
805807 }
806808 let current = state. selected_idx . unwrap_or ( 0 ) ;
807809 state. selected_idx = Some ( ( current + 1 ) % len) ;
810+ state. ensure_visible ( len, MAX_POPUP_ROWS . min ( len) ) ;
808811 }
809812
810813 fn model_apply_filter (
@@ -832,6 +835,7 @@ impl OnboardingWidget {
832835 } else {
833836 Some ( 0 )
834837 } ;
838+ state. scroll_top = 0 ;
835839 }
836840
837841 fn custom_model_name_handle_key ( & mut self , key : KeyEvent ) {
@@ -2070,15 +2074,9 @@ impl OnboardingWidget {
20702074
20712075 let max_visible = MAX_POPUP_ROWS . min ( filtered_indices. len ( ) . max ( 1 ) ) ;
20722076 let scroll_offset = state
2073- . selected_idx
2074- . map ( |sel| {
2075- if sel >= max_visible. saturating_sub ( 2 ) {
2076- sel. saturating_sub ( max_visible. saturating_sub ( 3 ) )
2077- } else {
2078- 0
2079- }
2080- } )
2081- . unwrap_or ( 0 ) ;
2077+ . scroll_top
2078+ . min ( filtered_indices. len ( ) . saturating_sub ( max_visible) ) ;
2079+ let mut anchor = None ;
20822080
20832081 for ( vis_idx, & actual_idx) in filtered_indices
20842082 . iter ( )
@@ -2088,20 +2086,25 @@ impl OnboardingWidget {
20882086 {
20892087 if let Some ( item) = items. get ( actual_idx) {
20902088 let is_selected = state. selected_idx == Some ( vis_idx) ;
2089+ let start = lines. len ( ) ;
20912090 let description = if item. display_name == item. slug {
20922091 None
20932092 } else {
20942093 Some ( item. display_name . clone ( ) )
20952094 } ;
20962095 Self :: render_option_row ( & mut lines, item. slug . clone ( ) , description, is_selected) ;
2096+ if is_selected {
2097+ anchor = Some ( ViewportAnchor {
2098+ start,
2099+ end : lines. len ( ) ,
2100+ } ) ;
2101+ }
20972102 }
20982103 }
20992104
2100- Self :: render_footer ( & mut lines, "Enter select" , "Esc cancel" ) ;
2101-
2102- Paragraph :: new ( lines)
2103- . wrap ( Wrap { trim : false } )
2104- . render ( content_area, buf) ;
2105+ let mut footer_lines = Vec :: new ( ) ;
2106+ Self :: render_footer ( & mut footer_lines, "Enter select" , "Esc cancel" ) ;
2107+ render_lines_with_fixed_footer ( lines, footer_lines, anchor, content_area, buf) ;
21052108 }
21062109
21072110 fn render_custom_model_name ( input : & str , cursor_pos : usize , area : Rect , buf : & mut Buffer ) {
@@ -2421,10 +2424,24 @@ impl Renderable for OnboardingWidget {
24212424 fn desired_height ( & self , _width : u16 ) -> u16 {
24222425 match & self . state {
24232426 OnboardingState :: ModelSelection {
2424- filtered_indices, ..
2427+ items,
2428+ state,
2429+ filtered_indices,
2430+ ..
24252431 } => {
2426- let items = MAX_POPUP_ROWS . min ( filtered_indices. len ( ) . max ( 1 ) ) as u16 ;
2427- items + 8
2432+ let max_visible = MAX_POPUP_ROWS . min ( filtered_indices. len ( ) . max ( 1 ) ) ;
2433+ let scroll_offset = state
2434+ . scroll_top
2435+ . min ( filtered_indices. len ( ) . saturating_sub ( max_visible) ) ;
2436+ let option_rows = filtered_indices
2437+ . iter ( )
2438+ . skip ( scroll_offset)
2439+ . take ( max_visible)
2440+ . filter_map ( |idx| items. get ( * idx) )
2441+ . map ( |item| if item. display_name == item. slug { 1 } else { 2 } )
2442+ . sum :: < u16 > ( )
2443+ . max ( 1 ) ;
2444+ option_rows + 9
24282445 }
24292446 OnboardingState :: CustomModelName { .. } => 8 ,
24302447 OnboardingState :: ProviderSelection { items, .. } => items. len ( ) as u16 * 2 + 6 ,
0 commit comments