magi-code 0.63.4

Repository-aware CLI coding agent for terminal work
Documentation
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);
    }
}