mirror of
https://github.com/openai/codex.git
synced 2026-10-19 08:16:48 +00:00
Add Daybreak state to the status line and terminal title (#49861)
## What changed Add a selectable `daybreak` item to the status line and terminal title, including setup previews and terminal-title validation. Display `Daybreak on` when enabled for the thread and `Daybreak off` otherwise, including during side conversations. ## Testing Add coverage for on/off rendering and side conversations, update setup preview snapshots, and verify that terminal-title diagnostics accept `daybreak`. GitOrigin-RevId: b0b7a6f0ee0a107ba4ee2d56113a5b9b6ebd1d41
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user