Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions apps/server-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,10 @@ impl ReadOnlyMuxStateSource {
return;
};
provider.switch_session(&name, client_tty);
// Move the sidebar highlight to the switched-to session, mirroring the
// move_focus path. Without this, Alt+digit switches the tmux session but
// the highlighted selection stays on the previously focused session.
*self.focused_session.lock().unwrap() = Some(name);
}

fn move_focus(&self, delta: i64, current_session: Option<&str>) -> Option<String> {
Expand Down
7 changes: 6 additions & 1 deletion apps/server-rs/tests/protocol_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,11 +551,16 @@ async fn websocket_switch_index_switches_to_visible_session() {
.await
.expect("refresh command should send");

let _ = timeout(Duration::from_secs(1), receiver.next())
let state = timeout(Duration::from_secs(1), receiver.next())
.await
.expect("refresh state should arrive before timeout")
.expect("refresh state should arrive")
.expect("refresh state should be valid");
let state_text = state.as_text().expect("state should be text");
assert!(
state_text.contains(r#""focusedSession":"worker""#),
"switch-index should move the sidebar highlight (focusedSession) to the switched-to session; got: {state_text}"
);
assert_eq!(
*mux.switch_calls.lock().unwrap(),
vec![("worker".to_string(), None)]
Expand Down
11 changes: 9 additions & 2 deletions packages/runtime-rs/src/tmux_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,17 @@ impl MuxProvider for TmuxProvider {
// pane works even when the parent pane's cwd is unrelated to the
// workspace (e.g. tmux sessions whose default cwd is `$HOME`). Falls
// back to the literal path if the env is unset.
let command = format!(
"OPENSESSIONS_SESSION_NAME={} OPENSESSIONS_WINDOW_ID={window_id} REFOCUS_WINDOW={window_id} exec \"${{OPENSESSIONS_DIR:-.}}\"/{scripts_dir}/start.sh",
//
// Wrap in `sh -c '...'`: tmux runs pane commands via the user's
// `default-command`/`default-shell`, which may be a non-POSIX shell
// (e.g. fish) that cannot parse `FOO=bar exec` or `${VAR:-default}`.
// Forcing `sh` keeps the launcher portable regardless of the user's
// interactive shell. Single quotes in the session name are escaped.
let inner = format!(
"OPENSESSIONS_SESSION_NAME=\"{}\" OPENSESSIONS_WINDOW_ID=\"{window_id}\" REFOCUS_WINDOW=\"{window_id}\" exec \"${{OPENSESSIONS_DIR:-.}}\"/{scripts_dir}/start.sh",
target.session_name,
);
let command = format!("sh -c '{}'", inner.replace('\'', r"'\''"));
let new_pane = self.client.split_sidebar_pane(
&target.id,
position == SidebarPosition::Left,
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-rs/tests/tmux_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ fn tmux_provider_spawns_sidebar_against_edge_pane_and_titles_it() {
assert_eq!(
split_call.last().map(String::as_str),
Some(
"OPENSESSIONS_SESSION_NAME=alpha OPENSESSIONS_WINDOW_ID=@1 REFOCUS_WINDOW=@1 exec \"${OPENSESSIONS_DIR:-.}\"//scripts/start.sh"
"sh -c 'OPENSESSIONS_SESSION_NAME=\"alpha\" OPENSESSIONS_WINDOW_ID=\"@1\" REFOCUS_WINDOW=\"@1\" exec \"${OPENSESSIONS_DIR:-.}\"//scripts/start.sh'"
)
);
assert!(
Expand Down