Serial/Telnet Send Break and DTR/RTS tab controls; fix Help and hints; drop unused log path; docs
This commit is contained in:
+25
-1
@@ -244,7 +244,8 @@ impl App {
|
||||
};
|
||||
let profile = self.s.svc.sessions.profile(id);
|
||||
let view = self.view_for(&info, profile.as_ref());
|
||||
self.s.panes.insert(id.into(), Pane { info, view, recording: false, local_exited: false, connected_since: None, highlight_until: 0.0 });
|
||||
let lines = profile.as_ref().and_then(|p| p.serial.as_ref()).map_or((true, true), |c| (c.dtr_on_connect, c.rts_on_connect));
|
||||
self.s.panes.insert(id.into(), Pane { info, view, recording: false, local_exited: false, connected_since: None, highlight_until: 0.0, lines });
|
||||
if tab {
|
||||
self.s.tabs.push(Tab { id: id.into(), activity: false, split: None });
|
||||
self.s.active = Some(id.into());
|
||||
@@ -691,6 +692,29 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Telnet or serial break.
|
||||
pub fn send_break(&mut self, id: &str) {
|
||||
match self.s.svc.sessions.send_break(id) {
|
||||
Ok(()) => self.s.info("Break sent"),
|
||||
Err(e) => self.s.error(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles a serial modem line: `dtr` true for DTR, false for RTS.
|
||||
pub fn toggle_line(&mut self, id: &str, dtr: bool) {
|
||||
let Some((d, r)) = self.s.pane(id).map(|p| p.lines) else { return };
|
||||
let (d, r) = if dtr { (!d, r) } else { (d, !r) };
|
||||
match self.s.svc.sessions.set_serial_lines(id, d, r) {
|
||||
Ok(()) => {
|
||||
if let Some(p) = self.s.pane_mut(id) {
|
||||
p.lines = (d, r);
|
||||
}
|
||||
self.s.info(format!("DTR {} · RTS {}", if d { "on" } else { "off" }, if r { "on" } else { "off" }));
|
||||
}
|
||||
Err(e) => self.s.error(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_broadcast(&mut self) {
|
||||
self.s.broadcast = !self.s.broadcast;
|
||||
if !self.s.broadcast {
|
||||
|
||||
@@ -77,7 +77,7 @@ pub fn title_bar(app: &mut App, ui: &mut Ui) {
|
||||
app.s.request(Request::ExportEncrypted(vec![]));
|
||||
}
|
||||
if item(ui, icon::FILE_ARROW_DOWN, "Import ~/.ssh/config", "") {
|
||||
match app.s.svc.import_ssh_config(None, "Imported") {
|
||||
match app.s.svc.import_ssh_config(None, "SSH Config") {
|
||||
Ok(n) => {
|
||||
app.s.success(format!("Imported {n} host(s) from ~/.ssh/config"));
|
||||
app.s.reload_profiles();
|
||||
|
||||
@@ -56,7 +56,9 @@ fn main() -> eframe::Result {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
println!("usage: nexus-terminal [--theme pro|midnight|light] [--data-dir DIR] [--screenshot out.png] [--size WxH]");
|
||||
println!(
|
||||
"usage: nexus-terminal [--theme pro|midnight|light] [--data-dir DIR] [--connect NAME] [--open WINDOW] [--run-local CMD] [--screenshot out.png] [--size WxH] [--version]"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ impl Recordings {
|
||||
ui.separator();
|
||||
if self.list.is_empty() {
|
||||
if !self.loading {
|
||||
widgets::empty(ui, icon::RECORD, "No recordings yet", "Use the record button on a terminal tab to capture a session.");
|
||||
widgets::empty(ui, icon::RECORD, "No recordings yet", "Press Ctrl/Cmd+Shift+R in a terminal, or use Session → Toggle recording.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ If your server requires a time-based one-time password (TOTP) during login, stor
|
||||
## Jump Hosts (Bastions)
|
||||
If you need to reach a server through an intermediate host, add one or more jump hosts in the connection editor. Nexus Terminal tunnels through each hop automatically.
|
||||
## Port Forwarding
|
||||
You can set up **local**, **remote**, and **dynamic** (SOCKS proxy) port forwards in the connection editor. These start automatically when the session connects.
|
||||
You can set up **local** and **remote** port forwards in the connection editor. These start automatically when the session connects.
|
||||
## Proxy Support
|
||||
Open the **Proxy** section in the profile editor to connect through a **SOCKS5** or **HTTP CONNECT** proxy, with optional username/password authentication. The proxy is used to reach the SSH server (or the first jump host if jump hosts are configured).
|
||||
## SFTP File Browser
|
||||
@@ -738,7 +738,7 @@ Enable **Auto-Reconnect** in the Advanced section to reconnect automatically whe
|
||||
- **Device** — pick the port from the list (refresh after plugging in an adapter), e.g. `/dev/ttyUSB0`, `/dev/cu.usbserial-…` or `COM3`.
|
||||
- **Line settings** — baud rate (default 115200), data bits, parity, stop bits and flow control (none, XON/XOFF, RTS/CTS).
|
||||
- **Behaviour** — line ending, local echo, encoding, and whether DTR/RTS are raised on connect.
|
||||
- **Tab controls** — send a break and toggle DTR/RTS from the tab.
|
||||
- **Tab controls** — right-click the tab to send a break or toggle DTR/RTS.
|
||||
- **Auto-reconnect** — unplugging the adapter disconnects the session; it reopens the port when it comes back.
|
||||
|
||||
On Linux your user usually needs to be in the `dialout` (or `uucp`) group. On macOS use the `/dev/cu.*` device. A \"busy\" error means another program has the port open.",
|
||||
|
||||
@@ -67,6 +67,8 @@ pub struct Pane {
|
||||
pub connected_since: Option<chrono::DateTime<chrono::Local>>,
|
||||
/// Bumped to make trigger highlights flash.
|
||||
pub highlight_until: f64,
|
||||
/// Serial DTR / RTS as last set (starts from the profile's on-connect settings).
|
||||
pub lines: (bool, bool),
|
||||
}
|
||||
|
||||
/// A tab. Its session is `id`; `split` lays out extra panes (which have no tab of their own).
|
||||
|
||||
+22
-1
@@ -95,7 +95,7 @@ fn strip(app: &mut App, ui: &mut Ui) {
|
||||
for id in &order {
|
||||
let Some(p) = app.s.pane(id) else { continue };
|
||||
let p_info = p.info.clone();
|
||||
let (p_recording, p_since) = (p.recording, p.connected_since);
|
||||
let (p_recording, p_since, lines) = (p.recording, p.connected_since, p.lines);
|
||||
let p = PaneInfo { info: p_info, recording: p_recording, connected_since: p_since };
|
||||
let tab_activity = app.s.tab(id).is_some_and(|tb| tb.activity);
|
||||
let active = app.s.active.as_deref() == Some(id.as_str());
|
||||
@@ -203,6 +203,24 @@ fn strip(app: &mut App, ui: &mut Ui) {
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
if matches!(p.info.protocol, Protocol::Telnet | Protocol::Serial) {
|
||||
ui.separator();
|
||||
if ui.button(format!("{} Send Break", icon::LIGHTNING)).clicked() {
|
||||
menu_action = Some((id.clone(), "break"));
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
if p.info.protocol == Protocol::Serial {
|
||||
let (d, r) = lines;
|
||||
if ui.button(format!("{} DTR", if d { icon::CHECK_SQUARE } else { icon::SQUARE })).clicked() {
|
||||
menu_action = Some((id.clone(), "dtr"));
|
||||
ui.close();
|
||||
}
|
||||
if ui.button(format!("{} RTS", if r { icon::CHECK_SQUARE } else { icon::SQUARE })).clicked() {
|
||||
menu_action = Some((id.clone(), "rts"));
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if !ui.input(|i| i.pointer.primary_down()) {
|
||||
@@ -284,6 +302,9 @@ fn tab_action(app: &mut App, id: &str, a: &str) {
|
||||
}
|
||||
}
|
||||
"reconnect" => app.reconnect(id),
|
||||
"break" => app.send_break(id),
|
||||
"dtr" => app.toggle_line(id, true),
|
||||
"rts" => app.toggle_line(id, false),
|
||||
"close" => app.request_close(id),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user