feat(mobile): drop the keyboard key beside the message box (#1096)

* fix(panel): drop the pid column from Info → Processes

The pid sat in a column of its own on every row, beside CPU% and memory.
It reads as a port number at a glance and is not what the list is read
for; the row's name, CPU% and memory already say which process is which.

* feat(mobile): drop the keyboard key beside the message box

A tap on the pane already brings up the keyboard for typing straight into
the terminal, so the round key that did the same only crowded the compose
row.
This commit is contained in:
l0ng-ai
2026-10-04 20:42:47 +08:00
committed by GitHub
parent fe80363e2a
commit 41a247a14f
5 changed files with 7 additions and 29 deletions
+1 -1
View File
@@ -325,7 +325,7 @@ The ⋯ drops a 232px raised panel, 14px corners, Float shadow, from the top-rig
Under a machine's title: an 8px dot plus words, 15px ink-2 with tabular numerals. "Direct · 4 ms" (done green), "Relay · n ms" (waiting amber), "Connecting…" (ink-3, breathing), "Offline" (danger). The terminal header repeats it compactly as a 7px dot and a 600-weight word ("Live", "Connecting", "Offline", "Closed").
### Terminal screen and keybar (signature)
The header is a tinted bar with back chevron, small avatar, title over the state line and Hack cwd, and a single fit/zoom toggle shown only when the pane is too wide to read. The pane keeps the desktop's cols×rows; the font shrinks to fit, or holds at 11px and pans to follow the cursor. Errors float in as a raised banner over the top of the pane; a dropped connection is retried on its own (1s, 2s, 4s … 15s, and at once when the network comes back), with the last good screen left up under a "Reconnecting…" banner until the new replay replaces it. A phone key beside it takes the pane over: it fills tint while held, the pane runs at the phone's grid at 11px with nothing to fit or pan, and the grid follows the keyboard and rotation once they settle. When the desktop takes it back, the banner says so with "Take over again"; taking over again is always a tap, never automatic. A copy key beside fit/zoom lays the whole buffer out as a plain page over the pane, wrapped to the phone and rejoined where the terminal wrapped, so the phone's own selection handles and Copy work on it. The keybar is a horizontally scrolling row of keycaps (esc, tab, ⇧tab, ctrl, ^C, arrows, paste, | / ~ -) fading out at the right edge, plus two fixed accent keys: compose and keyboard. Keys press to 0.94 scale in cell-press; the ctrl latch and an open compose box fill their key accent. The compose box docks on the keybar in the same surface: a pill field (16px system face, so iOS never zooms on focus) growing to about five lines, and a round accent send button. Send types the text, then Enter; several lines go in as one bracketed paste where the program asked for it. An agent's pane opens on the box with the keyboard down, so the screen is read before anything is typed.
The header is a tinted bar with back chevron, small avatar, title over the state line and Hack cwd, and a single fit/zoom toggle shown only when the pane is too wide to read. The pane keeps the desktop's cols×rows; the font shrinks to fit, or holds at 11px and pans to follow the cursor. Errors float in as a raised banner over the top of the pane; a dropped connection is retried on its own (1s, 2s, 4s … 15s, and at once when the network comes back), with the last good screen left up under a "Reconnecting…" banner until the new replay replaces it. A phone key beside it takes the pane over: it fills tint while held, the pane runs at the phone's grid at 11px with nothing to fit or pan, and the grid follows the keyboard and rotation once they settle. When the desktop takes it back, the banner says so with "Take over again"; taking over again is always a tap, never automatic. A copy key beside fit/zoom lays the whole buffer out as a plain page over the pane, wrapped to the phone and rejoined where the terminal wrapped, so the phone's own selection handles and Copy work on it. The keybar is a horizontally scrolling row of keycaps (esc, tab, ⇧tab, ctrl, ^C, arrows, paste, | / ~ -) fading out at the right edge, plus a fixed accent compose key. Keys press to 0.94 scale in cell-press; the ctrl latch and an open compose box fill their key accent. The compose box docks on the keybar in the same surface: a pill field (16px system face, so iOS never zooms on focus) growing to about five lines, and a round accent send button. Send types the text, then Enter; several lines go in as one bracketed paste where the program asked for it. An agent's pane opens on the box with the keyboard down, so the screen is read before anything is typed.
### Empty and loading states
Empty: centred, 56px top padding, the Duo logo mark (76px) or a 64px accent tile, a 22px headline, a 16px ink-2 sentence, and at most one primary button. Loading: three skeleton rows with hairline avatars and 12px bones breathing at 1.6s.
-3
View File
@@ -14,9 +14,6 @@ export const icon = {
server: stroke(
`<rect x="2" y="3" width="20" height="7.4" rx="2.4"/><rect x="2" y="13.6" width="20" height="7.4" rx="2.4"/><path d="M6.9 6.7h.01M6.9 17.3h.01"/>`,
),
keyboard: stroke(
`<rect x="2.5" y="6" width="19" height="12" rx="2.5"/><path d="M6.5 10h.01M10 10h.01M14 10h.01M17.5 10h.01M8 14h8"/>`,
),
up: stroke(`<path d="M12 18V6M6.5 11.5 12 6l5.5 5.5"/>`, 2),
down: stroke(`<path d="M12 6v12M6.5 12.5 12 18l5.5-5.5"/>`, 2),
left: stroke(`<path d="M18 12H6M11.5 6.5 6 12l5.5 5.5"/>`, 2),
+2 -13
View File
@@ -2743,9 +2743,6 @@ function terminalScreen(host: Host, place: Place, pane: PaneView, title: string,
};
writeFor(pane.agent);
const sendKey = h("button", { class: "round send", ariaLabel: "Send" }, ico("send"));
// Typing straight into the terminal, key by key, for what a message box
// cannot do: a full-screen program, a password prompt.
const keyboard = h("button", { class: "round", ariaLabel: "Type into the terminal" }, ico("keyboard"));
// Past messages: the whole list, searchable, while the box is empty; the
// ones that match, in place of the key row, as it is written in.
const historyKey = h("button", { class: "round", ariaLabel: "History" }, ico("history"));
@@ -2753,7 +2750,7 @@ function terminalScreen(host: Host, place: Place, pane: PaneView, title: string,
// an agent to be pointed at with whatever is written around it.
const attachKey = h("button", { class: "round", ariaLabel: "Attach a photo or file" }, ico("attach"));
const picker = h("input", { type: "file", multiple: true, hidden: true });
const compose = h("div", { class: "compose" }, attachKey, historyKey, field, sendKey, keyboard, picker);
const compose = h("div", { class: "compose" }, attachKey, historyKey, field, sendKey, picker);
// Files sent to the machine, waiting to go with the message: shown by
// name and picture, their paths written in only when it is sent.
const attached: { path: string; name: string; thumb: string | null }[] = [];
@@ -3110,10 +3107,9 @@ function terminalScreen(host: Host, place: Place, pane: PaneView, title: string,
const edited = () => {
if (field.value) drafts.set(draftKey, field.value);
else drafts.delete(draftKey);
// Written, the round button sends; empty, it is the keyboard's.
// Written, the round button sends; empty, it is not there.
const ready = !!field.value || attached.length > 0;
sendKey.hidden = !ready;
keyboard.hidden = ready;
historyKey.hidden = ready;
grow();
offer();
@@ -3291,19 +3287,12 @@ function terminalScreen(host: Host, place: Place, pane: PaneView, title: string,
sendKey.onpointerdown = (e) => e.preventDefault();
sendKey.onclick = () => (feel("key"), void submit());
keyboard.onpointerdown = (e) => e.preventDefault();
keyboard.onclick = () => {
if (document.activeElement === typing) typing.blur();
else typing.focus({ preventScroll: true });
};
// While the field has the keys the pane's cursor is drawn solid, as a
// focused terminal's is, so a tap on the pane shows where typing lands.
typing.addEventListener("focus", () => {
keyboard.classList.add("on");
term.options.cursorInactiveStyle = "block";
});
typing.addEventListener("blur", () => {
keyboard.classList.remove("on");
term.options.cursorInactiveStyle = "outline";
typing.value = "";
typed = "";
-5
View File
@@ -1668,11 +1668,6 @@ html.scanning #app {
height: 22px;
}
.compose .round.on {
background: var(--ink);
color: var(--accent-ink);
}
.round.send {
background: var(--ink);
color: var(--accent-ink);
+4 -7
View File
@@ -17,7 +17,6 @@ use crate::ui::right_panel::{META, META_MONO};
const CPU_W: f32 = 40.;
const MEM_W: f32 = 52.;
const PID_W: f32 = 44.;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Sample {
@@ -109,7 +108,7 @@ fn cell(text: String, w: f32, mono: SharedString, cx: &App) -> impl IntoElement
.child(text)
}
/// CPU%, memory and pid, right-aligned in fixed columns so rows line up.
/// CPU% and memory, right-aligned in fixed columns so rows line up.
pub(crate) fn row_cells(
p: &ProcEntry,
cpu: &CpuTracker,
@@ -128,10 +127,9 @@ pub(crate) fn row_cells(
.child(cell(
p.rss.map(compact_bytes).unwrap_or_default(),
MEM_W,
mono.clone(),
mono,
cx,
))
.child(cell(p.pid.to_string(), PID_W, mono, cx))
}
/// The Total line under the tree, or nothing from a daemon that reports no
@@ -169,10 +167,9 @@ pub(crate) fn total_row(
.child(cell(
rss.map(compact_bytes).unwrap_or_default(),
MEM_W,
mono.clone(),
mono,
cx,
))
.child(cell(String::new(), PID_W, mono, cx)),
)),
),
)
}