refactor onboarding screen to a separate "app" (#2524)
this is in preparation for adding more separate "modes" to the tui, in particular, a "transcript mode" to view a full history once #2316 lands. 1. split apart "tui events" from "app events". 2. remove onboarding-related events from AppEvent. 3. move several general drawing tools out of App and into a new Tui class
This commit is contained in:
@@ -64,6 +64,11 @@ use color_eyre::owo_colors::OwoColorize;
|
||||
|
||||
pub use cli::Cli;
|
||||
|
||||
use crate::onboarding::TrustDirectorySelection;
|
||||
use crate::onboarding::onboarding_screen::OnboardingScreenArgs;
|
||||
use crate::onboarding::onboarding_screen::run_onboarding_app;
|
||||
use crate::tui::Tui;
|
||||
|
||||
// (tests access modules directly within the crate)
|
||||
|
||||
pub async fn run_main(
|
||||
@@ -256,6 +261,7 @@ async fn run_ratatui_app(
|
||||
config: Config,
|
||||
should_show_trust_screen: bool,
|
||||
) -> color_eyre::Result<codex_core::protocol::TokenUsage> {
|
||||
let mut config = config;
|
||||
color_eyre::install()?;
|
||||
|
||||
// Forward panic reports through tracing so they appear in the UI status
|
||||
@@ -267,23 +273,44 @@ async fn run_ratatui_app(
|
||||
tracing::error!("panic: {info}");
|
||||
prev_hook(info);
|
||||
}));
|
||||
let mut terminal = tui::init(&config)?;
|
||||
let mut terminal = tui::init()?;
|
||||
terminal.clear()?;
|
||||
let mut tui = Tui::new(terminal);
|
||||
|
||||
// Initialize high-fidelity session event logging if enabled.
|
||||
session_log::maybe_init(&config);
|
||||
|
||||
let Cli { prompt, images, .. } = cli;
|
||||
let mut app = App::new(config.clone(), prompt, images, should_show_trust_screen);
|
||||
|
||||
let app_result = app.run(&mut terminal).await;
|
||||
let usage = app.token_usage();
|
||||
let login_status = get_login_status(&config);
|
||||
let should_show_onboarding =
|
||||
should_show_onboarding(login_status, &config, should_show_trust_screen);
|
||||
if should_show_onboarding {
|
||||
let directory_trust_decision = run_onboarding_app(
|
||||
OnboardingScreenArgs {
|
||||
codex_home: config.codex_home.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
show_login_screen: should_show_login_screen(login_status, &config),
|
||||
show_trust_screen: should_show_trust_screen,
|
||||
login_status,
|
||||
preferred_auth_method: config.preferred_auth_method,
|
||||
},
|
||||
&mut tui,
|
||||
)
|
||||
.await?;
|
||||
if let Some(TrustDirectorySelection::Trust) = directory_trust_decision {
|
||||
config.approval_policy = AskForApproval::OnRequest;
|
||||
config.sandbox_policy = SandboxPolicy::new_workspace_write_policy();
|
||||
}
|
||||
}
|
||||
|
||||
let app_result = App::run(&mut tui, config, prompt, images).await;
|
||||
|
||||
restore();
|
||||
// Mark the end of the recorded session.
|
||||
session_log::log_session_end();
|
||||
// ignore error when collecting usage – report underlying error instead
|
||||
app_result.map(|_| usage)
|
||||
app_result
|
||||
}
|
||||
|
||||
#[expect(
|
||||
@@ -357,3 +384,80 @@ fn determine_repo_trust_state(
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn should_show_onboarding(
|
||||
login_status: LoginStatus,
|
||||
config: &Config,
|
||||
show_trust_screen: bool,
|
||||
) -> bool {
|
||||
if show_trust_screen {
|
||||
return true;
|
||||
}
|
||||
|
||||
should_show_login_screen(login_status, config)
|
||||
}
|
||||
|
||||
fn should_show_login_screen(login_status: LoginStatus, config: &Config) -> bool {
|
||||
// Only show the login screen for providers that actually require OpenAI auth
|
||||
// (OpenAI or equivalents). For OSS/other providers, skip login entirely.
|
||||
if !config.model_provider.requires_openai_auth {
|
||||
return false;
|
||||
}
|
||||
|
||||
match login_status {
|
||||
LoginStatus::NotAuthenticated => true,
|
||||
LoginStatus::AuthMode(method) => method != config.preferred_auth_method,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn make_config(preferred: AuthMode) -> Config {
|
||||
let mut cfg = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
std::env::temp_dir(),
|
||||
)
|
||||
.expect("load default config");
|
||||
cfg.preferred_auth_method = preferred;
|
||||
cfg
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shows_login_when_not_authenticated() {
|
||||
let cfg = make_config(AuthMode::ChatGPT);
|
||||
assert!(should_show_login_screen(
|
||||
LoginStatus::NotAuthenticated,
|
||||
&cfg
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shows_login_when_api_key_but_prefers_chatgpt() {
|
||||
let cfg = make_config(AuthMode::ChatGPT);
|
||||
assert!(should_show_login_screen(
|
||||
LoginStatus::AuthMode(AuthMode::ApiKey),
|
||||
&cfg
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hides_login_when_api_key_and_prefers_api_key() {
|
||||
let cfg = make_config(AuthMode::ApiKey);
|
||||
assert!(!should_show_login_screen(
|
||||
LoginStatus::AuthMode(AuthMode::ApiKey),
|
||||
&cfg
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hides_login_when_chatgpt_and_prefers_chatgpt() {
|
||||
let cfg = make_config(AuthMode::ChatGPT);
|
||||
assert!(!should_show_login_screen(
|
||||
LoginStatus::AuthMode(AuthMode::ChatGPT),
|
||||
&cfg
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user