From 3f2c42513f87f173adf73cdb2fef49ff4127b688 Mon Sep 17 00:00:00 2001 From: Georg Bauer Date: Sun, 26 Jul 2026 09:33:14 +0200 Subject: [PATCH] Show the context fill grade as a pie chart Co-Authored-By: Claude Opus 5 --- src/app/view.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++ src/app/view/chat.rs | 28 +++++++++++++++------- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/app/view.rs b/src/app/view.rs index 79f54b7..dec94ee 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -845,6 +845,51 @@ fn sidebar_style(_: &Theme) -> container::Style { container::Style::default().background(Color::from_rgb8(23, 23, 25)) } +/// Fill grade from which the context pie warns that compaction is near. +const CONTEXT_WARN: f32 = 0.8; + +/// Draws the context fill grade as a pie the size of an icon; the exact token +/// counts live in a tooltip so the composer row stays compact. +fn context_pie<'a>(fraction: f32, size: u16) -> Svg<'a> { + svg(svg::Handle::from_memory( + context_pie_svg(fraction).into_bytes(), + )) + .width(size as f32) + .height(size as f32) +} + +fn context_pie_svg(fraction: f32) -> String { + let fraction = fraction.clamp(0.0, 1.0); + let palette = app_theme().palette(); + let fill = hex(if fraction >= CONTEXT_WARN { + Color::from_rgb8(224, 140, 58) + } else { + palette.primary + }); + let track = hex(muted_text()); + let wedge = if fraction >= 1.0 { + format!(r#""#) + } else { + let angle = fraction * std::f32::consts::TAU; + let (x, y) = (10.0 + 8.0 * angle.sin(), 10.0 - 8.0 * angle.cos()); + let large = u8::from(fraction > 0.5); + format!(r#""#) + }; + format!( + r#"{wedge}"# + ) +} + +fn hex(color: Color) -> String { + let channel = |value: f32| (value.clamp(0.0, 1.0) * 255.0).round() as u8; + format!( + "#{:02x}{:02x}{:02x}", + channel(color.r), + channel(color.g), + channel(color.b) + ) +} + // The lifetime is free: the handle owns its bytes and the style closure is // 'static, so an icon can join a row that borrows shorter-lived data. fn icon<'a>(data: &'static [u8], size: u16) -> Svg<'a> { @@ -860,6 +905,17 @@ fn icon<'a>(data: &'static [u8], size: u16) -> Svg<'a> { mod tests { use super::*; + #[test] + fn context_pie_wedge_follows_the_fill_grade() { + assert!(context_pie_svg(0.0).contains(r#"A8 8 0 0 1 10.00 2.00"#)); + assert!(context_pie_svg(0.25).contains(r#"A8 8 0 0 1 18.00 10.00"#)); + assert!(context_pie_svg(0.75).contains(r#"A8 8 0 1 1 2.00 10.00"#)); + assert!(context_pie_svg(1.5).contains("