use ratatui::DefaultTerminal;
use super::sessions_hub_tasks::SessionsDelete;
use super::{app_state::PointerAction, composer_pointer::ChoiceClick};
use super::{App, ComposerMode, InlineChoiceKeyOutcome, InlineChoicePending, InteractiveRuntime};
impl App {
pub(super) async fn handle_inline_choice_key(
&mut self,
key: crossterm::event::KeyEvent,
terminal: &mut DefaultTerminal,
agent: &mut InteractiveRuntime,
) -> anyhow::Result<bool> {
let outcome = match self.input_ui.composer_mut() {
ComposerMode::InlineChoice(modal) => modal.choice.handle_key(key),
_ => return Ok(false),
};
match outcome {
InlineChoiceKeyOutcome::Selected(value) => {
if !self.resolve_inline_choice(value, terminal, agent).await? {
return Ok(true);
}
}
InlineChoiceKeyOutcome::Cancelled => {
let ComposerMode::InlineChoice(modal) = self.input_ui.take_composer() else {
unreachable!("inline choice checked above");
};
match modal.pending {
InlineChoicePending::ComputerInstall => {
self.confirm_computer_installation("cancel", agent)
}
InlineChoicePending::ComputerAccess => {
self.confirm_computer_access("cancel", agent)
}
InlineChoicePending::CredentialStore { .. } => {
if let Some(parent) = modal.parent_picker {
self.set_status_quiet(parent.title.clone());
self.input_ui.set_composer(ComposerMode::Picker(*parent));
} else {
self.restore_after_cancelled_login();
}
}
InlineChoicePending::ClaudeCodeLogin
| InlineChoicePending::ClaudeCodeRelogin
| InlineChoicePending::ClaudeCodeLogout => {
self.set_status(self.busy_status_label());
}
InlineChoicePending::ContextHandoff(pending) => {
self.resolve_context_handoff(None, *pending, terminal, agent)
.await?;
}
InlineChoicePending::ConfirmSend(pending) => {
self.resolve_send_confirm(None, *pending, terminal, agent)
.await?;
}
InlineChoicePending::DeleteSession { .. }
| InlineChoicePending::DeleteDirectorySessions { .. }
| InlineChoicePending::CleanupMissingSessionDirectories { .. } => {
self.restore_session_choice_parent(modal.parent_picker);
}
InlineChoicePending::DeleteWorkflowPlan { .. }
| InlineChoicePending::DeleteWorkflowRun { .. } => {
self.open_workflow_hub_or_report();
}
InlineChoicePending::PromptHistoryLimit { .. } => {
self.open_main_config_picker_selected(
super::config_picker::PROMPT_HISTORY_LIMIT_VALUE,
)?;
}
InlineChoicePending::ClearPromptHistory => {
self.open_main_config_picker_selected(
super::config_picker::CLEAR_PROMPT_HISTORY_VALUE,
)?;
}
InlineChoicePending::TestWebSearch => {
self.submit_web_search_test_choice("cancel", modal.parent_picker)?;
}
}
}
InlineChoiceKeyOutcome::Handled => {}
}
self.input_ui.clear_paste_burst();
self.ctrl_c_streak = 0;
Ok(true)
}
pub(super) fn click_inline_choice(&mut self, index: usize, click: ChoiceClick) {
let ComposerMode::InlineChoice(modal) = self.input_ui.composer_mut() else {
return;
};
if !modal.choice.focus_option(index) {
return;
}
match click {
ChoiceClick::Single => {}
ChoiceClick::Double => self
.input_ui
.request_pointer_action(PointerAction::ConfirmInlineChoice),
}
}
pub(super) async fn resolve_inline_choice(
&mut self,
value: String,
terminal: &mut DefaultTerminal,
agent: &mut InteractiveRuntime,
) -> anyhow::Result<bool> {
let requires_full_visibility = match self.input_ui.composer() {
ComposerMode::InlineChoice(modal) => modal.choice.selected_requires_full_visibility(),
_ => return Ok(false),
};
if requires_full_visibility && !self.inline_choice_fully_visible(terminal) {
return Ok(false);
}
let ComposerMode::InlineChoice(modal) = self.input_ui.take_composer() else {
unreachable!("inline choice checked above");
};
match modal.pending {
InlineChoicePending::ComputerInstall => {
self.confirm_computer_installation(&value, agent)
}
InlineChoicePending::ComputerAccess => self.confirm_computer_access(&value, agent),
InlineChoicePending::CredentialStore { next } => {
if let Some(parent) = modal.parent_picker {
self.input_ui.set_composer(ComposerMode::Picker(*parent));
}
self.submit_credential_store_choice(modal.choice, next, terminal, agent)
.await?;
}
InlineChoicePending::ContextHandoff(pending) => {
self.resolve_context_handoff(Some(&value), *pending, terminal, agent)
.await?;
}
InlineChoicePending::ConfirmSend(pending) => {
self.resolve_send_confirm(Some(&value), *pending, terminal, agent)
.await?;
}
InlineChoicePending::ClaudeCodeLogin => {
self.submit_claude_code_login_choice(modal.choice, terminal)
.await?;
}
InlineChoicePending::ClaudeCodeRelogin => {
self.submit_claude_code_relogin_choice(modal.choice, terminal)
.await?;
}
InlineChoicePending::ClaudeCodeLogout => {
self.submit_claude_code_logout_choice(modal.choice).await?;
}
InlineChoicePending::DeleteSession { target } => {
self.submit_sessions_delete_choice(
&value,
SessionsDelete::One(target),
modal.parent_picker,
);
}
InlineChoicePending::DeleteDirectorySessions { cwd, targets } => {
self.submit_sessions_delete_choice(
&value,
SessionsDelete::Directory { cwd, targets },
modal.parent_picker,
);
}
InlineChoicePending::CleanupMissingSessionDirectories { targets } => {
self.submit_sessions_delete_choice(
&value,
SessionsDelete::CleanupMissing(targets),
modal.parent_picker,
);
}
InlineChoicePending::DeleteWorkflowPlan { plan_id } => {
self.submit_delete_workflow_plan_choice(&value, &plan_id)?;
}
InlineChoicePending::DeleteWorkflowRun { run_id } => {
self.submit_delete_workflow_run_choice(&value, &run_id)?;
}
InlineChoicePending::PromptHistoryLimit { new_limit } => {
self.submit_prompt_history_limit_choice(&value, new_limit)?;
}
InlineChoicePending::ClearPromptHistory => {
self.submit_clear_prompt_history_choice(&value)?;
}
InlineChoicePending::TestWebSearch => {
self.submit_web_search_test_choice(&value, modal.parent_picker)?;
}
}
Ok(true)
}
fn inline_choice_fully_visible(&mut self, terminal: &DefaultTerminal) -> bool {
let Ok(size) = terminal.size() else {
self.set_status("could not check choice visibility; resize the terminal or Esc cancel");
return false;
};
let frame = self.frame_context(ratatui::layout::Rect::new(0, 0, size.width, size.height));
let needed = frame.composer.lines.len();
let visible = usize::from(frame.layout.composer.height);
if frame.layout.composer_start != 0 || visible < needed {
self.set_status(format!("enlarge terminal to review choice: disclosure needs {needed} rows, {visible} visible; Esc cancel"));
return false;
}
true
}
}