aboutsummaryrefslogtreecommitdiff
path: root/src/app/mod.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-26 14:48:57 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-26 14:50:13 +0300
commit57c880f0ebaa5011abc90f4e2fd4f84fc072ce35 (patch)
tree9f662b19cec2e9c9e50e87eb5e96def9e8ae9822 /src/app/mod.rs
parent9eec3029e13b791b8a4954e38d5bb7db6e2ff68f (diff)
feat: add global last-pane navigation
refs #287
Diffstat (limited to 'src/app/mod.rs')
-rw-r--r--src/app/mod.rs89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs
index e4ec2751..534a81d8 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -392,6 +392,7 @@ impl App {
direct_attach_resize_locks: std::collections::HashSet::new(),
workspaces,
active,
+ previous_pane_focus: None,
selected,
mode,
should_quit: false,
@@ -2550,6 +2551,9 @@ mod tests {
.cwd = split_cwd.clone();
app.state.active = Some(0);
app.state.selected = 0;
+ app.state
+ .focus_pane_in_workspace(0, background_previous_focus);
+ app.state.focus_pane_in_workspace(0, active_pane);
let target_pane_id = app.pane_info(0, target_pane).unwrap().pane_id;
let target_tab_id = app.public_tab_id(0, background_tab).unwrap();
@@ -2592,6 +2596,14 @@ mod tests {
.pane_count(),
3
);
+ app.state.last_pane();
+ assert_eq!(app.state.workspaces[0].active_tab, background_tab);
+ assert_eq!(
+ app.state.workspaces[0].tabs[background_tab]
+ .layout
+ .focused(),
+ background_previous_focus
+ );
let runtimes: Vec<_> = app.terminal_runtimes.drain().collect();
for (_terminal_id, runtime) in runtimes {
@@ -2650,6 +2662,44 @@ mod tests {
}
}
+ #[tokio::test]
+ async fn focused_agent_start_records_previous_pane() {
+ let mut app = test_app();
+ let workspace = Workspace::test_new("agent-start-focus");
+ let root = workspace.tabs[0].root_pane;
+ app.state.workspaces = vec![workspace];
+ app.state.ensure_test_terminals();
+ app.state.active = Some(0);
+ app.state.selected = 0;
+
+ let response = app.handle_api_request(crate::api::schema::Request {
+ id: "req_agent_start_focus".into(),
+ method: crate::api::schema::Method::AgentStart(crate::api::schema::AgentStartParams {
+ name: "worker".into(),
+ cwd: None,
+ workspace_id: None,
+ tab_id: None,
+ split: Some(crate::api::schema::SplitDirection::Right),
+ focus: true,
+ argv: vec!["/usr/bin/true".into()],
+ }),
+ });
+ let response: serde_json::Value = serde_json::from_str(&response).unwrap();
+
+ assert_eq!(response["result"]["type"], "agent_started");
+ assert_ne!(app.state.workspaces[0].focused_pane_id(), Some(root));
+
+ app.state.last_pane();
+
+ assert_eq!(app.state.active, Some(0));
+ assert_eq!(app.state.workspaces[0].focused_pane_id(), Some(root));
+
+ let runtimes: Vec<_> = app.terminal_runtimes.drain().collect();
+ for (_terminal_id, runtime) in runtimes {
+ runtime.shutdown();
+ }
+ }
+
#[test]
fn pane_close_request_closes_only_the_target_tab_when_other_tabs_exist() {
let mut app = test_app();
@@ -2984,6 +3034,45 @@ mod tests {
);
}
+ #[test]
+ fn route_client_input_prefix_tab_dispatches_global_last_pane() {
+ let config: Config = toml::from_str(
+ r#"
+[keys]
+last_pane = "prefix+tab"
+"#,
+ )
+ .unwrap();
+ let mut app = test_app();
+ let mut first = Workspace::test_new("one");
+ let first_second_tab = first.test_add_tab(Some("logs"));
+ let first_second_root = first.tabs[first_second_tab].root_pane;
+ let second = Workspace::test_new("two");
+ let second_root = second.tabs[0].root_pane;
+ app.state.workspaces = vec![first, second];
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.keybinds = config.keybinds();
+ app.state.mode = Mode::Terminal;
+ app.state.switch_workspace_tab(0, first_second_tab);
+ app.state.switch_workspace_tab(1, 0);
+
+ app.route_client_input(vec![0x02, b'\t']);
+
+ assert_eq!(app.state.mode, Mode::Terminal);
+ assert_eq!(app.state.active, Some(0));
+ assert_eq!(app.state.workspaces[0].active_tab, first_second_tab);
+ assert_eq!(
+ app.state.workspaces[0].focused_pane_id(),
+ Some(first_second_root)
+ );
+
+ app.route_client_input(vec![0x02, b'\t']);
+
+ assert_eq!(app.state.active, Some(1));
+ assert_eq!(app.state.workspaces[1].focused_pane_id(), Some(second_root));
+ }
+
#[tokio::test]
async fn route_client_input_double_prefix_passes_prefix_through_to_focused_pane() {
let mut app = test_app();