From 1445b1151accab8744db6b9b5a7c5d34e2bd1a62 Mon Sep 17 00:00:00 2001 From: leetcrypt Date: Sat, 30 May 2026 22:03:00 -0700 Subject: [PATCH] feat(hh): /drive command (mobile-friendly) + Esc no longer quits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F2 is unreachable on phone/Termux keyboards, so add a /drive chat command to enter sandbox drive mode (type into the shared shell; Esc releases). F2 still works on desktop. Esc no longer quits from chat mode (footgun on mobile) — quit is Ctrl-Q only. Updated on-screen hints + sandbox pane title. Co-Authored-By: Claude Opus 4.8 --- hh/src/app.rs | 15 ++++++++++++--- hh/src/ui.rs | 7 ++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hh/src/app.rs b/hh/src/app.rs index f48c5e4..f79ea82 100644 --- a/hh/src/app.rs +++ b/hh/src/app.rs @@ -135,7 +135,7 @@ impl App { self.users = users; self.connected = true; self.sys(format!("joined as {} ⛧", self.me)); - self.sys("/send · /sendd · /sbx launch · F2 drive · ctrl-q quit"); + self.sys("/sbx launch · /drive (type in shell, Esc to release) · /send · ctrl-q quit"); } Net::Message(l) => self.lines.push(l), Net::Roster { users, capacity } => { @@ -391,7 +391,6 @@ pub async fn run(session: Session, theme: Theme) -> Result<()> { } } else { match k.code { - KeyCode::Esc => break Ok(()), KeyCode::Enter => { let line = app.input.trim().to_string(); app.input.clear(); @@ -507,7 +506,17 @@ fn handle_command( term: &Terminal>, ) { let room = &session.room; - if let Some(path) = line.strip_prefix("/sendd ").or_else(|| line.strip_prefix("/send ")) { + if line == "/drive" { + // Mobile-friendly alternative to F2 (no function key needed). + if app.sandbox.is_none() { + app.sys("no sandbox running — /sbx launch first"); + } else if app.can_drive() { + app.driving = true; + app.sys("⛧ drive mode ON — type into the shell · press Esc to release"); + } else { + app.sys("you don't have drive permission — the owner can /grant you"); + } + } else if let Some(path) = line.strip_prefix("/sendd ").or_else(|| line.strip_prefix("/send ")) { let path = path.trim(); match ft::read_payload(path) { Ok((name, bytes, dir)) => { diff --git a/hh/src/ui.rs b/hh/src/ui.rs index df86a18..f8ebc64 100644 --- a/hh/src/ui.rs +++ b/hh/src/ui.rs @@ -46,9 +46,9 @@ fn draw_sandbox(f: &mut Frame, area: ratatui::layout::Rect, app: &App, theme: &T .map(|r| Line::from(Span::styled(r, Style::default().fg(theme.title)))) .collect(); let drive = if app.driving { - " · DRIVING (esc to release)" + " · DRIVING — type here · Esc to release" } else { - " · F2 to drive" + " · type /drive (or F2) to take the shell" }; let title = format!(" sandbox · {}{} ", sv.backend, drive); let border = if app.driving { theme.accent } else { theme.border }; @@ -157,7 +157,8 @@ fn draw_input(f: &mut Frame, area: ratatui::layout::Rect, app: &App, theme: &The .title(Span::styled( match &app.pending_offer { Some(o) => format!(" ⛧ incoming: {} — /accept or /reject ", o.name), - None => " message · enter send · esc quit ".to_string(), + None if app.driving => " ⛧ DRIVING the shell — Esc to release ".to_string(), + None => " message · enter send · /drive for shell · ctrl-q quit ".to_string(), }, Style::default().fg(theme.title), )),