starweaver-cli 0.2.1

Command-line interface for Starweaver
Documentation
use super::{FooterMode, InteractiveTuiState, PendingSessionCommand};

impl InteractiveTuiState {
    pub(crate) fn open_session_picker(&mut self) {
        if self.running {
            self.push_transcript_notice(
                "[SYS] Session selection is available after the current run finishes.".to_string(),
            );
            self.input_status = Some("session blocked".to_string());
            return;
        }
        self.clear_composer_input();
        self.reset_composer_scroll();
        self.pending_attachments.clear();
        self.footer_mode = FooterMode::Context;
        if self.session_choices.is_empty() {
            self.session_picker_open = false;
            self.push_transcript_notice("[SYS] No sessions found.");
            self.input_status = Some("no sessions".to_string());
            return;
        }
        self.model_picker_open = false;
        self.session_picker_open = true;
        self.sync_session_picker_index_to_current();
        self.input_status = Some("session picker".to_string());
    }

    pub(in crate::tui) fn close_session_picker(&mut self) {
        self.session_picker_open = false;
        self.input_status = Some("session picker closed".to_string());
    }

    pub(in crate::tui) fn move_session_picker_selection(&mut self, delta: isize) {
        let len = self.session_choices.len();
        if len == 0 {
            self.session_picker_index = 0;
            return;
        }
        let current = self.session_picker_index.min(len.saturating_sub(1));
        let steps = delta.unsigned_abs() % len;
        self.session_picker_index = if delta.is_negative() {
            (current + len - steps) % len
        } else {
            (current + steps) % len
        };
        self.input_status = Some("session picker".to_string());
    }

    pub(in crate::tui) fn select_session_picker_choice(&mut self) {
        let Some(session_id) = self.selected_session_picker_id() else {
            self.close_session_picker();
            return;
        };
        self.pending_session_command = Some(session_id);
        self.session_picker_open = false;
        self.input_status = Some("session selected".to_string());
    }

    fn selected_session_picker_id(&self) -> Option<String> {
        self.session_choices
            .get(self.session_picker_index)
            .map(|choice| choice.session_id.clone())
    }

    pub(super) fn sync_session_picker_index_to_current(&mut self) {
        self.session_picker_index = self
            .session_id
            .as_deref()
            .and_then(|session_id| {
                self.session_choices
                    .iter()
                    .position(|choice| choice.session_id == session_id)
            })
            .unwrap_or(0)
            .min(self.session_choices.len().saturating_sub(1));
    }

    pub(in crate::tui) fn take_pending_session_command(&mut self) -> Option<PendingSessionCommand> {
        self.pending_session_command.take().map(|requested| {
            if requested.is_empty() {
                PendingSessionCommand::Current
            } else {
                PendingSessionCommand::Select(requested)
            }
        })
    }

    pub(in crate::tui) const fn model_picker_visible(&self) -> bool {
        self.model_picker_open
    }

    pub(in crate::tui) const fn model_picker_index(&self) -> usize {
        self.model_picker_index
    }

    pub(in crate::tui) fn open_model_picker(&mut self) {
        if self.running {
            self.push_transcript_notice(
                "[SYS] Model selection is available after the current run finishes.".to_string(),
            );
            self.input_status = Some("model blocked".to_string());
            return;
        }
        self.clear_composer_input();
        self.reset_composer_scroll();
        self.pending_attachments.clear();
        self.footer_mode = FooterMode::Context;
        self.session_picker_open = false;
        self.model_picker_open = true;
        self.sync_model_picker_index_to_current();
        self.input_status = Some("model picker".to_string());
    }

    pub(in crate::tui) fn close_model_picker(&mut self) {
        self.model_picker_open = false;
        self.input_status = Some("model picker closed".to_string());
    }

    pub(in crate::tui) fn move_model_picker_selection(&mut self, delta: isize) {
        let len = self.model_choices.len();
        if len == 0 {
            self.model_picker_index = 0;
            return;
        }
        let current = self.model_picker_index.min(len.saturating_sub(1));
        let steps = delta.unsigned_abs() % len;
        self.model_picker_index = if delta.is_negative() {
            (current + len - steps) % len
        } else {
            (current + steps) % len
        };
        self.input_status = Some("model picker".to_string());
    }

    pub(in crate::tui) fn select_model_picker_choice(&mut self) {
        let Some(choice) = self.model_choices.get(self.model_picker_index).cloned() else {
            self.close_model_picker();
            return;
        };
        self.apply_model_choice(&choice);
        self.model_picker_open = false;
        self.input_status = Some("model selected".to_string());
    }

    pub(super) fn sync_model_picker_index_to_current(&mut self) {
        self.model_picker_index = self
            .model_choices
            .iter()
            .position(|choice| choice.profile == self.profile)
            .unwrap_or(0)
            .min(self.model_choices.len().saturating_sub(1));
    }
}