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;
}
}
}