use std::sync::OnceLock;
use std::time::Duration;
use frameplay::{FrameTimeReference, Frameplay, FrameplayOptions};
use ratatui::prelude::*;
use super::theme;
const FRAMES: [&str; 6] = ["⠹", "⠼", "⠶", "⠧", "⠏", "⠛"];
const GENERATING_FRAMES: [&str; 18] = [
" ⢀⣾⣿⡿⠁ ",
" ⠐⣻⣿⣯⠄ ",
" ⠚⢛⣿⣥⡤ ",
" ⠐⠛⠛⣫⣤⣤⠄ ",
" ⠚⠛⠛⢁⣤⣤⡤ ",
"⠐⠛⠛⠋ ⣠⣤⣤⠄",
"⠚⠛⠛⠁ ⢀⣤⣤⡤",
"⠻⠛⠋ ⣠⣤⣦",
"⢿⠛⠁ ⢀⣤⣷",
"⣿⠏ ⣰⣿",
"⣿⡥ ⢚⣿",
"⣷⣤⠄ ⠐⠛⢿",
"⣦⣤⡤ ⠚⠛⠻",
"⣠⣤⣤⠄ ⠐⠛⠛⠋",
"⢀⣤⣤⡤ ⠚⠛⠛⠁",
" ⣠⣤⣤⠔⠛⠛⠋ ",
" ⢀⣤⣤⡾⠛⠛⠁ ",
" ⣠⣴⣿⠟⠋ ",
];
const WAIT_FRAMES: [&str; 17] = [
"⣿⣿⣿⣿⣉⣉⣉⣉⣹",
"⣏⣿⣿⣿⣏⣉⣉⣉⣹",
"⣏⣹⣿⣿⣿⣉⣉⣉⣹",
"⣏⣉⣿⣿⣿⣏⣉⣉⣹",
"⣏⣉⣹⣿⣿⣿⣉⣉⣹",
"⣏⣉⣉⣿⣿⣿⣏⣉⣹",
"⣏⣉⣉⣹⣿⣿⣿⣉⣹",
"⣏⣉⣉⣉⣿⣿⣿⣏⣹",
"⣏⣉⣉⣉⣹⣿⣿⣿⣹",
"⣏⣉⣉⣉⣉⣿⣿⣿⣿",
"⣏⣉⣉⣉⣹⣿⣿⣿⣹",
"⣏⣉⣉⣉⣿⣿⣿⣏⣹",
"⣏⣉⣉⣹⣿⣿⣿⣉⣹",
"⣏⣉⣹⣿⣿⣿⣉⣉⣹",
"⣏⣉⣿⣿⣿⣏⣉⣉⣹",
"⣏⣹⣿⣿⣿⣉⣉⣉⣹",
"⣏⣿⣿⣿⣏⣉⣉⣉⣹",
];
const TOOL_FRAMES: [&str; 28] = [
" ",
"⡀ ⢀",
"⣄ ⣠",
"⣦⡀ ⢀⣴",
"⣷⣄ ⣠⣾",
"⣿⣦⡀ ⢀⣴⣿",
"⣿⣷⣄ ⣠⣾⣿",
"⣿⣿⣦⡀ ⢀⣴⣿⣿",
"⢿⣿⣷⣄ ⣠⣾⣿⡿",
"⠻⣿⣿⣦⣀⣴⣿⣿⠟",
"⠙⢿⣿⣷⣤⣾⣿⡿⠋",
"⠈⠻⣿⣿⣶⣿⣿⠟⠁",
" ⠙⢿⣿⣿⣿⡿⠋ ",
" ⢻⣿⣿⣿⡟ ",
" ⢸⣿⣿⣿⡇ ",
" ⣼⣿⣿⣿⣧ ",
" ⣠⣾⣿⣿⣿⣷⣄ ",
"⢀⣴⣿⣿⠿⣿⣿⣦⡀",
"⣠⣾⣿⡿⠛⢿⣿⣷⣄",
"⣴⣿⣿⠟⠉⠻⣿⣿⣦",
"⣾⣿⡿⠋ ⠙⢿⣿⣷",
"⣿⣿⠟⠁ ⠈⠻⣿⣿",
"⣿⡿⠋ ⠙⢿⣿",
"⣿⠟⠁ ⠈⠻⣿",
"⡿⠋ ⠙⢿",
"⠟⠁ ⠈⠻",
"⠋ ⠙",
"⠁ ⠈",
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SpinnerKind {
Inline,
Generating,
Tool,
Waiting,
}
fn spinner_options(frame_rate: u32) -> FrameplayOptions {
FrameplayOptions {
frame_time_reference: FrameTimeReference::StartTime,
frame_rate,
}
}
fn frameplay(kind: SpinnerKind) -> &'static Frameplay<&'static str> {
static INLINE: OnceLock<Frameplay<&'static str>> = OnceLock::new();
static GENERATING: OnceLock<Frameplay<&'static str>> = OnceLock::new();
static TOOL: OnceLock<Frameplay<&'static str>> = OnceLock::new();
static WAITING: OnceLock<Frameplay<&'static str>> = OnceLock::new();
match kind {
SpinnerKind::Inline => INLINE.get_or_init(|| Frameplay::new(FRAMES, spinner_options(8))),
SpinnerKind::Generating => {
GENERATING.get_or_init(|| Frameplay::new(GENERATING_FRAMES, spinner_options(14)))
}
SpinnerKind::Tool => TOOL.get_or_init(|| Frameplay::new(TOOL_FRAMES, spinner_options(14))),
SpinnerKind::Waiting => {
WAITING.get_or_init(|| Frameplay::new(WAIT_FRAMES, spinner_options(14)))
}
}
}
pub fn next_wake(kinds: impl IntoIterator<Item = SpinnerKind>) -> Option<Duration> {
kinds
.into_iter()
.filter_map(|kind| frameplay(kind).time_to_next_frame())
.min()
}
pub fn spinner_frame() -> &'static str {
frameplay(SpinnerKind::Inline).get_frame()
}
pub fn spinner() -> Span<'static> {
Span::raw(spinner_frame()).fg(theme::accent())
}
pub fn generating_spinner() -> Span<'static> {
Span::raw(*frameplay(SpinnerKind::Generating).get_frame()).fg(theme::accent())
}
pub fn tool_spinner() -> Span<'static> {
Span::raw(*frameplay(SpinnerKind::Tool).get_frame()).fg(theme::accent())
}
pub fn wait_spinner() -> Span<'static> {
Span::raw(*frameplay(SpinnerKind::Waiting).get_frame()).fg(theme::accent())
}