mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
fix(tui): add reasoning effort fallback shortcuts (#25623)
This commit is contained in:
committed by
GitHub
Unverified
parent
4d4837c495
commit
8285cd278b
@@ -2426,58 +2426,67 @@ async fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
assert_chatwidget_snapshot!("model_reasoning_selection_popup_extra_high_warning", popup);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn alt_period_raises_reasoning_effort() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
async fn assert_reasoning_shortcuts_update_effort(
|
||||
key_events: [KeyEvent; 2],
|
||||
expected_effort: ReasoningEffortConfig,
|
||||
expect_model_update: bool,
|
||||
) {
|
||||
for key_event in key_events {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
|
||||
chat.handle_key_event(KeyEvent::new(KeyCode::Char('.'), KeyModifiers::ALT));
|
||||
chat.handle_key_event(key_event);
|
||||
|
||||
let events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
assert!(
|
||||
events
|
||||
.iter()
|
||||
.any(|event| matches!(event, AppEvent::UpdateModel(model) if model == "gpt-5.4")),
|
||||
"expected model update event; events: {events:?}"
|
||||
);
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::UpdateReasoningEffort(Some(ReasoningEffortConfig::High))
|
||||
)),
|
||||
"expected reasoning update event; events: {events:?}"
|
||||
);
|
||||
assert!(
|
||||
events
|
||||
.iter()
|
||||
.all(|event| !matches!(event, AppEvent::PersistModelSelection { .. })),
|
||||
"expected no model persistence event; events: {events:?}"
|
||||
);
|
||||
let events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
if expect_model_update {
|
||||
assert!(
|
||||
events.iter().any(
|
||||
|event| matches!(event, AppEvent::UpdateModel(model) if model == "gpt-5.4")
|
||||
),
|
||||
"expected model update event for {key_event:?}; events: {events:?}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::UpdateReasoningEffort(Some(effort)) if effort == &expected_effort
|
||||
)),
|
||||
"expected reasoning update event for {key_event:?}; events: {events:?}"
|
||||
);
|
||||
assert!(
|
||||
events
|
||||
.iter()
|
||||
.all(|event| !matches!(event, AppEvent::PersistModelSelection { .. })),
|
||||
"expected no model persistence event for {key_event:?}; events: {events:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn alt_comma_lowers_reasoning_effort() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
async fn reasoning_up_shortcuts_raise_reasoning_effort() {
|
||||
assert_reasoning_shortcuts_update_effort(
|
||||
[
|
||||
KeyEvent::new(KeyCode::Char('.'), KeyModifiers::ALT),
|
||||
KeyEvent::new(KeyCode::Up, KeyModifiers::SHIFT),
|
||||
],
|
||||
ReasoningEffortConfig::High,
|
||||
/*expect_model_update*/ true,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
chat.handle_key_event(KeyEvent::new(KeyCode::Char(','), KeyModifiers::ALT));
|
||||
|
||||
let events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::UpdateReasoningEffort(Some(ReasoningEffortConfig::Low))
|
||||
)),
|
||||
"expected reasoning update event; events: {events:?}"
|
||||
);
|
||||
assert!(
|
||||
events
|
||||
.iter()
|
||||
.all(|event| !matches!(event, AppEvent::PersistModelSelection { .. })),
|
||||
"expected no model persistence event; events: {events:?}"
|
||||
);
|
||||
#[tokio::test]
|
||||
async fn reasoning_down_shortcuts_lower_reasoning_effort() {
|
||||
assert_reasoning_shortcuts_update_effort(
|
||||
[
|
||||
KeyEvent::new(KeyCode::Char(','), KeyModifiers::ALT),
|
||||
KeyEvent::new(KeyCode::Down, KeyModifiers::SHIFT),
|
||||
],
|
||||
ReasoningEffortConfig::Low,
|
||||
/*expect_model_update*/ false,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
+112
-5
@@ -25,6 +25,7 @@ use codex_config::types::MAX_FUNCTION_KEY;
|
||||
use codex_config::types::TuiKeymap;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyModifiers;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Runtime keymap used by TUI input handlers.
|
||||
@@ -423,7 +424,7 @@ impl RuntimeKeymap {
|
||||
)?,
|
||||
};
|
||||
|
||||
let chat = ChatKeymap {
|
||||
let mut chat = ChatKeymap {
|
||||
interrupt_turn: resolve_bindings(
|
||||
keymap.chat.interrupt_turn.as_ref(),
|
||||
&defaults.chat.interrupt_turn,
|
||||
@@ -738,6 +739,22 @@ impl RuntimeKeymap {
|
||||
cancel: resolve_local!(keymap, defaults, vim_text_object, cancel),
|
||||
};
|
||||
|
||||
// Reasoning arrow aliases are fallback defaults: existing explicit
|
||||
// bindings on the same input path keep the keys, while explicit
|
||||
// reasoning bindings remain authoritative.
|
||||
if keymap.chat.decrease_reasoning_effort.is_none()
|
||||
&& configured_main_surface_alias_is_used(keymap, "shift-down")
|
||||
{
|
||||
chat.decrease_reasoning_effort
|
||||
.retain(|binding| *binding != key_hint::shift(KeyCode::Down));
|
||||
}
|
||||
if keymap.chat.increase_reasoning_effort.is_none()
|
||||
&& configured_main_surface_alias_is_used(keymap, "shift-up")
|
||||
{
|
||||
chat.increase_reasoning_effort
|
||||
.retain(|binding| *binding != key_hint::shift(KeyCode::Up));
|
||||
}
|
||||
|
||||
let pager = PagerKeymap {
|
||||
scroll_up: resolve_local!(keymap, defaults, pager, scroll_up),
|
||||
scroll_down: resolve_local!(keymap, defaults, pager, scroll_down),
|
||||
@@ -902,8 +919,14 @@ impl RuntimeKeymap {
|
||||
},
|
||||
chat: ChatKeymap {
|
||||
interrupt_turn: default_bindings![plain(KeyCode::Esc)],
|
||||
decrease_reasoning_effort: default_bindings![alt(KeyCode::Char(','))],
|
||||
increase_reasoning_effort: default_bindings![alt(KeyCode::Char('.'))],
|
||||
decrease_reasoning_effort: default_bindings![
|
||||
alt(KeyCode::Char(',')),
|
||||
shift(KeyCode::Down)
|
||||
],
|
||||
increase_reasoning_effort: default_bindings![
|
||||
alt(KeyCode::Char('.')),
|
||||
shift(KeyCode::Up)
|
||||
],
|
||||
edit_queued_message: default_bindings![alt(KeyCode::Up), shift(KeyCode::Left)],
|
||||
},
|
||||
composer: ComposerKeymap {
|
||||
@@ -1856,6 +1879,52 @@ fn configured_bindings_to_preserve<const N: usize>(
|
||||
configured_bindings
|
||||
}
|
||||
|
||||
fn configured_main_surface_alias_is_used(keymap: &TuiKeymap, alias: &str) -> bool {
|
||||
let mut global = keymap.global.clone();
|
||||
if keymap.composer.submit.is_some() {
|
||||
global.submit = None;
|
||||
}
|
||||
if keymap.composer.queue.is_some() {
|
||||
global.queue = None;
|
||||
}
|
||||
if keymap.composer.toggle_shortcuts.is_some() {
|
||||
global.toggle_shortcuts = None;
|
||||
}
|
||||
|
||||
// Reasoning shortcuts run before composer/editor key handling, so fallback
|
||||
// aliases must yield to any explicit binding on the same main-surface input
|
||||
// path.
|
||||
configured_context_alias_is_used(&global, alias)
|
||||
|| configured_context_alias_is_used(&keymap.chat, alias)
|
||||
|| configured_context_alias_is_used(&keymap.composer, alias)
|
||||
|| configured_context_alias_is_used(&keymap.editor, alias)
|
||||
|| configured_context_alias_is_used(&keymap.vim_normal, alias)
|
||||
|| configured_context_alias_is_used(&keymap.vim_operator, alias)
|
||||
|| configured_context_alias_is_used(&keymap.vim_text_object, alias)
|
||||
}
|
||||
|
||||
fn configured_context_alias_is_used(context: &impl Serialize, alias: &str) -> bool {
|
||||
let Ok(value) = serde_json::to_value(context) else {
|
||||
return false;
|
||||
};
|
||||
keymap_value_contains_alias(&value, alias)
|
||||
}
|
||||
|
||||
fn keymap_value_contains_alias(value: &serde_json::Value, alias: &str) -> bool {
|
||||
match value {
|
||||
serde_json::Value::String(value) => value == alias,
|
||||
serde_json::Value::Array(values) => values
|
||||
.iter()
|
||||
.any(|value| keymap_value_contains_alias(value, alias)),
|
||||
serde_json::Value::Object(values) => values
|
||||
.values()
|
||||
.any(|value| keymap_value_contains_alias(value, alias)),
|
||||
serde_json::Value::Bool(_) | serde_json::Value::Number(_) | serde_json::Value::Null => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_new_default_bindings(
|
||||
configured: Option<&KeybindingsSpec>,
|
||||
fallback: &[KeyBinding],
|
||||
@@ -2142,11 +2211,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
runtime.chat.decrease_reasoning_effort,
|
||||
vec![key_hint::alt(KeyCode::Char(','))]
|
||||
vec![
|
||||
key_hint::alt(KeyCode::Char(',')),
|
||||
key_hint::shift(KeyCode::Down),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
runtime.chat.increase_reasoning_effort,
|
||||
vec![key_hint::alt(KeyCode::Char('.'))]
|
||||
vec![
|
||||
key_hint::alt(KeyCode::Char('.')),
|
||||
key_hint::shift(KeyCode::Up),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
runtime.chat.edit_queued_message,
|
||||
@@ -2220,6 +2295,38 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_main_surface_bindings_prune_reasoning_fallback_aliases() {
|
||||
let mut keymap = TuiKeymap::default();
|
||||
keymap.editor.move_up = Some(one("shift-up"));
|
||||
keymap.vim_text_object.word = Some(one("shift-down"));
|
||||
|
||||
let runtime = RuntimeKeymap::from_config(&keymap).expect("config should parse");
|
||||
|
||||
assert_eq!(runtime.editor.move_up, vec![key_hint::shift(KeyCode::Up)]);
|
||||
assert_eq!(
|
||||
runtime.vim_text_object.word,
|
||||
vec![key_hint::shift(KeyCode::Down)]
|
||||
);
|
||||
assert_eq!(
|
||||
runtime.chat.decrease_reasoning_effort,
|
||||
vec![key_hint::alt(KeyCode::Char(','))]
|
||||
);
|
||||
assert_eq!(
|
||||
runtime.chat.increase_reasoning_effort,
|
||||
vec![key_hint::alt(KeyCode::Char('.'))]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_reasoning_binding_still_conflicts_with_editor_binding() {
|
||||
let mut keymap = TuiKeymap::default();
|
||||
keymap.editor.move_up = Some(one("shift-up"));
|
||||
keymap.chat.increase_reasoning_effort = Some(one("shift-up"));
|
||||
|
||||
expect_conflict(&keymap, "chat.increase_reasoning_effort", "editor.move_up");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_legacy_list_bindings_prune_new_default_overlaps() {
|
||||
let mut keymap = TuiKeymap::default();
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ Clear Terminal | ctrl-l | Global clear_terminal Clear Terminal Clear the termina
|
||||
Toggle Vim Mode | unbound | Global toggle_vim_mode Toggle Vim Mode Turn Vim composer mode on or off. unbound Default
|
||||
Toggle Raw Output | alt-r | Global toggle_raw_output Toggle Raw Output Toggle raw scrollback mode. alt-r Default
|
||||
Interrupt Turn | esc | Chat interrupt_turn Interrupt Turn Interrupt the active turn. esc Default
|
||||
Decrease Reasoning Effort | alt-, | Chat decrease_reasoning_effort Decrease Reasoning Effort Decrease reasoning effort. alt-, Default
|
||||
Increase Reasoning Effort | alt-. | Chat increase_reasoning_effort Increase Reasoning Effort Increase reasoning effort. alt-. Default
|
||||
Decrease Reasoning Effort | alt-,, shift-down | Chat decrease_reasoning_effort Decrease Reasoning Effort Decrease reasoning effort. alt-,, shift-down Default
|
||||
Increase Reasoning Effort | alt-., shift-up | Chat increase_reasoning_effort Increase Reasoning Effort Increase reasoning effort. alt-., shift-up Default
|
||||
Edit Queued Message | alt-up, shift-left | Chat edit_queued_message Edit Queued Message Edit the most recently queued message. alt-up, shift-left Default
|
||||
Submit | enter | Composer submit Submit Submit the current composer draft. enter Default
|
||||
Queue | tab | Composer queue Queue Queue the draft while a task is running. tab Default
|
||||
|
||||
+1
-1
@@ -17,6 +17,6 @@ expression: "render_picker(params, 120)"
|
||||
Global - Toggle Vim Mode unbound
|
||||
Global Toggle Raw Output alt-r
|
||||
Chat Interrupt Turn esc
|
||||
Chat Decrease Reasoning Effort alt-,
|
||||
Chat Decrease Reasoning Effort alt-,, shift-down
|
||||
|
||||
left/right group · enter edit shortcut · * custom · - unbound · esc close
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ Clear Terminal | ctrl-l | Global clear_terminal Clear Terminal Clear the termina
|
||||
Toggle Vim Mode | unbound | Global toggle_vim_mode Toggle Vim Mode Turn Vim composer mode on or off. unbound Default
|
||||
Toggle Raw Output | alt-r | Global toggle_raw_output Toggle Raw Output Toggle raw scrollback mode. alt-r Default
|
||||
Interrupt Turn | esc | Chat interrupt_turn Interrupt Turn Interrupt the active turn. esc Default
|
||||
Decrease Reasoning Effort | alt-, | Chat decrease_reasoning_effort Decrease Reasoning Effort Decrease reasoning effort. alt-, Default
|
||||
Increase Reasoning Effort | alt-. | Chat increase_reasoning_effort Increase Reasoning Effort Increase reasoning effort. alt-. Default
|
||||
Decrease Reasoning Effort | alt-,, shift-down | Chat decrease_reasoning_effort Decrease Reasoning Effort Decrease reasoning effort. alt-,, shift-down Default
|
||||
Increase Reasoning Effort | alt-., shift-up | Chat increase_reasoning_effort Increase Reasoning Effort Increase reasoning effort. alt-., shift-up Default
|
||||
Edit Queued Message | alt-up, shift-left | Chat edit_queued_message Edit Queued Message Edit the most recently queued message. alt-up, shift-left Default
|
||||
Submit | enter | Composer submit Submit Submit the current composer draft. enter Default
|
||||
Queue | tab | Composer queue Queue Queue the draft while a task is running. tab Default
|
||||
|
||||
+1
-1
@@ -18,6 +18,6 @@ expression: "render_picker(params, 78)"
|
||||
Global - Toggle Vim Mode unbound
|
||||
Global Toggle Raw Output alt-r
|
||||
Chat Interrupt Turn esc
|
||||
Chat Decrease Reasoning Effort alt-,
|
||||
Chat Decrease Reasoning Effort alt-,, shift-down
|
||||
|
||||
left/right group · enter edit shortcut · * custom · - unbound · esc close
|
||||
|
||||
@@ -17,6 +17,6 @@ expression: "render_picker(params, 120)"
|
||||
Global - Toggle Vim Mode unbound
|
||||
Global Toggle Raw Output alt-r
|
||||
Chat Interrupt Turn esc
|
||||
Chat Decrease Reasoning Effort alt-,
|
||||
Chat Decrease Reasoning Effort alt-,, shift-down
|
||||
|
||||
left/right group · enter edit shortcut · * custom · - unbound · esc close
|
||||
|
||||
Reference in New Issue
Block a user