Skip to main content

ai_agent/commands/
plugin_pagination.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4const DEFAULT_MAX_VISIBLE: usize = 5;
5
6pub struct PaginationOptions {
7    pub total_items: usize,
8    pub max_visible: Option<usize>,
9    pub selected_index: Option<usize>,
10}
11
12pub struct PaginationResult<T> {
13    pub current_page: usize,
14    pub total_pages: usize,
15    pub start_index: usize,
16    pub end_index: usize,
17    pub needs_pagination: bool,
18    pub page_size: usize,
19    pub get_visible_items: Box<dyn Fn(&[T]) -> Vec<T>>,
20    pub to_actual_index: Box<dyn Fn(usize) -> usize>,
21    pub is_on_current_page: Box<dyn Fn(usize) -> bool>,
22    pub go_to_page: Box<dyn Fn(usize)>,
23    pub next_page: Box<dyn Fn()>,
24    pub prev_page: Box<dyn Fn()>,
25    pub handle_selection_change: Box<dyn Fn(usize, &mut usize)>,
26    pub handle_pagenavigation: Box<dyn Fn(&str, &mut usize) -> bool>,
27    pub scroll_position: ScrollPosition,
28}
29
30pub struct ScrollPosition {
31    pub current: usize,
32    pub total: usize,
33    pub can_scroll_up: bool,
34    pub can_scroll_down: bool,
35}
36
37pub fn use_pagination<T: Clone>(options: PaginationOptions) -> PaginationResult<T> {
38    let total_items = options.total_items;
39    let max_visible = options.max_visible.unwrap_or(DEFAULT_MAX_VISIBLE);
40    let selected_index = options.selected_index.unwrap_or(0);
41
42    let needs_pagination = total_items > max_visible;
43
44    let scroll_offset_ref: Rc<RefCell<usize>> = Rc::new(RefCell::new(0));
45
46    let scroll_offset = if !needs_pagination {
47        0
48    } else {
49        let prev_offset = *scroll_offset_ref.borrow();
50
51        if selected_index < prev_offset {
52            *scroll_offset_ref.borrow_mut() = selected_index;
53            selected_index
54        } else if selected_index >= prev_offset + max_visible {
55            let new_offset = selected_index - max_visible + 1;
56            *scroll_offset_ref.borrow_mut() = new_offset;
57            new_offset
58        } else {
59            let max_offset = total_items.saturating_sub(max_visible);
60            let clamped_offset = prev_offset.min(max_offset);
61            *scroll_offset_ref.borrow_mut() = clamped_offset;
62            clamped_offset
63        }
64    };
65
66    let start_index = scroll_offset;
67    let end_index = (scroll_offset + max_visible).min(total_items);
68
69    let needs_pagination_clone = needs_pagination;
70    let start_index_clone = start_index;
71    let end_index_clone = end_index;
72    let get_visible_items = Box::new(move |items: &[T]| -> Vec<T> {
73        if !needs_pagination_clone {
74            items.to_vec()
75        } else {
76            items[start_index_clone..end_index_clone].to_vec()
77        }
78    });
79
80    let start_index_for_actual = start_index;
81    let to_actual_index =
82        Box::new(move |visible_index: usize| -> usize { start_index_for_actual + visible_index });
83
84    let start_index_for_page = start_index;
85    let end_index_for_page = end_index;
86    let is_on_current_page = Box::new(move |actual_index: usize| -> bool {
87        actual_index >= start_index_for_page && actual_index < end_index_for_page
88    });
89
90    let go_to_page = Box::new(|_page: usize| {});
91    let next_page = Box::new(|| {});
92    let prev_page = Box::new(|| {});
93
94    let total_items_for_selection = total_items;
95    let handle_selection_change = Box::new(move |new_index: usize, selected: &mut usize| {
96        let clamped_index = new_index.min(total_items_for_selection.saturating_sub(1));
97        *selected = clamped_index;
98    });
99
100    let handle_page_navigation =
101        Box::new(|_direction: &str, _set_selected_index: &mut usize| -> bool { false });
102
103    let total_pages = ((total_items + max_visible - 1) / max_visible).max(1);
104    let current_page = scroll_offset / max_visible;
105
106    PaginationResult {
107        current_page,
108        total_pages,
109        start_index,
110        end_index,
111        needs_pagination,
112        page_size: max_visible,
113        get_visible_items,
114        to_actual_index,
115        is_on_current_page,
116        go_to_page,
117        next_page,
118        prev_page,
119        handle_selection_change,
120        handle_pagenavigation: handle_page_navigation,
121        scroll_position: ScrollPosition {
122            current: selected_index + 1,
123            total: total_items,
124            can_scroll_up: scroll_offset > 0,
125            can_scroll_down: scroll_offset + max_visible < total_items,
126        },
127    }
128}