mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 16:02:24 +00:00
fix(settings): let the Keybindings page speak the app's own words
The page the docs call *the* shortcut reference rendered 89 undivided rows of humanize_action's CamelCase split — English in a three-locale app, and a fourth vocabulary on top of the menu bar, the palette and the docs: "Toggle Maximize Pane" for what everything else calls Zoom Pane, "Close Active Tab" for Close Pane / Tab, "Toggle Palette" for Command Palette. Adds keymap::action_entry, which routes every action's name and section through the strings the rest of the app already uses. The list is now read in the same seven sections the command palette groups by, and the displaced-binding notice uses the same names as the rows it is talking about. Seven new keys across en/zh/ja; the nine tab and nine workspace jumps are templated rather than eighteen hand-written strings per locale. every_action_has_an_authored_name fails if a new action would fall back to the CamelCase split, so the page cannot drift back one action at a time.
This commit is contained in:
+3
-5
@@ -34,9 +34,7 @@ use crate::ui::palette::{
|
||||
};
|
||||
use crate::ui::pane::{CloseOutcome, Dir, Pane, PaneSlot};
|
||||
use crate::ui::presets::Fill;
|
||||
use crate::ui::settings::{
|
||||
Recording, SettingsSection, SettingsState, ThemeEditor, humanize_action,
|
||||
};
|
||||
use crate::ui::settings::{Recording, SettingsSection, SettingsState, ThemeEditor};
|
||||
use crate::ui::theme::{apply_theme, set_menus, window_background};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -4859,8 +4857,8 @@ impl Tty7App {
|
||||
t_fmt(
|
||||
L10nKey::AppKeybindingDisplacedNote,
|
||||
&[
|
||||
("action", &humanize_action(&action)),
|
||||
("previous", &humanize_action(other)),
|
||||
("action", &crate::ui::keymap::action_entry(&action).1),
|
||||
("previous", &crate::ui::keymap::action_entry(other).1),
|
||||
],
|
||||
)
|
||||
});
|
||||
|
||||
@@ -452,6 +452,13 @@ pub fn translate_en(key: L10nKey) -> &'static str {
|
||||
"With a prefix active, a bare prefix key reaches the shell after a ~1s pause, and prefix + an unbound key is sent through to the terminal."
|
||||
}
|
||||
L10nKey::SettingsRestoreAllDefaults => "Restore all defaults",
|
||||
L10nKey::KeybindGoToTab => "Go to Tab {n}",
|
||||
L10nKey::KeybindGoToWorkspace => "Go to Workspace {n}",
|
||||
L10nKey::KeybindInsertNewline => "Insert Newline",
|
||||
L10nKey::KeybindForkSessionRight => "Fork Session Right",
|
||||
L10nKey::KeybindForkSessionLeft => "Fork Session Left",
|
||||
L10nKey::KeybindForkSessionDown => "Fork Session Down",
|
||||
L10nKey::KeybindForkSessionUp => "Fork Session Up",
|
||||
L10nKey::SettingsAboutDesc1 => {
|
||||
"A terminal workbench: persistent sessions, remote work, agents."
|
||||
}
|
||||
|
||||
@@ -450,6 +450,13 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> {
|
||||
"プレフィックスが有効な場合、プレフィックスキーを単独で押すと約 1 秒後にシェルに渡され、プレフィックス + 未割り当てのキーはターミナルへそのまま送信されます"
|
||||
}
|
||||
L10nKey::SettingsRestoreAllDefaults => "すべてのデフォルトを復元",
|
||||
L10nKey::KeybindGoToTab => "タブ {n} へ移動",
|
||||
L10nKey::KeybindGoToWorkspace => "ワークスペース {n} へ移動",
|
||||
L10nKey::KeybindInsertNewline => "改行を挿入",
|
||||
L10nKey::KeybindForkSessionRight => "右にセッションをフォーク",
|
||||
L10nKey::KeybindForkSessionLeft => "左にセッションをフォーク",
|
||||
L10nKey::KeybindForkSessionDown => "下にセッションをフォーク",
|
||||
L10nKey::KeybindForkSessionUp => "上にセッションをフォーク",
|
||||
L10nKey::SettingsAboutDesc1 => {
|
||||
"ターミナルワークベンチ: 常駐セッション、リモート作業、エージェント"
|
||||
}
|
||||
|
||||
@@ -388,6 +388,13 @@ pub enum L10nKey {
|
||||
SettingsKeybindingsIntroDesc,
|
||||
SettingsPrefixNote,
|
||||
SettingsRestoreAllDefaults,
|
||||
KeybindGoToTab,
|
||||
KeybindGoToWorkspace,
|
||||
KeybindInsertNewline,
|
||||
KeybindForkSessionRight,
|
||||
KeybindForkSessionLeft,
|
||||
KeybindForkSessionDown,
|
||||
KeybindForkSessionUp,
|
||||
SettingsAboutDesc1,
|
||||
SettingsVersion,
|
||||
SettingsUpdates,
|
||||
@@ -1405,6 +1412,13 @@ mod tests {
|
||||
L10nKey::SettingsKeybindingsIntroDesc,
|
||||
L10nKey::SettingsPrefixNote,
|
||||
L10nKey::SettingsRestoreAllDefaults,
|
||||
L10nKey::KeybindGoToTab,
|
||||
L10nKey::KeybindGoToWorkspace,
|
||||
L10nKey::KeybindInsertNewline,
|
||||
L10nKey::KeybindForkSessionRight,
|
||||
L10nKey::KeybindForkSessionLeft,
|
||||
L10nKey::KeybindForkSessionDown,
|
||||
L10nKey::KeybindForkSessionUp,
|
||||
L10nKey::SettingsAboutDesc1,
|
||||
L10nKey::SettingsUpdates,
|
||||
L10nKey::SettingsUpdateAndRelaunch,
|
||||
|
||||
@@ -399,6 +399,13 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> {
|
||||
"启用前缀后,单独按前缀键约 1 秒后会传给 shell,前缀 + 未绑定的按键会直接发送到终端。"
|
||||
}
|
||||
L10nKey::SettingsRestoreAllDefaults => "恢复全部默认值",
|
||||
L10nKey::KeybindGoToTab => "跳到第 {n} 个标签页",
|
||||
L10nKey::KeybindGoToWorkspace => "跳到第 {n} 个工作区",
|
||||
L10nKey::KeybindInsertNewline => "插入换行",
|
||||
L10nKey::KeybindForkSessionRight => "向右 Fork 会话",
|
||||
L10nKey::KeybindForkSessionLeft => "向左 Fork 会话",
|
||||
L10nKey::KeybindForkSessionDown => "向下 Fork 会话",
|
||||
L10nKey::KeybindForkSessionUp => "向上 Fork 会话",
|
||||
L10nKey::SettingsAboutDesc1 => "终端工作台:常驻会话、远程工作、agent。",
|
||||
L10nKey::SettingsVersion => "版本",
|
||||
L10nKey::SettingsUpdates => "更新",
|
||||
|
||||
@@ -6,6 +6,9 @@ use crate::terminal::view::{
|
||||
ClearScrollback, CopyText, FindInTerminal, FindNext, FindPrevious, InsertNewline,
|
||||
InsertNewlineFallback, PasteText,
|
||||
};
|
||||
use crate::ui::i18n::{L10nKey, t, t_fmt};
|
||||
use crate::ui::palette::CommandGroup;
|
||||
use crate::ui::settings::humanize_action;
|
||||
use crate::ui::theme::set_menus;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -319,6 +322,288 @@ pub(crate) fn default_bindings() -> Vec<(&'static str, &'static str)> {
|
||||
]
|
||||
}
|
||||
|
||||
/// Where an action sits on the Keybindings page, and what it is called there.
|
||||
///
|
||||
/// The page used to render `humanize_action`'s CamelCase split — English in a
|
||||
/// three-locale app, and a fourth vocabulary on top of the menu bar, the
|
||||
/// palette and the docs ("Toggle Maximize Pane" for what everything else calls
|
||||
/// Zoom Pane). This routes both the name and the section through the strings
|
||||
/// the rest of the app already uses.
|
||||
pub(crate) fn action_entry(action: &str) -> (CommandGroup, String) {
|
||||
authored_entry(action).unwrap_or_else(|| {
|
||||
// An action with no entry still renders, under Application and with its
|
||||
// name split on capitals — a new action shows up on the page rather
|
||||
// than disappearing from it. `every_action_has_an_authored_name` is
|
||||
// what keeps that from being how the page looks.
|
||||
(CommandGroup::Application, humanize_action(action))
|
||||
})
|
||||
}
|
||||
|
||||
fn authored_entry(action: &str) -> Option<(CommandGroup, String)> {
|
||||
// The numbered families are one row each in nine copies; a templated label
|
||||
// beats nine hand-written strings per locale.
|
||||
if let Some(n) = action.strip_prefix("ActivateTab") {
|
||||
return Some((
|
||||
CommandGroup::TabsPanes,
|
||||
t_fmt(L10nKey::KeybindGoToTab, &[("n", n)]),
|
||||
));
|
||||
}
|
||||
if let Some(n) = action.strip_prefix("SelectWorkspace") {
|
||||
return Some((
|
||||
CommandGroup::Workspaces,
|
||||
t_fmt(L10nKey::KeybindGoToWorkspace, &[("n", n)]),
|
||||
));
|
||||
}
|
||||
Some(match action {
|
||||
"NewTab" => (CommandGroup::TabsPanes, t(L10nKey::CmdNewTab).to_string()),
|
||||
"CloseActiveTab" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdClosePaneTab).to_string(),
|
||||
),
|
||||
"RenameTab" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdRenameTab).to_string(),
|
||||
),
|
||||
"NewWorktreeTab" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdNewWorktreeTab).to_string(),
|
||||
),
|
||||
"CloseOtherTabs" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdCloseOtherTabs).to_string(),
|
||||
),
|
||||
"CloseTabsToTheRight" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdCloseTabsToTheRight).to_string(),
|
||||
),
|
||||
"CopyWorkingDirectory" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdCopyWorkingDirectory).to_string(),
|
||||
),
|
||||
"MarkTabUnread" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdMarkTabAsUnread).to_string(),
|
||||
),
|
||||
"ReopenClosedTab" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdReopenClosedTab).to_string(),
|
||||
),
|
||||
"SplitRight" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdSplitRight).to_string(),
|
||||
),
|
||||
"SplitDown" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdSplitDown).to_string(),
|
||||
),
|
||||
"ToggleMaximizePane" => (CommandGroup::TabsPanes, t(L10nKey::CmdZoomPane).to_string()),
|
||||
"FocusNextPane" => (CommandGroup::TabsPanes, t(L10nKey::CmdNextPane).to_string()),
|
||||
"FocusPrevPane" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdPreviousPane).to_string(),
|
||||
),
|
||||
"FocusPaneLeft" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdFocusPaneLeft).to_string(),
|
||||
),
|
||||
"FocusPaneRight" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdFocusPaneRight).to_string(),
|
||||
),
|
||||
"FocusPaneUp" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdFocusPaneUp).to_string(),
|
||||
),
|
||||
"FocusPaneDown" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdFocusPaneDown).to_string(),
|
||||
),
|
||||
"ResizePaneLeft" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdResizePaneLeft).to_string(),
|
||||
),
|
||||
"ResizePaneRight" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdResizePaneRight).to_string(),
|
||||
),
|
||||
"ResizePaneUp" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdResizePaneUp).to_string(),
|
||||
),
|
||||
"ResizePaneDown" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdResizePaneDown).to_string(),
|
||||
),
|
||||
"SwapPaneNext" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdSwapPaneNext).to_string(),
|
||||
),
|
||||
"SwapPanePrev" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdSwapPanePrevious).to_string(),
|
||||
),
|
||||
"NextTab" => (CommandGroup::TabsPanes, t(L10nKey::CmdNextTab).to_string()),
|
||||
"PrevTab" => (
|
||||
CommandGroup::TabsPanes,
|
||||
t(L10nKey::CmdPreviousTab).to_string(),
|
||||
),
|
||||
"NewWorkspace" => (
|
||||
CommandGroup::Workspaces,
|
||||
t(L10nKey::CmdNewWorkspace).to_string(),
|
||||
),
|
||||
"RenameWorkspace" => (
|
||||
CommandGroup::Workspaces,
|
||||
t(L10nKey::CmdRenameWorkspace).to_string(),
|
||||
),
|
||||
"StopWorkspace" => (
|
||||
CommandGroup::Workspaces,
|
||||
t(L10nKey::CmdStopWorkspace).to_string(),
|
||||
),
|
||||
"DeleteWorkspace" => (
|
||||
CommandGroup::Workspaces,
|
||||
t(L10nKey::CmdDeleteWorkspace).to_string(),
|
||||
),
|
||||
"ToggleSwitcher" => (
|
||||
CommandGroup::Workspaces,
|
||||
t(L10nKey::HomeSwitchWorkspace).to_string(),
|
||||
),
|
||||
"IncreaseFontSize" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::AppMenuIncreaseFontSize).to_string(),
|
||||
),
|
||||
"DecreaseFontSize" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::AppMenuDecreaseFontSize).to_string(),
|
||||
),
|
||||
"ResetFontSize" => (CommandGroup::View, t(L10nKey::CmdResetFontSize).to_string()),
|
||||
"ToggleFullscreen" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::CmdEnterFullScreen).to_string(),
|
||||
),
|
||||
"ToggleTabSidebar" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::AppMenuTabBarPosition).to_string(),
|
||||
),
|
||||
"ToggleLeftPanel" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::AppMenuLeftSidebar).to_string(),
|
||||
),
|
||||
"ToggleRightPanel" => (
|
||||
CommandGroup::View,
|
||||
t(L10nKey::AppMenuRightPanel).to_string(),
|
||||
),
|
||||
"ToggleCodePanel" => (CommandGroup::View, t(L10nKey::AppMenuCodePanel).to_string()),
|
||||
"FindInTerminal" => (
|
||||
CommandGroup::Terminal,
|
||||
t(L10nKey::CmdFindInTerminal).to_string(),
|
||||
),
|
||||
"FindNext" => (CommandGroup::Terminal, t(L10nKey::CmdFindNext).to_string()),
|
||||
"FindPrevious" => (
|
||||
CommandGroup::Terminal,
|
||||
t(L10nKey::CmdFindPrevious).to_string(),
|
||||
),
|
||||
"ClearScrollback" => (
|
||||
CommandGroup::Terminal,
|
||||
t(L10nKey::CmdClearScrollback).to_string(),
|
||||
),
|
||||
"CopyText" => (CommandGroup::Terminal, t(L10nKey::CmdCopy).to_string()),
|
||||
"PasteText" => (CommandGroup::Terminal, t(L10nKey::CmdPaste).to_string()),
|
||||
"InsertNewline" => (
|
||||
CommandGroup::Terminal,
|
||||
t(L10nKey::KeybindInsertNewline).to_string(),
|
||||
),
|
||||
"EditorSave" => (CommandGroup::Terminal, t(L10nKey::Save).to_string()),
|
||||
"OpenSshProfiles" => (
|
||||
CommandGroup::Ssh,
|
||||
t(L10nKey::CmdSshManageProfiles).to_string(),
|
||||
),
|
||||
"RestartSshSession" => (CommandGroup::Ssh, t(L10nKey::CmdSshReconnect).to_string()),
|
||||
"ToggleSftp" => (CommandGroup::Ssh, t(L10nKey::CmdSshRemoteFiles).to_string()),
|
||||
"ShowSshForwards" => (
|
||||
CommandGroup::Ssh,
|
||||
t(L10nKey::CmdSshPortForwarding).to_string(),
|
||||
),
|
||||
"ForkAgentSession" => (CommandGroup::Agents, t(L10nKey::CmdForkSession).to_string()),
|
||||
"ForkAgentSessionRight" => (
|
||||
CommandGroup::Agents,
|
||||
t(L10nKey::KeybindForkSessionRight).to_string(),
|
||||
),
|
||||
"ForkAgentSessionLeft" => (
|
||||
CommandGroup::Agents,
|
||||
t(L10nKey::KeybindForkSessionLeft).to_string(),
|
||||
),
|
||||
"ForkAgentSessionDown" => (
|
||||
CommandGroup::Agents,
|
||||
t(L10nKey::KeybindForkSessionDown).to_string(),
|
||||
),
|
||||
"ForkAgentSessionUp" => (
|
||||
CommandGroup::Agents,
|
||||
t(L10nKey::KeybindForkSessionUp).to_string(),
|
||||
),
|
||||
"CopyAgentSessionId" => (
|
||||
CommandGroup::Agents,
|
||||
t(L10nKey::CmdCopySessionId).to_string(),
|
||||
),
|
||||
"TogglePalette" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuCommandPalette).to_string(),
|
||||
),
|
||||
"OpenSettings" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdSettings).to_string(),
|
||||
),
|
||||
"ShowKeyboardShortcuts" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdKeyboardShortcuts).to_string(),
|
||||
),
|
||||
"About" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdAboutTty7).to_string(),
|
||||
),
|
||||
"CheckForUpdates" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdCheckForUpdates).to_string(),
|
||||
),
|
||||
"OpenDocumentation" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdDocumentation).to_string(),
|
||||
),
|
||||
"OpenDiscord" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdJoinDiscord).to_string(),
|
||||
),
|
||||
"ReportIssue" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdReportIssue).to_string(),
|
||||
),
|
||||
"HideApp" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuHideApp).to_string(),
|
||||
),
|
||||
"HideOthers" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuHideOthers).to_string(),
|
||||
),
|
||||
"ShowAll" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuShowAll).to_string(),
|
||||
),
|
||||
"MinimizeWindow" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuMinimize).to_string(),
|
||||
),
|
||||
"ZoomWindow" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::AppMenuZoom).to_string(),
|
||||
),
|
||||
"Quit" => (
|
||||
CommandGroup::Application,
|
||||
t(L10nKey::CmdQuitTty7).to_string(),
|
||||
),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn effective_bindings(cx: &App) -> Vec<(String, String)> {
|
||||
let cfg = cx.global::<Config>();
|
||||
let mut effective: Vec<(String, String)> = default_bindings()
|
||||
@@ -627,6 +912,40 @@ fn make_binding(action: &str, keystroke: &str) -> Option<KeyBinding> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_action_has_an_authored_name() {
|
||||
crate::ui::i18n::set_locale("en");
|
||||
let missing: Vec<&str> = default_bindings()
|
||||
.into_iter()
|
||||
.map(|(a, _)| a)
|
||||
.filter(|a| authored_entry(a).is_none())
|
||||
.collect();
|
||||
assert!(
|
||||
missing.is_empty(),
|
||||
"these actions would render on the Keybindings page as a CamelCase \
|
||||
split of their internal name, in English, in a three-locale app: {missing:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_page_speaks_the_same_words_as_the_rest_of_the_app() {
|
||||
crate::ui::i18n::set_locale("en");
|
||||
// The four names the critique found for one action; the menu bar, the
|
||||
// palette and the docs all say Zoom Pane.
|
||||
assert_eq!(action_entry("ToggleMaximizePane").1, "Zoom Pane");
|
||||
assert_eq!(action_entry("CloseActiveTab").1, "Close Pane / Tab");
|
||||
assert_eq!(action_entry("ClearScrollback").1, "Clear Scrollback");
|
||||
assert_eq!(action_entry("TogglePalette").1, "Command Palette…");
|
||||
// The numbered families are templated, not nine strings per locale.
|
||||
assert_eq!(action_entry("ActivateTab3").1, "Go to Tab 3");
|
||||
assert_eq!(action_entry("SelectWorkspace7").1, "Go to Workspace 7");
|
||||
// And they land in the section they belong to.
|
||||
assert_eq!(action_entry("ActivateTab3").0, CommandGroup::TabsPanes);
|
||||
assert_eq!(action_entry("SelectWorkspace7").0, CommandGroup::Workspaces);
|
||||
assert_eq!(action_entry("ToggleSftp").0, CommandGroup::Ssh);
|
||||
assert_eq!(action_entry("ForkAgentSessionUp").0, CommandGroup::Agents);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const SECONDARY: &str = "⌘";
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
|
||||
+2
-2
@@ -284,7 +284,7 @@ pub enum CommandGroup {
|
||||
}
|
||||
|
||||
impl CommandGroup {
|
||||
const ORDER: [CommandGroup; 7] = [
|
||||
pub(crate) const ORDER: [CommandGroup; 7] = [
|
||||
CommandGroup::TabsPanes,
|
||||
CommandGroup::Workspaces,
|
||||
CommandGroup::View,
|
||||
@@ -294,7 +294,7 @@ impl CommandGroup {
|
||||
CommandGroup::Application,
|
||||
];
|
||||
|
||||
fn title(self) -> &'static str {
|
||||
pub(crate) fn title(self) -> &'static str {
|
||||
match self {
|
||||
CommandGroup::TabsPanes => t(L10nKey::CmdGroupTabsPanes),
|
||||
CommandGroup::Workspaces => t(L10nKey::CmdGroupWorkspaces),
|
||||
|
||||
+54
-9
@@ -4530,9 +4530,47 @@ impl Tty7App {
|
||||
)
|
||||
.child(h_flex().flex_shrink_0().child(prefix_control));
|
||||
|
||||
let count = effective.len();
|
||||
// Eighty-nine undivided rows, in the order the binding table happens to
|
||||
// be written. Read them in the same seven sections the command palette
|
||||
// uses, so a shortcut is found by where it belongs rather than by
|
||||
// scrolling.
|
||||
let mut grouped: Vec<(
|
||||
crate::ui::palette::CommandGroup,
|
||||
Vec<(String, String, String)>,
|
||||
)> = Vec::new();
|
||||
for (action, key) in effective {
|
||||
let (group, label) = crate::ui::keymap::action_entry(&action);
|
||||
let slot = match grouped.iter_mut().find(|(g, _)| *g == group) {
|
||||
Some(slot) => slot,
|
||||
None => {
|
||||
grouped.push((group, Vec::new()));
|
||||
grouped.last_mut().expect("just pushed")
|
||||
}
|
||||
};
|
||||
slot.1.push((action, key, label));
|
||||
}
|
||||
grouped.sort_by_key(|(g, _)| {
|
||||
crate::ui::palette::CommandGroup::ORDER
|
||||
.iter()
|
||||
.position(|o| o == g)
|
||||
.unwrap_or(usize::MAX)
|
||||
});
|
||||
let rows: Vec<(String, String, String)> = grouped
|
||||
.iter()
|
||||
.flat_map(|(_, rows)| rows.iter().cloned())
|
||||
.collect();
|
||||
let heading_at: std::collections::HashMap<usize, &'static str> = {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
let mut at = 0usize;
|
||||
for (group, rows) in &grouped {
|
||||
map.insert(at, group.title());
|
||||
at += rows.len();
|
||||
}
|
||||
map
|
||||
};
|
||||
let count = rows.len();
|
||||
let mut list = v_flex().mt_2();
|
||||
for (i, (action, key)) in effective.into_iter().enumerate() {
|
||||
for (i, (action, key, label)) in rows.into_iter().enumerate() {
|
||||
let is_recording = recording.as_ref().is_some_and(|(a, _)| a == &action);
|
||||
let is_overridden = overridden.contains(&action);
|
||||
|
||||
@@ -4609,18 +4647,25 @@ impl Tty7App {
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(title) = heading_at.get(&i) {
|
||||
list = list.child(
|
||||
div()
|
||||
.pt_5()
|
||||
.pb_1p5()
|
||||
.text_xs()
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.text_color(muted)
|
||||
.child(*title),
|
||||
);
|
||||
}
|
||||
let last_in_group = heading_at.contains_key(&(i + 1)) || i + 1 == count;
|
||||
list = list.child(
|
||||
h_flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.py_1p5()
|
||||
.when(i + 1 < count, |s| s.border_b_1().border_color(border))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(foreground)
|
||||
.child(humanize_action(&action)),
|
||||
)
|
||||
.when(!last_in_group, |s| s.border_b_1().border_color(border))
|
||||
.child(div().text_sm().text_color(foreground).child(label))
|
||||
.child(right),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user