Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions crates/tui/src/tui/key_actions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Keyboard event action handlers extracted from `ui.rs`.
//!
//! Each function handles a focused subset of keyboard input so the
//! main event loop stays lean.

use crossterm::event::{KeyCode, KeyEvent};

use super::app::App;

// ── File-tree key handling ───────────────────────────────────────

/// Handle keyboard input when the file-tree pane is visible.
///
/// Returns `true` when the key was consumed (caller should `continue`).
pub fn handle_file_tree_key(app: &mut App, key: &KeyEvent) -> bool {
// Guard: do not intercept keys when the file-tree pane is not visible.
if !app.file_tree_visible {
return false;
}

// Esc closes the tree even when entries are still loading.
if key.code == KeyCode::Esc && app.file_tree.is_some() {
Comment thread
sximelon marked this conversation as resolved.
app.file_tree = None;
app.status_message = Some("File tree closed".to_string());
app.needs_redraw = true;
return true;
}

let Some(file_tree) = app.file_tree.as_mut() else {
return false;
};

match key.code {
KeyCode::Up => {
file_tree.cursor_up();
app.needs_redraw = true;
true
}
KeyCode::Down => {
file_tree.cursor_down();
app.needs_redraw = true;
true
}
KeyCode::Enter => {
if let Some(rel_path) = file_tree.activate() {
let path_str = rel_path.to_string_lossy().to_string();
app.status_message = Some(format!("Attached @{path_str}"));
app.insert_str(&format!("@{path_str} "));
} else {
app.needs_redraw = true;
}
true
}
_ => false,
}
}
1 change: 1 addition & 0 deletions crates/tui/src/tui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub mod footer_ui;
pub mod format_helpers;
pub mod frame_rate_limiter;
pub mod history;
pub mod key_actions;
pub mod key_shortcuts;
pub mod keybindings;
pub mod live_transcript;
Expand Down
46 changes: 5 additions & 41 deletions crates/tui/src/tui/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ use crate::tui::views::subagent_view_agents;
use crate::tui::vim_mode;
use crate::tui::workspace_context;

use super::key_actions;

use super::app::{
App, AppAction, AppMode, OnboardingState, QueuedMessage, ReasoningEffort, SidebarFocus,
StatusToastLevel, SubmitDisposition, TaskPanelEntry, TuiOptions,
Expand Down Expand Up @@ -2676,47 +2678,9 @@ async fn run_event_loop(
continue;
}

// File-tree navigation: intercept keys when the file-tree pane is
// visible so Up/Down/Enter/Esc operate on the tree rather than
// falling through to composer or modal handlers.
if app.file_tree_visible {
match key.code {
KeyCode::Up => {
if let Some(state) = app.file_tree.as_mut() {
state.cursor_up();
}
app.needs_redraw = true;
continue;
}
KeyCode::Down => {
if let Some(state) = app.file_tree.as_mut() {
state.cursor_down();
}
app.needs_redraw = true;
continue;
}
KeyCode::Enter => {
if let Some(state) = app.file_tree.as_mut() {
if let Some(rel_path) = state.activate() {
// Insert @path into the composer.
let path_str = rel_path.to_string_lossy().to_string();
app.status_message = Some(format!("Attached @{path_str}"));
app.insert_str(&format!("@{path_str} "));
} else {
// Directory was expanded/collapsed; rebuild.
app.needs_redraw = true;
}
}
continue;
}
KeyCode::Esc => {
app.file_tree = None;
app.status_message = Some("File tree closed".to_string());
app.needs_redraw = true;
continue;
}
_ => {}
}
// File-tree navigation: delegated to key_actions module.
if key_actions::handle_file_tree_key(app, &key) {
continue;
}

if app.is_history_search_active() {
Expand Down