Migrate GitWarning to OnboardingScreen (#1915)
This paves the way to do per-directory approval settings (https://github.com/openai/codex/pull/1912). This also lets us pass in a Config/ChatWidgetArgs into onboarding which can then mutate it and emit the ChatWidgetArgs it wants at the end which may be modified by the said approval settings. <img width="1180" height="428" alt="CleanShot 2025-08-06 at 19 30 55" src="https://github.com/user-attachments/assets/4dcfda42-0f5e-4b6d-a16d-2597109cc31c" />
This commit is contained in:
@@ -18,18 +18,23 @@ use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::colors::LIGHT_BLUE;
|
||||
use crate::colors::SUCCESS_GREEN;
|
||||
use crate::onboarding::onboarding_screen::KeyEventResult;
|
||||
use crate::onboarding::onboarding_screen::KeyboardHandler;
|
||||
use crate::onboarding::onboarding_screen::StepStateProvider;
|
||||
use crate::shimmer::FrameTicker;
|
||||
use crate::shimmer::shimmer_spans;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::onboarding_screen::StepState;
|
||||
// no additional imports
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SignInState {
|
||||
PickMode,
|
||||
ChatGptContinueInBrowser(#[allow(dead_code)] ContinueInBrowserState),
|
||||
ChatGptSuccessMessage,
|
||||
ChatGptSuccess,
|
||||
EnvVarMissing,
|
||||
EnvVarFound,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -38,7 +43,6 @@ pub(crate) struct ContinueInBrowserState {
|
||||
_login_child: Option<codex_login::SpawnedLogin>,
|
||||
_frame_ticker: Option<FrameTicker>,
|
||||
}
|
||||
|
||||
impl Drop for ContinueInBrowserState {
|
||||
fn drop(&mut self) {
|
||||
if let Some(child) = &self._login_child {
|
||||
@@ -52,54 +56,45 @@ impl Drop for ContinueInBrowserState {
|
||||
}
|
||||
|
||||
impl KeyboardHandler for AuthModeWidget {
|
||||
fn handle_key_event(&mut self, key_event: KeyEvent) -> KeyEventResult {
|
||||
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||
match key_event.code {
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
self.mode = AuthMode::ChatGPT;
|
||||
KeyEventResult::None
|
||||
self.highlighted_mode = AuthMode::ChatGPT;
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
self.mode = AuthMode::ApiKey;
|
||||
KeyEventResult::None
|
||||
self.highlighted_mode = AuthMode::ApiKey;
|
||||
}
|
||||
KeyCode::Char('1') => {
|
||||
self.mode = AuthMode::ChatGPT;
|
||||
self.start_chatgpt_login();
|
||||
KeyEventResult::None
|
||||
}
|
||||
KeyCode::Char('2') => {
|
||||
self.mode = AuthMode::ApiKey;
|
||||
self.verify_api_key()
|
||||
}
|
||||
KeyCode::Enter => match self.mode {
|
||||
AuthMode::ChatGPT => match &self.sign_in_state {
|
||||
SignInState::PickMode => self.start_chatgpt_login(),
|
||||
SignInState::ChatGptContinueInBrowser(_) => KeyEventResult::None,
|
||||
SignInState::ChatGptSuccess => KeyEventResult::Continue,
|
||||
KeyCode::Char('2') => self.verify_api_key(),
|
||||
KeyCode::Enter => match self.sign_in_state {
|
||||
SignInState::PickMode => match self.highlighted_mode {
|
||||
AuthMode::ChatGPT => self.start_chatgpt_login(),
|
||||
AuthMode::ApiKey => self.verify_api_key(),
|
||||
},
|
||||
AuthMode::ApiKey => self.verify_api_key(),
|
||||
SignInState::EnvVarMissing => self.sign_in_state = SignInState::PickMode,
|
||||
SignInState::ChatGptSuccessMessage => {
|
||||
self.sign_in_state = SignInState::ChatGptSuccess
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
KeyCode::Esc => {
|
||||
if matches!(self.sign_in_state, SignInState::ChatGptContinueInBrowser(_)) {
|
||||
self.sign_in_state = SignInState::PickMode;
|
||||
self.event_tx.send(AppEvent::RequestRedraw);
|
||||
KeyEventResult::None
|
||||
} else {
|
||||
KeyEventResult::Quit
|
||||
}
|
||||
}
|
||||
KeyCode::Char('q') => KeyEventResult::Quit,
|
||||
_ => KeyEventResult::None,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AuthModeWidget {
|
||||
pub mode: AuthMode,
|
||||
pub event_tx: AppEventSender,
|
||||
pub highlighted_mode: AuthMode,
|
||||
pub error: Option<String>,
|
||||
pub sign_in_state: SignInState,
|
||||
pub event_tx: AppEventSender,
|
||||
pub codex_home: PathBuf,
|
||||
}
|
||||
|
||||
@@ -121,7 +116,7 @@ impl AuthModeWidget {
|
||||
text: &str,
|
||||
description: &str|
|
||||
-> Vec<Line<'static>> {
|
||||
let is_selected = self.mode == selected_mode;
|
||||
let is_selected = self.highlighted_mode == selected_mode;
|
||||
let caret = if is_selected { ">" } else { " " };
|
||||
|
||||
let line1 = if is_selected {
|
||||
@@ -192,7 +187,7 @@ impl AuthModeWidget {
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_chatgpt_success(&self, area: Rect, buf: &mut Buffer) {
|
||||
fn render_chatgpt_success_message(&self, area: Rect, buf: &mut Buffer) {
|
||||
let lines = vec![
|
||||
Line::from("✓ Signed in with your ChatGPT account")
|
||||
.style(Style::default().fg(SUCCESS_GREEN)),
|
||||
@@ -219,7 +214,40 @@ impl AuthModeWidget {
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn start_chatgpt_login(&mut self) -> KeyEventResult {
|
||||
fn render_chatgpt_success(&self, area: Rect, buf: &mut Buffer) {
|
||||
let lines = vec![
|
||||
Line::from("✓ Signed in with your ChatGPT account")
|
||||
.style(Style::default().fg(SUCCESS_GREEN)),
|
||||
];
|
||||
|
||||
Paragraph::new(lines)
|
||||
.wrap(Wrap { trim: false })
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_env_var_found(&self, area: Rect, buf: &mut Buffer) {
|
||||
let lines =
|
||||
vec![Line::from("✓ Using OPENAI_API_KEY").style(Style::default().fg(SUCCESS_GREEN))];
|
||||
|
||||
Paragraph::new(lines)
|
||||
.wrap(Wrap { trim: false })
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_env_var_missing(&self, area: Rect, buf: &mut Buffer) {
|
||||
let lines = vec![
|
||||
Line::from("✘ OPENAI_API_KEY not found").style(Style::default().fg(Color::Red)),
|
||||
Line::from(""),
|
||||
Line::from(" Press Enter to return")
|
||||
.style(Style::default().add_modifier(Modifier::DIM)),
|
||||
];
|
||||
|
||||
Paragraph::new(lines)
|
||||
.wrap(Wrap { trim: false })
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn start_chatgpt_login(&mut self) {
|
||||
self.error = None;
|
||||
match codex_login::spawn_login_with_chatgpt(&self.codex_home) {
|
||||
Ok(child) => {
|
||||
@@ -230,27 +258,23 @@ impl AuthModeWidget {
|
||||
_frame_ticker: Some(FrameTicker::new(self.event_tx.clone())),
|
||||
});
|
||||
self.event_tx.send(AppEvent::RequestRedraw);
|
||||
KeyEventResult::None
|
||||
}
|
||||
Err(e) => {
|
||||
self.sign_in_state = SignInState::PickMode;
|
||||
self.error = Some(e.to_string());
|
||||
self.event_tx.send(AppEvent::RequestRedraw);
|
||||
KeyEventResult::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Read/write from the correct hierarchy config overrides + auth json + OPENAI_API_KEY.
|
||||
fn verify_api_key(&mut self) -> KeyEventResult {
|
||||
fn verify_api_key(&mut self) {
|
||||
if std::env::var("OPENAI_API_KEY").is_err() {
|
||||
self.error =
|
||||
Some("Set OPENAI_API_KEY in your environment. Learn more: https://platform.openai.com/docs/libraries".to_string());
|
||||
self.event_tx.send(AppEvent::RequestRedraw);
|
||||
KeyEventResult::None
|
||||
self.sign_in_state = SignInState::EnvVarMissing;
|
||||
} else {
|
||||
KeyEventResult::Continue
|
||||
self.sign_in_state = SignInState::EnvVarFound;
|
||||
}
|
||||
self.event_tx.send(AppEvent::RequestRedraw);
|
||||
}
|
||||
|
||||
fn spawn_completion_poller(&self, child: codex_login::SpawnedLogin) {
|
||||
@@ -299,6 +323,18 @@ impl AuthModeWidget {
|
||||
}
|
||||
}
|
||||
|
||||
impl StepStateProvider for AuthModeWidget {
|
||||
fn get_step_state(&self) -> StepState {
|
||||
match &self.sign_in_state {
|
||||
SignInState::PickMode
|
||||
| SignInState::EnvVarMissing
|
||||
| SignInState::ChatGptContinueInBrowser(_)
|
||||
| SignInState::ChatGptSuccessMessage => StepState::InProgress,
|
||||
SignInState::ChatGptSuccess | SignInState::EnvVarFound => StepState::Complete,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for AuthModeWidget {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
match self.sign_in_state {
|
||||
@@ -308,9 +344,18 @@ impl WidgetRef for AuthModeWidget {
|
||||
SignInState::ChatGptContinueInBrowser(_) => {
|
||||
self.render_continue_in_browser(area, buf);
|
||||
}
|
||||
SignInState::ChatGptSuccessMessage => {
|
||||
self.render_chatgpt_success_message(area, buf);
|
||||
}
|
||||
SignInState::ChatGptSuccess => {
|
||||
self.render_chatgpt_success(area, buf);
|
||||
}
|
||||
SignInState::EnvVarMissing => {
|
||||
self.render_env_var_missing(area, buf);
|
||||
}
|
||||
SignInState::EnvVarFound => {
|
||||
self.render_env_var_found(area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user