Skip to content

Commit dcaeb72

Browse files
subiniumclaude
andauthored
feat(tui): add Tab navigation with wrap-around + fix --version flag (#14)
- Tab/Shift+Tab navigate all selector menus with wrap-around - Enter on Resume → mode picker; Enter on Agent → permission picker - Handle --version/-V before clap parsing Closes #12 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8fdb57d commit dcaeb72

3 files changed

Lines changed: 54 additions & 36 deletions

File tree

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "agf"
3-
version = "0.7.1"
3+
version = "0.7.2"
44
edition = "2021"
55
description = "AI Agent Session Finder TUI — unified launcher for Claude Code, Codex, and OpenCode sessions"
66
license = "MIT"

‎src/main.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ enum Commands {
4141
},
4242
}
4343

44+
const VERSION: &str = env!("CARGO_PKG_VERSION");
45+
4446
fn main() -> anyhow::Result<()> {
47+
// Handle --version / -V manually (clap hides it due to args_conflicts_with_subcommands)
48+
let args: Vec<String> = std::env::args().collect();
49+
if args.iter().any(|a| a == "--version" || a == "-V") {
50+
println!("agf {VERSION}");
51+
return Ok(());
52+
}
53+
4554
let cli = Cli::parse();
4655

4756
match cli.command {

‎src/tui/mod.rs‎

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -509,43 +509,47 @@ fn ui_action_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<St
509509
app.mode = Mode::Browse;
510510
}
511511

512-
if (ui.key_code(slt::KeyCode::Up)
512+
let action_count = actions.len();
513+
514+
if ui.key_code(slt::KeyCode::BackTab) {
515+
app.action_index = (app.action_index + action_count - 1) % action_count;
516+
} else if (ui.key_code(slt::KeyCode::Up)
513517
|| ui.key_mod('p', slt::KeyModifiers::CONTROL)
514518
|| ui.key_mod('k', slt::KeyModifiers::CONTROL))
515519
&& app.action_index > 0
516520
{
517521
app.action_index -= 1;
518522
}
519523

520-
if (ui.key_code(slt::KeyCode::Down)
524+
if ui.key_code(slt::KeyCode::Tab) {
525+
app.action_index = (app.action_index + 1) % action_count;
526+
} else if (ui.key_code(slt::KeyCode::Down)
521527
|| ui.key_mod('n', slt::KeyModifiers::CONTROL)
522528
|| ui.key_mod('j', slt::KeyModifiers::CONTROL))
523-
&& app.action_index < actions.len() - 1
529+
&& app.action_index < action_count - 1
524530
{
525531
app.action_index += 1;
526532
}
527533

528-
for i in 0..actions.len().min(9) {
534+
for i in 0..action_count.min(9) {
529535
let key = char::from_u32((b'1' + i as u8) as u32).unwrap_or('1');
530536
if ui.key(key) {
531537
app.action_index = i;
532538
dispatch_action(ui, app, actions[app.action_index], result);
533539
}
534540
}
535541

536-
if ui.key_code(slt::KeyCode::Tab)
537-
&& actions[app.action_index] == Action::Resume
538-
&& app.selected_session().is_some()
539-
{
540-
if let Some(session) = app.selected_session() {
541-
app.resume_mode_options = session.agent.resume_mode_options().to_vec();
542-
app.resume_mode_index = 0;
543-
app.mode = Mode::ResumeSelect;
544-
}
545-
}
546-
547542
if ui.key_code(slt::KeyCode::Enter) {
548-
dispatch_action(ui, app, actions[app.action_index], result);
543+
// Resume → go to mode picker; others → dispatch directly
544+
if actions[app.action_index] == Action::Resume {
545+
if let Some(session) = app.selected_session() {
546+
app.resume_mode_options = session.agent.resume_mode_options().to_vec();
547+
app.resume_mode_index = 0;
548+
app.mode = Mode::ResumeSelect;
549+
}
550+
} else {
551+
dispatch_action(ui, app, actions[app.action_index], result);
552+
}
549553
}
550554

551555
let Some(session) = app.selected_session() else {
@@ -635,11 +639,7 @@ fn ui_action_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<St
635639

636640
ui.text("");
637641
ui.separator_colored(SEPARATOR);
638-
if actions[app.action_index] == Action::Resume {
639-
let _ = ui.help(&[("Tab", "mode"), ("Enter", "confirm"), ("Esc", "back")]);
640-
} else {
641-
let _ = ui.help(&[("Enter", "confirm"), ("Esc", "back")]);
642-
}
642+
let _ = ui.help(&[("Tab", "nav"), ("Enter", "select"), ("Esc", "back")]);
643643
});
644644
}
645645

@@ -679,15 +679,19 @@ fn ui_agent_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<Str
679679
app.mode = Mode::ActionSelect;
680680
}
681681

682-
if (ui.key_code(slt::KeyCode::Up)
682+
if ui.key_code(slt::KeyCode::BackTab) && option_count > 0 {
683+
app.agent_index = (app.agent_index + option_count - 1) % option_count;
684+
} else if (ui.key_code(slt::KeyCode::Up)
683685
|| ui.key_mod('p', slt::KeyModifiers::CONTROL)
684686
|| ui.key_mod('k', slt::KeyModifiers::CONTROL))
685687
&& app.agent_index > 0
686688
{
687689
app.agent_index -= 1;
688690
}
689691

690-
if (ui.key_code(slt::KeyCode::Down)
692+
if ui.key_code(slt::KeyCode::Tab) && option_count > 0 {
693+
app.agent_index = (app.agent_index + 1) % option_count;
694+
} else if (ui.key_code(slt::KeyCode::Down)
691695
|| ui.key_mod('n', slt::KeyModifiers::CONTROL)
692696
|| ui.key_mod('j', slt::KeyModifiers::CONTROL))
693697
&& option_count > 0
@@ -704,18 +708,15 @@ fn ui_agent_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<Str
704708
}
705709
}
706710

707-
if ui.key_code(slt::KeyCode::Tab) && app.new_session_options.get(app.agent_index).is_some() {
711+
if ui.key_code(slt::KeyCode::Enter) {
712+
// Enter → go to permission mode picker
708713
if let Some(opt) = app.new_session_options.get(app.agent_index) {
709714
app.mode_options = permission_options_for(opt.agent);
710715
app.mode_index = 0;
711716
app.mode = Mode::PermissionSelect;
712717
}
713718
}
714719

715-
if ui.key_code(slt::KeyCode::Enter) {
716-
dispatch_agent_option(ui, app, result);
717-
}
718-
719720
let Some(session) = app.selected_session() else {
720721
app.mode = Mode::Browse;
721722
return;
@@ -726,7 +727,7 @@ fn ui_agent_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<Str
726727
ui.line(|ui| {
727728
ui.text(" New session in ").fg(BRIGHT_WHITE);
728729
ui.text(session.display_path()).fg(GRAY_500);
729-
ui.text(" (tab -> permission mode)").fg(GRAY_500);
730+
ui.text(" (enter -> permission mode)").fg(GRAY_500);
730731
});
731732
ui.separator_colored(SEPARATOR);
732733
ui.text("");
@@ -772,8 +773,8 @@ fn ui_agent_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<Str
772773
ui.separator_colored(SEPARATOR);
773774
let _ = ui.help(&[
774775
("1-9", "select"),
775-
("Tab", "mode"),
776-
("Enter", "confirm"),
776+
("Tab", "nav"),
777+
("Enter", "mode"),
777778
("Esc", "back"),
778779
]);
779780
});
@@ -827,15 +828,19 @@ fn ui_permission_select(ui: &mut slt::Context, app: &mut App, result: &mut Optio
827828
app.mode = Mode::AgentSelect;
828829
}
829830

830-
if (ui.key_code(slt::KeyCode::Up)
831+
if ui.key_code(slt::KeyCode::BackTab) && option_count > 0 {
832+
app.mode_index = (app.mode_index + option_count - 1) % option_count;
833+
} else if (ui.key_code(slt::KeyCode::Up)
831834
|| ui.key_mod('p', slt::KeyModifiers::CONTROL)
832835
|| ui.key_mod('k', slt::KeyModifiers::CONTROL))
833836
&& app.mode_index > 0
834837
{
835838
app.mode_index -= 1;
836839
}
837840

838-
if (ui.key_code(slt::KeyCode::Down)
841+
if ui.key_code(slt::KeyCode::Tab) && option_count > 0 {
842+
app.mode_index = (app.mode_index + 1) % option_count;
843+
} else if (ui.key_code(slt::KeyCode::Down)
839844
|| ui.key_mod('n', slt::KeyModifiers::CONTROL)
840845
|| ui.key_mod('j', slt::KeyModifiers::CONTROL))
841846
&& option_count > 0
@@ -939,15 +944,19 @@ fn ui_resume_select(ui: &mut slt::Context, app: &mut App, result: &mut Option<St
939944
app.mode = Mode::ActionSelect;
940945
}
941946

942-
if (ui.key_code(slt::KeyCode::Up)
947+
if ui.key_code(slt::KeyCode::BackTab) && option_count > 0 {
948+
app.resume_mode_index = (app.resume_mode_index + option_count - 1) % option_count;
949+
} else if (ui.key_code(slt::KeyCode::Up)
943950
|| ui.key_mod('p', slt::KeyModifiers::CONTROL)
944951
|| ui.key_mod('k', slt::KeyModifiers::CONTROL))
945952
&& app.resume_mode_index > 0
946953
{
947954
app.resume_mode_index -= 1;
948955
}
949956

950-
if (ui.key_code(slt::KeyCode::Down)
957+
if ui.key_code(slt::KeyCode::Tab) && option_count > 0 {
958+
app.resume_mode_index = (app.resume_mode_index + 1) % option_count;
959+
} else if (ui.key_code(slt::KeyCode::Down)
951960
|| ui.key_mod('n', slt::KeyModifiers::CONTROL)
952961
|| ui.key_mod('j', slt::KeyModifiers::CONTROL))
953962
&& option_count > 0

0 commit comments

Comments
 (0)