magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SkillToggleRow {
    pub(crate) name: String,
    pub(crate) source: String,
    pub(crate) enabled: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct McpServerToggleRow {
    pub(crate) name: String,
    pub(crate) kind: &'static str,
    pub(crate) enabled: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ToolToggleRow {
    pub(crate) name: String,
    pub(crate) kind: String,
    pub(crate) enabled: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SubagentProfileToggleRow {
    pub(crate) id: String,
    pub(crate) description: String,
    pub(crate) enabled: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SkillsModalState {
    pub(crate) rows: Vec<SkillToggleRow>,
    pub(crate) selected: usize,
    pub(crate) settings_scope: SettingsScope,
    pub(crate) scope_label: String,
}

impl SkillsModalState {
    pub(in crate::tui::state) fn clamp(&mut self) {
        let item_count = self.rows.len();
        clamp_picker_selection(&mut self.selected, item_count);
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct McpModalState {
    pub(crate) rows: Vec<McpServerToggleRow>,
    pub(crate) selected: usize,
}

impl McpModalState {
    pub(in crate::tui::state) fn clamp(&mut self) {
        let item_count = self.rows.len();
        clamp_picker_selection(&mut self.selected, item_count);
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ToolsModalState {
    pub(crate) rows: Vec<ToolToggleRow>,
    pub(crate) selected: usize,
    pub(crate) settings_scope: SettingsScope,
    pub(crate) scope_label: String,
    pub(crate) code_mode_tools: bool,
}

impl ToolsModalState {
    pub(in crate::tui::state) fn clamp(&mut self) {
        let item_count = self.rows.len();
        clamp_picker_selection(&mut self.selected, item_count);
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SubagentsModalState {
    pub(crate) rows: Vec<SubagentProfileToggleRow>,
    pub(crate) selected: usize,
    pub(crate) settings_scope: SettingsScope,
    pub(crate) scope_label: String,
}

impl SubagentsModalState {
    pub(in crate::tui::state) fn clamp(&mut self) {
        let item_count = self.rows.len();
        clamp_picker_selection(&mut self.selected, item_count);
    }
}

impl MissionControlState {
    pub(crate) fn open_skills_modal_scoped(
        &mut self,
        rows: Vec<SkillToggleRow>,
        _visible: usize,
        scope: SettingsScope,
        label: String,
    ) {
        let mut m = SkillsModalState {
            rows,
            selected: 0,
            settings_scope: scope,
            scope_label: label,
        };
        m.clamp();
        self.modals.skills_modal = Some(m);
        self.set_scroll_offset(&self.scroll_views.skills_modal, 0);
        self.hide_autocomplete();
    }
    pub(crate) fn open_tools_modal_scoped(
        &mut self,
        rows: Vec<ToolToggleRow>,
        _visible: usize,
        scope: SettingsScope,
        label: String,
    ) {
        let mut m = ToolsModalState {
            rows,
            selected: 0,
            settings_scope: scope,
            scope_label: label,
            code_mode_tools: false,
        };
        m.clamp();
        self.modals.tools_modal = Some(m);
        self.set_scroll_offset(&self.scroll_views.tools_modal, 0);
        self.hide_autocomplete();
    }
    pub(crate) fn open_subagents_modal_scoped(
        &mut self,
        rows: Vec<SubagentProfileToggleRow>,
        _visible: usize,
        scope: SettingsScope,
        label: String,
    ) {
        let mut m = SubagentsModalState {
            rows,
            selected: 0,
            settings_scope: scope,
            scope_label: label,
        };
        m.clamp();
        self.modals.subagents_modal = Some(m);
        self.set_scroll_offset(&self.scroll_views.subagents_modal, 0);
        self.hide_autocomplete();
    }
    pub(crate) fn skills_modal_scope(&self) -> Option<SettingsScope> {
        self.modals
            .skills_modal
            .as_ref()
            .map(|modal| modal.settings_scope)
    }
    pub(crate) fn set_skills_modal_scope_rows(
        &mut self,
        scope: SettingsScope,
        label: String,
        rows: Vec<SkillToggleRow>,
        _visible: usize,
    ) {
        if let Some(m) = &mut self.modals.skills_modal {
            m.settings_scope = scope;
            m.scope_label = label;
            m.rows = rows;
            m.clamp();
        }
        self.set_scroll_offset(&self.scroll_views.skills_modal, 0);
    }
    pub(crate) fn close_skills_modal(&mut self) -> bool {
        self.modals.skills_modal.take().is_some()
    }
    pub(crate) fn skills_modal_up(&mut self, visible_rows: usize) -> bool {
        self.move_skill_selection(false, visible_rows)
    }
    pub(crate) fn skills_modal_down(&mut self, visible_rows: usize) -> bool {
        self.move_skill_selection(true, visible_rows)
    }
    pub(super) fn move_skill_selection(&mut self, down: bool, visible_rows: usize) -> bool {
        let before_offset = self.scroll_offset(&self.scroll_views.skills_modal);
        let (before_selected, selected, offset) = {
            let Some(modal) = self.modals.skills_modal.as_mut() else {
                return false;
            };
            let before_selected = modal.selected;
            let last = modal.rows.len().saturating_sub(1);
            modal.selected = if down {
                modal.selected.saturating_add(1).min(last)
            } else {
                modal.selected.saturating_sub(1)
            };
            let (selected_visual_row, document_rows) =
                skill_row_visual_position(&modal.rows, modal.selected);
            let visible_rows = visible_rows.max(1);
            let mut offset = before_offset.min(document_rows.saturating_sub(visible_rows));
            if modal.selected == 0 {
                offset = 0;
            }
            if selected_visual_row < offset {
                offset = selected_visual_row;
            } else if selected_visual_row >= offset.saturating_add(visible_rows) {
                offset = selected_visual_row
                    .saturating_add(1)
                    .saturating_sub(visible_rows);
            }
            (before_selected, modal.selected, offset)
        };
        self.set_scroll_offset(&self.scroll_views.skills_modal, offset);
        before_selected != selected
            || before_offset != self.scroll_offset(&self.scroll_views.skills_modal)
    }
    pub(crate) fn set_skill_row_enabled(&mut self, name: &str, enabled: bool) {
        if let Some(m) = &mut self.modals.skills_modal
            && let Some(r) = m.rows.iter_mut().find(|r| r.name == name)
        {
            r.enabled = enabled;
        }
    }

    pub(crate) fn open_mcp_modal(&mut self, rows: Vec<McpServerToggleRow>, _visible: usize) {
        let mut m = McpModalState { rows, selected: 0 };
        m.clamp();
        self.modals.mcp_modal = Some(m);
        self.set_scroll_offset(&self.scroll_views.mcp_modal, 0);
        self.hide_autocomplete();
    }
    pub(crate) fn close_mcp_modal(&mut self) -> bool {
        self.modals.mcp_modal.take().is_some()
    }
    pub(crate) fn mcp_modal_up(&mut self, visible_rows: usize) -> bool {
        self.move_mcp_selection(false, visible_rows)
    }
    pub(crate) fn mcp_modal_down(&mut self, visible_rows: usize) -> bool {
        self.move_mcp_selection(true, visible_rows)
    }
    pub(super) fn move_mcp_selection(&mut self, down: bool, visible_rows: usize) -> bool {
        let before_offset = self.scroll_offset(&self.scroll_views.mcp_modal);
        let (before_selected, selected, offset) = {
            let Some(modal) = self.modals.mcp_modal.as_mut() else {
                return false;
            };
            let before_selected = modal.selected;
            let (selected, offset) = picker_selection_after_move(
                modal.selected,
                modal.rows.len(),
                1,
                down,
                visible_rows,
                before_offset,
            );
            modal.selected = selected;
            (before_selected, selected, offset)
        };
        self.set_scroll_offset(&self.scroll_views.mcp_modal, offset);
        before_selected != selected
            || before_offset != self.scroll_offset(&self.scroll_views.mcp_modal)
    }
    pub(crate) fn set_mcp_server_row_enabled(&mut self, name: &str, enabled: bool) {
        if let Some(m) = &mut self.modals.mcp_modal
            && let Some(r) = m.rows.iter_mut().find(|r| r.name == name)
        {
            r.enabled = enabled;
        }
    }

    pub(crate) fn tools_modal_scope(&self) -> Option<SettingsScope> {
        self.modals.tools_modal.as_ref().map(|m| m.settings_scope)
    }
    pub(crate) fn set_tools_modal_scope_rows(
        &mut self,
        scope: SettingsScope,
        label: String,
        rows: Vec<ToolToggleRow>,
        _visible: usize,
    ) {
        if let Some(m) = &mut self.modals.tools_modal {
            m.settings_scope = scope;
            m.scope_label = label;
            m.rows = rows;
            m.clamp();
        }
        self.set_scroll_offset(&self.scroll_views.tools_modal, 0);
    }
    pub(crate) fn close_tools_modal(&mut self) -> bool {
        self.modals.tools_modal.take().is_some()
    }
    pub(crate) fn tools_modal_up(&mut self, visible_rows: usize) -> bool {
        self.move_tools_selection(false, visible_rows)
    }
    pub(crate) fn tools_modal_down(&mut self, visible_rows: usize) -> bool {
        self.move_tools_selection(true, visible_rows)
    }
    pub(super) fn move_tools_selection(&mut self, down: bool, visible_rows: usize) -> bool {
        let before_offset = self.scroll_offset(&self.scroll_views.tools_modal);
        let (before_selected, selected, offset) = {
            let Some(modal) = self.modals.tools_modal.as_mut() else {
                return false;
            };
            let before_selected = modal.selected;
            let (selected, offset) = picker_selection_after_move(
                modal.selected,
                modal.rows.len(),
                1,
                down,
                visible_rows,
                before_offset,
            );
            modal.selected = selected;
            (before_selected, selected, offset)
        };
        self.set_scroll_offset(&self.scroll_views.tools_modal, offset);
        before_selected != selected
            || before_offset != self.scroll_offset(&self.scroll_views.tools_modal)
    }
    pub(crate) fn set_tool_row_enabled(&mut self, name: &str, enabled: bool) {
        if let Some(m) = &mut self.modals.tools_modal
            && let Some(r) = m.rows.iter_mut().find(|r| r.name == name)
        {
            r.enabled = enabled;
        }
    }

    pub(crate) fn subagents_modal_scope(&self) -> Option<SettingsScope> {
        self.modals
            .subagents_modal
            .as_ref()
            .map(|m| m.settings_scope)
    }
    pub(crate) fn set_subagents_modal_scope_rows(
        &mut self,
        scope: SettingsScope,
        label: String,
        rows: Vec<SubagentProfileToggleRow>,
        _visible: usize,
    ) {
        if let Some(m) = &mut self.modals.subagents_modal {
            m.settings_scope = scope;
            m.scope_label = label;
            m.rows = rows;
            m.clamp();
        }
        self.set_scroll_offset(&self.scroll_views.subagents_modal, 0);
    }
    pub(crate) fn close_subagents_modal(&mut self) -> bool {
        self.modals.subagents_modal.take().is_some()
    }
    pub(crate) fn subagents_modal_up(&mut self, visible_rows: usize) -> bool {
        self.move_subagents_selection(false, visible_rows)
    }
    pub(crate) fn subagents_modal_down(&mut self, visible_rows: usize) -> bool {
        self.move_subagents_selection(true, visible_rows)
    }
    pub(super) fn move_subagents_selection(&mut self, down: bool, visible_rows: usize) -> bool {
        let before_offset = self.scroll_offset(&self.scroll_views.subagents_modal);
        let (before_selected, selected, offset) = {
            let Some(modal) = self.modals.subagents_modal.as_mut() else {
                return false;
            };
            let before_selected = modal.selected;
            let (selected, offset) = picker_selection_after_move(
                modal.selected,
                modal.rows.len(),
                1,
                down,
                visible_rows,
                before_offset,
            );
            modal.selected = selected;
            (before_selected, selected, offset)
        };
        self.set_scroll_offset(&self.scroll_views.subagents_modal, offset);
        before_selected != selected
            || before_offset != self.scroll_offset(&self.scroll_views.subagents_modal)
    }
    pub(crate) fn set_subagent_profile_row_enabled(&mut self, id: &str, enabled: bool) {
        if let Some(m) = &mut self.modals.subagents_modal
            && let Some(r) = m.rows.iter_mut().find(|r| r.id == id)
        {
            r.enabled = enabled;
        }
    }
}