fix(ui): safe delete prompt default, platform-aware reveal labels, and searchable agent settings (#255)

* fix(ui): make Cancel the default button on the file-tree delete confirmation

The file tree's delete prompt was the only destructive prompt in tty7 with
the destructive action first. On macOS (NSAlert) and Windows (TaskDialog)
the first button is the Return-key default, so pressing Return deleted -
including recursive folder deletion. Linux uses gpui's fallback renderer,
which is click-only, so the swap only reorders the buttons there.

Safe option first, matching every other destructive prompt; the literal
"Cancel" is what gpui maps to PromptButton::Cancel and the Escape key.

* fix(ui): stop saying "Finder" on Linux and Windows

The file-tree context menu and the SFTP job tooltip hardcoded
Finder-flavoured labels; only the right panel's Info row was
platform-conditional. Extract that conditional into
right_panel::reveal_label() and use it at all three sites, so the action
reads "Reveal in Finder" on macOS and "Open Folder" elsewhere.

Side effect of sharing the helper: the SFTP tooltip's "Show in Finder"
becomes "Reveal in Finder" on macOS, converging a third name for the
same action.

* fix(settings): index the Grok Build agent row and re-align drifted index titles

The Grok Build agent renders a settings row but had no search-index
entry, so the row was unreachable by search. The other five agent
entries carried mechanism suffixes ("Claude Code hooks", "Pi
extension") that no longer match the rendered rows, which are titled by
HookAgent::display_name(); the index said "Option acts as Meta" where
the row says "Option (⌥) acts as Meta".

Align the titles with the rows, keep the mechanism words as search
keywords, and extend the index tests: the pinned-title list gains the
Option row, and a new test derives the Agents entries from
HookAgent::ALL so adding or renaming an agent without updating the index
fails the suite.

* no-mistakes(review): add plain search keywords for Option-acts-as-Meta entry

* no-mistakes(document): document copy fixes in changelog and apply rustfmt

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
l0ng-ai
2026-07-29 16:19:43 +08:00
committed by GitHub
co-authored by l0ng-ai
parent 6f842c3007
commit f660ddb1cc
5 changed files with 92 additions and 26 deletions
+25
View File
@@ -163,6 +163,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
involved something in the Wayland presentation path, were not reproducible on
macOS and are not claimed to be confirmed. (#243)
- **Return no longer confirms the file tree's delete prompt** — it was the only
destructive prompt in tty7 with the destructive action first, and on macOS
(NSAlert) and Windows (TaskDialog) the first button is the Return-key
default, so pressing Return deleted — recursive folder deletion included. The
buttons now put the safe option first, matching every other destructive
prompt, with Escape still cancelling. Linux uses gpui's click-only fallback
dialog, so the swap only reorders the buttons there.
- **"Finder" is no longer named on Linux and Windows** — the file-tree context
menu and the SFTP job list's reveal tooltip hardcoded Finder-flavoured
labels on every platform; only the Info row's button was conditional. All
three sites now share that one conditional, so the action reads "Reveal in
Finder" on macOS and "Open Folder" everywhere else. On macOS the SFTP
tooltip's "Show in Finder" becomes "Reveal in Finder" too, retiring a third
name for the same action.
- **Grok Build turns up in settings search** — the agent renders a Settings →
Agents row but had no search-index entry, so searching could never surface
it. The other five agent entries had drifted from what their rows actually
say ("Claude Code hooks" for a row titled "Claude Code"), as had "Option
acts as Meta" from the rendered "Option (⌥) acts as Meta"; the index titles
now match the rows, the mechanism words (hooks / plugin / extension) stay
behind as search keywords, and a test derives the Agents index from the
agent list so a future agent can't ship unsearchable.
## [26.7.6] - 2026-07-28
### Added
+13 -8
View File
@@ -1602,11 +1602,14 @@ impl Tty7App {
PromptLevel::Warning,
&format!("Delete \"{name}\"?"),
Some(detail),
&["Delete", "Cancel"],
// Safe option first: the leading button is the Return-key default on
// macOS (NSAlert) and Windows (TaskDialog); "Cancel" is what gpui maps
// to the Escape key.
&["Cancel", "Delete"],
cx,
);
cx.spawn_in(window, async move |app, cx| {
let Ok(0) = answer.await else { return };
let Ok(1) = answer.await else { return };
let _ = app.update_in(cx, |app, window, cx| {
let Some(host) = app.active_host(cx) else {
return;
@@ -2055,12 +2058,14 @@ impl Tty7App {
cx.write_to_clipboard(gpui::ClipboardItem::new_string(p.display().to_string()));
}
}))
.item(PopupMenuItem::new("Reveal in Finder").on_click({
let p = p.clone();
move |_, _window, cx| {
cx.reveal_path(&p);
}
}));
.item(
PopupMenuItem::new(crate::ui::right_panel::reveal_label()).on_click({
let p = p.clone();
move |_, _window, cx| {
cx.reveal_path(&p);
}
}),
);
menu = menu.separator().item(dotfiles_menu_item(show_hidden, app));
+13 -5
View File
@@ -706,11 +706,7 @@ impl Tty7App {
/// clipboard. An "open in $EDITOR" button would need a picker, a stored
/// choice and a settings page to change it; that's a feature, not a row.
fn cwd_actions(&self, cwd: PathBuf, cx: &mut Context<Self>) -> AnyElement {
let reveal_label = if cfg!(target_os = "macos") {
"Reveal in Finder"
} else {
"Open Folder"
};
let reveal_label = reveal_label();
h_flex()
.gap(px(2.))
.px(px(tile_trailing_inset_sm()))
@@ -1477,6 +1473,18 @@ pub(crate) fn info_chip(
.into_any_element()
}
/// The label for revealing a path in the OS file manager: only macOS has a
/// "Finder", so everywhere else it's the generic "Open Folder". Shared by the
/// Info row, the file-tree context menu and the SFTP job list so the action
/// carries one name per platform.
pub fn reveal_label() -> &'static str {
if cfg!(target_os = "macos") {
"Reveal in Finder"
} else {
"Open Folder"
}
}
/// The one-word status the Info row shows next to the agent's name.
fn agent_status_label(status: crate::core::cli_agent::AgentStatus) -> &'static str {
use crate::core::cli_agent::AgentStatus::*;
+40 -12
View File
@@ -270,8 +270,8 @@ fn settings_search_entries() -> &'static [SearchEntry] {
},
SearchEntry {
section: Input,
title: "Option acts as Meta",
keywords: "alt keyboard modifier escape macos option meta",
title: "Option (⌥) acts as Meta",
keywords: "alt keyboard modifier escape macos option meta option acts as meta",
},
SearchEntry {
section: Input,
@@ -311,30 +311,38 @@ fn settings_search_entries() -> &'static [SearchEntry] {
keywords: "ssh tunnel local remote dynamic socks forward rule",
},
// ── Agents ──────────────────────────────────────────────────────────
// Titles mirror `HookAgent::display_name()`, which is what each row is
// rendered with; the mechanism word (hooks/plugin/extension) lives in
// `keywords`. Pinned by `agent_rows_are_in_the_search_index`.
SearchEntry {
section: Agents,
title: "Claude Code hooks",
keywords: "agent integration install uninstall status rich session working waiting tab bar sidebar badge claude",
title: "Claude Code",
keywords: "agent integration hooks install uninstall status rich session working waiting tab bar sidebar badge claude",
},
SearchEntry {
section: Agents,
title: "Codex hooks",
keywords: "agent integration install openai codex",
title: "Codex",
keywords: "agent integration hooks install openai codex",
},
SearchEntry {
section: Agents,
title: "Copilot CLI hooks",
keywords: "agent integration install github copilot",
title: "Copilot CLI",
keywords: "agent integration hooks install github copilot",
},
SearchEntry {
section: Agents,
title: "OpenCode plugin",
keywords: "agent integration install opencode",
title: "OpenCode",
keywords: "agent integration plugin install opencode",
},
SearchEntry {
section: Agents,
title: "Pi extension",
keywords: "agent integration install pi",
title: "Pi",
keywords: "agent integration extension install pi",
},
SearchEntry {
section: Agents,
title: "Grok Build",
keywords: "agent integration hooks install xai grok build",
},
// ── Window & Tabs ───────────────────────────────────────────────────
SearchEntry {
@@ -5625,6 +5633,7 @@ mod tests {
"Tab completion",
"History search",
"Dim inactive panes",
"Option (⌥) acts as Meta",
] {
assert!(
settings_search_entries().iter().any(|e| e.title == title),
@@ -5633,6 +5642,25 @@ mod tests {
}
}
/// The Agents rows are titled by [`HookAgent::display_name`], so the index
/// is derived rather than pinned: every hook-capable agent must have an
/// Agents-section entry under exactly that name. Adding an agent to
/// `HookAgent::ALL` without indexing it — how Grok Build became
/// unsearchable — fails here, as does renaming an agent without moving its
/// index entry.
#[test]
fn agent_rows_are_in_the_search_index() {
for agent in crate::core::agent_hooks::HookAgent::ALL {
assert!(
settings_search_entries().iter().any(
|e| e.section == SettingsSection::Agents && e.title == agent.display_name()
),
"no Agents index entry titled {:?}",
agent.display_name()
);
}
}
#[test]
fn humanize_action_splits_on_capitals() {
assert_eq!(humanize_action("NewTab"), "New Tab");
+1 -1
View File
@@ -1816,7 +1816,7 @@ impl Tty7App {
.icon(IconName::FolderOpen)
.xsmall()
.ghost()
.tooltip("Show in Finder")
.tooltip(crate::ui::right_panel::reveal_label())
.on_click(cx.listener(move |this, _, _w, cx| {
this.sftp_reveal_download(local.clone(), cx)
})),