mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
## Why Picker-style UI in the TUI has accumulated a mix of hardcoded navigation keys. Some lists supported page movement, some did not; some accepted Vim-like keys, while others only accepted arrows; and tabbed or horizontally adjustable pickers had no shared keymap action for left/right movement. This PR makes picker/list navigation consistent and configurable so users can rely on the same defaults across the TUI. ## What Changed - Adds shared list keymap actions for: - vertical movement: `move_up`, `move_down` - horizontal movement: `move_left`, `move_right` - paging and jumps: `page_up`, `page_down`, `jump_top`, `jump_bottom` - Adds defaults: - Up/down: arrows, `Ctrl+P/N`, `Ctrl+K/J`, and plain `k/j` where text input is not active - Page up/down: `PageUp/PageDown` and `Ctrl+B/F` - First/last: `Home/End` - Left/right: `Left/Right` and `Ctrl+H/L` - Wires the shared list keymap through picker and list surfaces including session resume, multi-select, tabbed selection lists, settings-style lists, app-link selection, MCP elicitation, request-user-input, and the OSS selection wizard. - Keeps search behavior intact by reserving printable characters for query text in searchable pickers. - Updates keymap setup actions, config schema, snapshots, and focused coverage for the new list actions. ## How to Test 1. Start Codex from this branch and open the session picker, for example with an existing session history. 2. In the session list, verify that `Ctrl+J/K` moves the selection down/up. 3. Verify that `Ctrl+F/B` pages down/up and `Home/End` jumps to the first/last visible session. 4. Type printable search text such as `j` or `k` and confirm it updates the query instead of navigating. 5. Focus a picker control that changes values horizontally, such as a session picker toolbar control, and verify `Ctrl+H/L` changes the focused value like left/right arrows. Targeted tests run: - `cargo test -p codex-tui keymap::tests::` - `cargo test -p codex-tui keymap_setup::tests::` - `cargo test -p codex-tui horizontal_list_keys` - `cargo test -p codex-tui page_and_jump_navigation_use_list_keymap` - `cargo test -p codex-tui ctrl_h_l_move_provider_selection` - `cargo test -p codex-tui scroll_state::tests` - `cargo test -p codex-tui switching_tabs_changes_visible_items_and_clears_search` - `cargo test -p codex-tui toggle_sort_key_reloads_with_new_sort` Also ran `just write-config-schema`, `just fmt`, `just fix -p codex-tui`, `just argument-comment-lint`, and `git diff --check`. Note: `cargo test -p codex-tui` was attempted and still aborts in the pre-existing `tests::fork_last_filters_latest_session_by_cwd_unless_show_all` stack overflow, which is unrelated to this branch.
548 lines
20 KiB
Rust
548 lines
20 KiB
Rust
//! Terminal title configuration view for customizing the terminal window/tab title.
|
|
//!
|
|
//! This module provides an interactive picker for selecting which items appear
|
|
//! in the terminal title. Users can:
|
|
//!
|
|
//! - Select items
|
|
//! - Reorder items
|
|
//! - Preview the rendered title
|
|
|
|
use itertools::Itertools;
|
|
use ratatui::buffer::Buffer;
|
|
use ratatui::layout::Rect;
|
|
use ratatui::text::Line;
|
|
use strum::IntoEnumIterator;
|
|
use strum_macros::Display;
|
|
use strum_macros::EnumIter;
|
|
use strum_macros::EnumString;
|
|
|
|
use crate::app_event::AppEvent;
|
|
use crate::app_event_sender::AppEventSender;
|
|
use crate::bottom_pane::ACTION_REQUIRED_PREVIEW_PREFIX;
|
|
use crate::bottom_pane::CancellationEvent;
|
|
use crate::bottom_pane::bottom_pane_view::BottomPaneView;
|
|
use crate::bottom_pane::build_action_required_title_text;
|
|
use crate::bottom_pane::multi_select_picker::MultiSelectItem;
|
|
use crate::bottom_pane::multi_select_picker::MultiSelectPicker;
|
|
use crate::bottom_pane::status_surface_preview::StatusSurfacePreviewData;
|
|
use crate::bottom_pane::status_surface_preview::StatusSurfacePreviewItem;
|
|
use crate::keymap::ListKeymap;
|
|
use crate::render::renderable::Renderable;
|
|
|
|
/// Available items that can be displayed in the terminal title.
|
|
///
|
|
/// Variants serialize to kebab-case identifiers (e.g. `AppName` -> `"app-name"`)
|
|
/// via strum. These identifiers are persisted in user config files, so renaming
|
|
/// or removing a variant is a breaking config change.
|
|
#[derive(EnumIter, EnumString, Display, Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
|
#[strum(serialize_all = "kebab-case")]
|
|
pub(crate) enum TerminalTitleItem {
|
|
/// Codex app name.
|
|
AppName,
|
|
/// Project root name, or a compact cwd fallback.
|
|
#[strum(to_string = "project-name", serialize = "project")]
|
|
Project,
|
|
/// Current working directory path.
|
|
CurrentDir,
|
|
/// Terminal-title activity indicator while active.
|
|
#[strum(to_string = "activity", serialize = "spinner")]
|
|
Spinner,
|
|
/// Compact runtime run-state text.
|
|
#[strum(to_string = "run-state", serialize = "status")]
|
|
Status,
|
|
/// Current thread title (if available).
|
|
#[strum(to_string = "thread-title", serialize = "thread")]
|
|
Thread,
|
|
/// Current git branch (if available).
|
|
GitBranch,
|
|
/// Percentage of context window remaining.
|
|
ContextRemaining,
|
|
/// Percentage of context window used.
|
|
#[strum(to_string = "context-used", serialize = "context-usage")]
|
|
ContextUsed,
|
|
/// Remaining usage on the 5-hour rate limit.
|
|
FiveHourLimit,
|
|
/// Remaining usage on the weekly rate limit.
|
|
WeeklyLimit,
|
|
/// Codex application version.
|
|
CodexVersion,
|
|
/// Total tokens used in the current session.
|
|
UsedTokens,
|
|
/// Total input tokens consumed.
|
|
TotalInputTokens,
|
|
/// Total output tokens generated.
|
|
TotalOutputTokens,
|
|
/// Full thread UUID.
|
|
#[strum(to_string = "thread-id", serialize = "session-id")]
|
|
SessionId,
|
|
/// Whether Fast mode is currently active.
|
|
FastMode,
|
|
/// Current model name.
|
|
#[strum(to_string = "model", serialize = "model-name")]
|
|
Model,
|
|
/// Current model name with reasoning level.
|
|
ModelWithReasoning,
|
|
/// Latest checklist task progress from `update_plan` (if available).
|
|
TaskProgress,
|
|
}
|
|
|
|
impl TerminalTitleItem {
|
|
pub(crate) fn description(self) -> &'static str {
|
|
match self {
|
|
TerminalTitleItem::AppName => "Codex app name",
|
|
TerminalTitleItem::Project => "Project name (falls back to current directory name)",
|
|
TerminalTitleItem::CurrentDir => "Current working directory",
|
|
TerminalTitleItem::Spinner => {
|
|
"Spinner while working, action-required message while blocked."
|
|
}
|
|
TerminalTitleItem::Status => {
|
|
"Compact session run-state text (Ready, Working, Thinking)"
|
|
}
|
|
TerminalTitleItem::Thread => "Current thread title, or thread identifier when unnamed",
|
|
TerminalTitleItem::GitBranch => "Current Git branch (omitted when unavailable)",
|
|
TerminalTitleItem::ContextRemaining => {
|
|
"Percentage of context window remaining (omitted when unknown)"
|
|
}
|
|
TerminalTitleItem::ContextUsed => {
|
|
"Percentage of context window used (omitted when unknown)"
|
|
}
|
|
TerminalTitleItem::FiveHourLimit => {
|
|
"Remaining usage on 5-hour usage limit (omitted when unavailable)"
|
|
}
|
|
TerminalTitleItem::WeeklyLimit => {
|
|
"Remaining usage on weekly usage limit (omitted when unavailable)"
|
|
}
|
|
TerminalTitleItem::CodexVersion => "Codex application version",
|
|
TerminalTitleItem::UsedTokens => "Total tokens used in session (omitted when zero)",
|
|
TerminalTitleItem::TotalInputTokens => "Total input tokens used in session",
|
|
TerminalTitleItem::TotalOutputTokens => "Total output tokens used in session",
|
|
TerminalTitleItem::SessionId => {
|
|
"Current thread identifier (omitted until thread starts)"
|
|
}
|
|
TerminalTitleItem::FastMode => "Whether Fast mode is currently active",
|
|
TerminalTitleItem::Model => "Current model name",
|
|
TerminalTitleItem::ModelWithReasoning => "Current model name with reasoning level",
|
|
TerminalTitleItem::TaskProgress => {
|
|
"Latest task progress from update_plan (omitted until available)"
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn preview_item(self) -> Option<StatusSurfacePreviewItem> {
|
|
match self {
|
|
TerminalTitleItem::AppName => Some(StatusSurfacePreviewItem::AppName),
|
|
TerminalTitleItem::Project => Some(StatusSurfacePreviewItem::ProjectName),
|
|
TerminalTitleItem::CurrentDir => Some(StatusSurfacePreviewItem::CurrentDir),
|
|
TerminalTitleItem::Spinner => None,
|
|
TerminalTitleItem::Status => Some(StatusSurfacePreviewItem::Status),
|
|
TerminalTitleItem::Thread => Some(StatusSurfacePreviewItem::ThreadTitle),
|
|
TerminalTitleItem::GitBranch => Some(StatusSurfacePreviewItem::GitBranch),
|
|
TerminalTitleItem::ContextRemaining => Some(StatusSurfacePreviewItem::ContextRemaining),
|
|
TerminalTitleItem::ContextUsed => Some(StatusSurfacePreviewItem::ContextUsed),
|
|
TerminalTitleItem::FiveHourLimit => Some(StatusSurfacePreviewItem::FiveHourLimit),
|
|
TerminalTitleItem::WeeklyLimit => Some(StatusSurfacePreviewItem::WeeklyLimit),
|
|
TerminalTitleItem::CodexVersion => Some(StatusSurfacePreviewItem::CodexVersion),
|
|
TerminalTitleItem::UsedTokens => Some(StatusSurfacePreviewItem::UsedTokens),
|
|
TerminalTitleItem::TotalInputTokens => Some(StatusSurfacePreviewItem::TotalInputTokens),
|
|
TerminalTitleItem::TotalOutputTokens => {
|
|
Some(StatusSurfacePreviewItem::TotalOutputTokens)
|
|
}
|
|
TerminalTitleItem::SessionId => Some(StatusSurfacePreviewItem::SessionId),
|
|
TerminalTitleItem::FastMode => Some(StatusSurfacePreviewItem::FastMode),
|
|
TerminalTitleItem::Model => Some(StatusSurfacePreviewItem::Model),
|
|
TerminalTitleItem::ModelWithReasoning => {
|
|
Some(StatusSurfacePreviewItem::ModelWithReasoning)
|
|
}
|
|
TerminalTitleItem::TaskProgress => Some(StatusSurfacePreviewItem::TaskProgress),
|
|
}
|
|
}
|
|
|
|
/// Returns the separator to place before this item in a rendered title.
|
|
///
|
|
/// The activity indicator gets a plain space on either side so it reads as
|
|
/// `my-project <activity> Working` rather than `my-project | <activity> | Working`.
|
|
/// All other adjacent items are joined with ` | `.
|
|
pub(crate) fn separator_from_previous(self, previous: Option<Self>) -> &'static str {
|
|
match previous {
|
|
None => "",
|
|
Some(previous)
|
|
if previous == TerminalTitleItem::Spinner || self == TerminalTitleItem::Spinner =>
|
|
{
|
|
" "
|
|
}
|
|
Some(_) => " | ",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn preview_line_for_title_items(
|
|
items: &[TerminalTitleItem],
|
|
preview_data: &StatusSurfacePreviewData,
|
|
) -> Option<Line<'static>> {
|
|
if items.contains(&TerminalTitleItem::Spinner) {
|
|
let preview = build_action_required_title_text(
|
|
ACTION_REQUIRED_PREVIEW_PREFIX,
|
|
items.iter().copied(),
|
|
&[],
|
|
|item| {
|
|
item.preview_item()
|
|
.and_then(|preview_item| preview_data.value_for(preview_item))
|
|
.map(str::to_owned)
|
|
},
|
|
);
|
|
return Some(Line::from(preview));
|
|
}
|
|
|
|
let mut previous = None;
|
|
let preview = items
|
|
.iter()
|
|
.copied()
|
|
.fold(String::new(), |mut preview, item| {
|
|
let Some(value) = item
|
|
.preview_item()
|
|
.and_then(|preview_item| preview_data.value_for(preview_item))
|
|
else {
|
|
return preview;
|
|
};
|
|
preview.push_str(item.separator_from_previous(previous));
|
|
preview.push_str(value);
|
|
previous = Some(item);
|
|
preview
|
|
});
|
|
if preview.is_empty() {
|
|
None
|
|
} else {
|
|
Some(Line::from(preview))
|
|
}
|
|
}
|
|
|
|
fn parse_terminal_title_items<T>(ids: impl Iterator<Item = T>) -> Option<Vec<TerminalTitleItem>>
|
|
where
|
|
T: AsRef<str>,
|
|
{
|
|
// Treat parsing as all-or-nothing so preview/confirm callbacks never emit
|
|
// a partially interpreted ordering. Invalid ids are ignored when building
|
|
// the picker, but once the user is interacting with the picker we only want
|
|
// to persist or preview a fully valid selection.
|
|
ids.map(|id| id.as_ref().parse::<TerminalTitleItem>())
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.ok()
|
|
}
|
|
|
|
/// Interactive view for configuring terminal-title items.
|
|
pub(crate) struct TerminalTitleSetupView {
|
|
picker: MultiSelectPicker,
|
|
}
|
|
|
|
impl TerminalTitleSetupView {
|
|
/// Creates the terminal-title picker, preserving the configured item order first.
|
|
///
|
|
/// Unknown configured ids are skipped here instead of surfaced inline. The
|
|
/// main TUI still warns about them when rendering the actual title, but the
|
|
/// picker itself only exposes the selectable items it can meaningfully
|
|
/// preview and persist.
|
|
pub(crate) fn new(
|
|
title_items: Option<&[String]>,
|
|
preview_data: StatusSurfacePreviewData,
|
|
app_event_tx: AppEventSender,
|
|
list_keymap: ListKeymap,
|
|
) -> Self {
|
|
let selected_items = title_items
|
|
.into_iter()
|
|
.flatten()
|
|
.filter_map(|id| id.parse::<TerminalTitleItem>().ok())
|
|
.unique()
|
|
.collect_vec();
|
|
let selected_set = selected_items
|
|
.iter()
|
|
.copied()
|
|
.collect::<std::collections::HashSet<_>>();
|
|
let items = selected_items
|
|
.into_iter()
|
|
.map(|item| Self::title_select_item(item, /*enabled*/ true))
|
|
.chain(
|
|
TerminalTitleItem::iter()
|
|
.filter(|item| !selected_set.contains(item))
|
|
.map(|item| Self::title_select_item(item, /*enabled*/ false)),
|
|
)
|
|
.collect();
|
|
|
|
Self {
|
|
picker: MultiSelectPicker::builder(
|
|
"Configure Terminal Title".to_string(),
|
|
Some("Select which items to display in the terminal title.".to_string()),
|
|
app_event_tx,
|
|
)
|
|
.list_keymap(list_keymap)
|
|
.items(items)
|
|
.enable_ordering()
|
|
.on_preview(move |items| {
|
|
let items = parse_terminal_title_items(
|
|
items
|
|
.iter()
|
|
.filter(|item| item.enabled)
|
|
.map(|item| item.id.as_str()),
|
|
)?;
|
|
preview_line_for_title_items(&items, &preview_data)
|
|
})
|
|
.on_change(|items, app_event| {
|
|
let Some(items) = parse_terminal_title_items(
|
|
items
|
|
.iter()
|
|
.filter(|item| item.enabled)
|
|
.map(|item| item.id.as_str()),
|
|
) else {
|
|
return;
|
|
};
|
|
app_event.send(AppEvent::TerminalTitleSetupPreview { items });
|
|
})
|
|
.on_confirm(|ids, app_event| {
|
|
let Some(items) = parse_terminal_title_items(ids.iter().map(String::as_str)) else {
|
|
return;
|
|
};
|
|
app_event.send(AppEvent::TerminalTitleSetup { items });
|
|
})
|
|
.on_cancel(|app_event| {
|
|
app_event.send(AppEvent::TerminalTitleSetupCancelled);
|
|
})
|
|
.build(),
|
|
}
|
|
}
|
|
|
|
fn title_select_item(item: TerminalTitleItem, enabled: bool) -> MultiSelectItem {
|
|
MultiSelectItem {
|
|
id: item.to_string(),
|
|
name: item.to_string(),
|
|
description: Some(item.description().to_string()),
|
|
enabled,
|
|
orderable: true,
|
|
section_break_after: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BottomPaneView for TerminalTitleSetupView {
|
|
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
|
|
self.picker.handle_key_event(key_event);
|
|
}
|
|
|
|
fn is_complete(&self) -> bool {
|
|
self.picker.complete
|
|
}
|
|
|
|
fn on_ctrl_c(&mut self) -> CancellationEvent {
|
|
self.picker.close();
|
|
CancellationEvent::Handled
|
|
}
|
|
}
|
|
|
|
impl Renderable for TerminalTitleSetupView {
|
|
fn render(&self, area: Rect, buf: &mut Buffer) {
|
|
self.picker.render(area, buf);
|
|
}
|
|
|
|
fn desired_height(&self, width: u16) -> u16 {
|
|
self.picker.desired_height(width)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use insta::assert_snapshot;
|
|
use pretty_assertions::assert_eq;
|
|
use tokio::sync::mpsc::unbounded_channel;
|
|
|
|
fn render_lines(view: &TerminalTitleSetupView, width: u16) -> String {
|
|
let height = view.desired_height(width);
|
|
let area = Rect::new(0, 0, width, height);
|
|
let mut buf = Buffer::empty(area);
|
|
view.render(area, &mut buf);
|
|
|
|
let lines: Vec<String> = (0..area.height)
|
|
.map(|row| {
|
|
let mut line = String::new();
|
|
for col in 0..area.width {
|
|
let symbol = buf[(area.x + col, area.y + row)].symbol();
|
|
if symbol.is_empty() {
|
|
line.push(' ');
|
|
} else {
|
|
line.push_str(symbol);
|
|
}
|
|
}
|
|
line
|
|
})
|
|
.collect();
|
|
lines.join("\n")
|
|
}
|
|
|
|
#[test]
|
|
fn renders_title_setup_popup() {
|
|
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
|
let tx = AppEventSender::new(tx_raw);
|
|
let selected = [
|
|
"project-name".to_string(),
|
|
"activity".to_string(),
|
|
"run-state".to_string(),
|
|
"thread-title".to_string(),
|
|
];
|
|
let view = TerminalTitleSetupView::new(
|
|
Some(&selected),
|
|
StatusSurfacePreviewData::default(),
|
|
tx,
|
|
crate::keymap::RuntimeKeymap::defaults().list,
|
|
);
|
|
assert_snapshot!(
|
|
"terminal_title_setup_basic",
|
|
render_lines(&view, /*width*/ 84)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_terminal_title_items_preserves_order() {
|
|
let items = parse_terminal_title_items(
|
|
["project-name", "activity", "run-state", "thread-title"].into_iter(),
|
|
);
|
|
assert_eq!(
|
|
items,
|
|
Some(vec![
|
|
TerminalTitleItem::Project,
|
|
TerminalTitleItem::Spinner,
|
|
TerminalTitleItem::Status,
|
|
TerminalTitleItem::Thread,
|
|
])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_terminal_title_items_rejects_invalid_ids() {
|
|
let items = parse_terminal_title_items(["project", "not-a-title-item"].into_iter());
|
|
assert_eq!(items, None);
|
|
}
|
|
|
|
#[test]
|
|
fn activity_is_canonical_and_accepts_spinner_legacy_id() {
|
|
assert_eq!(TerminalTitleItem::Spinner.to_string(), "activity");
|
|
assert_eq!(
|
|
"activity".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Spinner)
|
|
);
|
|
assert_eq!(
|
|
"spinner".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Spinner)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn project_name_is_canonical_and_accepts_project_legacy_id() {
|
|
assert_eq!(TerminalTitleItem::Project.to_string(), "project-name");
|
|
assert_eq!(
|
|
"project-name".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Project)
|
|
);
|
|
assert_eq!(
|
|
"project".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Project)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn thread_title_is_canonical_and_accepts_thread_legacy_id() {
|
|
assert_eq!(TerminalTitleItem::Thread.to_string(), "thread-title");
|
|
assert_eq!(
|
|
"thread-title".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Thread)
|
|
);
|
|
assert_eq!(
|
|
"thread".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Thread)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn model_is_canonical_and_accepts_model_name_legacy_id() {
|
|
assert_eq!(TerminalTitleItem::Model.to_string(), "model");
|
|
assert_eq!(
|
|
"model".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Model)
|
|
);
|
|
assert_eq!(
|
|
"model-name".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Model)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn run_state_is_canonical_and_accepts_status_legacy_id() {
|
|
assert_eq!(TerminalTitleItem::Status.to_string(), "run-state");
|
|
assert_eq!(
|
|
"run-state".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Status)
|
|
);
|
|
assert_eq!(
|
|
"status".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::Status)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn model_with_reasoning_has_distinct_id() {
|
|
assert_eq!(
|
|
TerminalTitleItem::ModelWithReasoning.to_string(),
|
|
"model-with-reasoning"
|
|
);
|
|
assert_eq!(
|
|
"model-with-reasoning".parse::<TerminalTitleItem>(),
|
|
Ok(TerminalTitleItem::ModelWithReasoning)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_terminal_title_items_accepts_kebab_case_variants() {
|
|
let items = parse_terminal_title_items(
|
|
[
|
|
"app-name",
|
|
"context-remaining",
|
|
"context-used",
|
|
"five-hour-limit",
|
|
"git-branch",
|
|
"activity",
|
|
"current-dir",
|
|
"project-name",
|
|
"model",
|
|
"model-with-reasoning",
|
|
"weekly-limit",
|
|
"codex-version",
|
|
"used-tokens",
|
|
"total-input-tokens",
|
|
"total-output-tokens",
|
|
"session-id",
|
|
"fast-mode",
|
|
]
|
|
.into_iter(),
|
|
);
|
|
assert_eq!(
|
|
items,
|
|
Some(vec![
|
|
TerminalTitleItem::AppName,
|
|
TerminalTitleItem::ContextRemaining,
|
|
TerminalTitleItem::ContextUsed,
|
|
TerminalTitleItem::FiveHourLimit,
|
|
TerminalTitleItem::GitBranch,
|
|
TerminalTitleItem::Spinner,
|
|
TerminalTitleItem::CurrentDir,
|
|
TerminalTitleItem::Project,
|
|
TerminalTitleItem::Model,
|
|
TerminalTitleItem::ModelWithReasoning,
|
|
TerminalTitleItem::WeeklyLimit,
|
|
TerminalTitleItem::CodexVersion,
|
|
TerminalTitleItem::UsedTokens,
|
|
TerminalTitleItem::TotalInputTokens,
|
|
TerminalTitleItem::TotalOutputTokens,
|
|
TerminalTitleItem::SessionId,
|
|
TerminalTitleItem::FastMode,
|
|
])
|
|
);
|
|
}
|
|
}
|