use super::mux_transport::MuxTransport;
use crate::app::tmux_handler::tmux_state::TmuxTransport;
use par_term_mux::{AgentEntry, AttachOutcome};
use par_term_tmux::{TmuxPaneId, TmuxWindowId};
use std::io;
pub(crate) fn attach_sequence(
transport: &MuxTransport,
name: &str,
size: Option<(u16, u16)>,
cell_px: Option<(u16, u16)>,
colors: &(String, String),
env: &std::collections::HashMap<String, String>,
) -> io::Result<AttachSequence> {
let daemon_version = transport.client().daemon_version().ok();
let outcome = transport.client().create_or_attach_with_env(name, env)?;
let mut existing_windows = Vec::new();
let mut window_names = Vec::new();
if matches!(outcome, AttachOutcome::Attached(_)) {
for window in transport.client().list_windows()? {
existing_windows.push(window.id);
if !window.name.is_empty() {
window_names.push((window.id, window.name));
}
}
}
let panes = transport.client().list_panes()?;
if let (Some((cols, rows)), Some(first)) = (size, panes.first()) {
let cell_px = cell_px.unwrap_or((10, 20));
transport
.client()
.set_client_size(*first, cols, rows, cell_px)?;
}
if let Err(e) = transport.client().set_client_colors(&colors.0, &colors.1) {
crate::debug_error!("MUX", "set-client-colors on attach failed: {e}");
}
let mut screens = Vec::new();
for pane in panes {
let reply = transport.client().refresh_pane(pane)?;
let mut bytes = b"\x1b[H\x1b[2J".to_vec();
bytes.extend_from_slice(&reply.join("\n").into_bytes());
screens.push((pane, bytes));
}
let agents = transport.client().list_agents().unwrap_or_else(|e| {
crate::debug_error!("MUX", "list-agents roster fill failed: {e}");
Vec::new()
});
let mut titles = Vec::new();
for pane in screens.iter().map(|(pane, _)| *pane) {
let effective = transport.client().pane_title(pane).unwrap_or_else(|e| {
crate::debug_error!("MUX", "pane-title query failed for %{pane}: {e}");
String::new()
});
if effective.is_empty() {
continue;
}
let is_user = probe_pane_title_is_user(transport, pane, &effective);
titles.push((pane, (effective, is_user)));
}
Ok(AttachSequence {
daemon_version,
outcome,
existing_windows,
window_names,
screens,
agents,
titles,
})
}
fn probe_pane_title_is_user(transport: &MuxTransport, pane: TmuxPaneId, effective: &str) -> bool {
let clear = format!("select-pane -t %{pane} -T ''");
if let Err(e) = transport.client().send(&clear) {
crate::debug_error!("MUX", "pane-title probe clear failed for %{pane}: {e}");
return false;
}
let after_clear = transport.client().pane_title(pane).unwrap_or_default();
if after_clear == effective {
return false;
}
let restore = format!(
"select-pane -t %{pane} -T {}",
par_term_mux::quote_env_value(effective)
);
if let Err(e) = transport.client().send(&restore) {
crate::debug_error!("MUX", "pane-title probe restore failed for %{pane}: {e}");
}
true
}
pub(crate) fn mux_attach_refusal(target_socket: &std::path::Path) -> Option<&'static str> {
std::env::var_os("PAR_MUX_ENV")?;
let outer = std::env::var_os("PAR_MUX_SOCKET")?;
if std::path::Path::new(&outer) == target_socket {
return Some(
"this par-term already runs inside that par-mux session — attaching \
would render the session inside itself",
);
}
None
}
pub(super) fn stamp_pane_session_id(transport: &dyn TmuxTransport, session: Option<u64>) {
let Some(session) = session else {
return;
};
let value = par_term_mux::quote_env_value(&format!("w0t0p0:{}", uuid::Uuid::new_v4()));
if let Err(e) = transport.send_command(&format!(
"set-environment -t ${session} ITERM_SESSION_ID {value}"
)) {
crate::debug_error!("MUX", "set-environment ITERM_SESSION_ID failed: {e}");
}
}
pub(crate) struct AttachSequence {
pub(crate) daemon_version: Option<String>,
pub(crate) outcome: AttachOutcome,
pub(crate) existing_windows: Vec<TmuxWindowId>,
pub(crate) window_names: Vec<(TmuxWindowId, String)>,
pub(crate) screens: Vec<(TmuxPaneId, Vec<u8>)>,
pub(crate) agents: Vec<AgentEntry>,
pub(crate) titles: Vec<(TmuxPaneId, (String, bool))>,
}