diff --git a/codex-rs/cli/src/doctor/title.rs b/codex-rs/cli/src/doctor/title.rs index 9894263dd9..79d1a6a9db 100644 --- a/codex-rs/cli/src/doctor/title.rs +++ b/codex-rs/cli/src/doctor/title.rs @@ -142,6 +142,7 @@ fn terminal_title_item_id(item: &str) -> Option<&'static str> { "total-output-tokens" => Some("total-output-tokens"), "thread-id" | "session-id" => Some("thread-id"), "fast-mode" => Some("fast-mode"), + "daybreak" => Some("daybreak"), "model" | "model-name" => Some("model"), "model-with-reasoning" => Some("model-with-reasoning"), "reasoning" => Some("reasoning"), @@ -309,11 +310,12 @@ mod tests { } #[test] - fn terminal_title_accepts_thread_usage_items() { + fn terminal_title_accepts_configured_items() { let check = terminal_title_check_from_inputs(TerminalTitleInputs { configured_items: Some(vec![ "thread-credits".to_string(), "estimated-thread-cost".to_string(), + "daybreak".to_string(), ]), cwd: PathBuf::from("/workspace/project"), project_root: None, @@ -321,11 +323,9 @@ mod tests { assert_eq!(check.status, CheckStatus::Ok); assert_eq!(check.summary, "terminal title configured"); - assert!( - check.details.contains( - &"terminal title items: thread-credits, estimated-thread-cost".to_string() - ) - ); + assert!(check.details.contains( + &"terminal title items: thread-credits, estimated-thread-cost, daybreak".to_string() + )); assert!(check.issues.is_empty()); } diff --git a/codex-rs/tui/src/bottom_pane/status_line_setup.rs b/codex-rs/tui/src/bottom_pane/status_line_setup.rs index 64562d146c..3d20f5cb93 100644 --- a/codex-rs/tui/src/bottom_pane/status_line_setup.rs +++ b/codex-rs/tui/src/bottom_pane/status_line_setup.rs @@ -141,6 +141,9 @@ pub(crate) enum StatusLineItem { /// Whether Fast mode is currently active. FastMode, + /// Whether Daybreak is enabled for this thread. + Daybreak, + /// Whether raw scrollback mode is currently active. RawOutput, @@ -204,6 +207,7 @@ impl StatusLineItem { } StatusLineItem::SessionId => "Current thread identifier (omitted until thread starts)", StatusLineItem::FastMode => "Whether Fast mode is currently active", + StatusLineItem::Daybreak => "Whether Daybreak is enabled for this thread", StatusLineItem::RawOutput => "Whether raw scrollback mode is active", StatusLineItem::ThreadName => "Current thread name (omitted when unnamed)", StatusLineItem::ThreadTitle => { @@ -245,6 +249,7 @@ impl StatusLineItem { StatusLineItem::EstimatedThreadCost => StatusSurfacePreviewItem::EstimatedThreadCost, StatusLineItem::SessionId => StatusSurfacePreviewItem::SessionId, StatusLineItem::FastMode => StatusSurfacePreviewItem::FastMode, + StatusLineItem::Daybreak => StatusSurfacePreviewItem::Daybreak, StatusLineItem::RawOutput => StatusSurfacePreviewItem::RawOutput, StatusLineItem::ThreadName => StatusSurfacePreviewItem::ThreadName, StatusLineItem::ThreadTitle => StatusSurfacePreviewItem::ThreadTitle, diff --git a/codex-rs/tui/src/bottom_pane/status_line_style.rs b/codex-rs/tui/src/bottom_pane/status_line_style.rs index 8c44ce425b..2fd4b2c85f 100644 --- a/codex-rs/tui/src/bottom_pane/status_line_style.rs +++ b/codex-rs/tui/src/bottom_pane/status_line_style.rs @@ -54,7 +54,9 @@ impl StatusLineAccent { StatusLineItem::CodexVersion | StatusLineItem::Hostname | StatusLineItem::SessionId => { Self::Metadata } - StatusLineItem::FastMode | StatusLineItem::RawOutput => Self::Mode, + StatusLineItem::FastMode | StatusLineItem::Daybreak | StatusLineItem::RawOutput => { + Self::Mode + } StatusLineItem::Permissions => Self::Mode, StatusLineItem::ApprovalMode => Self::Mode, StatusLineItem::ThreadName diff --git a/codex-rs/tui/src/bottom_pane/status_surface_preview.rs b/codex-rs/tui/src/bottom_pane/status_surface_preview.rs index f2e6f9005e..7389236c07 100644 --- a/codex-rs/tui/src/bottom_pane/status_surface_preview.rs +++ b/codex-rs/tui/src/bottom_pane/status_surface_preview.rs @@ -34,6 +34,7 @@ pub(crate) enum StatusSurfacePreviewItem { EstimatedThreadCost, SessionId, FastMode, + Daybreak, RawOutput, WorkspaceHeadline, Model, @@ -71,6 +72,7 @@ impl StatusSurfacePreviewItem { StatusSurfacePreviewItem::EstimatedThreadCost => "~$1.82", StatusSurfacePreviewItem::SessionId => "550e8400-e29b-41d4", StatusSurfacePreviewItem::FastMode => "Fast on", + StatusSurfacePreviewItem::Daybreak => "Daybreak off", StatusSurfacePreviewItem::RawOutput => "raw output", StatusSurfacePreviewItem::WorkspaceHeadline => "Workspace headline", StatusSurfacePreviewItem::Model => "gpt-5.2-codex", @@ -108,6 +110,7 @@ impl StatusSurfacePreviewItem { Self::EstimatedThreadCost, Self::SessionId, Self::FastMode, + Self::Daybreak, Self::RawOutput, Self::WorkspaceHeadline, Self::Model, diff --git a/codex-rs/tui/src/bottom_pane/title_setup.rs b/codex-rs/tui/src/bottom_pane/title_setup.rs index cc34c036f9..ebfc53ca97 100644 --- a/codex-rs/tui/src/bottom_pane/title_setup.rs +++ b/codex-rs/tui/src/bottom_pane/title_setup.rs @@ -83,6 +83,8 @@ pub(crate) enum TerminalTitleItem { SessionId, /// Whether Fast mode is currently active. FastMode, + /// Whether Daybreak is enabled for this thread. + Daybreak, /// Current model name. #[strum(to_string = "model", serialize = "model-name")] Model, @@ -135,6 +137,7 @@ impl TerminalTitleItem { "Current thread identifier (omitted until thread starts)" } TerminalTitleItem::FastMode => "Whether Fast mode is currently active", + TerminalTitleItem::Daybreak => "Whether Daybreak is enabled for this thread", TerminalTitleItem::Model => "Current model name", TerminalTitleItem::ModelWithReasoning => "Current model name with reasoning level", TerminalTitleItem::Reasoning => "Current reasoning level", @@ -170,6 +173,7 @@ impl TerminalTitleItem { } TerminalTitleItem::SessionId => Some(StatusSurfacePreviewItem::SessionId), TerminalTitleItem::FastMode => Some(StatusSurfacePreviewItem::FastMode), + TerminalTitleItem::Daybreak => Some(StatusSurfacePreviewItem::Daybreak), TerminalTitleItem::Model => Some(StatusSurfacePreviewItem::Model), TerminalTitleItem::ModelWithReasoning => { Some(StatusSurfacePreviewItem::ModelWithReasoning) diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_setup_popup_mixed.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_setup_popup_mixed.snap index 8b3db4c492..7006478c55 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_setup_popup_mixed.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_setup_popup_mixed.snap @@ -12,9 +12,9 @@ expression: status_line_popup_snapshot(&mut chat) [x] project-name Project name (omitted when unavailable) [x] git-branch Current Git branch (omitted when unavailable) [x] thread-title Current thread title, or thread identifier when unnamed + [x] daybreak Whether Daybreak is enabled for this thread [ ] model Current model name [ ] model-with-reasoning Current model name with reasoning level - [ ] reasoning Current reasoning level ↓ - my-project · feature/mixed-preview · Mixed preview thread + my-project · feature/mixed-preview · Mixed preview thread · Daybreak on space toggle · ←/→ reorder · enter save · esc cancel diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_setup_popup_mixed.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_setup_popup_mixed.snap index 15bdb1207e..f746c4793f 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_setup_popup_mixed.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_setup_popup_mixed.snap @@ -10,11 +10,11 @@ expression: terminal_title_popup_snapshot(&mut chat) › [x] project-name Project name (falls back to current directory name) [x] thread-title Current thread title, or thread identifier when unnamed [x] task-progress Latest task progress from update_plan (omitted until available) + [x] daybreak Whether Daybreak is enabled for this thread [ ] app-name Codex app name [ ] current-dir Current working directory [ ] activity Spinner while working, action-required message while blocked [ ] run-state Compact session run-state text (Ready, Working, Thinking) - [ ] thread-name Current thread name (omitted when unnamed) ↓ - project | Mixed preview thread | Tasks 0/0 + project | Mixed preview thread | Tasks 0/0 | Daybreak on space toggle · ←/→ reorder · enter save · esc cancel diff --git a/codex-rs/tui/src/chatwidget/status_surfaces.rs b/codex-rs/tui/src/chatwidget/status_surfaces.rs index 616dd6df1e..46727cfc32 100644 --- a/codex-rs/tui/src/chatwidget/status_surfaces.rs +++ b/codex-rs/tui/src/chatwidget/status_surfaces.rs @@ -815,6 +815,13 @@ impl ChatWidget { "Fast off".to_string() } }), + StatusLineItem::Daybreak => Some( + if self.daybreak_enabled && !self.side_conversation_active() { + "Daybreak on".to_string() + } else { + "Daybreak off".to_string() + }, + ), StatusLineItem::RawOutput => self.raw_output_mode().then(|| "raw output".to_string()), StatusLineItem::ThreadName => { self.thread_name.as_deref().and_then(normalize_thread_name) @@ -868,6 +875,7 @@ impl ChatWidget { StatusSurfacePreviewItem::EstimatedThreadCost => StatusLineItem::EstimatedThreadCost, StatusSurfacePreviewItem::SessionId => StatusLineItem::SessionId, StatusSurfacePreviewItem::FastMode => StatusLineItem::FastMode, + StatusSurfacePreviewItem::Daybreak => StatusLineItem::Daybreak, StatusSurfacePreviewItem::RawOutput => StatusLineItem::RawOutput, StatusSurfacePreviewItem::WorkspaceHeadline => StatusLineItem::WorkspaceHeadline, StatusSurfacePreviewItem::Model => StatusLineItem::ModelName, @@ -947,6 +955,9 @@ impl ChatWidget { TerminalTitleItem::FastMode => self .status_line_value_for_item(StatusLineItem::FastMode) .map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)), + TerminalTitleItem::Daybreak => { + self.status_line_value_for_item(StatusLineItem::Daybreak) + } TerminalTitleItem::Model => Some(Self::truncate_terminal_title_part( self.model_display_name().to_string(), /*max_chars*/ 32, diff --git a/codex-rs/tui/src/chatwidget/tests/status_surface_previews.rs b/codex-rs/tui/src/chatwidget/tests/status_surface_previews.rs index 60c741562a..86fb1cbd1c 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_surface_previews.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_surface_previews.rs @@ -448,10 +448,12 @@ async fn status_line_setup_popup_mixed_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.status_line_branch = Some("feature/mixed-preview".to_string()); chat.thread_name = Some("Mixed preview thread".to_string()); + chat.set_daybreak_enabled(/*enabled*/ true); chat.local_settings.tui.status_line = Some(vec![ "project-name".to_string(), "git-branch".to_string(), "thread-title".to_string(), + "daybreak".to_string(), ]); assert_chatwidget_snapshot!( @@ -499,10 +501,12 @@ async fn terminal_title_setup_popup_hardcoded_only_snapshot() { async fn terminal_title_setup_popup_mixed_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.thread_name = Some("Mixed preview thread".to_string()); + chat.set_daybreak_enabled(/*enabled*/ true); chat.local_settings.tui.terminal_title = Some(vec![ "project-name".to_string(), "thread-title".to_string(), "task-progress".to_string(), + "daybreak".to_string(), ]); assert_chatwidget_snapshot!( diff --git a/codex-rs/tui/src/chatwidget/tests/terminal_title.rs b/codex-rs/tui/src/chatwidget/tests/terminal_title.rs index f454398803..abf13c7786 100644 --- a/codex-rs/tui/src/chatwidget/tests/terminal_title.rs +++ b/codex-rs/tui/src/chatwidget/tests/terminal_title.rs @@ -4,6 +4,34 @@ use super::*; use crate::bottom_pane::goal_status_indicator_line; use pretty_assertions::assert_eq; +#[tokio::test] +async fn daybreak_status_surfaces_follow_the_thread_preference() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + chat.local_settings.tui.terminal_title = Some(vec!["daybreak".into()]); + let mut values = Vec::new(); + for enabled in [false, true] { + chat.daybreak_enabled = enabled; + chat.refresh_terminal_title(); + values.push(format!( + "{} | {}", + chat.status_line_value(StatusLineItem::Daybreak).unwrap(), + chat.last_terminal_title.as_deref().unwrap() + )); + } + chat.set_side_conversation_active(/*active*/ true); + chat.refresh_terminal_title(); + values.push(format!( + "{} | {}", + chat.status_line_value(StatusLineItem::Daybreak).unwrap(), + chat.last_terminal_title.as_deref().unwrap() + )); + insta::assert_snapshot!(values.join("\n"), @r" + Daybreak off | Daybreak off + Daybreak on | Daybreak on + Daybreak off | Daybreak off + "); +} + #[tokio::test] async fn goal_clock_refresh_redraws_only_when_elapsed_label_changes() { let (frame_requester, mut draw_rx) = FrameRequester::test_channel();