bezel

Components / Status

Loaders

Markdown crates/ui/src/loaders.rs docs.rs

use motion::Painter;
use ui::loaders::{self, Orb};

loaders::orb(Orb::Cluster, "thinking", 44.0, &theme, Painter::of(cx), cx)

One function with a shape parameter rather than four functions: they are the same operation, and what differs is an argument.

Shapes

pub enum Orb {
    /// Blobs whose sizes swing so the count you perceive changes.
    Cluster,
    /// Dots on a circle, brightness chasing round.
    Ring,
    /// Dots gathering to a point and opening back out.
    Converge,
    /// Rings leaving the centre — the only one that travels outward, which
    /// reads as a signal rather than a wait.
    Bloom,
}

Cell grids

loaders::pulse_loader("busy", &theme, 6.0, view, cx)          // a row
loaders::gradient_spinner("busy", &theme, 6.0, view, cx)      // 3×3
loaders::mini_gradient_spinner("busy", &theme, 5.0, view, cx) // 2×3
loaders::loading_word(&theme)                                 // the L O A D I N G caption

API

// ui::loaders

/// One tint, from the theme's accent.
pub fn orb(
    shape: Orb,
    key: impl Into<SharedString>,
    size_px: f32,
    theme: &Theme,
    painter: Painter,
    cx: &mut App,
) -> impl IntoElement;

/// The older grids. Cells animate inside fixed slots, so nothing reflows.
pub fn pulse_loader(
    id: &'static str,
    theme: &Theme,
    cell_px: f32,
    painter: Painter,
    cx: &mut App,
) -> impl IntoElement;

pub fn loading_word(theme: &Theme) -> impl IntoElement;

// ...

Every loader takes the calling view's Painter and drives off the shared 30fps pulse clock, so instances stay phase-locked and the clock parks when the last one unmounts. Reduced motion snaps every cell to its rest state.

Loaders