Skip to content

Commit a0d5654

Browse files
tui: breathing spinner on true-color terms (#4853)
uses the same logic as shimmer_spans to render the `•` spinner. on terminals without true-color support, fall back to the existing `•/◦` blinking logic. https://github.com/user-attachments/assets/19db76f2-8fa2-440d-9fde-7bed67f4c4dc
1 parent 226215f commit a0d5654

File tree

2 files changed

+14
-10
lines changed

2 files changed

+14
-10
lines changed

codex-rs/tui/src/exec_cell/render.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::history_cell::HistoryCell;
88
use crate::render::highlight::highlight_bash_to_lines;
99
use crate::render::line_utils::prefix_lines;
1010
use crate::render::line_utils::push_owned_lines;
11+
use crate::shimmer::shimmer_spans;
1112
use crate::wrapping::RtOptions;
1213
use crate::wrapping::word_wrap_line;
1314
use codex_ansi_escape::ansi_escape_line;
@@ -116,10 +117,16 @@ pub(crate) fn output_lines(
116117
}
117118

118119
pub(crate) fn spinner(start_time: Option<Instant>) -> Span<'static> {
119-
let blink_on = start_time
120-
.map(|st| ((st.elapsed().as_millis() / 600) % 2) == 0)
121-
.unwrap_or(false);
122-
if blink_on { "•".into() } else { "◦".dim() }
120+
let elapsed = start_time.map(|st| st.elapsed()).unwrap_or_default();
121+
if supports_color::on_cached(supports_color::Stream::Stdout)
122+
.map(|level| level.has_16m)
123+
.unwrap_or(false)
124+
{
125+
shimmer_spans("•")[0].clone()
126+
} else {
127+
let blink_on = (elapsed.as_millis() / 600).is_multiple_of(2);
128+
if blink_on { "•".into() } else { "◦".dim() }
129+
}
123130
}
124131

125132
impl HistoryCell for ExecCell {

codex-rs/tui/src/status_indicator_widget.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use ratatui::widgets::WidgetRef;
1515

1616
use crate::app_event::AppEvent;
1717
use crate::app_event_sender::AppEventSender;
18+
use crate::exec_cell::spinner;
1819
use crate::key_hint;
1920
use crate::shimmer::shimmer_spans;
2021
use crate::tui::FrameRequester;
@@ -163,15 +164,11 @@ impl WidgetRef for StatusIndicatorWidget {
163164
let now = Instant::now();
164165
let elapsed_duration = self.elapsed_duration_at(now);
165166
let pretty_elapsed = fmt_elapsed_compact(elapsed_duration.as_secs());
166-
let blink_on = (elapsed_duration.as_millis() / 600).is_multiple_of(2);
167167

168168
// Plain rendering: no borders or padding so the live cell is visually indistinguishable from terminal scrollback.
169169
let mut spans = Vec::with_capacity(5);
170-
if blink_on {
171-
spans.push("• ".into());
172-
} else {
173-
spans.push("◦ ".dim());
174-
}
170+
spans.push(spinner(Some(self.last_resume_at)));
171+
spans.push(" ".into());
175172
spans.extend(shimmer_spans(&self.header));
176173
spans.extend(vec![
177174
" ".into(),

0 commit comments

Comments
 (0)