mirror of
https://github.com/openai/codex.git
synced 2026-09-16 12:13:30 +00:00
Merge 673a348681 into sapling-pr-archive-bolinfest
This commit is contained in:
@@ -443,10 +443,10 @@ impl RequestUserInputOverlay {
|
||||
};
|
||||
tips.push(enter_tip);
|
||||
if question_count > 1 {
|
||||
if is_last_question {
|
||||
tips.push(FooterTip::new("ctrl + n first question"));
|
||||
} else {
|
||||
tips.push(FooterTip::new("ctrl + n next question"));
|
||||
if self.has_options() && !self.focus_is_notes() {
|
||||
tips.push(FooterTip::new("←/→ to navigate questions"));
|
||||
} else if !self.has_options() {
|
||||
tips.push(FooterTip::new("ctrl + p / ctrl + n change question"));
|
||||
}
|
||||
}
|
||||
if !(self.has_options() && notes_visible) {
|
||||
@@ -1042,6 +1042,14 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
self.move_question(false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Left,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('l'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
@@ -1050,6 +1058,14 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
self.move_question(true);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Right,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(true);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1643,6 +1659,97 @@ mod tests {
|
||||
assert_eq!(overlay.current_index(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_right_move_between_questions_in_options() {
|
||||
let (tx, _rx) = test_sender();
|
||||
let mut overlay = RequestUserInputOverlay::new(
|
||||
request_event(
|
||||
"turn-1",
|
||||
vec![
|
||||
question_with_options("q1", "Pick one"),
|
||||
question_with_options("q2", "Pick two"),
|
||||
],
|
||||
),
|
||||
tx,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(overlay.current_index(), 0);
|
||||
overlay.handle_key_event(KeyEvent::from(KeyCode::Right));
|
||||
assert_eq!(overlay.current_index(), 1);
|
||||
overlay.handle_key_event(KeyEvent::from(KeyCode::Left));
|
||||
assert_eq!(overlay.current_index(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_notes_focus_hides_question_navigation_tip() {
|
||||
let (tx, _rx) = test_sender();
|
||||
let mut overlay = RequestUserInputOverlay::new(
|
||||
request_event(
|
||||
"turn-1",
|
||||
vec![
|
||||
question_with_options("q1", "Pick one"),
|
||||
question_with_options("q2", "Pick two"),
|
||||
],
|
||||
),
|
||||
tx,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
let tips = overlay.footer_tips();
|
||||
let tip_texts = tips.iter().map(|tip| tip.text.as_str()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
tip_texts,
|
||||
vec![
|
||||
"tab to add notes",
|
||||
"enter to submit answer",
|
||||
"←/→ to navigate questions",
|
||||
"esc to interrupt",
|
||||
]
|
||||
);
|
||||
|
||||
overlay.handle_key_event(KeyEvent::from(KeyCode::Tab));
|
||||
let tips = overlay.footer_tips();
|
||||
let tip_texts = tips.iter().map(|tip| tip.text.as_str()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
tip_texts,
|
||||
vec!["tab or esc to clear notes", "enter to submit answer",]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freeform_shows_ctrl_p_and_ctrl_n_question_navigation_tip() {
|
||||
let (tx, _rx) = test_sender();
|
||||
let mut overlay = RequestUserInputOverlay::new(
|
||||
request_event(
|
||||
"turn-1",
|
||||
vec![
|
||||
question_with_options("q1", "Area"),
|
||||
question_without_options("q2", "Goal"),
|
||||
],
|
||||
),
|
||||
tx,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
overlay.move_question(true);
|
||||
|
||||
let tips = overlay.footer_tips();
|
||||
let tip_texts = tips.iter().map(|tip| tip.text.as_str()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
tip_texts,
|
||||
vec![
|
||||
"enter to submit all",
|
||||
"ctrl + p / ctrl + n change question",
|
||||
"esc to interrupt",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab_opens_notes_when_option_selected() {
|
||||
let (tx, _rx) = test_sender();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/request_user_input/mod.rs
|
||||
assertion_line: 2600
|
||||
expression: "render_snapshot(&overlay, area)"
|
||||
---
|
||||
|
||||
@@ -11,4 +12,4 @@ expression: "render_snapshot(&overlay, area)"
|
||||
3. Option 3 Third choice.
|
||||
|
||||
tab to add notes | enter to submit answer
|
||||
ctrl + n next question | esc to interrupt
|
||||
←/→ to navigate questions | esc to interrupt
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/request_user_input/mod.rs
|
||||
assertion_line: 2744
|
||||
expression: "render_snapshot(&overlay, area)"
|
||||
---
|
||||
|
||||
@@ -10,4 +11,4 @@ expression: "render_snapshot(&overlay, area)"
|
||||
2. Option 2 Second choice.
|
||||
3. Option 3 Third choice.
|
||||
|
||||
tab to add notes | enter to submit answer | ctrl + n next question | esc to interrupt
|
||||
tab to add notes | enter to submit answer | ←/→ to navigate questions | esc to interrupt
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/request_user_input/mod.rs
|
||||
assertion_line: 2770
|
||||
expression: "render_snapshot(&overlay, area)"
|
||||
---
|
||||
|
||||
@@ -12,4 +13,4 @@ expression: "render_snapshot(&overlay, area)"
|
||||
|
||||
|
||||
|
||||
enter to submit all | ctrl + n first question | esc to interrupt
|
||||
enter to submit all | ctrl + p / ctrl + n change question | esc to interrupt
|
||||
|
||||
@@ -4492,12 +4492,7 @@ impl ChatWidget {
|
||||
fn prefetch_rate_limits(&mut self) {
|
||||
self.stop_rate_limit_poller();
|
||||
|
||||
if !self
|
||||
.auth_manager
|
||||
.auth_cached()
|
||||
.as_ref()
|
||||
.is_some_and(CodexAuth::is_chatgpt_auth)
|
||||
{
|
||||
if !self.should_prefetch_rate_limits() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4522,6 +4517,17 @@ impl ChatWidget {
|
||||
self.rate_limit_poller = Some(handle);
|
||||
}
|
||||
|
||||
fn should_prefetch_rate_limits(&self) -> bool {
|
||||
if !self.config.model_provider.requires_openai_auth {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.auth_manager
|
||||
.auth_cached()
|
||||
.as_ref()
|
||||
.is_some_and(CodexAuth::is_chatgpt_auth)
|
||||
}
|
||||
|
||||
fn lower_cost_preset(&self) -> Option<ModelPreset> {
|
||||
let models = self.models_manager.try_list_models(&self.config).ok()?;
|
||||
models
|
||||
|
||||
@@ -1138,6 +1138,22 @@ fn set_chatgpt_auth(chat: &mut ChatWidget) {
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prefetch_rate_limits_is_gated_on_chatgpt_auth_provider() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
assert!(!chat.should_prefetch_rate_limits());
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
assert!(chat.should_prefetch_rate_limits());
|
||||
|
||||
chat.config.model_provider.requires_openai_auth = false;
|
||||
assert!(!chat.should_prefetch_rate_limits());
|
||||
|
||||
chat.prefetch_rate_limits();
|
||||
assert!(chat.rate_limit_poller.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn worked_elapsed_from_resets_when_timer_restarts() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
Reference in New Issue
Block a user