magi-code 0.64.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::super::*;
use super::{
    MissionControlApp, PendingSessionSwitch,
    actions::{ActionContext, AppControlFlow, TuiAction},
};

impl MissionControlApp {
    fn start_session_switch_worker(
        &mut self,
        session_id: String,
        ui_state: &mut state::MissionControlState,
    ) {
        if self.pending_session_switch.is_some() {
            ui_state.status = "session switch already loading".to_string();
            return;
        }
        if self.active_run || self.worker.is_some() {
            ui_state.status = "cannot switch sessions while a prompt is running".to_string();
            return;
        }
        if self.state.active_session_id() == Some(session_id.as_str()) {
            ui_state.close_session_picker();
            ui_state.status = format!("session unchanged: {}", short_session_id(&session_id));
            return;
        }
        self.next_session_switch_request_id = self.next_session_switch_request_id.saturating_add(1);
        let request_id = self.next_session_switch_request_id;
        self.pending_session_switch = Some(PendingSessionSwitch {
            request_id,
            session_id: session_id.clone(),
            generation: self.session_generation,
        });
        ui_state.status = "loading session…".to_string();
        let manager = self.state.session_manager.clone();
        let config = self.state.config.clone();
        let sender = self.events.clone();
        thread::spawn(move || {
            let result = start_session_switch_load(&manager, config.as_ref(), &session_id);
            let _ = send_tui_event(
                &sender,
                TuiEvent::SessionSwitchLoaded {
                    request_id,
                    session_id,
                    result: Box::new(result),
                },
            );
        });
    }

