mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
feat(remote): say which machine the window is on with the tab bar on top too
The badge lands under the workspace head in the tab rail, and the horizontal strip has no workspace head — so a window on another computer said nothing at all about it whenever the tab bar was on top. That is the state the badge exists to prevent, and it was the default half the time. - The strip carries the same dot-plus-name, in the drag slack after the New Tab button. It rides there rather than beside the chips so it can never take width from the tabs. - A chip has no second line for the rail's machine row, so a pane on another machine had nothing but a 6px dot. Its tooltip now leads with the machine — the half a reader cannot infer from the chip, since the title is at least partly on screen and the machine is a dot.
This commit is contained in:
@@ -70,7 +70,9 @@ badge — an SSH pane opened inside a local workspace, or a `wsl` shell beside
|
||||
Windows ones.
|
||||
|
||||
An empty remote workspace has no rail to carry the badge, so the home screen
|
||||
names the machine a new tab would open a shell on.
|
||||
names the machine a new tab would open a shell on. With the tab bar on top the
|
||||
badge sits beside the **+**, and a tab chip names its machine under the
|
||||
pointer.
|
||||
|
||||
## What gets installed
|
||||
|
||||
|
||||
+5
-1
@@ -384,7 +384,11 @@ impl Tab {
|
||||
|
||||
/// The leaf a tab names itself after — its title and, with it, the home
|
||||
/// that title's path is measured against.
|
||||
fn title_leaf(&self, window: Option<&Window>, cx: &App) -> Option<Entity<TerminalView>> {
|
||||
pub(crate) fn title_leaf(
|
||||
&self,
|
||||
window: Option<&Window>,
|
||||
cx: &App,
|
||||
) -> Option<Entity<TerminalView>> {
|
||||
let leaf = match window {
|
||||
Some(window) => self
|
||||
.pane
|
||||
|
||||
+101
-12
@@ -466,6 +466,19 @@ pub(crate) const BUTTON_ICON_SCALE: f32 = 0.75;
|
||||
/// Connection…* reaches every host anyway.
|
||||
const NEW_TAB_MENU_HOSTS: usize = 6;
|
||||
|
||||
/// What a tab chip says under the pointer: the machine it is on, then the title
|
||||
/// it could not fit, joined only when there are both.
|
||||
///
|
||||
/// The machine leads because it is the half a reader cannot infer from the chip
|
||||
/// — the title is at least partly on screen, and the machine is a 6px dot.
|
||||
fn chip_tooltip(machine: Option<String>, title: Option<String>) -> Option<SharedString> {
|
||||
match (machine, title) {
|
||||
(None, None) => None,
|
||||
(None, Some(one)) | (Some(one), None) => Some(SharedString::from(one)),
|
||||
(Some(machine), Some(title)) => Some(SharedString::from(format!("{machine} · {title}"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const MIN_TARGET: f32 = 24.;
|
||||
|
||||
pub(crate) fn hit_target(button: Button) -> Button {
|
||||
@@ -974,17 +987,27 @@ impl Tty7App {
|
||||
window: Option<&Window>,
|
||||
cx: &App,
|
||||
) -> Option<SharedString> {
|
||||
if tab.name.as_ref().is_some_and(|n| !n.trim().is_empty()) {
|
||||
return None;
|
||||
}
|
||||
let (raw, home) = tab.leaf_title_and_home(window, cx);
|
||||
let raw = raw.trim();
|
||||
if raw.is_empty() || raw == self.tab_label(tab, index, window, cx) {
|
||||
return None;
|
||||
}
|
||||
Some(SharedString::from(
|
||||
abbreviate_home(raw, home.as_deref()).into_owned(),
|
||||
))
|
||||
// A chip has no second line, so a pane on another machine has nothing
|
||||
// but a 6px dot to say so. The rail's rows spell the machine out; here
|
||||
// the pointer is the only place it fits. Leads the tooltip, because it
|
||||
// is the part a reader cannot infer from the chip.
|
||||
let machine = tab
|
||||
.title_leaf(window, cx)
|
||||
.and_then(|leaf| leaf.read(cx).remote_context())
|
||||
.map(|remote| remote.target)
|
||||
.filter(|target| !target.trim().is_empty());
|
||||
let title = match tab.name.as_ref().filter(|n| !n.trim().is_empty()) {
|
||||
Some(_) => None,
|
||||
None => {
|
||||
let (raw, home) = tab.leaf_title_and_home(window, cx);
|
||||
let raw = raw.trim();
|
||||
match raw.is_empty() || raw == self.tab_label(tab, index, window, cx) {
|
||||
true => None,
|
||||
false => Some(abbreviate_home(raw, home.as_deref()).into_owned()),
|
||||
}
|
||||
}
|
||||
};
|
||||
chip_tooltip(machine, title)
|
||||
}
|
||||
|
||||
pub(crate) fn tab_label(
|
||||
@@ -1012,6 +1035,46 @@ impl Tty7App {
|
||||
}
|
||||
}
|
||||
|
||||
/// The machine this window is on, for the horizontal tab strip.
|
||||
///
|
||||
/// The rail carries this under the workspace head, and the strip has no
|
||||
/// workspace head — so with the tab bar on top a window on another computer
|
||||
/// said nothing at all about it, which is the state this badge exists to
|
||||
/// prevent. Same dot-plus-name as the rail's and the switcher's, so one
|
||||
/// machine reads the same wherever it is shown.
|
||||
///
|
||||
/// `None` on a local workspace: a badge that is always there stops being
|
||||
/// read.
|
||||
fn strip_machine_badge(&self, cx: &mut Context<Self>) -> Option<impl IntoElement + use<>> {
|
||||
let machine = self.window_machine(cx)?;
|
||||
let theme = cx.theme();
|
||||
let muted = theme.muted_foreground;
|
||||
let dot = machine.status.dot(theme);
|
||||
let state = machine.status.short_label();
|
||||
Some(
|
||||
h_flex()
|
||||
.id("strip-machine")
|
||||
.flex_shrink(1.)
|
||||
.min_w_0()
|
||||
.items_center()
|
||||
.gap(px(6.))
|
||||
.pl(px(8.))
|
||||
.text_xs()
|
||||
.text_color(muted)
|
||||
.child(div().flex_shrink_0().size(px(6.)).rounded_full().bg(dot))
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.truncate()
|
||||
.child(SharedString::from(machine.label)),
|
||||
)
|
||||
.when_some(state, |row, state| {
|
||||
row.child(div().flex_shrink_0().child("·"))
|
||||
.child(div().flex_shrink_0().child(state))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn attach_new_tab_menu(
|
||||
&self,
|
||||
button: Button,
|
||||
@@ -1679,7 +1742,17 @@ impl Tty7App {
|
||||
.when_some(left_group, |this, g| this.child(g))
|
||||
.child(chips)
|
||||
.when(show_chips, move |this| this.child(add_button))
|
||||
.child(div().flex_1().min_w(px(GRAB_HANDLE_W)))
|
||||
// The machine badge rides in the drag slack rather than beside the
|
||||
// chips: it must never take width from the tabs, and there is
|
||||
// normally far more room here than it needs.
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_1()
|
||||
.min_w(px(GRAB_HANDLE_W))
|
||||
.items_center()
|
||||
.overflow_hidden()
|
||||
.children(self.strip_machine_badge(cx)),
|
||||
)
|
||||
.when_some(right_chrome, |this, chrome| match chrome_band_w {
|
||||
Some(w) => this.child(
|
||||
h_flex()
|
||||
@@ -1698,6 +1771,22 @@ impl Tty7App {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::TestAppContext;
|
||||
|
||||
/// A chip has no second line, so a pane on another machine has nothing but
|
||||
/// a 6px dot to say so — the pointer is the only place the machine fits.
|
||||
#[test]
|
||||
fn a_chip_names_its_machine_before_the_title_it_could_not_fit() {
|
||||
let m = || Some("java-box".to_string());
|
||||
let t = || Some("java@java: ~".to_string());
|
||||
assert_eq!(chip_tooltip(None, None), None);
|
||||
assert_eq!(chip_tooltip(m(), None).as_deref(), Some("java-box"));
|
||||
assert_eq!(chip_tooltip(None, t()).as_deref(), Some("java@java: ~"));
|
||||
assert_eq!(
|
||||
chip_tooltip(m(), t()).as_deref(),
|
||||
Some("java-box · java@java: ~"),
|
||||
"the machine leads: it is the half the chip cannot show at all"
|
||||
);
|
||||
}
|
||||
use std::path::Path;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user