mirror of
https://github.com/openai/codex.git
synced 2026-05-01 20:02:05 +03:00
feat(tui): add /statusline command for interactive status line configuration (#10546)
## Summary - Adds a new `/statusline` command to configure TUI footer status line - Introduces reusable `MultiSelectPicker` component with keyboard navigation, optional ordering and toggle support - Implement status line setup modal that persist configuration to config.toml ## Status Line Items The following items can be displayed in the status line: - **Model**: Current model name (with optional reasoning level) - **Context**: Remaining/used context window percentage - **Rate Limits**: 5-day and weekly usage limits - **Git**: Current branch (with optimized lookups) - **Tokens**: Used tokens, input/output token counts - **Session**: Session ID (full or shortened prefix) - **Paths**: Current directory, project root - **Version**: Codex version ## Features - Live preview while configuring status line items - Fuzzy search filtering in the picker - Intelligent truncation when items don't fit - Items gracefully omit when data is unavailable - Configuration persists to `config.toml` - Validates and warns about invalid status line items ## Test plan - [x] Run `/statusline` and verify picker UI appears - [x] Toggle items on/off and verify live preview updates - [x] Confirm selection persists after restart - [x] Verify truncation behavior with many items selected - [x] Test git branch detection in and out of git repos --------- Co-authored-by: Josh McKinney <joshka@openai.com>
This commit is contained in:
@@ -106,15 +106,20 @@ fn line_width(line: &Line<'_>) -> usize {
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> {
|
||||
pub(crate) fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> {
|
||||
if max_width == 0 {
|
||||
return Line::from(Vec::<Span<'static>>::new());
|
||||
}
|
||||
|
||||
let Line {
|
||||
style,
|
||||
alignment,
|
||||
spans,
|
||||
} = line;
|
||||
let mut used = 0usize;
|
||||
let mut spans_out: Vec<Span<'static>> = Vec::new();
|
||||
|
||||
for span in line.spans {
|
||||
for span in spans {
|
||||
let text = span.content.into_owned();
|
||||
let style = span.style;
|
||||
let span_width = UnicodeWidthStr::width(text.as_str());
|
||||
@@ -151,10 +156,17 @@ fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static
|
||||
break;
|
||||
}
|
||||
|
||||
Line::from(spans_out)
|
||||
Line {
|
||||
style,
|
||||
alignment,
|
||||
spans: spans_out,
|
||||
}
|
||||
}
|
||||
|
||||
fn truncate_line_with_ellipsis_if_overflow(line: Line<'static>, max_width: usize) -> Line<'static> {
|
||||
pub(crate) fn truncate_line_with_ellipsis_if_overflow(
|
||||
line: Line<'static>,
|
||||
max_width: usize,
|
||||
) -> Line<'static> {
|
||||
if max_width == 0 {
|
||||
return Line::from(Vec::<Span<'static>>::new());
|
||||
}
|
||||
@@ -165,10 +177,18 @@ fn truncate_line_with_ellipsis_if_overflow(line: Line<'static>, max_width: usize
|
||||
}
|
||||
|
||||
let truncated = truncate_line_to_width(line, max_width.saturating_sub(1));
|
||||
let mut spans = truncated.spans;
|
||||
let Line {
|
||||
style,
|
||||
alignment,
|
||||
mut spans,
|
||||
} = truncated;
|
||||
let ellipsis_style = spans.last().map(|span| span.style).unwrap_or_default();
|
||||
spans.push(Span::styled("…", ellipsis_style));
|
||||
Line::from(spans)
|
||||
Line {
|
||||
style,
|
||||
alignment,
|
||||
spans,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the shared start column used for descriptions in selection rows.
|
||||
|
||||
Reference in New Issue
Block a user