Skip to main content

tmai_core/
command_sender.rs

1use anyhow::Result;
2use std::sync::Arc;
3
4use crate::ipc::server::IpcServer;
5use crate::state::SharedState;
6use crate::tmux::TmuxClient;
7
8/// Unified command sender that tries IPC first, falls back to tmux
9pub struct CommandSender {
10    ipc_server: Option<Arc<IpcServer>>,
11    tmux_client: TmuxClient,
12    app_state: SharedState,
13}
14
15impl CommandSender {
16    /// Create a new CommandSender
17    pub fn new(
18        ipc_server: Option<Arc<IpcServer>>,
19        tmux_client: TmuxClient,
20        app_state: SharedState,
21    ) -> Self {
22        Self {
23            ipc_server,
24            tmux_client,
25            app_state,
26        }
27    }
28
29    /// Send keys via IPC if connected, otherwise via tmux
30    pub fn send_keys(&self, target: &str, keys: &str) -> Result<()> {
31        if let Some(ref ipc) = self.ipc_server {
32            if let Some(pane_id) = self.get_pane_id_for_target(target) {
33                if ipc.try_send_keys(&pane_id, keys, false) {
34                    return Ok(());
35                }
36            }
37        }
38        self.tmux_client.send_keys(target, keys)
39    }
40
41    /// Send literal keys via IPC if connected, otherwise via tmux
42    pub fn send_keys_literal(&self, target: &str, keys: &str) -> Result<()> {
43        if let Some(ref ipc) = self.ipc_server {
44            if let Some(pane_id) = self.get_pane_id_for_target(target) {
45                if ipc.try_send_keys(&pane_id, keys, true) {
46                    return Ok(());
47                }
48            }
49        }
50        self.tmux_client.send_keys_literal(target, keys)
51    }
52
53    /// Send text + Enter via IPC if connected, otherwise via tmux
54    pub fn send_text_and_enter(&self, target: &str, text: &str) -> Result<()> {
55        if let Some(ref ipc) = self.ipc_server {
56            if let Some(pane_id) = self.get_pane_id_for_target(target) {
57                if ipc.try_send_keys_and_enter(&pane_id, text) {
58                    return Ok(());
59                }
60            }
61        }
62        self.tmux_client.send_text_and_enter(target, text)
63    }
64
65    /// Access the underlying TmuxClient for direct operations (focus_pane, kill_pane, etc.)
66    pub fn tmux_client(&self) -> &TmuxClient {
67        &self.tmux_client
68    }
69
70    /// Access the IPC server (needed for Poller registry)
71    pub fn ipc_server(&self) -> Option<&Arc<IpcServer>> {
72        self.ipc_server.as_ref()
73    }
74
75    /// Look up pane_id from target using the mapping in AppState
76    fn get_pane_id_for_target(&self, target: &str) -> Option<String> {
77        let state = self.app_state.read();
78        state.target_to_pane_id.get(target).cloned()
79    }
80}