fix(reveal): stop offering Finder a path that is not on this machine

The file tree and the Session panel both list whatever host the workspace
spawns on, and every other action they offer goes through that host. Only
"Reveal in Finder" does not: `cx.reveal_path` talks to the local file
manager, so in a remote workspace it was handed a path that lives on the
other machine. Finder either does nothing at all — a menu item that
silently no-ops — or, if a local path happens to collide, opens the wrong
directory.

Both are now hidden unless the paths really are local:

| | Gate |
|---|---|
| File tree context menu | `spawn_host(cx).is_local()`, the same predicate `guard_local_spawn` uses |
| Session panel button | `view.local_cwd().is_some()` — a helper that already existed for exactly this (`remote_context().is_none() && host_id.is_local()`) and that this call site was not using |

Copy Path stays in both places: a remote path is still worth copying.

Checked on screen that the local file tree keeps the item and its
separators. The remote branch is reasoned from the predicate, not
exercised — it needs a real SSH host.
This commit is contained in:
l0ng-ai
2026-08-08 07:22:41 +08:00
parent b77f70310d
commit fa12d84b9b
2 changed files with 42 additions and 32 deletions
+19 -13
View File
@@ -1445,6 +1445,7 @@ impl Tty7App {
let path = path.clone();
let is_root = row.is_root;
let show_hidden = self.file_tree.show_hidden;
let paths_are_local = self.spawn_host(cx).is_local();
move |menu, _window, cx| {
let danger = cx.theme().danger;
Self::tree_row_context_menu(
@@ -1453,6 +1454,7 @@ impl Tty7App {
is_dir,
is_root,
show_hidden,
paths_are_local,
danger,
&app,
)
@@ -1489,6 +1491,12 @@ impl Tty7App {
is_dir: bool,
is_root: bool,
show_hidden: bool,
// The tree lists whatever host the workspace spawns on. Everything else
// in this menu goes through that host; the file manager only knows this
// machine, so over SSH the item would hand Finder a path that is not
// here — silently opening nothing, or the wrong thing if a local path
// happens to collide.
paths_are_local: bool,
danger: gpui::Hsla,
app: &gpui::WeakEntity<Self>,
) -> PopupMenu {
@@ -1588,19 +1596,16 @@ impl Tty7App {
);
}
menu = menu
.separator()
.item(
PopupMenuItem::new(t(L10nKey::FileTreeContextCopyPath)).on_click({
let p = p.clone();
move |_, _window, cx| {
cx.write_to_clipboard(gpui::ClipboardItem::new_string(
p.display().to_string(),
));
}
}),
)
.item(
menu = menu.separator().item(
PopupMenuItem::new(t(L10nKey::FileTreeContextCopyPath)).on_click({
let p = p.clone();
move |_, _window, cx| {
cx.write_to_clipboard(gpui::ClipboardItem::new_string(p.display().to_string()));
}
}),
);
if paths_are_local {
menu = menu.item(
PopupMenuItem::new(crate::ui::right_panel::reveal_label()).on_click({
let p = p.clone();
move |_, _window, cx| {
@@ -1608,6 +1613,7 @@ impl Tty7App {
}
}),
);
}
menu = menu.separator().item(dotfiles_menu_item(show_hidden, app));
+23 -19
View File
@@ -357,7 +357,7 @@ impl Tty7App {
fn render_panel_info(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
let title = self.panel_title(t(L10nKey::PanelInfoTitle), None, None, window, cx);
let mut rows: Vec<(&'static str, String)> = Vec::new();
let mut cwd_for_actions: Option<PathBuf> = None;
let mut cwd_for_actions: Option<(PathBuf, bool)> = None;
let mut pane_id: Option<u64> = None;
let mut forwards_pane: Option<u64> = None;
@@ -371,7 +371,9 @@ impl Tty7App {
.or_else(|| view.cwd())
{
rows.push((t(L10nKey::PanelCwd), compact_path(&cwd)));
cwd_for_actions = Some(cwd);
// Copy Path is right either way; Reveal only means anything
// when the path is on the machine the file manager can see.
cwd_for_actions = Some((cwd, view.local_cwd().is_some()));
}
let shell = match view.shell_spec().map(|s| s.program.clone()) {
Some(program) => crate::core::shells::default_shell_name(Some(&program)),
@@ -454,8 +456,8 @@ impl Tty7App {
let inner = v_flex()
.child(self.panel_subtitle(t(L10nKey::PanelSessionSubtitle), false, None, cx))
.child(list)
.when_some(cwd_for_actions, |this, cwd| {
this.child(self.cwd_actions(cwd, cx))
.when_some(cwd_for_actions, |this, (cwd, local)| {
this.child(self.cwd_actions(cwd, local, cx))
})
.children(self.procs_section(pane_id, cx))
.children(self.ports_section(pane_id, cx))
@@ -464,27 +466,29 @@ impl Tty7App {
self.panel_scroll(inner, title)
}
fn cwd_actions(&self, cwd: PathBuf, cx: &mut Context<Self>) -> AnyElement {
fn cwd_actions(&self, cwd: PathBuf, local: bool, cx: &mut Context<Self>) -> AnyElement {
let reveal_label = reveal_label();
h_flex()
.gap(px(2.))
.px(px(tile_trailing_inset_sm()))
.pt(px(6.))
.child(
crate::ui::tab_strip::chrome_tile_sized(
Button::new("panel-info-reveal").icon(Icon::new(IconName::FolderOpen)),
TILE_SIZE_SM,
TILE_GLYPH_SM,
false,
cx,
.when(local, |this| {
this.child(
crate::ui::tab_strip::chrome_tile_sized(
Button::new("panel-info-reveal").icon(Icon::new(IconName::FolderOpen)),
TILE_SIZE_SM,
TILE_GLYPH_SM,
false,
cx,
)
.rounded_md()
.tooltip(reveal_label)
.on_click({
let cwd = cwd.clone();
move |_, _window, cx| cx.reveal_path(&cwd)
}),
)
.rounded_md()
.tooltip(reveal_label)
.on_click({
let cwd = cwd.clone();
move |_, _window, cx| cx.reveal_path(&cwd)
}),
)
})
.child(
crate::ui::tab_strip::chrome_tile_sized(
Button::new("panel-info-copy-path").icon(Icon::new(IconName::Copy)),