varios improvements to /switch, /model, and /tools
This commit is contained in:
parent
54a456581d
commit
e1dd961f3f
361
src/cli.rs
361
src/cli.rs
|
|
@ -3,9 +3,9 @@ use anyhow::Result;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
create_client, get_provider_for_model, provider::get_all_models, provider::get_supported_models,
|
create_client, get_provider_for_model, provider::get_all_models, provider::get_supported_models,
|
||||||
provider::is_model_supported, ChatClient, Session,
|
ChatClient, Session,
|
||||||
};
|
};
|
||||||
use crate::utils::{Display, InputHandler};
|
use crate::utils::{Display, InputHandler, SessionAction};
|
||||||
|
|
||||||
pub struct ChatCLI {
|
pub struct ChatCLI {
|
||||||
session: Session,
|
session: Session,
|
||||||
|
|
@ -134,7 +134,7 @@ impl ChatCLI {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
"/model" => {
|
"/model" => {
|
||||||
self.handle_model_command(&parts).await?;
|
self.model_switcher().await?;
|
||||||
}
|
}
|
||||||
"/models" => {
|
"/models" => {
|
||||||
self.list_models();
|
self.list_models();
|
||||||
|
|
@ -145,22 +145,16 @@ impl ChatCLI {
|
||||||
"/new" => {
|
"/new" => {
|
||||||
self.handle_new_session(&parts)?;
|
self.handle_new_session(&parts)?;
|
||||||
}
|
}
|
||||||
"/switch" => {
|
"/switch" | "/sessions" => {
|
||||||
self.handle_switch_session(&parts).await?;
|
self.session_manager().await?;
|
||||||
}
|
}
|
||||||
"/clear" => {
|
"/clear" => {
|
||||||
self.session.clear_messages();
|
self.session.clear_messages();
|
||||||
self.session.save()?;
|
self.session.save()?;
|
||||||
self.display.print_command_result("Conversation cleared");
|
self.display.print_command_result("Conversation cleared");
|
||||||
}
|
}
|
||||||
"/delete" => {
|
"/tools" => {
|
||||||
self.handle_delete_session(&parts).await?;
|
self.tools_manager().await?;
|
||||||
}
|
|
||||||
"/tool" => {
|
|
||||||
self.handle_tool_command(&parts)?;
|
|
||||||
}
|
|
||||||
"/reasoning" => {
|
|
||||||
self.handle_reasoning_command(&parts)?;
|
|
||||||
}
|
}
|
||||||
"/effort" => {
|
"/effort" => {
|
||||||
self.handle_effort_command(&parts)?;
|
self.handle_effort_command(&parts)?;
|
||||||
|
|
@ -182,42 +176,35 @@ impl ChatCLI {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_model_command(&mut self, parts: &[&str]) -> Result<()> {
|
async fn model_switcher(&mut self) -> Result<()> {
|
||||||
if parts.len() == 1 {
|
let all_models = get_all_models();
|
||||||
let all_models = get_all_models();
|
let selection = self.input.select_from_list(
|
||||||
let selection = self.input.select_from_list(
|
"Select a model:",
|
||||||
"Select a model:",
|
&all_models,
|
||||||
&all_models,
|
Some(&self.session.model),
|
||||||
Some(&self.session.model),
|
)?;
|
||||||
)?;
|
|
||||||
|
match selection {
|
||||||
if let Some(model) = selection {
|
Some(model) => {
|
||||||
self.session.model = model.to_string();
|
if model.to_string() == self.session.model {
|
||||||
let provider = get_provider_for_model(&self.session.model);
|
self.display.print_info("Already using that model");
|
||||||
self.display.print_command_result(&format!(
|
} else {
|
||||||
"Model switched to {} ({})",
|
self.session.model = model.to_string();
|
||||||
self.session.model,
|
let provider = get_provider_for_model(&self.session.model);
|
||||||
provider.as_str()
|
self.display.print_command_result(&format!(
|
||||||
));
|
"Model switched to {} ({})",
|
||||||
self.client = None; // Force client recreation
|
self.session.model,
|
||||||
|
provider.as_str()
|
||||||
|
));
|
||||||
|
self.client = None; // Force client recreation
|
||||||
|
self.session.save()?; // Save the model change
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if parts.len() == 2 {
|
None => {
|
||||||
let model = parts[1];
|
self.display.print_info("Model selection cancelled");
|
||||||
if !is_model_supported(model) {
|
|
||||||
self.display.print_error("Unsupported model. Use /models to see the list of supported models.");
|
|
||||||
} else {
|
|
||||||
self.session.model = model.to_string();
|
|
||||||
let provider = get_provider_for_model(&self.session.model);
|
|
||||||
self.display.print_command_result(&format!(
|
|
||||||
"Model switched to {} ({})",
|
|
||||||
self.session.model,
|
|
||||||
provider.as_str()
|
|
||||||
));
|
|
||||||
self.client = None; // Force client recreation
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.display.print_error("Usage: /model [model_name]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,150 +264,188 @@ impl ChatCLI {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_switch_session(&mut self, parts: &[&str]) -> Result<()> {
|
async fn session_manager(&mut self) -> Result<()> {
|
||||||
if parts.len() == 1 {
|
loop {
|
||||||
let sessions = Session::list_sessions()?;
|
let sessions = Session::list_sessions()?;
|
||||||
let session_names: Vec<String> = sessions
|
let session_names: Vec<String> = sessions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, _)| name)
|
.map(|(name, _)| name)
|
||||||
.filter(|name| name != &self.session.name)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(selection) = self.input.select_from_list(
|
if session_names.is_empty() {
|
||||||
"Switch to session:",
|
self.display.print_info("No sessions available");
|
||||||
&session_names,
|
|
||||||
None,
|
|
||||||
)? {
|
|
||||||
self.session.save()?;
|
|
||||||
match Session::load(&selection) {
|
|
||||||
Ok(session) => {
|
|
||||||
self.session = session;
|
|
||||||
self.display.print_command_result(&format!(
|
|
||||||
"Switched to session '{}' (model={})",
|
|
||||||
self.session.name, self.session.model
|
|
||||||
));
|
|
||||||
self.client = None; // Force client recreation
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.display.print_error(&format!("Failed to load session: {}", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if parts.len() == 2 {
|
|
||||||
let session_name = parts[1];
|
|
||||||
self.session.save()?;
|
|
||||||
match Session::load(session_name) {
|
|
||||||
Ok(session) => {
|
|
||||||
self.session = session;
|
|
||||||
self.display.print_command_result(&format!(
|
|
||||||
"Switched to session '{}' (model={})",
|
|
||||||
self.session.name, self.session.model
|
|
||||||
));
|
|
||||||
self.client = None; // Force client recreation
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.display.print_error(&format!("Failed to load session: {}", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.display.print_error("Usage: /switch [session_name]");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_delete_session(&mut self, parts: &[&str]) -> Result<()> {
|
|
||||||
let target = if parts.len() == 1 {
|
|
||||||
let sessions = Session::list_sessions()?;
|
|
||||||
let session_names: Vec<String> = sessions
|
|
||||||
.into_iter()
|
|
||||||
.map(|(name, _)| name)
|
|
||||||
.filter(|name| name != &self.session.name)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.input.select_from_list("Delete session:", &session_names, None)?
|
|
||||||
} else if parts.len() == 2 {
|
|
||||||
Some(parts[1].to_string())
|
|
||||||
} else {
|
|
||||||
self.display.print_error("Usage: /delete [session_name]");
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(target) = target {
|
|
||||||
if target == self.session.name {
|
|
||||||
self.display.print_error(
|
|
||||||
"Cannot delete the session you are currently using. Switch to another session first."
|
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.input.confirm(&format!("Delete session '{}'?", target))? {
|
let action = self.input.session_manager(
|
||||||
match Session::delete_session(&target) {
|
"Session Manager:",
|
||||||
Ok(()) => {
|
&session_names,
|
||||||
self.display.print_command_result(&format!("Session '{}' deleted", target));
|
Some(&self.session.name),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match action {
|
||||||
|
SessionAction::Switch(session_name) => {
|
||||||
|
if session_name == self.session.name {
|
||||||
|
self.display.print_info("Already in that session");
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
self.display.print_error(&format!("Failed to delete session: {}", e));
|
self.session.save()?;
|
||||||
|
match Session::load(&session_name) {
|
||||||
|
Ok(session) => {
|
||||||
|
self.session = session;
|
||||||
|
self.display.print_command_result(&format!(
|
||||||
|
"Switched to session '{}' (model={})",
|
||||||
|
self.session.name, self.session.model
|
||||||
|
));
|
||||||
|
self.client = None; // Force client recreation
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.display.print_error(&format!("Failed to load session: {}", e));
|
||||||
|
// Don't return, allow user to try again or cancel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
SessionAction::Delete(session_name) => {
|
||||||
}
|
match Session::delete_session(&session_name) {
|
||||||
|
Ok(()) => {
|
||||||
Ok(())
|
self.display.print_command_result(&format!("Session '{}' deleted", session_name));
|
||||||
}
|
|
||||||
|
// If we deleted the current session, we need to handle this specially
|
||||||
fn handle_tool_command(&mut self, parts: &[&str]) -> Result<()> {
|
if session_name == self.session.name {
|
||||||
if parts.len() != 3 || parts[1].to_lowercase() != "websearch" || !["on", "off"].contains(&parts[2]) {
|
// Try to switch to another session or create a default one
|
||||||
self.display.print_error("Usage: /tool websearch on|off");
|
let remaining_sessions = Session::list_sessions()?;
|
||||||
return Ok(());
|
let remaining_names: Vec<String> = remaining_sessions
|
||||||
}
|
.into_iter()
|
||||||
|
.map(|(name, _)| name)
|
||||||
let enable = parts[2] == "on";
|
.collect();
|
||||||
|
|
||||||
if enable {
|
if remaining_names.is_empty() {
|
||||||
let model = self.session.model.clone();
|
// No sessions left, create a default one
|
||||||
if let Ok(client) = self.get_client() {
|
self.session = Session::new("default".to_string(), self.session.model.clone());
|
||||||
if !client.supports_feature_for_model("web_search", &model) {
|
self.display.print_command_result("Created new default session");
|
||||||
let provider = get_provider_for_model(&model);
|
return Ok(());
|
||||||
self.display.print_warning(&format!(
|
} else {
|
||||||
"Web search is not supported by {} models",
|
// Switch to the first available session
|
||||||
provider.as_str()
|
match Session::load(&remaining_names[0]) {
|
||||||
));
|
Ok(session) => {
|
||||||
|
self.session = session;
|
||||||
|
self.display.print_command_result(&format!(
|
||||||
|
"Switched to session '{}' (model={})",
|
||||||
|
self.session.name, self.session.model
|
||||||
|
));
|
||||||
|
self.client = None;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.display.print_error(&format!("Failed to load fallback session: {}", e));
|
||||||
|
// Create a new default session as fallback
|
||||||
|
self.session = Session::new("default".to_string(), self.session.model.clone());
|
||||||
|
self.display.print_command_result("Created new default session");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Continue to show updated session list if we didn't delete current session
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.display.print_error(&format!("Failed to delete session: {}", e));
|
||||||
|
// Continue to allow retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SessionAction::Cancel => {
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.session.enable_web_search = enable;
|
|
||||||
let state = if enable { "enabled" } else { "disabled" };
|
|
||||||
self.display.print_command_result(&format!("Web search tool {}", state));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_reasoning_command(&mut self, parts: &[&str]) -> Result<()> {
|
|
||||||
if parts.len() != 2 || !["on", "off"].contains(&parts[1]) {
|
|
||||||
self.display.print_error("Usage: /reasoning on|off");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let enable = parts[1] == "on";
|
async fn tools_manager(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
if enable {
|
// Show current tool status
|
||||||
|
self.display.print_info("Tool Management:");
|
||||||
|
|
||||||
|
let web_status = if self.session.enable_web_search { "✓ enabled" } else { "✗ disabled" };
|
||||||
|
let reasoning_status = if self.session.enable_reasoning_summary { "✓ enabled" } else { "✗ disabled" };
|
||||||
|
|
||||||
|
println!(" Web Search: {}", web_status);
|
||||||
|
println!(" Reasoning Summaries: {}", reasoning_status);
|
||||||
|
println!(" Reasoning Effort: {}", self.session.reasoning_effort);
|
||||||
|
|
||||||
|
// Check model compatibility
|
||||||
let model = self.session.model.clone();
|
let model = self.session.model.clone();
|
||||||
if let Ok(client) = self.get_client() {
|
let provider = get_provider_for_model(&model);
|
||||||
if !client.supports_feature_for_model("reasoning_summary", &model) {
|
let web_enabled = self.session.enable_web_search;
|
||||||
let provider = get_provider_for_model(&model);
|
let reasoning_enabled = self.session.enable_reasoning_summary;
|
||||||
self.display.print_warning(&format!(
|
|
||||||
"Reasoning summaries are not supported by {} models",
|
// Show compatibility warnings based on provider
|
||||||
provider.as_str()
|
match provider {
|
||||||
));
|
crate::core::provider::Provider::Anthropic => {
|
||||||
|
if web_enabled {
|
||||||
|
self.display.print_warning("Web search is not supported by Anthropic models");
|
||||||
|
}
|
||||||
|
if reasoning_enabled {
|
||||||
|
self.display.print_warning("Reasoning summaries are not supported by Anthropic models");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::core::provider::Provider::OpenAI => {
|
||||||
|
// OpenAI models generally support these features
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tool management options
|
||||||
|
let options = vec![
|
||||||
|
"Toggle Web Search",
|
||||||
|
"Toggle Reasoning Summaries",
|
||||||
|
"Set Reasoning Effort",
|
||||||
|
"Done"
|
||||||
|
];
|
||||||
|
|
||||||
|
let selection = self.input.select_from_list(
|
||||||
|
"Select an option:",
|
||||||
|
&options,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match selection.as_deref() {
|
||||||
|
Some("Toggle Web Search") => {
|
||||||
|
self.session.enable_web_search = !self.session.enable_web_search;
|
||||||
|
let state = if self.session.enable_web_search { "enabled" } else { "disabled" };
|
||||||
|
self.display.print_command_result(&format!("Web search {}", state));
|
||||||
|
}
|
||||||
|
Some("Toggle Reasoning Summaries") => {
|
||||||
|
self.session.enable_reasoning_summary = !self.session.enable_reasoning_summary;
|
||||||
|
let state = if self.session.enable_reasoning_summary { "enabled" } else { "disabled" };
|
||||||
|
self.display.print_command_result(&format!("Reasoning summaries {}", state));
|
||||||
|
}
|
||||||
|
Some("Set Reasoning Effort") => {
|
||||||
|
let effort_options = vec!["low", "medium", "high"];
|
||||||
|
if let Some(effort) = self.input.select_from_list(
|
||||||
|
"Select reasoning effort:",
|
||||||
|
&effort_options,
|
||||||
|
Some(&self.session.reasoning_effort),
|
||||||
|
)? {
|
||||||
|
self.session.reasoning_effort = effort.to_string();
|
||||||
|
self.display.print_command_result(&format!("Reasoning effort set to {}", effort));
|
||||||
|
|
||||||
|
if !self.session.model.starts_with("gpt-5") {
|
||||||
|
self.display.print_warning("Reasoning effort is only supported by GPT-5 models");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some("Done") | None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.session.save()?; // Save changes after each modification
|
||||||
|
println!(); // Add spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
self.session.enable_reasoning_summary = enable;
|
|
||||||
let state = if enable { "enabled" } else { "disabled" };
|
|
||||||
self.display.print_command_result(&format!("Reasoning summaries {}", state));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,15 +70,14 @@ impl Display {
|
||||||
Available Commands:
|
Available Commands:
|
||||||
/help - Show this help message
|
/help - Show this help message
|
||||||
/exit - Exit the CLI
|
/exit - Exit the CLI
|
||||||
/model [model_name] - Switch model or show interactive picker
|
/model - Interactive model switcher
|
||||||
/models - List all supported models
|
/models - List all supported models
|
||||||
/list - List all saved sessions
|
/list - List all saved sessions
|
||||||
/new <session_name> - Create a new session
|
/new <session_name> - Create a new session
|
||||||
/switch [session_name] - Switch session or show interactive picker
|
/switch - Interactive session manager (switch/delete)
|
||||||
|
/sessions - Alias for /switch
|
||||||
/clear - Clear current conversation
|
/clear - Clear current conversation
|
||||||
/delete [session_name] - Delete a session
|
/tools - Interactive tool and feature manager
|
||||||
/tool websearch on|off - Enable/disable web search (OpenAI only)
|
|
||||||
/reasoning on|off - Enable/disable reasoning summaries (OpenAI only)
|
|
||||||
/effort [low|medium|high] - Set reasoning effort level (GPT-5 only)
|
/effort [low|medium|high] - Set reasoning effort level (GPT-5 only)
|
||||||
/stats - Show current session statistics
|
/stats - Show current session statistics
|
||||||
/optimize - Optimize session memory usage
|
/optimize - Optimize session memory usage
|
||||||
|
|
|
||||||
|
|
@ -67,13 +67,17 @@ impl InputHandler {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let selection = Select::with_theme(&theme)
|
match Select::with_theme(&theme)
|
||||||
.with_prompt(title)
|
.with_prompt(title)
|
||||||
.items(items)
|
.items(items)
|
||||||
.default(default_index)
|
.default(default_index)
|
||||||
.interact_opt()?;
|
.interact_opt() {
|
||||||
|
Ok(selection) => Ok(selection.map(|idx| items[idx].clone())),
|
||||||
Ok(selection.map(|idx| items[idx].clone()))
|
Err(_) => {
|
||||||
|
// Handle any error (ESC, Ctrl+C, etc.) as cancellation
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn confirm(&self, message: &str) -> Result<bool> {
|
pub fn confirm(&self, message: &str) -> Result<bool> {
|
||||||
|
|
@ -85,6 +89,123 @@ impl InputHandler {
|
||||||
|
|
||||||
Ok(confirmation)
|
Ok(confirmation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interactive session manager with support for switching and deletion
|
||||||
|
pub fn session_manager<T: ToString + Clone>(
|
||||||
|
&mut self,
|
||||||
|
title: &str,
|
||||||
|
sessions: &[T],
|
||||||
|
current_session: Option<&str>,
|
||||||
|
) -> Result<SessionAction<T>> {
|
||||||
|
if sessions.is_empty() {
|
||||||
|
println!("(no sessions available)");
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create display items with current session marker
|
||||||
|
let display_items: Vec<String> = sessions
|
||||||
|
.iter()
|
||||||
|
.map(|session| {
|
||||||
|
let name = session.to_string();
|
||||||
|
if Some(name.as_str()) == current_session {
|
||||||
|
format!("{} (current)", name)
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("\n{}", title);
|
||||||
|
println!("Use ↑/↓ arrows to navigate, Enter to switch session, Esc to cancel");
|
||||||
|
println!("To delete a session, first switch away from it, then use this menu again.\n");
|
||||||
|
|
||||||
|
let theme = ColorfulTheme::default();
|
||||||
|
|
||||||
|
// Find default selection index
|
||||||
|
let default_index = if let Some(current) = current_session {
|
||||||
|
sessions.iter().position(|item| item.to_string() == current).unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the selection menu
|
||||||
|
let selection_result = match Select::with_theme(&theme)
|
||||||
|
.with_prompt("Select session")
|
||||||
|
.items(&display_items)
|
||||||
|
.default(default_index)
|
||||||
|
.interact_opt() {
|
||||||
|
Ok(selection) => selection,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match selection_result {
|
||||||
|
Some(index) => {
|
||||||
|
let selected_session = sessions[index].clone();
|
||||||
|
|
||||||
|
// If it's the current session, show options
|
||||||
|
if Some(selected_session.to_string().as_str()) == current_session {
|
||||||
|
let options = vec!["Delete this session", "Cancel"];
|
||||||
|
let action_result = match Select::with_theme(&theme)
|
||||||
|
.with_prompt("This is your current session. What would you like to do?")
|
||||||
|
.items(&options)
|
||||||
|
.interact_opt() {
|
||||||
|
Ok(selection) => selection,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match action_result {
|
||||||
|
Some(0) => {
|
||||||
|
if self.confirm(&format!("Delete current session '{}'? You will need to create or switch to another session after deletion.", selected_session.to_string()))? {
|
||||||
|
return Ok(SessionAction::Delete(selected_session));
|
||||||
|
}
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
_ => return Ok(SessionAction::Cancel),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Different session selected - offer to switch or delete
|
||||||
|
let options = vec![
|
||||||
|
format!("Switch to '{}'", selected_session.to_string()),
|
||||||
|
format!("Delete '{}'", selected_session.to_string()),
|
||||||
|
"Cancel".to_string()
|
||||||
|
];
|
||||||
|
|
||||||
|
let action_result = match Select::with_theme(&theme)
|
||||||
|
.with_prompt("What would you like to do?")
|
||||||
|
.items(&options)
|
||||||
|
.interact_opt() {
|
||||||
|
Ok(selection) => selection,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match action_result {
|
||||||
|
Some(0) => return Ok(SessionAction::Switch(selected_session)),
|
||||||
|
Some(1) => {
|
||||||
|
if self.confirm(&format!("Delete session '{}'?", selected_session.to_string()))? {
|
||||||
|
return Ok(SessionAction::Delete(selected_session));
|
||||||
|
}
|
||||||
|
return Ok(SessionAction::Cancel);
|
||||||
|
}
|
||||||
|
_ => return Ok(SessionAction::Cancel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return Ok(SessionAction::Cancel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SessionAction<T> {
|
||||||
|
Switch(T),
|
||||||
|
Delete(T),
|
||||||
|
Cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InputHandler {
|
impl Default for InputHandler {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue