magi-code 0.64.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::{overlay_block, overlay_text_area, render_overlay_fill};
use ratatui::{Frame, layout::Rect, widgets::Clear};

pub(super) mod auth;
pub(super) mod mcp;
pub(super) mod model;
pub(super) mod models;
pub(super) mod rewind;
pub(super) mod sessions;
pub(super) mod skills;
pub(super) mod subagents;
pub(super) mod system_prompt;
pub(super) mod tools;
pub(super) mod usage;

pub(crate) use auth::auth_picker_list_area;
pub(super) use auth::{
    draw_active_login, draw_custom_provider_replacement_confirmation, draw_custom_provider_setup,
    draw_login_picker, draw_logout_confirmation,
};
pub(super) use mcp::draw_mcp_modal;
pub(crate) use mcp::mcp_modal_list_area;
pub(super) use model::draw_model_picker;
pub(crate) use model::model_picker_list_area;
pub(super) use models::draw_models_modal;
pub(crate) use models::models_modal_list_area;
pub(super) use rewind::draw_rewind_modal;
pub(crate) use rewind::rewind_modal_text_area;
pub(super) use sessions::draw_sessions_modal;
pub(crate) use sessions::{
    sessions_modal_areas, sessions_modal_list_area, sessions_modal_preview_area,
};
pub(super) use skills::draw_skills_modal;
pub(crate) use skills::skills_modal_list_area;
pub(super) use subagents::draw_subagents_modal;
pub(crate) use subagents::subagents_modal_list_area;
pub(super) use system_prompt::draw_system_prompt_modal;
pub(crate) use system_prompt::system_prompt_text_area;
pub(super) use tools::draw_tools_modal;
pub(crate) use tools::tools_modal_list_area;
pub(super) use usage::draw_usage_modal;
pub(crate) use usage::usage_modal_text_area;

#[derive(Debug, Clone, Copy)]
pub(in crate::tui::render) struct ModalSpec {
    min_width: u16,
    min_height: u16,
    width_ratio: (u16, u16),
    height: ModalHeight,
}

#[derive(Debug, Clone, Copy)]
pub(in crate::tui::render) enum ModalHeight {
    Ratio(u16, u16),
    Fixed(u16),
}

pub(in crate::tui::render) const SYSTEM_PROMPT_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 5,
    width_ratio: (9, 10),
    height: ModalHeight::Ratio(4, 5),
};
pub(super) const SESSIONS_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 5,
    width_ratio: (9, 10),
    height: ModalHeight::Ratio(4, 5),
};
pub(in crate::tui::render) const PICKER_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 5,
    width_ratio: (4, 5),
    height: ModalHeight::Ratio(3, 5),
};
pub(super) const CONFIRMATION_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 5,
    width_ratio: (3, 5),
    height: ModalHeight::Fixed(7),
};
pub(super) const CUSTOM_PROVIDER_REPLACEMENT_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 5,
    width_ratio: (3, 5),
    height: ModalHeight::Fixed(8),
};
pub(super) const CUSTOM_PROVIDER_SETUP_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 7,
    width_ratio: (4, 5),
    height: ModalHeight::Fixed(14),
};
pub(super) const ACTIVE_LOGIN_MODAL: ModalSpec = ModalSpec {
    min_width: 12,
    min_height: 7,
    width_ratio: (9, 10),
    height: ModalHeight::Ratio(2, 3),
};

pub(in crate::tui::render) fn modal_rect(area: Rect, spec: ModalSpec) -> Option<Rect> {
    if area.width < spec.min_width || area.height < spec.min_height {
        return None;
    }
    let width = area
        .width
        .saturating_mul(spec.width_ratio.0)
        .saturating_div(spec.width_ratio.1)
        .clamp(spec.min_width, area.width);
    let height = match spec.height {
        ModalHeight::Ratio(numerator, denominator) => area
            .height
            .saturating_mul(numerator)
            .saturating_div(denominator)
            .clamp(spec.min_height, area.height),
        ModalHeight::Fixed(height) => height.min(area.height).max(spec.min_height),
    };
    Some(Rect::new(
        area.x + area.width.saturating_sub(width) / 2,
        area.y + area.height.saturating_sub(height) / 2,
        width,
        height,
    ))
}

pub(super) fn modal_text_area(area: Rect, title: &'static str, spec: ModalSpec) -> Option<Rect> {
    let modal = modal_rect(area, spec)?;
    let block = overlay_block(title);
    Some(overlay_text_area(block.inner(modal)))
}

pub(super) fn draw_modal_panel(
    frame: &mut Frame<'_>,
    area: Rect,
    title: &'static str,
    spec: ModalSpec,
) -> Option<Rect> {
    let modal = modal_rect(area, spec)?;
    frame.render_widget(Clear, modal);
    let block = overlay_block(title);
    let inner = block.inner(modal);
    frame.render_widget(block, modal);
    Some(render_overlay_fill(frame, inner))
}

pub(super) fn block_inner(area: Rect) -> Rect {
    Rect::new(
        area.x.saturating_add(1),
        area.y.saturating_add(1),
        area.width.saturating_sub(2),
        area.height.saturating_sub(2),
    )
}

pub(super) fn picker_modal_list_area(
    area: Rect,
    title: &'static str,
    header_rows: u16,
) -> Option<Rect> {
    let text_area = modal_text_area(area, title, PICKER_MODAL)?;
    if text_area.height == 0 {
        return None;
    }
    let list_y = text_area.y.saturating_add(header_rows);
    let list_height = text_area
        .y
        .saturating_add(text_area.height)
        .saturating_sub(list_y);
    (list_height > 0).then_some(Rect::new(text_area.x, list_y, text_area.width, list_height))
}