feat: remove ModePicker overlay and related commands; update mode handling and documentation
This commit is contained in:
@@ -30,7 +30,6 @@ pub enum Action {
|
|||||||
OpenOverlay(Overlay),
|
OpenOverlay(Overlay),
|
||||||
CloseOverlay,
|
CloseOverlay,
|
||||||
ToggleYoloArm,
|
ToggleYoloArm,
|
||||||
CycleAgentMode,
|
|
||||||
SystemNote {
|
SystemNote {
|
||||||
kind: String,
|
kind: String,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -242,13 +241,6 @@ pub fn apply_action(state: &mut AppStateRest, action: Action) {
|
|||||||
state.misc.yolo_armed = !state.misc.yolo_armed;
|
state.misc.yolo_armed = !state.misc.yolo_armed;
|
||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
}
|
}
|
||||||
Action::CycleAgentMode => {
|
|
||||||
let next = state.mode.cycle();
|
|
||||||
state.set_mode(next);
|
|
||||||
let toast = Toast::new(ToastKind::Info, format!("Mode: {}", state.mode.name()));
|
|
||||||
state.push_toast(toast);
|
|
||||||
state.dirty = true;
|
|
||||||
}
|
|
||||||
Action::SystemNote { kind: _kind, message } => {
|
Action::SystemNote { kind: _kind, message } => {
|
||||||
let toast = crate::app::state::types::Toast::new(
|
let toast = crate::app::state::types::Toast::new(
|
||||||
crate::app::state::types::ToastKind::Info,
|
crate::app::state::types::ToastKind::Info,
|
||||||
|
|||||||
@@ -75,14 +75,6 @@ const COMMANDS: &[&str] = &[
|
|||||||
"/lesson import",
|
"/lesson import",
|
||||||
"/lesson accept",
|
"/lesson accept",
|
||||||
"/lesson reject",
|
"/lesson reject",
|
||||||
"/mode chat",
|
|
||||||
"/mode bash",
|
|
||||||
"/mode help",
|
|
||||||
"/mode settings",
|
|
||||||
"/mode effort",
|
|
||||||
"/mode mcp",
|
|
||||||
"/mode rewind",
|
|
||||||
"/mode editor",
|
|
||||||
"/login",
|
"/login",
|
||||||
"/login zen",
|
"/login zen",
|
||||||
"/login openai",
|
"/login openai",
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl AppStateRest {
|
|||||||
.map(|n| n.to_string_lossy().to_string())
|
.map(|n| n.to_string_lossy().to_string())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
AppStateRest {
|
AppStateRest {
|
||||||
mode: AgentMode::Normal,
|
mode: AgentMode::Auto,
|
||||||
settings,
|
settings,
|
||||||
app_config,
|
app_config,
|
||||||
workspace_roots,
|
workspace_roots,
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ pub enum Overlay {
|
|||||||
Loading,
|
Loading,
|
||||||
ModelSelector,
|
ModelSelector,
|
||||||
ClearConfirm,
|
ClearConfirm,
|
||||||
ModePicker,
|
|
||||||
LoginPicker,
|
LoginPicker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub fn parse_command(text: &str) -> Command {
|
|||||||
"/quit" => Command::Quit,
|
"/quit" => Command::Quit,
|
||||||
"/clear" if arg1.is_empty() => Command::ClearConfirm,
|
"/clear" if arg1.is_empty() => Command::ClearConfirm,
|
||||||
"/clear" => Command::Clear,
|
"/clear" => Command::Clear,
|
||||||
"/mode" if arg1.is_empty() => Command::Help, // open help to show available modes
|
"/mode" if arg1.is_empty() => Command::Unknown("/mode requires a subcommand: chat, bash, help, settings".to_string()),
|
||||||
"/mode" => {
|
"/mode" => {
|
||||||
let mode = match arg1 {
|
let mode = match arg1 {
|
||||||
"chat" | "c" => ModeKind::Chat,
|
"chat" | "c" => ModeKind::Chat,
|
||||||
|
|||||||
@@ -124,10 +124,6 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
state.misc.selected_index = if state.misc.selected_index == 0 { n.saturating_sub(1) } else { state.misc.selected_index - 1 };
|
state.misc.selected_index = if state.misc.selected_index == 0 { n.saturating_sub(1) } else { state.misc.selected_index - 1 };
|
||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else if state.misc.overlay == Overlay::ModePicker {
|
|
||||||
state.misc.selected_index = if state.misc.selected_index == 0 { 3 } else { state.misc.selected_index - 1 };
|
|
||||||
state.dirty = true;
|
|
||||||
Vec::new()
|
|
||||||
} else if state.misc.overlay == Overlay::LoginPicker {
|
} else if state.misc.overlay == Overlay::LoginPicker {
|
||||||
let n = crate::app::mode::onboard_provider::PROVIDERS.len();
|
let n = crate::app::mode::onboard_provider::PROVIDERS.len();
|
||||||
state.misc.selected_index = if state.misc.selected_index == 0 { n.saturating_sub(1) } else { state.misc.selected_index - 1 };
|
state.misc.selected_index = if state.misc.selected_index == 0 { n.saturating_sub(1) } else { state.misc.selected_index - 1 };
|
||||||
@@ -162,10 +158,6 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
state.misc.selected_index = if n == 0 { 0 } else { (state.misc.selected_index + 1) % n };
|
state.misc.selected_index = if n == 0 { 0 } else { (state.misc.selected_index + 1) % n };
|
||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else if state.misc.overlay == Overlay::ModePicker {
|
|
||||||
state.misc.selected_index = (state.misc.selected_index + 1) % 4;
|
|
||||||
state.dirty = true;
|
|
||||||
Vec::new()
|
|
||||||
} else if state.misc.overlay == Overlay::LoginPicker {
|
} else if state.misc.overlay == Overlay::LoginPicker {
|
||||||
let n = crate::app::mode::onboard_provider::PROVIDERS.len();
|
let n = crate::app::mode::onboard_provider::PROVIDERS.len();
|
||||||
state.misc.selected_index = (state.misc.selected_index + 1) % n;
|
state.misc.selected_index = (state.misc.selected_index + 1) % n;
|
||||||
@@ -181,33 +173,6 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
KeyCode::PageDown => {
|
KeyCode::PageDown => {
|
||||||
vec![Action::ScrollDown]
|
vec![Action::ScrollDown]
|
||||||
}
|
}
|
||||||
KeyCode::Char('h') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Help)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('p') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Settings)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('a') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::ToggleYoloArm]
|
|
||||||
}
|
|
||||||
KeyCode::Char('m') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::CycleAgentMode]
|
|
||||||
}
|
|
||||||
KeyCode::Char('b') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Bash)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('S') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Security)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('t') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Todo)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('w') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Workflow)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('k') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::KeyInput)]
|
|
||||||
}
|
|
||||||
KeyCode::Esc => {
|
KeyCode::Esc => {
|
||||||
if state.input.autocomplete_visible {
|
if state.input.autocomplete_visible {
|
||||||
state.input.close_autocomplete();
|
state.input.close_autocomplete();
|
||||||
@@ -230,12 +195,6 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
}
|
}
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
KeyCode::Char('l') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Learning)]
|
|
||||||
}
|
|
||||||
KeyCode::Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
vec![Action::OpenOverlay(Overlay::Usage)]
|
|
||||||
}
|
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
if state.input.autocomplete_visible {
|
if state.input.autocomplete_visible {
|
||||||
state.input.close_autocomplete();
|
state.input.close_autocomplete();
|
||||||
@@ -341,17 +300,6 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
Overlay::ModePicker => {
|
|
||||||
let mode = state.mode.cycle();
|
|
||||||
state.mode = mode;
|
|
||||||
state.push_toast(crate::app::state::types::Toast::new(
|
|
||||||
crate::app::state::types::ToastKind::Info,
|
|
||||||
format!("Mode: {}", mode.name()),
|
|
||||||
));
|
|
||||||
state.misc.overlay = Overlay::None;
|
|
||||||
state.dirty = true;
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
Overlay::LoginPicker => {
|
Overlay::LoginPicker => {
|
||||||
state.misc.overlay = Overlay::OnboardProvider;
|
state.misc.overlay = Overlay::OnboardProvider;
|
||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
|
|||||||
@@ -232,7 +232,6 @@ fn apply_client_update(
|
|||||||
Some("Loading") => Overlay::Loading,
|
Some("Loading") => Overlay::Loading,
|
||||||
Some("ModelSelector") => Overlay::ModelSelector,
|
Some("ModelSelector") => Overlay::ModelSelector,
|
||||||
Some("ClearConfirm") => Overlay::ClearConfirm,
|
Some("ClearConfirm") => Overlay::ClearConfirm,
|
||||||
Some("ModePicker") => Overlay::ModePicker,
|
|
||||||
Some("LoginPicker") => Overlay::LoginPicker,
|
Some("LoginPicker") => Overlay::LoginPicker,
|
||||||
_ => Overlay::None,
|
_ => Overlay::None,
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ Modes:
|
|||||||
Input:
|
Input:
|
||||||
/help Show help
|
/help Show help
|
||||||
/clear Clear screen
|
/clear Clear screen
|
||||||
/mode Show current mode
|
/model Select AI model provider
|
||||||
/exit Exit application
|
/exit Exit application
|
||||||
/settings Open settings
|
/settings Open settings
|
||||||
/session Session management
|
/session Session management
|
||||||
|
|||||||
@@ -608,43 +608,6 @@ fn render_overlay(frame: &mut Frame, area: Rect, overlay: crate::app::state::typ
|
|||||||
let paragraph = Paragraph::new(lines).block(block);
|
let paragraph = Paragraph::new(lines).block(block);
|
||||||
frame.render_widget(paragraph, overlay_area);
|
frame.render_widget(paragraph, overlay_area);
|
||||||
}
|
}
|
||||||
crate::app::state::types::Overlay::ModePicker => {
|
|
||||||
let block = block.title(" Mode ");
|
|
||||||
let modes = [
|
|
||||||
("Auto", "auto-approve tools"),
|
|
||||||
("Normal", "ask before risky tools"),
|
|
||||||
("Plan", "plan-only mode"),
|
|
||||||
("Yolo", "full auto"),
|
|
||||||
];
|
|
||||||
let current = state.mode.name();
|
|
||||||
let mut lines: Vec<Line> = vec![
|
|
||||||
Line::from(Span::styled(
|
|
||||||
format!("Current: {}", current),
|
|
||||||
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
|
||||||
)),
|
|
||||||
Line::from(Span::styled("", Style::default())),
|
|
||||||
];
|
|
||||||
for (i, (name, desc)) in modes.iter().enumerate() {
|
|
||||||
let is_selected = i == state.misc.selected_index;
|
|
||||||
let prefix = if is_selected { "▸ " } else { " " };
|
|
||||||
let label = format!("{}{} ({})", prefix, name, desc);
|
|
||||||
let style = if *name == current {
|
|
||||||
Style::default().fg(Theme::HIGHLIGHT).add_modifier(Modifier::BOLD).add_modifier(Modifier::ITALIC)
|
|
||||||
} else if is_selected {
|
|
||||||
Style::default().fg(Theme::BG).bg(Theme::HIGHLIGHT)
|
|
||||||
} else {
|
|
||||||
Style::default().fg(Theme::TEXT)
|
|
||||||
};
|
|
||||||
lines.push(Line::from(Span::styled(label, style)));
|
|
||||||
}
|
|
||||||
lines.push(Line::from(Span::styled("", Style::default())));
|
|
||||||
lines.push(Line::from(Span::styled(
|
|
||||||
"↑↓ navigate · Enter select · Esc close",
|
|
||||||
Style::default().fg(Theme::DIM),
|
|
||||||
)));
|
|
||||||
let paragraph = Paragraph::new(lines).block(block);
|
|
||||||
frame.render_widget(paragraph, overlay_area);
|
|
||||||
}
|
|
||||||
crate::app::state::types::Overlay::LoginPicker => {
|
crate::app::state::types::Overlay::LoginPicker => {
|
||||||
let block = block.title(" Login ");
|
let block = block.title(" Login ");
|
||||||
let providers = crate::app::mode::onboard_provider::PROVIDERS;
|
let providers = crate::app::mode::onboard_provider::PROVIDERS;
|
||||||
|
|||||||
Reference in New Issue
Block a user