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#""#
+ )
+}
+
+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("