From f660ddb1ccf6d98960f0af7eac800096cecd1ef1 Mon Sep 17 00:00:00 2001 From: l0ng-ai Date: Wed, 29 Jul 2026 16:19:43 +0800 Subject: [PATCH] fix(ui): safe delete prompt default, platform-aware reveal labels, and searchable agent settings (#255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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> --- CHANGELOG.md | 25 +++++++++++++++++++++ src/ui/file_tree.rs | 21 ++++++++++------- src/ui/right_panel.rs | 18 ++++++++++----- src/ui/settings.rs | 52 +++++++++++++++++++++++++++++++++---------- src/ui/sftp.rs | 2 +- 5 files changed, 92 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59039984..c1745700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/ui/file_tree.rs b/src/ui/file_tree.rs index 3c01a1a7..09475437 100644 --- a/src/ui/file_tree.rs +++ b/src/ui/file_tree.rs @@ -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)); diff --git a/src/ui/right_panel.rs b/src/ui/right_panel.rs index 88e10be5..9df59afe 100644 --- a/src/ui/right_panel.rs +++ b/src/ui/right_panel.rs @@ -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) -> 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::*; diff --git a/src/ui/settings.rs b/src/ui/settings.rs index f7bd3c7e..cd494207 100644 --- a/src/ui/settings.rs +++ b/src/ui/settings.rs @@ -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"); diff --git a/src/ui/sftp.rs b/src/ui/sftp.rs index c639456c..85b6ee02 100644 --- a/src/ui/sftp.rs +++ b/src/ui/sftp.rs @@ -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) })),