Skip to content

Commit 888a22d

Browse files
committed
update login ux
1 parent 4a97cb4 commit 888a22d

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

crates/q_cli/src/cli/mod.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,17 @@ impl Cli {
426426
pub async fn execute_chat(subcmd: &str, args: Option<Vec<String>>, enforce_login: bool) -> Result<ExitCode> {
427427
if enforce_login && !is_logged_in_check().await {
428428
if subcmd == "chat" {
429-
let options = ["Yes", "No"];
430-
match crate::util::choose(" You are not logged in. Login now?", &options)? {
431-
Some(0) => {},
432-
_ => bail!("Login is required to use chat"),
429+
match crate::util::welcome_login_prompt()? {
430+
true => {
431+
crate::cli::user::login_interactive(Default::default()).await?;
432+
},
433+
false => {
434+
bail!(
435+
"You are not logged in, please log in with {}",
436+
format!("{CLI_BINARY_NAME} login").bold()
437+
);
438+
},
433439
}
434-
crate::cli::user::login_interactive(Default::default()).await?;
435440
} else {
436441
bail!(
437442
"You are not logged in, please log in with {}",

crates/q_cli/src/cli/user.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ pub async fn login_interactive(args: LoginArgs) -> Result<()> {
269269

270270
let mut pre_portal_spinner = Spinner::new(vec![
271271
SpinnerComponent::Spinner,
272-
SpinnerComponent::Text(" Opening auth portal and logging in...".into()),
272+
SpinnerComponent::Text(crate::util::login_spinner_message()),
273273
]);
274274

275275
// Try unified portal, fallback to device flow on any error

crates/q_cli/src/util/mod.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ use regex::Regex;
5353
pub use region_check::region_check;
5454
use tracing::warn;
5555

56+
// ANSI color codes
57+
const PURPLE: u8 = 93;
58+
const GRAY: u8 = 8;
59+
const MAGENTA: u8 = 13;
60+
5661
/// Glob patterns against full paths
5762
pub fn glob_dir(glob: &GlobSet, directory: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
5863
let mut files = Vec::new();
@@ -276,6 +281,71 @@ pub fn choose(prompt: impl Display, options: &[impl ToString]) -> Result<Option<
276281
}
277282
}
278283

284+
pub fn welcome_login_prompt() -> Result<bool> {
285+
use crossterm::event::{
286+
self,
287+
Event,
288+
KeyCode,
289+
KeyEvent,
290+
};
291+
use crossterm::style::{
292+
Color,
293+
ResetColor,
294+
SetForegroundColor,
295+
};
296+
use crossterm::terminal::{
297+
disable_raw_mode,
298+
enable_raw_mode,
299+
};
300+
301+
if !stdout().is_terminal() {
302+
warn!("called welcome_login_prompt while stdout is not a terminal");
303+
return Ok(true);
304+
}
305+
306+
println!();
307+
print!("Welcome to ");
308+
print!(
309+
"{}{}{}",
310+
SetForegroundColor(Color::AnsiValue(PURPLE)),
311+
PRODUCT_NAME,
312+
ResetColor
313+
);
314+
println!(", let's get you signed in!");
315+
println!();
316+
317+
// Print instruction line with styling
318+
print!("{}Press ", SetForegroundColor(Color::AnsiValue(GRAY)));
319+
print!(
320+
"{}enter{}",
321+
SetForegroundColor(Color::AnsiValue(MAGENTA)),
322+
SetForegroundColor(Color::AnsiValue(GRAY))
323+
);
324+
print!(" to continue to the browser or ");
325+
print!(
326+
"{}esc{}",
327+
SetForegroundColor(Color::AnsiValue(MAGENTA)),
328+
SetForegroundColor(Color::AnsiValue(GRAY))
329+
);
330+
println!(" to cancel{}", ResetColor);
331+
println!();
332+
333+
enable_raw_mode()?;
334+
let result = loop {
335+
if let Ok(Event::Key(KeyEvent { code, .. })) = event::read() {
336+
match code {
337+
KeyCode::Enter => break Ok(true),
338+
KeyCode::Esc => break Ok(false),
339+
KeyCode::Char('c') if event::read().is_ok() => break Ok(false), // Ctrl+C
340+
_ => {},
341+
}
342+
}
343+
};
344+
disable_raw_mode()?;
345+
346+
result
347+
}
348+
279349
pub fn input(prompt: &str, initial_text: Option<&str>) -> Result<String> {
280350
if !stdout().is_terminal() {
281351
warn!("called input while stdout is not a terminal");
@@ -319,6 +389,14 @@ pub fn dialoguer_theme() -> ColorfulTheme {
319389
..ColorfulTheme::default()
320390
}
321391
}
392+
393+
pub fn login_spinner_message() -> String {
394+
format!(
395+
" \x1b[38;5;{}mOpening browser... | Press \x1b[38;5;{}m(^) + C\x1b[38;5;{}m to cancel\x1b[0m",
396+
GRAY, MAGENTA, GRAY
397+
)
398+
}
399+
322400
pub async fn is_logged_in_check() -> bool {
323401
std::env::var("AMAZON_Q_SIGV4").is_ok_and(|v| !v.is_empty()) || fig_auth::is_logged_in().await
324402
}

0 commit comments

Comments
 (0)