intelli_shell/widgets/
loading.rs

1use std::borrow::Cow;
2
3use ratatui::{
4    Frame,
5    layout::Rect,
6    style::Style,
7    widgets::{Clear, Paragraph},
8};
9
10use crate::config::Theme;
11
12/// The characters for the spinner animation
13pub const SPINNER_CHARS: [&str; 10] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
14
15/// A widget to display a centered loading spinner and message as an overlay
16pub struct LoadingSpinner<'a> {
17    /// The current state of the loading spinner animation
18    spinner_state: usize,
19    /// The style for the spinner text
20    style: Style,
21    /// Optional message to display with the spinner
22    message: Option<Cow<'a, str>>,
23}
24
25impl<'a> LoadingSpinner<'a> {
26    /// Creates a new [`LoadingSpinner`] styled according to the provided theme
27    pub fn new(theme: &Theme) -> Self {
28        Self {
29            spinner_state: 0,
30            style: theme.primary.into(),
31            message: None,
32        }
33    }
34
35    /// Sets or replaces the message to be displayed with the spinner
36    pub fn with_message(mut self, message: impl Into<Cow<'a, str>>) -> Self {
37        self.set_message(message);
38        self
39    }
40
41    /// Sets or replaces the message to be displayed with the spinner
42    pub fn set_message(&mut self, message: impl Into<Cow<'a, str>>) {
43        self.message = Some(message.into());
44    }
45
46    /// Advances the spinner animation by one tick
47    pub fn tick(&mut self) {
48        self.spinner_state = (self.spinner_state + 1) % SPINNER_CHARS.len();
49    }
50
51    /// Renders the loading spinner in the center of the given area
52    pub fn render_in(&self, frame: &mut Frame, area: Rect) {
53        let spinner_char = SPINNER_CHARS[self.spinner_state];
54        let loading_text = if let Some(ref msg) = self.message {
55            format!(" {spinner_char} {msg} ")
56        } else {
57            format!(" {spinner_char} ")
58        };
59
60        // Create the main paragraph widget
61        let loading_paragraph = Paragraph::new(loading_text).style(self.style);
62
63        // Clear the entire area before rendering
64        frame.render_widget(Clear, area);
65        frame.render_widget(loading_paragraph, area);
66    }
67}