From 1e7284db2bb1fd19e87e89f6a5fbb4f1be23a859 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sat, 8 Aug 2026 07:57:37 +0800 Subject: [PATCH] fix(palette): give the "edit" badge a chord that can actually fire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every row with an edit variant — quick-connect and every saved SSH profile — advertised "→ edit", and neither of the two gestures behind it worked: - `→` never reaches the palette. gpui-component's `Input` binds bare `right` to MoveRight inside its own key context, and the query field has focus the whole time the palette is open, so the keystroke is consumed before the scrim's handler sees it. - `⌘↵` never reaches it either. The app binds `secondary-enter` to ToggleFullscreen, which matches first — pressing it in the palette put the window into fullscreen. So a saved profile could not be opened for editing from the palette at all, which is the only row-scoped way in; Settings → SSH lists them, but loses which row you were on. The chord is now `secondary-e`, claimed by neither the input nor the app. It lives in one `EDIT_GESTURE` constant next to the matcher that reads it, and the badge renders through `key_tokens`, so it says ⌘E on macOS and Ctrl E elsewhere rather than a hardcoded arrow baked into three locale tables. `EditHint` is now just the word ("edit" / "编辑" / "編集"). Verified on screen: the badge reads "edit ⌘E", and ⌘E on a quick-connect row opens the SSH profile form with the host, user and port already filled in from the query. --- src/ui/i18n/en.rs | 2 +- src/ui/i18n/ja.rs | 2 +- src/ui/i18n/zh.rs | 2 +- src/ui/palette.rs | 34 +++++++++++++++++++++++++++++----- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/ui/i18n/en.rs b/src/ui/i18n/en.rs index 6cf0ae03..f883464d 100644 --- a/src/ui/i18n/en.rs +++ b/src/ui/i18n/en.rs @@ -33,7 +33,7 @@ pub fn translate_en(key: L10nKey) -> &'static str { L10nKey::Delete => "Delete", L10nKey::NoMatchingCommands => "No matching commands", L10nKey::ConnectSshHint => "Type user@host to connect over SSH instead.", - L10nKey::EditHint => "→ edit", + L10nKey::EditHint => "edit", L10nKey::OpenFileFromTree => "Open a file from the file tree", L10nKey::TreeDirLoading => "Reading…", L10nKey::TreeDirEmpty => "Empty", diff --git a/src/ui/i18n/ja.rs b/src/ui/i18n/ja.rs index 0a1a7a5a..7fbe1803 100644 --- a/src/ui/i18n/ja.rs +++ b/src/ui/i18n/ja.rs @@ -33,7 +33,7 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { L10nKey::Delete => "削除", L10nKey::NoMatchingCommands => "一致するコマンドがありません", L10nKey::ConnectSshHint => "SSH で接続するには user@host を入力してください", - L10nKey::EditHint => "→ 編集", + L10nKey::EditHint => "編集", L10nKey::OpenFileFromTree => "ファイルツリーからファイルを開く", L10nKey::TreeDirLoading => "読み込み中…", L10nKey::TreeDirEmpty => "空", diff --git a/src/ui/i18n/zh.rs b/src/ui/i18n/zh.rs index e7129ead..70a82fb7 100644 --- a/src/ui/i18n/zh.rs +++ b/src/ui/i18n/zh.rs @@ -33,7 +33,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::Delete => "删除", L10nKey::NoMatchingCommands => "没有匹配的命令", L10nKey::ConnectSshHint => "输入 user@host 改为通过 SSH 连接。", - L10nKey::EditHint => "→ 编辑", + L10nKey::EditHint => "编辑", L10nKey::OpenFileFromTree => "从文件树打开文件", L10nKey::TreeDirLoading => "读取中…", L10nKey::TreeDirEmpty => "空", diff --git a/src/ui/palette.rs b/src/ui/palette.rs index 5e59fae7..a24f02fe 100644 --- a/src/ui/palette.rs +++ b/src/ui/palette.rs @@ -878,10 +878,13 @@ impl ListDelegate for PaletteDelegate { .child(left); if cmd.kind.edit_variant().is_some() { row = row.child( - div() + h_flex() + .items_center() + .gap_1() .text_xs() .text_color(muted) - .child(crate::ui::i18n::t(crate::ui::i18n::L10nKey::EditHint)), + .child(crate::ui::i18n::t(crate::ui::i18n::L10nKey::EditHint)) + .child(crate::ui::keymap::key_tokens(EDIT_GESTURE).join("")), ); } if let Some(tokens) = keys { @@ -1070,6 +1073,29 @@ impl PaletteView { impl EventEmitter for PaletteView {} const PALETTE_ROW_H: f32 = 30.; + +/// The chord that opens the selected row for editing instead of running it. +/// +/// It cannot be `→`: gpui-component's `Input` binds bare `right` to MoveRight +/// in its own key context, so with the query field focused the palette never +/// sees the key — the old `→ edit` badge was advertising a gesture that could +/// not fire. `⌘↵` is no better; the app binds it to ToggleFullscreen, which +/// wins for the same reason. `secondary-e` is claimed by neither. +const EDIT_GESTURE: &str = "secondary-e"; + +/// Matches `EDIT_GESTURE` against a live keystroke. Keep the two in step. +fn is_edit_gesture(ks: &gpui::Keystroke) -> bool { + if ks.key != "e" { + return false; + } + let m = &ks.modifiers; + let secondary = if cfg!(target_os = "macos") { + m.platform + } else { + m.control + }; + secondary && !m.shift && !m.alt +} const PALETTE_VISIBLE_ROWS: f32 = 12.; const RECENT_ROWS: usize = 5; @@ -1106,9 +1132,7 @@ impl Render for PaletteView { .bg(scrim) .on_key_down(cx.listener(|this, ev: &gpui::KeyDownEvent, _window, cx| { let ks = &ev.keystroke; - let is_edit_gesture = (ks.key == "enter" && ks.modifiers.platform) - || (ks.key == "right" && !ks.modifiers.platform); - if is_edit_gesture { + if is_edit_gesture(ks) { if let Some(edit) = this.selected_edit_command(cx) { cx.stop_propagation(); cx.emit(PaletteEvent::Confirm(edit));