use super::*;
pub(crate) fn prompt_cursor_blink_due(
state: &state::MissionControlState,
last_blink: Instant,
) -> bool {
state.is_prompt_focused()
&& state.input.is_empty()
&& last_blink.elapsed() >= PROMPT_CURSOR_BLINK_INTERVAL
}
pub(crate) fn next_ui_timer_timeout(
state: &state::MissionControlState,
last_blink: Instant,
last_footer_git_branch_refresh: Instant,
now: Instant,
) -> Option<Duration> {
let mut next_due = last_footer_git_branch_refresh + FOOTER_GIT_BRANCH_REFRESH_INTERVAL;
if state.is_prompt_focused() && state.input.is_empty() {
next_due = next_due.min(last_blink + PROMPT_CURSOR_BLINK_INTERVAL);
}
if let Some(toast) = &state.toast {
next_due = next_due.min(toast.expires_at());
}
Some(next_due.saturating_duration_since(now))
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) enum RedrawIntent {
#[default]
None,
ScrollOnly,
Immediate,
}
impl RedrawIntent {
pub(crate) fn request_immediate(&mut self) {
*self = Self::Immediate;
}
pub(crate) fn request_scroll(&mut self) {
if *self == Self::None {
*self = Self::ScrollOnly;
}
}
pub(crate) fn clear(&mut self) {
*self = Self::None;
}
}
pub(crate) fn should_draw_frame(intent: RedrawIntent) -> bool {
matches!(intent, RedrawIntent::Immediate | RedrawIntent::ScrollOnly)
}
pub(crate) fn coalesce_mouse_scroll_event(
first: crossterm::event::MouseEvent,
terminal_area: ratatui::layout::Rect,
state: &state::MissionControlState,
bridged_events: &Receiver<TerminalInputEvent>,
pending_events: &mut VecDeque<TerminalInputEvent>,
) -> (crossterm::event::MouseEvent, u16) {
let Some(first_batch) = mouse_scroll_batch(first, terminal_area, state) else {
return (first, state::MOUSE_WHEEL_SCROLL_ROWS);
};
let mut notches = 1u16;
while let Ok(event) = bridged_events.try_recv() {
let TerminalInputEvent::Event(crossterm::event::Event::Mouse(mouse)) = event else {
pending_events.push_back(event);
break;
};
let Some(next_batch) = mouse_scroll_batch(mouse, terminal_area, state) else {
pending_events.push_back(TerminalInputEvent::Event(crossterm::event::Event::Mouse(
mouse,
)));
break;
};
if !first_batch.coalesces_with(next_batch) {
pending_events.push_back(TerminalInputEvent::Event(crossterm::event::Event::Mouse(
mouse,
)));
break;
}
notches = notches.saturating_add(1);
}
(
first,
notches.saturating_mul(state::MOUSE_WHEEL_SCROLL_ROWS),
)
}
impl DrainResult {
pub(crate) fn merge(&mut self, other: DrainResult) {
self.changed |= other.changed;
self.run_finished |= other.run_finished;
self.model_catalog_finished |= other.model_catalog_finished;
self.model_catalog_loaded.extend(other.model_catalog_loaded);
self.startup_loaded.extend(other.startup_loaded);
self.session_preview_loaded
.extend(other.session_preview_loaded);
self.session_switch_loaded
.extend(other.session_switch_loaded);
self.footer_git_branch_loaded
.extend(other.footer_git_branch_loaded);
}
}