Show the context fill grade as a pie chart
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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#"<circle cx="10" cy="10" r="8" fill="{fill}"/>"#)
|
||||
} 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#"<path d="M10 10 L10 2 A8 8 0 {large} 1 {x:.2} {y:.2} Z" fill="{fill}"/>"#)
|
||||
};
|
||||
format!(
|
||||
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8.5" fill="none" stroke="{track}" stroke-width="1.5" stroke-opacity="0.5"/>{wedge}</svg>"#
|
||||
)
|
||||
}
|
||||
|
||||
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("<circle cx=\"10\" cy=\"10\" r=\"8\""));
|
||||
let warning = hex(Color::from_rgb8(224, 140, 58));
|
||||
assert!(!context_pie_svg(CONTEXT_WARN - 0.01).contains(&warning));
|
||||
assert!(context_pie_svg(CONTEXT_WARN).contains(&warning));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raised_surfaces_stay_dark() {
|
||||
let theme = app_theme();
|
||||
|
||||
@@ -159,18 +159,27 @@ impl App {
|
||||
container(
|
||||
column![
|
||||
composer,
|
||||
progress_bar(0.0..=1.0, context_fraction).height(3),
|
||||
row![
|
||||
icon(ICON_PAPERCLIP, 19),
|
||||
text(format!(
|
||||
"{} / {} tokens ({:.0}%) • {}",
|
||||
self.context_used,
|
||||
self.context_limit,
|
||||
context_fraction * 100.0,
|
||||
self.tokens_per_second.map_or_else(
|
||||
|| "— tok/s".to_owned(),
|
||||
|speed| format!("{speed:.1} tok/s")
|
||||
tooltip(
|
||||
context_pie(context_fraction, 19),
|
||||
container(
|
||||
text(format!(
|
||||
"{} / {} tokens ({:.0}%)",
|
||||
self.context_used,
|
||||
self.context_limit,
|
||||
context_fraction * 100.0
|
||||
))
|
||||
.size(12)
|
||||
)
|
||||
.padding(10)
|
||||
.style(preference_group_style),
|
||||
tooltip::Position::Top,
|
||||
)
|
||||
.gap(6),
|
||||
text(self.tokens_per_second.map_or_else(
|
||||
|| "— tok/s".to_owned(),
|
||||
|speed| format!("{speed:.1} tok/s")
|
||||
))
|
||||
.size(11)
|
||||
.color(muted_text()),
|
||||
@@ -184,6 +193,7 @@ impl App {
|
||||
.size(12),
|
||||
action,
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(Alignment::Center),
|
||||
]
|
||||
.spacing(8),
|
||||
|
||||
Reference in New Issue
Block a user