aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell/actions.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-06 02:27:06 +0300
committerGitHub <noreply@github.com>2026-09-06 02:27:06 +0300
commit9e9bc8a144667a6b7debfd4f0c7a31ff0e11493a (patch)
tree03f1259f91fd9edeacfd4b904f2e84d4acf25232 /src/client/shell/actions.rs
parent35b0dff96579f099e0436290085024424ffb289c (diff)
feat: manage multiple ssh machines from one client (#3670)
Diffstat (limited to 'src/client/shell/actions.rs')
-rw-r--r--src/client/shell/actions.rs75
1 files changed, 72 insertions, 3 deletions
diff --git a/src/client/shell/actions.rs b/src/client/shell/actions.rs
index 1ebd4e29..f99d8f3a 100644
--- a/src/client/shell/actions.rs
+++ b/src/client/shell/actions.rs
@@ -147,6 +147,9 @@ impl ClientShellState {
}
return;
}
+ if self.handle_endpoint_navigation(action, outcome) {
+ return;
+ }
if let Some(method) = self.endpoint_method_for_action(action) {
self.push_endpoint_method(method, outcome);
return;
@@ -408,7 +411,9 @@ impl ClientShellState {
kind: PendingEndpointKind,
outcome: &mut ClientShellInput,
) -> bool {
- if self.snapshot.is_none() {
+ if !self.endpoint_is_online(&self.active_endpoint_id) {
+ let label = self.active_endpoint_label().to_owned();
+ outcome.repaint |= self.receive_endpoint_unavailable(format!("{label} is not ready"));
return false;
}
let method_name = crate::api::api_method_name(&method).to_owned();
@@ -423,7 +428,9 @@ impl ClientShellState {
);
return false;
}
- let snapshot = self.snapshot.as_deref().expect("checked snapshot");
+ let Some(snapshot) = self.snapshot.as_deref() else {
+ return false;
+ };
let confirmation_workspace_id = match &method {
crate::api::schema::Method::TabClose(target) => snapshot
.tabs
@@ -450,6 +457,7 @@ impl ClientShellState {
},
);
outcome.actions.push(ClientShellAction::Endpoint {
+ endpoint_id: self.active_endpoint_id.clone(),
boot_id: snapshot.boot_id.clone(),
request: Box::new(crate::api::schema::Request {
id: request_id,
@@ -468,6 +476,58 @@ impl ClientShellState {
)
}
+ pub(crate) fn receive_endpoint_unavailable(&mut self, message: String) -> bool {
+ self.push_endpoint_notice(
+ ClientEndpointNoticeKind::Unavailable,
+ message.clone(),
+ "Endpoint unavailable",
+ message,
+ )
+ }
+
+ pub(crate) fn focus_endpoint_target(
+ &mut self,
+ target: ClientEndpointFocusTarget,
+ ) -> Vec<ClientShellAction> {
+ let method = match target {
+ ClientEndpointFocusTarget::Workspace(workspace_id) => {
+ crate::api::schema::Method::WorkspaceFocus(crate::api::schema::WorkspaceTarget {
+ workspace_id,
+ })
+ }
+ ClientEndpointFocusTarget::Tab(tab_id) => {
+ crate::api::schema::Method::TabFocus(crate::api::schema::TabTarget { tab_id })
+ }
+ ClientEndpointFocusTarget::Pane(pane_id) => {
+ crate::api::schema::Method::PaneFocus(crate::api::schema::PaneTarget { pane_id })
+ }
+ };
+ let mut outcome = ClientShellInput::default();
+ self.push_endpoint_method(method, &mut outcome);
+ outcome.actions
+ }
+
+ pub(crate) fn cancel_endpoint_request(&mut self, request_id: &str) -> bool {
+ let Some(pending) = self.pending_requests.get(request_id) else {
+ return false;
+ };
+ let boot_id = pending.boot_id.clone();
+ let (repaint, actions) = self.handle_endpoint_result(
+ &boot_id,
+ request_id,
+ Err(ClientShellEndpointError {
+ code: Some("endpoint_cancelled".into()),
+ message: "This server action was interrupted. Check its state before retrying."
+ .into(),
+ }),
+ );
+ debug_assert!(
+ actions.is_empty(),
+ "cancellation must not start another action"
+ );
+ repaint
+ }
+
pub(crate) fn handle_endpoint_result(
&mut self,
boot_id: &str,
@@ -506,6 +566,12 @@ impl ClientShellState {
"Server timed out",
format!("This server did not respond to {}.", pending.method_name),
),
+ "endpoint_cancelled" => (
+ ClientEndpointNoticeKind::Unavailable,
+ "cancelled".to_owned(),
+ "Action interrupted",
+ error.message.clone(),
+ ),
"server_unavailable" => (
ClientEndpointNoticeKind::Unavailable,
"server".to_owned(),
@@ -613,6 +679,9 @@ impl ClientShellState {
Some("endpoint returned an unexpected selection result".to_owned());
(true, fallback())
}
+ Err(error) if error.code.as_deref() == Some("endpoint_cancelled") => {
+ (true, Vec::new())
+ }
Err(_) => (true, fallback()),
};
}
@@ -724,7 +793,7 @@ impl ClientShellState {
Err(error)
if matches!(
error.code.as_deref(),
- Some("stale_content" | "stale_target")
+ Some("stale_content" | "stale_target" | "endpoint_cancelled")
) =>
{
self.url_click_consumes_until_up = completed_before_release;