From 172f8a2901119ceab4d8552e906ea65d98626076 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Thu, 17 Sep 2026 01:09:34 +0000 Subject: [PATCH] Use syntax theme colors for inline code and file paths (#46069) ## Why Inline code and local file paths in the TUI used a fixed cyan foreground regardless of the active syntax theme. ## What changed Use the active theme's Markdown raw-text foreground for inline code and local file paths, with cyan as the fallback when no matching color is available. Isolate the active theme and its revision per test thread to prevent parallel tests from affecting each other's rendering. ## Testing Add rendering snapshots for dark, light, ANSI, and fallback themes, plus a regression test for theme and revision isolation between test threads. GitOrigin-RevId: 40d84131d0abc9f177019d9b0b3e19536d5cd553 --- codex-rs/tui/src/markdown_render.rs | 18 ++- ...y_and_fallback_presentations_snapshot.snap | 4 +- codex-rs/tui/src/markdown_render_tests.rs | 135 +++++++++++++----- codex-rs/tui/src/render/highlight.rs | 60 ++++++-- .../tui/src/render/highlight_streaming.rs | 6 +- ...de_and_file_paths_follow_syntax_theme.snap | 66 +++++++++ ...els_have_a_visible_underline_snapshot.snap | 2 +- ...er__tests__incremental_file_citations.snap | 4 +- 8 files changed, 247 insertions(+), 48 deletions(-) create mode 100644 codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__inline_code_and_file_paths_follow_syntax_theme.snap diff --git a/codex-rs/tui/src/markdown_render.rs b/codex-rs/tui/src/markdown_render.rs index 759fd9c3f4..43a111ee76 100644 --- a/codex-rs/tui/src/markdown_render.rs +++ b/codex-rs/tui/src/markdown_render.rs @@ -35,10 +35,13 @@ //! unusably short chunks, expansive cells form tall narrow strips across enough //! body rows, or even 3-char-wide columns cannot fit, body rows render as //! key/value records. +//! +//! Inline code and local file paths share the active syntax theme's raw-markup foreground. use crate::markdown_text_merge::DecodedTextMerge; use crate::render::highlight::current_syntax_theme; use crate::render::highlight::foreground_style_for_scopes; +use crate::render::highlight::foreground_style_for_scopes_with_theme; use crate::render::highlight::highlight_code_to_lines; use crate::render::line_utils::line_to_static; use crate::style::table_separator_style; @@ -109,6 +112,12 @@ struct MarkdownStyles { impl Default for MarkdownStyles { fn default() -> Self { + Self::for_theme(&crate::render::highlight::current_syntax_theme()) + } +} + +impl MarkdownStyles { + fn for_theme(theme: &syntect::highlighting::Theme) -> Self { Self { h1: Style::new().bold().underlined(), h2: Style::new().bold(), @@ -116,7 +125,14 @@ impl Default for MarkdownStyles { h4: Style::new().italic(), h5: Style::new().italic(), h6: Style::new().italic(), - code: Style::new().cyan(), + code: foreground_style_for_scopes_with_theme( + theme, + &[ + "markup.inline.raw.string.markdown", + "markup.raw.inline.markdown", + ], + ) + .unwrap_or_else(|| Style::new().cyan()), emphasis: Style::new().italic(), strong: Style::new().bold(), strikethrough: Style::new().crossed_out(), diff --git a/codex-rs/tui/src/markdown_render/snapshots/codex_tui__markdown_render__web_links__tests__label_only_and_fallback_presentations_snapshot.snap b/codex-rs/tui/src/markdown_render/snapshots/codex_tui__markdown_render__web_links__tests__label_only_and_fallback_presentations_snapshot.snap index 7d84cf65fe..ae0bffb9d5 100644 --- a/codex-rs/tui/src/markdown_render/snapshots/codex_tui__markdown_render__web_links__tests__label_only_and_fallback_presentations_snapshot.snap +++ b/codex-rs/tui/src/markdown_render/snapshots/codex_tui__markdown_render__web_links__tests__label_only_and_fallback_presentations_snapshot.snap @@ -8,7 +8,7 @@ expression: presentations Span::from("plain "), Span::from("plain").cyan().underlined(), Span::from(" "), - Span::from("code").cyan(), + Span::from("code").fg(Color::Rgb(166, 227, 161)), Span::from(" "), Span::from("code").cyan().underlined(), ]), @@ -31,7 +31,7 @@ expression: presentations Span::from("https://example.com").cyan().underlined(), Span::from(")"), Span::from(" "), - Span::from("code").cyan(), + Span::from("code").fg(Color::Rgb(166, 227, 161)), ]), Line::from_iter([ Span::from("code").cyan().underlined(), diff --git a/codex-rs/tui/src/markdown_render_tests.rs b/codex-rs/tui/src/markdown_render_tests.rs index b2e7ec8f3f..b1c08d528e 100644 --- a/codex-rs/tui/src/markdown_render_tests.rs +++ b/codex-rs/tui/src/markdown_render_tests.rs @@ -1,3 +1,4 @@ +use super::MarkdownStyles; use pretty_assertions::assert_eq; use ratatui::style::Modifier; use ratatui::style::Stylize; @@ -711,10 +712,60 @@ fn ordered_item_with_indented_continuation_is_tight() { #[test] fn inline_code() { let text = render_markdown_text("Example of `Inline code`"); - let expected = Line::from_iter(["Example of ".into(), "Inline code".cyan()]).into(); + let expected = Line::from_iter([ + "Example of ".into(), + Span::styled("Inline code", MarkdownStyles::default().code), + ]) + .into(); assert_eq!(text, expected); } +#[test] +fn inline_code_and_file_paths_follow_syntax_theme() { + let markdown = "Use `src/main.rs` and [src/lib.rs](./src/lib.rs)."; + let mut rendered = Vec::new(); + for name in [ + "catppuccin-mocha", + "catppuccin-latte", + "ansi", + "no-markup-color", + ] { + let theme = if name == "no-markup-color" { + syntect::highlighting::Theme::default() + } else { + crate::render::highlight::resolve_theme_by_name(name, /*codex_home*/ None) + .expect("bundled theme") + }; + let styles = MarkdownStyles::for_theme(&theme); + let code_style = styles.code; + let mut writer = super::Writer::new( + markdown, + pulldown_cmark::Parser::new(markdown).into_offset_iter(), + /*wrap_width*/ Some(24), + /*cwd*/ None, + &|_| false, + ); + writer.styles = styles; + writer.run(); + let lines = crate::terminal_hyperlinks::visible_lines(writer.text); + let paths = lines + .iter() + .flat_map(|line| &line.spans) + .filter(|span| matches!(span.content.as_ref(), "src/main.rs" | "src/lib.rs")) + .cloned() + .collect::>(); + assert_eq!( + paths, + vec![ + Span::styled("src/main.rs", code_style), + Span::styled("src/lib.rs", code_style), + ], + ); + rendered.push((name, lines)); + } + assert_debug_snapshot!(rendered); +} + #[test] fn strong() { assert_eq!( @@ -779,7 +830,7 @@ fn web_link_labels_use_link_style_and_preserve_inline_formatting() { "https://example.com".cyan().underlined(), ")".into(), " after ".into(), - "code".cyan(), + Span::styled("code", MarkdownStyles::default().code), ])); assert_eq!(text, expected, "label: {label}"); } @@ -828,7 +879,10 @@ fn file_link_hides_destination() { "[/Users/example/code/codex/codex-rs/tui/src/My%20File.rs](/Users/example/code/codex/codex-rs/tui/src/My%20File.rs)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter(["codex-rs/tui/src/My File.rs".cyan()])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/My File.rs", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -840,11 +894,14 @@ fn file_link_keeps_descriptive_label_and_target() { ); let expected = Text::from(Line::from_iter([ "Your ".into(), - "codex".cyan(), + Span::styled("codex", MarkdownStyles::default().code), " launcher ".into(), "automatically adds those overrides".into(), " (".into(), - "project/dotslash-gen/bin/codex:1105".cyan(), + Span::styled( + "project/dotslash-gen/bin/codex:1105", + MarkdownStyles::default().code, + ), ")".into(), ", even though you did not specify any.".into(), ])); @@ -921,7 +978,10 @@ fn file_link_decodes_percent_encoded_bare_path_destination() { let expected = Text::from(Line::from_iter([ "open Example Folder/Résumé/report.md".into(), " (".into(), - "Example Folder/Résumé/report.md".cyan(), + Span::styled( + "Example Folder/Résumé/report.md", + MarkdownStyles::default().code, + ), ")".into(), ])); assert_eq!(text, expected); @@ -952,7 +1012,7 @@ fn file_link_keeps_unrelated_relative_label_with_matching_suffix() { let expected = Text::from(Line::from_iter([ "other/src/lib.rs".into(), " (".into(), - "src/lib.rs".cyan(), + Span::styled("src/lib.rs", MarkdownStyles::default().code), ")".into(), ])); assert_eq!(text, expected); @@ -965,9 +1025,10 @@ fn file_link_appends_line_number_when_label_lacks_it() { "[markdown_render.rs](/Users/example/code/codex/codex-rs/tui/src/markdown_render.rs:74)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74".cyan() - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -977,9 +1038,10 @@ fn file_link_keeps_absolute_paths_outside_cwd() { "[README.md:74](/Users/example/code/codex/README.md:74)", Path::new("/Users/example/code/codex/codex-rs/tui"), ); - let expected = Text::from(Line::from_iter([ - "/Users/example/code/codex/README.md:74".cyan() - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "/Users/example/code/codex/README.md:74", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1010,9 +1072,10 @@ fn file_link_appends_hash_anchor_when_label_lacks_it() { "[markdown_render.rs](file:///Users/example/code/codex/codex-rs/tui/src/markdown_render.rs#L74C3)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1022,9 +1085,10 @@ fn file_link_uses_target_path_for_hash_anchor() { "[markdown_render.rs#L74C3](file:///Users/example/code/codex/codex-rs/tui/src/markdown_render.rs#L74C3)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1034,9 +1098,10 @@ fn file_link_appends_range_when_label_lacks_it() { "[markdown_render.rs](/Users/example/code/codex/codex-rs/tui/src/markdown_render.rs:74:3-76:9)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3-76:9".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3-76:9", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1046,9 +1111,10 @@ fn file_link_uses_target_path_for_range() { "[markdown_render.rs:74:3-76:9](/Users/example/code/codex/codex-rs/tui/src/markdown_render.rs:74:3-76:9)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3-76:9".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3-76:9", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1058,9 +1124,10 @@ fn file_link_appends_hash_range_when_label_lacks_it() { "[markdown_render.rs](file:///Users/example/code/codex/codex-rs/tui/src/markdown_render.rs#L74C3-L76C9)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3-76:9".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3-76:9", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } @@ -1077,7 +1144,10 @@ fn multiline_file_link_label_after_styled_prefix_does_not_panic() { " ".into(), "bar".into(), " (".into(), - "codex-rs/tui/src/markdown_render.rs:74:3".cyan(), + Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3", + MarkdownStyles::default().code, + ), ")".into(), ])); assert_eq!(text, expected); @@ -1089,9 +1159,10 @@ fn file_link_uses_target_path_for_hash_range() { "[markdown_render.rs#L74C3-L76C9](file:///Users/example/code/codex/codex-rs/tui/src/markdown_render.rs#L74C3-L76C9)", Path::new("/Users/example/code/codex"), ); - let expected = Text::from(Line::from_iter([ - "codex-rs/tui/src/markdown_render.rs:74:3-76:9".cyan(), - ])); + let expected = Text::from(Line::from_iter([Span::styled( + "codex-rs/tui/src/markdown_render.rs:74:3-76:9", + MarkdownStyles::default().code, + )])); assert_eq!(text, expected); } diff --git a/codex-rs/tui/src/render/highlight.rs b/codex-rs/tui/src/render/highlight.rs index d9eaaf6491..59ade8c1a8 100644 --- a/codex-rs/tui/src/render/highlight.rs +++ b/codex-rs/tui/src/render/highlight.rs @@ -16,7 +16,8 @@ //! config is resolved) to persist the user preference and seed the `THEME` //! lock. After that, [`set_syntax_theme`] and [`current_syntax_theme`] can //! swap/snapshot the theme for live preview. All highlighting functions read -//! the theme via `theme_lock()`. +//! the theme via `theme_lock()`. Unit tests isolate the active theme and its +//! revision per thread so parallel tests cannot change each other’s rendering. //! //! **Guardrails:** inputs exceeding 512 KB or 10 000 lines, or containing an //! individual line longer than 4 KiB, are rejected early (returns `None`) to @@ -32,7 +33,9 @@ use std::path::Path; use std::path::PathBuf; use std::sync::OnceLock; use std::sync::RwLock; +#[cfg(not(test))] use std::sync::atomic::AtomicU64; +#[cfg(not(test))] use std::sync::atomic::Ordering; use syntect::easy::HighlightLines; use syntect::highlighting::Color as SyntectColor; @@ -55,11 +58,19 @@ pub(crate) use streaming::StreamingCodeHighlighter; // -- Global singletons ------------------------------------------------------- static SYNTAX_SET: OnceLock = OnceLock::new(); +#[cfg(not(test))] static THEME: OnceLock> = OnceLock::new(); +#[cfg(not(test))] static THEME_REVISION: AtomicU64 = AtomicU64::new(0); static THEME_OVERRIDE: OnceLock> = OnceLock::new(); static CODEX_HOME: OnceLock> = OnceLock::new(); +#[cfg(test)] +thread_local! { + static TEST_THEME: std::cell::OnceCell>> = const { std::cell::OnceCell::new() }; + static TEST_THEME_REVISION: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + // Syntect/bat encode ANSI palette semantics in alpha: // `a=0` => indexed ANSI palette via RGB payload, `a=1` => terminal default. const ANSI_ALPHA_INDEX: u8 = 0x00; @@ -95,7 +106,11 @@ pub(crate) fn set_theme_override( let warning = validate_theme_name(name.as_deref(), codex_home.as_deref()); let override_set_ok = THEME_OVERRIDE.set(name.clone()).is_ok(); let codex_home_set_ok = CODEX_HOME.set(codex_home.clone()).is_ok(); - if THEME.get().is_some() { + #[cfg(not(test))] + let initialized = THEME.get().is_some(); + #[cfg(test)] + let initialized = TEST_THEME.with(|theme| theme.get().is_some()); + if initialized { set_syntax_theme(resolve_theme_with_override( name.as_deref(), codex_home.as_deref(), @@ -243,28 +258,47 @@ fn build_default_theme() -> Theme { resolve_theme_with_override(name, codex_home) } -fn theme_lock() -> &'static RwLock { - THEME.get_or_init(|| RwLock::new(build_default_theme())) +fn theme_lock() -> impl std::ops::Deref> { + #[cfg(not(test))] + { + THEME.get_or_init(|| RwLock::new(build_default_theme())) + } + #[cfg(test)] + TEST_THEME.with(|theme| { + theme + .get_or_init(|| std::sync::Arc::new(RwLock::new(build_default_theme()))) + .clone() + }) } /// Swap the active syntax theme at runtime and invalidate rendered-content caches. pub(crate) fn set_syntax_theme(theme: Theme) { - let mut guard = match theme_lock().write() { + let active_theme = theme_lock(); + let mut guard = match active_theme.write() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), }; *guard = theme; + #[cfg(not(test))] THEME_REVISION.fetch_add(1, Ordering::Release); + #[cfg(test)] + TEST_THEME_REVISION.with(|revision| revision.set(revision.get().wrapping_add(1))); } /// Return the revision of the active syntax theme for rendered-content caches. pub(crate) fn syntax_theme_revision() -> u64 { - THEME_REVISION.load(Ordering::Acquire) + #[cfg(not(test))] + { + THEME_REVISION.load(Ordering::Acquire) + } + #[cfg(test)] + TEST_THEME_REVISION.with(std::cell::Cell::get) } /// Clone the current syntax theme (e.g. to save for cancel-restore). pub(crate) fn current_syntax_theme() -> Theme { - match theme_lock().read() { + let active_theme = theme_lock(); + match active_theme.read() { Ok(theme) => theme.clone(), Err(poisoned) => poisoned.into_inner().clone(), } @@ -666,7 +700,8 @@ fn highlighted_line_spans(ranges: Vec<(SyntectStyle, &str)>) -> Vec Option>>> { - let theme_guard = match theme_lock().read() { + let active_theme = theme_lock(); + let theme_guard = match active_theme.read() { Ok(theme_guard) => theme_guard, Err(poisoned) => poisoned.into_inner(), }; @@ -732,6 +767,15 @@ mod tests { use syntect::highlighting::ThemeItem; use syntect::highlighting::ThemeSettings; + #[test] + fn active_theme_and_revision_are_isolated_between_test_threads() { + let original = (current_syntax_theme(), syntax_theme_revision()); + std::thread::spawn(|| set_syntax_theme(Theme::default())) + .join() + .unwrap(); + assert_eq!((current_syntax_theme(), syntax_theme_revision()), original); + } + fn write_minimal_tmtheme(path: &Path) { // Minimal valid .tmTheme plist (enough for syntect to parse). std::fs::write( diff --git a/codex-rs/tui/src/render/highlight_streaming.rs b/codex-rs/tui/src/render/highlight_streaming.rs index 3eb4de85b8..1dc5e1f71a 100644 --- a/codex-rs/tui/src/render/highlight_streaming.rs +++ b/codex-rs/tui/src/render/highlight_streaming.rs @@ -34,7 +34,8 @@ impl StreamingCodeHighlighter { /// Unknown languages and oversized input retain a permanent plain-text fallback. A changed /// theme or failed replay returns `None`, requiring the caller to render the whole fence again. pub(crate) fn new(code: &str, lang: &str, theme_revision: u64) -> Option { - let theme = match theme_lock().read() { + let active_theme = theme_lock(); + let theme = match active_theme.read() { Ok(theme) => theme, Err(poisoned) => poisoned.into_inner(), }; @@ -90,7 +91,8 @@ impl StreamingCodeHighlighter { { return None; } - let theme = match theme_lock().read() { + let active_theme = theme_lock(); + let theme = match active_theme.read() { Ok(theme) => theme, Err(poisoned) => poisoned.into_inner(), }; diff --git a/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__inline_code_and_file_paths_follow_syntax_theme.snap b/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__inline_code_and_file_paths_follow_syntax_theme.snap new file mode 100644 index 0000000000..e5f6ff5aa1 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__inline_code_and_file_paths_follow_syntax_theme.snap @@ -0,0 +1,66 @@ +--- +source: tui/src/markdown_render_tests.rs +expression: rendered +--- +[ + ( + "catppuccin-mocha", + [ + Line::from_iter([ + Span::from("Use "), + Span::from("src/main.rs").fg(Color::Rgb(166, 227, 161)), + Span::from(" and "), + Span::from("./").fg(Color::Rgb(166, 227, 161)), + ]), + Line::from_iter([ + Span::from("src/lib.rs").fg(Color::Rgb(166, 227, 161)), + Span::from("."), + ]), + ], + ), + ( + "catppuccin-latte", + [ + Line::from_iter([ + Span::from("Use "), + Span::from("src/main.rs").fg(Color::Rgb(64, 160, 43)), + Span::from(" and "), + Span::from("./").fg(Color::Rgb(64, 160, 43)), + ]), + Line::from_iter([ + Span::from("src/lib.rs").fg(Color::Rgb(64, 160, 43)), + Span::from("."), + ]), + ], + ), + ( + "ansi", + [ + Line::from_iter([ + Span::from("Use "), + Span::from("src/main.rs").green(), + Span::from(" and "), + Span::from("./").green(), + ]), + Line::from_iter([ + Span::from("src/lib.rs").green(), + Span::from("."), + ]), + ], + ), + ( + "no-markup-color", + [ + Line::from_iter([ + Span::from("Use "), + Span::from("src/main.rs").cyan(), + Span::from(" and "), + Span::from("./").cyan(), + ]), + Line::from_iter([ + Span::from("src/lib.rs").cyan(), + Span::from("."), + ]), + ], + ), +] diff --git a/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__web_link_labels_have_a_visible_underline_snapshot.snap b/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__web_link_labels_have_a_visible_underline_snapshot.snap index 655ab46f78..fa0281ad31 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__web_link_labels_have_a_visible_underline_snapshot.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__web_link_labels_have_a_visible_underline_snapshot.snap @@ -2,4 +2,4 @@ source: tui/src/markdown_render_tests.rs expression: text --- -Text::from(Line::from_iter([Span::from("plain "), Span::from("plain").cyan().underlined(), Span::from(" ("), Span::from("https://example.com").cyan().underlined(), Span::from(")"), Span::from(" "), Span::from("code").cyan(), Span::from(" "), Span::from("code").cyan().underlined(), Span::from(" ("), Span::from("https://example.com").cyan().underlined(), Span::from(")")])) +Text::from(Line::from_iter([Span::from("plain "), Span::from("plain").cyan().underlined(), Span::from(" ("), Span::from("https://example.com").cyan().underlined(), Span::from(")"), Span::from(" "), Span::from("code").fg(Color::Rgb(166, 227, 161)), Span::from(" "), Span::from("code").cyan().underlined(), Span::from(" ("), Span::from("https://example.com").cyan().underlined(), Span::from(")")])) diff --git a/codex-rs/tui/src/streaming/snapshots/codex_tui__streaming__render__tests__incremental_file_citations.snap b/codex-rs/tui/src/streaming/snapshots/codex_tui__streaming__render__tests__incremental_file_citations.snap index 46f1f00bfb..aed6862c2a 100644 --- a/codex-rs/tui/src/streaming/snapshots/codex_tui__streaming__render__tests__incremental_file_citations.snap +++ b/codex-rs/tui/src/streaming/snapshots/codex_tui__streaming__render__tests__incremental_file_citations.snap @@ -18,7 +18,7 @@ expression: rendered_cases HyperlinkLine { line: Line::from_iter([ Span::from("- "), - Span::from("Quarterly Report.xlsx").cyan(), + Span::from("Quarterly Report.xlsx").fg(Color::Rgb(166, 227, 161)), ]), hyperlinks: [], }, @@ -46,7 +46,7 @@ expression: rendered_cases HyperlinkLine { line: Line::from_iter([ Span::from("- "), - Span::from("Résumé *final* ✨.xlsx").cyan(), + Span::from("Résumé *final* ✨.xlsx").fg(Color::Rgb(166, 227, 161)), ]), hyperlinks: [], },