Add keybindings for cycling TUI permission modes (#39873)

## What changed

- Add unbound `previous_permission_mode` and `next_permission_mode` actions to
  `tui.keymap.chat` so users can configure shortcuts that cycle through the
  available built-in permission modes.
- Apply shortcut selections only to the active session, without writing them
  to `config.toml`, and report update failures in the transcript.
- Exclude modes disallowed by managed requirements or platform safety checks,
  reject printable-key bindings, and ignore these bindings in project config.

## Testing

- Cover mode cycling, unavailable modes, session-only updates, server errors,
  stale threads, keymap validation, and project-config sanitization.

GitOrigin-RevId: c3cfdf1e732b7b7bfe8f53fcac19a37e593bbfab
This commit is contained in:
Eric Traut authored and copyberry committed 2026-08-21 06:19:23 +00:00
1 parent 44e95c857f
commit 2aaefa32b0
32 files changed
+708 -47

No files matched your search

+13
View File
@@ -1127,6 +1127,19 @@ fn sanitize_project_config(config: &mut TomlValue) -> Vec<String> {
{
ignored_keys.push("features.respect_system_proxy".to_string());
}
// Repository contents must not turn an ordinary key into a permission increase.
if let Some(chat) = table
.get_mut("tui")
.and_then(|tui| tui.get_mut("keymap"))
.and_then(|keymap| keymap.get_mut("chat"))
.and_then(TomlValue::as_table_mut)
{
for key in ["previous_permission_mode", "next_permission_mode"] {
if chat.remove(key).is_some() {
ignored_keys.push(format!("tui.keymap.chat.{key}"));
}
}
}
ignored_keys
}
+13
View File
@@ -19,6 +19,19 @@ use tempfile::tempdir;
pub(super) struct TestFileSystem;
#[test]
fn project_config_cannot_bind_permission_shortcuts() {
let safe = "[tui.keymap.chat]\nincrease_reasoning_effort = 'f9'\n";
for key in ["previous_permission_mode", "next_permission_mode"] {
let mut config = toml::from_str(&format!("{safe}{key} = 'page-down'")).unwrap();
assert_eq!(
sanitize_project_config(&mut config),
[format!("tui.keymap.chat.{key}")]
);
assert_eq!(config, toml::from_str::<TomlValue>(safe).unwrap());
}
}
#[tokio::test]
async fn managed_browser_import_denial_survives_user_and_session_config() {
let tmp = tempdir().expect("tempdir");
+4
View File
@@ -129,6 +129,10 @@ pub struct TuiChatKeymap {
pub decrease_reasoning_effort: Option<KeybindingsSpec>,
/// Increase the active reasoning effort.
pub increase_reasoning_effort: Option<KeybindingsSpec>,
/// Switch to the previous available permission mode.
pub previous_permission_mode: Option<KeybindingsSpec>,
/// Switch to the next available permission mode.
pub next_permission_mode: Option<KeybindingsSpec>,
/// Edit the most recently queued message.
pub edit_queued_message: Option<KeybindingsSpec>,
}