    pub(super) fn apply_input_action(
        &mut self,
        action: TuiAction,
        ui_state: &mut state::MissionControlState,
        context: &mut ActionContext<'_>,
    ) -> AppControlFlow {
        let TuiAction::Input(action) = action;
        match action {
            input::InputAction::Submit(prompt) => {
                self.submit(prompt, ui_state, context.receiver, context.last_area);
            }
            input::InputAction::SelectModel(model_id) => {
                self.select_model(&model_id, ui_state);
            }
            input::InputAction::SelectSession(session_id) => {
                self.start_session_switch_worker(session_id, ui_state);
            }
            input::InputAction::SetSkillEnabled { name, enabled } => {
                self.set_skill_enabled(&name, enabled, ui_state);
                refresh_skill_autocomplete_candidates(
                    context.autocomplete_candidates,
                    &self.skills,
                );
            }
            input::InputAction::ToggleSkillsSettingsScope => {
                self.toggle_skills_settings_scope(ui_state, context.last_area);
            }
            input::InputAction::SetMcpServerEnabled { name, enabled } => {
                self.set_mcp_server_enabled(&name, enabled, ui_state);
            }
            input::InputAction::SetToolEnabled { name, enabled } => {
                self.set_tool_enabled(&name, enabled, ui_state);
            }
            input::InputAction::ToggleToolsSettingsScope => {
                self.toggle_tools_settings_scope(ui_state, context.last_area);
            }
            input::InputAction::SetSubagentProfileEnabled { id, enabled } => {
                self.set_subagent_profile_enabled(&id, enabled, ui_state);
            }
            input::InputAction::ToggleSubagentsSettingsScope => {
                self.toggle_subagents_settings_scope(ui_state, context.last_area);
            }
            input::InputAction::SetModelEnabled { id, enabled } => {
                self.set_model_enabled(&id, enabled, ui_state);
            }
            input::InputAction::ToggleModelsSettingsScope => {
                self.toggle_models_settings_scope(ui_state, context.last_area);
            }
            input::InputAction::PrimaryAgentSelectionChanged(selected_id) => {
                self.persist_primary_agent_selection(selected_id.as_deref(), ui_state);
            }
            input::InputAction::ThinkingLevelSelectionChanged(level) => {
                self.persist_thinking_level(level, ui_state);
            }
            input::InputAction::SelectLoginProvider(provider_id) => {
                self.start_login(&provider_id, ui_state);
            }
            input::InputAction::SelectLogoutProvider(provider_id) => {
                self.logout_provider(&provider_id, ui_state);
            }
            input::InputAction::SubmitLoginFallback(input) => {
                self.submit_login_fallback(input, ui_state);
            }
            input::InputAction::SubmitCustomProviderSetupStep => {
                self.submit_custom_provider_setup_step(ui_state);
            }
            input::InputAction::CopyLoginUrl => {
                if let Some(url) = ui_state
                    .modals
                    .active_login
                    .as_ref()
                    .map(|login| login.url.clone())
                {
                    apply_copy_request(&url, ui_state, context.clipboard, Instant::now());
                }
            }
            input::InputAction::OpenLoginUrl => {
                ui_state.status = match ui_state
                    .modals
                    .active_login
                    .as_ref()
                    .map(|login| login.url.as_str())
                {
                    Some(url) if !url.is_empty() => match open_url_in_browser(url) {
                        Ok(()) => "opened OAuth URL in browser".to_string(),
                        Err(error) => {
                            format!("could not open browser: {error}; press [Ctrl-Y] to copy URL")
                        }
                    },
                    _ => "OAuth URL is not ready yet".to_string(),
                };
            }
            input::InputAction::CancelLogin => {
                if ui_state.modals.custom_provider_setup.is_some() {
                    ui_state.clear_custom_provider_setup();
                    ui_state.status =
                        "custom provider setup cancelled; settings unchanged".to_string();
                } else if let Some(status) = self.cancel_active_worker(ui_state) {
                    ui_state.status = status;
                    ui_state.clear_active_login();
                } else {
                    ui_state.status =
                        "OpenAI Codex login cancelled; credentials unchanged".to_string();
                    ui_state.clear_active_login();
                }
            }
            input::InputAction::ConfirmLogout(provider_id) => {
                self.remove_logout_provider(&provider_id, ui_state);
            }
            input::InputAction::CancelLogout => {
                ui_state.status = "logout cancelled; credentials unchanged".to_string();
            }
            input::InputAction::ConfirmCustomProviderReplacement => {
                self.confirm_custom_provider_replacement(ui_state);
            }
            input::InputAction::CancelCustomProviderReplacement => {
                ui_state.clear_custom_provider_setup();
                ui_state.status =
                    "custom provider replacement cancelled; existing provider unchanged"
                        .to_string();
            }
            input::InputAction::ConfirmRewind => {
                if let Some(plan) = ui_state.take_rewind_plan() {
                    let store = crate::checkpoints::CheckpointStore::from_paths(&self.config.paths);
                    let execution = store.execute_plan(&plan, &self.state.cwd);
                    let _ = record_session_event(
                        self.state.current_session.as_ref(),
                        &self.state.cwd,
                        SessionEventKind::Rewind,
                        crate::checkpoints::rewind_event_payload(&plan, &execution),
                    );
                    ui_state.status =
                        format!("rewind complete: {} operation(s)", execution.results.len());
                    ui_state.open_rewind_modal(
                        crate::shell::format_rewind_execution_for_tui(&execution),
                        None,
                        true,
                    );
                }
            }
            input::InputAction::CancelRewind => {
                ui_state.status = "rewind cancelled".to_string();
            }
            input::InputAction::CancelRunningPrompt => {
                self.cancel_running_prompt(ui_state);
            }
            input::InputAction::OpenSetModel => {
                if self.active_run || self.worker.is_some() {
                    ui_state.status = "cannot change model while a prompt is running".to_string();
                } else {
                    self.open_model_picker(ui_state, context.last_area);
                }
            }
            input::InputAction::Exit => {
                if let Some(status) = self.cancel_active_worker(ui_state) {
                    ui_state.status = status;
                }
                self.join_worker_with_timeout(ui_state, WORKER_EXIT_JOIN_TIMEOUT);
                return AppControlFlow::Exit;
            }
            input::InputAction::None => {}
        }
        AppControlFlow::Continue
    }
}