From ae4ed9ad5564c9d169c254a008372026acba11d8 Mon Sep 17 00:00:00 2001 From: wing-anara Date: Mon, 14 Sep 2026 08:28:07 +0000 Subject: [PATCH 1/2] Match the composited composer tone to the right pane --- crates/ui/src/composer.rs | 3 ++- crates/ui/src/theme.rs | 56 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/ui/src/composer.rs b/crates/ui/src/composer.rs index 2723a44a8..a57828427 100644 --- a/crates/ui/src/composer.rs +++ b/crates/ui/src/composer.rs @@ -7700,7 +7700,7 @@ impl Render for Composer { } else { theme.border }; - // Let the backdrop blur supply the glass surface without a color wash. + // Compensate for the transcript canvas beneath the frosted surface. // Keep the opaque fallback when frost is disabled or unsupported. let pill = div() .on_mouse_down( @@ -7716,6 +7716,7 @@ impl Render for Composer { .rounded(px(surface_radius)) .border_1() .border_color(pill_border) + .when(theme.is_frost(), |el| el.bg(theme.composer_sidebar_tint())) .when(!theme.is_frost(), |el| { el.bg(theme.input_glass_bg()).shadow_lg() }); diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs index bc6f88cd6..a21acfdaf 100644 --- a/crates/ui/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -924,6 +924,34 @@ impl Theme { )) } + /// Match the right pane over the transcript canvas while retaining blur. + /// Solve the overlay in RGB: target = tint * alpha + canvas * (1 - alpha). + pub fn composer_sidebar_tint(&self) -> Hsla { + let target = if self.is_glass() { + flatten(self.bg.opacity(0.4), flatten(self.glass(), self.bg)) + } else { + self.bg + }; + // The transcript has no fill of its own: its canvas is the shell glass. + let canvas = flatten(self.glass(), self.bg); + let canvas = hsl_to_rgb(canvas.h, canvas.s, canvas.l); + let target = hsl_to_rgb(target.h, target.s, target.l); + let mut alpha: f32 = 0.60; + for (base, desired) in canvas.into_iter().zip(target) { + let needed = if desired > base { + (desired - base) / (1.0 - base).max(f32::EPSILON) + } else { + (base - desired) / base.max(f32::EPSILON) + }; + alpha = alpha.max(needed); + } + let rgb = std::array::from_fn::<_, 3, _>(|i| { + ((target[i] - canvas[i] * (1.0 - alpha)) / alpha).clamp(0.0, 1.0) + }); + let (h, s, l) = rgb_to_hsl(rgb[0], rgb[1], rgb[2]); + hsla(h, s, l, alpha) + } + /// Shared fill for the composer, queue tray, and input panels. Without /// frost, composite the theme's input tint onto the page to preserve its /// color while hiding the transcript and overlapping surfaces underneath. @@ -2624,6 +2652,34 @@ mod tests { assert_eq!(mix(a, b, 2.0), b); } + #[test] + fn composer_tint_composites_to_right_pane_tone() { + for mut theme in [Theme::dark(), Theme::light()] { + for (canvas, shell) in [ + (theme.bg, theme.surface), + (hsla(0.58, 0.3, 0.12, 1.0), hsla(0.62, 0.25, 0.22, 1.0)), + (hsla(0.12, 0.2, 0.93, 1.0), hsla(0.08, 0.15, 0.82, 1.0)), + ] { + theme.bg = canvas; + theme.surface = shell; + let tint = theme.composer_sidebar_tint(); + let expected = if theme.is_glass() { + flatten(theme.bg.opacity(0.4), flatten(theme.glass(), theme.bg)) + } else { + theme.bg + }; + let actual = flatten(tint, flatten(theme.glass(), theme.bg)); + for (a, b) in hsl_to_rgb(actual.h, actual.s, actual.l) + .into_iter() + .zip(hsl_to_rgb(expected.h, expected.s, expected.l)) + { + assert!((a - b).abs() < 0.0001); + } + assert!((0.60..=1.0).contains(&tint.a)); + } + } + } + #[test] fn layout_numbers_match_zeron() { assert_eq!(Theme::HEADER_HEIGHT, 44.0); // h-11 From b0bef8471dfb58e33c61373f6f911f0d512ef49f Mon Sep 17 00:00:00 2001 From: wing-anara Date: Mon, 14 Sep 2026 08:34:02 +0000 Subject: [PATCH 2/2] Keep composer tint at fifteen percent to reveal the backdrop --- crates/ui/src/theme.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs index a21acfdaf..01a445f1c 100644 --- a/crates/ui/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -924,7 +924,7 @@ impl Theme { )) } - /// Match the right pane over the transcript canvas while retaining blur. + /// Move toward the right pane tone while keeping the backdrop visible. /// Solve the overlay in RGB: target = tint * alpha + canvas * (1 - alpha). pub fn composer_sidebar_tint(&self) -> Hsla { let target = if self.is_glass() { @@ -949,7 +949,8 @@ impl Theme { ((target[i] - canvas[i] * (1.0 - alpha)) / alpha).clamp(0.0, 1.0) }); let (h, s, l) = rgb_to_hsl(rgb[0], rgb[1], rgb[2]); - hsla(h, s, l, alpha) + // Use the compensated hue, but leave 85% of the blurred backdrop visible. + hsla(h, s, l, 0.15) } /// Shared fill for the composer, queue tray, and input panels. Without @@ -2653,7 +2654,7 @@ mod tests { } #[test] - fn composer_tint_composites_to_right_pane_tone() { + fn composer_tint_moves_toward_sidebar_without_hiding_backdrop() { for mut theme in [Theme::dark(), Theme::light()] { for (canvas, shell) in [ (theme.bg, theme.surface), @@ -2669,13 +2670,15 @@ mod tests { theme.bg }; let actual = flatten(tint, flatten(theme.glass(), theme.bg)); - for (a, b) in hsl_to_rgb(actual.h, actual.s, actual.l) - .into_iter() - .zip(hsl_to_rgb(expected.h, expected.s, expected.l)) - { - assert!((a - b).abs() < 0.0001); + let base = flatten(theme.glass(), theme.bg); + let base_rgb = hsl_to_rgb(base.h, base.s, base.l); + let target_rgb = hsl_to_rgb(expected.h, expected.s, expected.l); + let actual_rgb = hsl_to_rgb(actual.h, actual.s, actual.l); + for i in 0..3 { + assert!(actual_rgb[i] >= base_rgb[i].min(target_rgb[i]) - 0.0001); + assert!(actual_rgb[i] <= base_rgb[i].max(target_rgb[i]) + 0.0001); } - assert!((0.60..=1.0).contains(&tint.a)); + assert_eq!(tint.a, 0.15); } } }