feat - updating icons and glyphs, added claude code subscription integration

This commit is contained in:
2026-10-04 18:20:58 -05:00
parent 188489c890
commit b29882d56c
49 changed files with 2374 additions and 288 deletions
@@ -14,7 +14,7 @@ use string_offset::CharOffset;
use crate::color::ColorU;
pub use crate::elements::shimmering_text::config::ShimmerConfig;
use crate::elements::shimmering_text::glyph_index::GlyphIndex;
use crate::elements::{Axis, Point, DEFAULT_UI_LINE_HEIGHT_RATIO};
use crate::elements::{Axis, Icon, Point, DEFAULT_UI_LINE_HEIGHT_RATIO};
use crate::fonts::{FamilyId, Properties};
use crate::geometry::rect::RectF;
use crate::geometry::vector::{vec2f, Vector2F};
@@ -90,6 +90,7 @@ pub struct ShimmeringTextElement {
origin: Option<Point>,
handle: ShimmeringTextStateHandle,
leading_icon: Option<(Icon, f32, f32)>,
}
impl ShimmeringTextElement {
@@ -112,9 +113,16 @@ impl ShimmeringTextElement {
size: None,
origin: None,
handle: state_handle,
leading_icon: None,
}
}
/// Includes a leading icon in the same shimmer track as the text.
pub fn with_leading_icon(mut self, path: &'static str, size: f32, gap: f32) -> Self {
self.leading_icon = Some((Icon::new(path, self.base_color), size, gap));
self
}
/// Returns the center of the shimmer as a fractional glyph index along the "track".
fn shimmer_center(&self, number_of_glyphs: usize, state: &StateInternal) -> GlyphIndex<f32> {
if number_of_glyphs <= 1 {
@@ -165,11 +173,12 @@ impl ShimmeringTextElement {
return PaintStyleOverride::default();
}
let center = self.shimmer_center(n, &state);
let icon_offset = usize::from(self.leading_icon.is_some());
let center = self.shimmer_center(n + icon_offset, &state);
let mut overrides = RangeMap::new();
for (glyph_index, char_index) in glyph_indices_in_order.iter() {
let intensity = self.intensity_at(*glyph_index, center);
let intensity = self.intensity_at(GlyphIndex(glyph_index.0 + icon_offset), center);
let color = self
.base_color
.to_f32()
@@ -189,6 +198,20 @@ impl Element for ShimmeringTextElement {
ctx: &mut crate::LayoutContext,
app: &AppContext,
) -> Vector2F {
let icon_width = self
.leading_icon
.as_ref()
.map_or(0., |(_, size, gap)| size + gap);
if let Some((icon, size, _)) = &mut self.leading_icon {
icon.layout(
SizeConstraint {
min: vec2f(*size, *size),
max: vec2f(*size, *size),
},
ctx,
app,
);
}
let mut state = self.handle.get();
let text_len = self.text.chars().count();
@@ -197,7 +220,7 @@ impl Element for ShimmeringTextElement {
StyleAndFont::new(self.font_family, Properties::default(), TextStyle::new()),
)];
let max_width = constraint.max_along(Axis::Horizontal);
let max_width = (constraint.max_along(Axis::Horizontal) - icon_width).max(0.);
let layout_key = LayoutKey {
text: self.text.clone(),
@@ -247,15 +270,22 @@ impl Element for ShimmeringTextElement {
};
let size = vec2f(
line.width.max(constraint.min.x()).min(constraint.max.x()),
line.height(),
(line.width + icon_width)
.max(constraint.min.x())
.min(constraint.max.x()),
line.height()
.max(self.leading_icon.as_ref().map_or(0., |(_, size, _)| *size)),
);
self.size = Some(size);
size
}
fn after_layout(&mut self, _: &mut crate::AfterLayoutContext, _: &AppContext) {}
fn after_layout(&mut self, ctx: &mut crate::AfterLayoutContext, app: &AppContext) {
if let Some((icon, _, _)) = &mut self.leading_icon {
icon.after_layout(ctx, app);
}
}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
/// Duration, in ms, for which to repaint. Approximately 30fps.
@@ -273,7 +303,31 @@ impl Element for ShimmeringTextElement {
ctx.repaint_after(Duration::from_millis(REPAINT_DURATION));
let bounds = RectF::from_points(origin, origin + size);
let mut text_origin = origin;
let icon_color = {
let state = self.handle.get();
let center = self.shimmer_center(state.glyph_indices_in_order.len() + 1, &state);
self.base_color
.to_f32()
.lerp(
self.shimmer_color.to_f32(),
self.intensity_at(GlyphIndex(0), center),
)
.to_u8()
};
if let Some((icon, icon_size, gap)) = &mut self.leading_icon {
*icon = icon.with_color(icon_color);
icon.paint(origin + vec2f(0., (size.y() - *icon_size) / 2.), ctx, app);
text_origin += vec2f(*icon_size + *gap, 0.);
}
text_origin += vec2f(0., (size.y() - line.height()) / 2.);
let bounds = RectF::new(
text_origin,
vec2f(
(origin.x() + size.x() - text_origin.x()).max(0.),
line.height(),
),
);
let style_overrides = self.build_color_overrides();
line.paint(
@@ -475,9 +475,7 @@ impl TableStateHandle {
/// and you want all columns to resize to fit their new content.
pub fn invalidate_all_intrinsic_widths(&self) {
let mut state = self.inner.borrow_mut();
for width in &mut state.intrinsic_column_widths {
*width = None;
}
state.intrinsic_column_widths.fill(None);
}
/// Scroll to a specific row with an optional offset.
@@ -497,7 +497,9 @@ fn test_svg_text_rasterizes_with_loaded_system_fonts() {
assert!(image
.rgba_bytes()
.chunks_exact(4)
.as_chunks::<4>()
.0
.iter()
.any(|pixel| pixel[3] != 0));
}
@@ -534,7 +536,9 @@ fn test_svg_text_rasterizes_with_bundled_sans_serif_fallback() {
assert!(image
.rgba_bytes()
.chunks_exact(4)
.as_chunks::<4>()
.0
.iter()
.any(|pixel| pixel[3] != 0));
}
@@ -382,7 +382,7 @@ cfg_if::cfg_if! {
use crate::platform::CapturedFrameFormat;
let pixel_count = data.len() / 4;
let mut rgb = Vec::with_capacity(pixel_count * 3);
for chunk in data.chunks_exact(4) {
for chunk in data.as_chunks::<4>().0 {
match format {
CapturedFrameFormat::Rgba => {
rgb.push(chunk[0]);
@@ -334,7 +334,7 @@ pub async fn run_capture_loop(app: crate::App, state: CaptureLoopState) {
fn rgba_to_rgb(rgba: &[u8]) -> Vec<u8> {
let pixel_count = rgba.len() / 4;
let mut rgb = Vec::with_capacity(pixel_count * 3);
for chunk in rgba.chunks_exact(4) {
for chunk in rgba.as_chunks::<4>().0 {
rgb.push(chunk[0]);
rgb.push(chunk[1]);
rgb.push(chunk[2]);
+1 -1
View File
@@ -529,7 +529,7 @@ impl CapturedFrame {
pub fn ensure_rgba(&mut self) {
if self.format == CapturedFrameFormat::Bgra {
for chunk in self.data.chunks_exact_mut(4) {
for chunk in self.data.as_chunks_mut::<4>().0 {
chunk.swap(0, 2);
}
self.format = CapturedFrameFormat::Rgba;