feat: update splash (#3631)
- Update splash styling. - Add center truncation for long paths. (Uses new `center_truncate_path` utility.) - Update the suggested commands. ## New splash <img width="560" height="326" alt="image" src="https://github.com/user-attachments/assets/b80d7075-f376-4019-a464-b96a78b0676d" /> ## Example with truncation: <img width="524" height="317" alt="image" src="https://github.com/user-attachments/assets/b023c5cc-0bf0-4d21-9b98-bfea85546eda" />
This commit is contained in:
@@ -574,7 +574,7 @@ impl HistoryCell for CompletedMcpToolCallWithImageOutput {
|
||||
}
|
||||
|
||||
const TOOL_CALL_MAX_LINES: usize = 5;
|
||||
const SESSION_HEADER_MAX_INNER_WIDTH: usize = 70;
|
||||
const SESSION_HEADER_MAX_INNER_WIDTH: usize = 56; // Just an eyeballed value
|
||||
|
||||
fn title_case(s: &str) -> String {
|
||||
if s.is_empty() {
|
||||
@@ -628,24 +628,29 @@ pub(crate) fn new_session_info(
|
||||
|
||||
// Help lines below the header (new copy and list)
|
||||
let help_lines: Vec<Line<'static>> = vec![
|
||||
"Describe a task to get started or try one of the following commands:"
|
||||
" To get started, describe a task or try one of these commands:"
|
||||
.dim()
|
||||
.into(),
|
||||
Line::from("".dim()),
|
||||
Line::from(""),
|
||||
Line::from(vec![
|
||||
"1. ".into(),
|
||||
"/status".bold(),
|
||||
" - show current session configuration and token usage".dim(),
|
||||
" ".into(),
|
||||
"/init".into(),
|
||||
" - create an AGENTS.md file with instructions for Codex".dim(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"2. ".into(),
|
||||
"/compact".bold(),
|
||||
" - compact the chat history to avoid context limits".dim(),
|
||||
" ".into(),
|
||||
"/status".into(),
|
||||
" - show current session configuration".dim(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"3. ".into(),
|
||||
"/prompts".bold(),
|
||||
" - explore starter prompts to get to know Codex".dim(),
|
||||
" ".into(),
|
||||
"/approvals".into(),
|
||||
" - choose what Codex can do without approval".dim(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
" ".into(),
|
||||
"/model".into(),
|
||||
" - choose what model and reasoning effort to use".dim(),
|
||||
]),
|
||||
];
|
||||
|
||||
@@ -715,16 +720,31 @@ impl SessionHeaderHistoryCell {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_directory(&self) -> String {
|
||||
if let Some(rel) = relativize_to_home(&self.directory) {
|
||||
fn format_directory(&self, max_width: Option<usize>) -> String {
|
||||
Self::format_directory_inner(&self.directory, max_width)
|
||||
}
|
||||
|
||||
fn format_directory_inner(directory: &Path, max_width: Option<usize>) -> String {
|
||||
let formatted = if let Some(rel) = relativize_to_home(directory) {
|
||||
if rel.as_os_str().is_empty() {
|
||||
"~".to_string()
|
||||
} else {
|
||||
format!("~{}{}", std::path::MAIN_SEPARATOR, rel.display())
|
||||
}
|
||||
} else {
|
||||
self.directory.display().to_string()
|
||||
directory.display().to_string()
|
||||
};
|
||||
|
||||
if let Some(max_width) = max_width {
|
||||
if max_width == 0 {
|
||||
return String::new();
|
||||
}
|
||||
if UnicodeWidthStr::width(formatted.as_str()) > max_width {
|
||||
return crate::text_formatting::center_truncate_path(&formatted, max_width);
|
||||
}
|
||||
}
|
||||
|
||||
formatted
|
||||
}
|
||||
|
||||
fn reasoning_label(&self) -> Option<&'static str> {
|
||||
@@ -753,79 +773,93 @@ impl HistoryCell for SessionHeaderHistoryCell {
|
||||
top.push('╭');
|
||||
top.push_str(&"─".repeat(inner_width));
|
||||
top.push('╮');
|
||||
out.push(Line::from(top));
|
||||
out.push(Line::from(top.dim()));
|
||||
|
||||
// Title line rendered inside the box: " >_ OpenAI Codex (vX)"
|
||||
let title_text = format!(" >_ OpenAI Codex (v{})", self.version);
|
||||
let title_w = UnicodeWidthStr::width(title_text.as_str());
|
||||
let pad_w = inner_width.saturating_sub(title_w);
|
||||
let mut title_spans: Vec<Span<'static>> = vec![
|
||||
"│".into(),
|
||||
" ".into(),
|
||||
">_ ".into(),
|
||||
"OpenAI Codex".bold(),
|
||||
" ".into(),
|
||||
format!("(v{})", self.version).dim(),
|
||||
Span::from("│").dim(),
|
||||
Span::from(" ").dim(),
|
||||
Span::from(">_ ").dim(),
|
||||
Span::from("OpenAI Codex").bold(),
|
||||
Span::from(" ").dim(),
|
||||
Span::from(format!("(v{})", self.version)).dim(),
|
||||
];
|
||||
if pad_w > 0 {
|
||||
title_spans.push(" ".repeat(pad_w).into());
|
||||
title_spans.push(Span::from(" ".repeat(pad_w)).dim());
|
||||
}
|
||||
title_spans.push("│".into());
|
||||
title_spans.push(Span::from("│").dim());
|
||||
out.push(Line::from(title_spans));
|
||||
|
||||
// Spacer row between title and details
|
||||
out.push(Line::from(vec![
|
||||
"│".into(),
|
||||
" ".repeat(inner_width).into(),
|
||||
"│".into(),
|
||||
Span::from(format!("│{}│", " ".repeat(inner_width))).dim(),
|
||||
]));
|
||||
|
||||
// Model line: " Model: <model> <reasoning_label> (change with /model)"
|
||||
const CHANGE_MODEL_HINT: &str = "(change with /model)";
|
||||
// Model line: " model: <model> <reasoning_label> (change with /model)"
|
||||
const CHANGE_MODEL_HINT_COMMAND: &str = "/model";
|
||||
const CHANGE_MODEL_HINT_EXPLANATION: &str = " to change";
|
||||
const DIR_LABEL: &str = "directory:";
|
||||
let label_width = DIR_LABEL.len();
|
||||
let model_label = format!(
|
||||
"{model_label:<label_width$}",
|
||||
model_label = "model:",
|
||||
label_width = label_width
|
||||
);
|
||||
let reasoning_label = self.reasoning_label();
|
||||
let mut model_text = format!(" Model: {}", self.model);
|
||||
let mut model_value_for_width = self.model.clone();
|
||||
if let Some(reasoning) = reasoning_label {
|
||||
model_text.push(' ');
|
||||
model_text.push_str(reasoning);
|
||||
model_value_for_width.push(' ');
|
||||
model_value_for_width.push_str(reasoning);
|
||||
}
|
||||
model_text.push(' ');
|
||||
model_text.push_str(CHANGE_MODEL_HINT);
|
||||
let model_w = UnicodeWidthStr::width(model_text.as_str());
|
||||
let model_text_for_width_calc = format!(
|
||||
" {model_label} {model_value_for_width} {CHANGE_MODEL_HINT_COMMAND}{CHANGE_MODEL_HINT_EXPLANATION}",
|
||||
);
|
||||
let model_w = UnicodeWidthStr::width(model_text_for_width_calc.as_str());
|
||||
let pad_w = inner_width.saturating_sub(model_w);
|
||||
let mut spans: Vec<Span<'static>> = vec![
|
||||
"│".into(),
|
||||
" ".into(),
|
||||
"Model: ".bold(),
|
||||
self.model.clone().into(),
|
||||
Span::from(format!("│ {model_label} ")).dim(),
|
||||
Span::from(self.model.clone()),
|
||||
];
|
||||
if let Some(reasoning) = reasoning_label {
|
||||
spans.push(" ".into());
|
||||
spans.push(reasoning.into());
|
||||
spans.push(Span::from(" "));
|
||||
spans.push(Span::from(reasoning));
|
||||
}
|
||||
spans.push(" ".into());
|
||||
spans.push(CHANGE_MODEL_HINT.dim());
|
||||
spans.push(Span::from(" ").dim());
|
||||
spans.push(Span::from(CHANGE_MODEL_HINT_COMMAND).cyan());
|
||||
spans.push(Span::from(CHANGE_MODEL_HINT_EXPLANATION).dim());
|
||||
if pad_w > 0 {
|
||||
spans.push(" ".repeat(pad_w).into());
|
||||
spans.push(Span::from(" ".repeat(pad_w)).dim());
|
||||
}
|
||||
spans.push("│".into());
|
||||
spans.push(Span::from("│").dim());
|
||||
out.push(Line::from(spans));
|
||||
|
||||
// Directory line: " Directory: <cwd>"
|
||||
let dir = self.format_directory();
|
||||
let dir_text = format!(" Directory: {dir}");
|
||||
let dir_label = format!("{DIR_LABEL:<label_width$}");
|
||||
let dir_prefix = format!(" {dir_label} ");
|
||||
let dir_max_width = inner_width.saturating_sub(UnicodeWidthStr::width(dir_prefix.as_str()));
|
||||
let dir = self.format_directory(Some(dir_max_width));
|
||||
let dir_text = format!(" {dir_label} {dir}");
|
||||
let dir_w = UnicodeWidthStr::width(dir_text.as_str());
|
||||
let pad_w = inner_width.saturating_sub(dir_w);
|
||||
let mut spans: Vec<Span<'static>> =
|
||||
vec!["│".into(), " ".into(), "Directory: ".bold(), dir.into()];
|
||||
let mut spans: Vec<Span<'static>> = vec![
|
||||
Span::from("│").dim(),
|
||||
Span::from(" ").dim(),
|
||||
Span::from(dir_label).dim(),
|
||||
Span::from(" ").dim(),
|
||||
Span::from(dir),
|
||||
];
|
||||
if pad_w > 0 {
|
||||
spans.push(" ".repeat(pad_w).into());
|
||||
spans.push(Span::from(" ".repeat(pad_w)).dim());
|
||||
}
|
||||
spans.push("│".into());
|
||||
spans.push(Span::from("│").dim());
|
||||
out.push(Line::from(spans));
|
||||
|
||||
// Bottom border
|
||||
let bottom = format!("╰{}╯", "─".repeat(inner_width));
|
||||
out.push(Line::from(bottom));
|
||||
out.push(Line::from(bottom.dim()));
|
||||
|
||||
out
|
||||
}
|
||||
@@ -1523,6 +1557,7 @@ mod tests {
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::ConfigToml;
|
||||
use dirs::home_dir;
|
||||
|
||||
fn test_config() -> Config {
|
||||
Config::load_from_base_config_with_overrides(
|
||||
@@ -1561,11 +1596,35 @@ mod tests {
|
||||
let lines = render_lines(&cell.display_lines(80));
|
||||
let model_line = lines
|
||||
.into_iter()
|
||||
.find(|line| line.contains("Model:"))
|
||||
.find(|line| line.contains("model:"))
|
||||
.expect("model line");
|
||||
|
||||
assert!(model_line.contains("Model: gpt-4o high"));
|
||||
assert!(model_line.contains("(change with /model)"));
|
||||
assert!(model_line.contains("gpt-4o high"));
|
||||
assert!(model_line.contains("/model to change"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_header_directory_center_truncates() {
|
||||
let mut dir = home_dir().expect("home directory");
|
||||
for part in ["hello", "the", "fox", "is", "very", "fast"] {
|
||||
dir.push(part);
|
||||
}
|
||||
|
||||
let formatted = SessionHeaderHistoryCell::format_directory_inner(&dir, Some(24));
|
||||
let sep = std::path::MAIN_SEPARATOR;
|
||||
let expected = format!("~{sep}hello{sep}the{sep}…{sep}very{sep}fast");
|
||||
assert_eq!(formatted, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_header_directory_front_truncates_long_segment() {
|
||||
let mut dir = home_dir().expect("home directory");
|
||||
dir.push("supercalifragilisticexpialidocious");
|
||||
|
||||
let formatted = SessionHeaderHistoryCell::format_directory_inner(&dir, Some(18));
|
||||
let sep = std::path::MAIN_SEPARATOR;
|
||||
let expected = format!("~{sep}…cexpialidocious");
|
||||
assert_eq!(formatted, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user