1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//! Loader math — the pure phase functions behind the loading indicators.
//!
//! These are the curves and constants the gpui viewport animates with
//! (`motion`, `bezel::loaders`), kept pure so any
//! surface animates the *same* loaders rather than inventing its own spinner.
//! A loading indicator is a brand surface; two of them that disagree read as
//! two products.
//!
//! Everything is a pure function of a phase in `0..1`, so a caller can drive
//! it from a frame delta or from wall-clock elapsed time and get identical
//! output.
/// Pulse loader period.
pub const PULSE_MS: u64 = 2_400;
/// Gradient matrix spinner wave period.
pub const GRADIENT_SPIN_MS: u64 = 750;
/// Cells in the pulse wave loader.
pub const PULSE_CELLS: usize = 5;
/// Side length of the gradient spinner matrix.
pub const MATRIX_SIDE: usize = 3;
/// Pulse loader cells rest at this opacity between pulses.
pub const PULSE_MIN_OPACITY: f32 = 0.08;
/// …and at this scale.
pub const PULSE_MIN_SCALE: f32 = 0.9;
/// Per-cell stagger, as a fraction of the pulse period (0.15s of 2.4s).
pub const PULSE_STAGGER: f32 = 0.15 / 2.4;
/// Per-row tints of the gradient matrix spinner — a "sunrise" gradient
/// sampled at each row: cool blue at the top, through amber, to pink.
pub const GSPIN_ROW_TINTS: = ;
/// Opacity a gradient-spinner cell rests at between pulses.
pub const GSPIN_DIM: f32 = 0.1;
/// Clockwise ring position of each `(row, col)` cell of the 2×3 mini spinner,
/// top-left first: (0,0) → (0,1) → (1,1) → (2,1) → (2,0) → (1,0). Every cell of
/// a 2×3 grid is on the ring, so the brightness chases around it.
pub const MINI_RING: = ;
/// Cells in the mini spinner's ring.
pub const MINI_RING_LEN: f32 = 6.0;
/// Orb period. Slower than either spinner: these breathe rather than tick, and
/// a tick at this size reads as impatience.
pub const ORB_MS: u64 = 2_000;
/// Orbs in the [`ORB_SEATS`] cluster.
pub const ORBS: usize = 3;
/// Where each cluster orb sits, as `(x, y)` fractions of the box — a triangle,
/// so the group reads as one object and still fills a square slot.
pub const ORB_SEATS: = ;
/// A cluster orb's diameter at its smallest and largest, as fractions of the
/// box. The swing is the whole point: at the bottom an orb is nearly gone, so
/// the *count* you perceive changes as they trade places. A fixed size would
/// leave the silhouette constant and the thing would read as three dots
/// dimming.
pub const ORB_MIN_SIZE: f32 = 0.14;
pub const ORB_MAX_SIZE: f32 = 0.62;
/// How far a cluster orb wanders from its seat, as a fraction of the box. Wide
/// enough that neighbours overlap at one end of the walk and separate at the
/// other.
pub const ORB_DRIFT: f32 = 0.13;
/// An orb never goes out entirely — the cluster dims, it does not blink.
pub const ORB_MIN_OPACITY: f32 = 0.35;
/// Glow radius at rest and at full breath, as fractions of the box.
pub const ORB_GLOW_MIN: f32 = 0.10;
pub const ORB_GLOW_MAX: f32 = 0.40;
/// Dots in the [`orb_ring_seat`] ring.
pub const ORB_RING_DOTS: usize = 8;
/// The ring's radius and its dot diameter, as fractions of the box.
pub const ORB_RING_RADIUS: f32 = 0.34;
pub const ORB_RING_DOT: f32 = 0.16;
/// Rings in flight at once in the bloom.
pub const ORB_BLOOM_RINGS: usize = 3;
/// Where a bloom ring starts and ends, as fractions of the box.
pub const ORB_BLOOM_MIN: f32 = 0.16;
pub const ORB_BLOOM_MAX: f32 = 1.0;
// The swing has to be visible or the cluster is three dots dimming: an orb at
// the trough is at most a quarter the diameter of one at the crest. A compile
// error rather than a test, since both sides are constants.
const _: = assert!;
/// One cluster orb's opacity as it breathes: [`ORB_MIN_OPACITY`] → 1 → back.
/// One cluster orb's diameter, as a fraction of the box.
/// One cluster orb's glow radius, as a fraction of the box. In step with the
/// opacity, because a glow that peaks off-beat reads as two lights rather than
/// one breathing.
/// How far a cluster orb has drifted from its seat, as `(dx, dy)` fractions of
/// the box — a small circle walked once per period.
///
/// Being a circle, the drift returns exactly to zero every period, so nothing
/// accumulates however long it runs.
/// Where ring dot `index` sits on a circle of `radius`, as `(x, y)` fractions
/// of the box. Twelve o'clock first, going clockwise.
///
/// The radius is an argument because two shapes want the same circle: the ring
/// holds it still and the converge pulses it. Two functions here would be one
/// function and a number.
/// The converge's radius at `phase`: in to nothing, back out to the ring.
///
/// Every dot shares this one radius, so they arrive at the centre together and
/// stack into a single point — the frame that makes this read as a gathering
/// rather than as a ring that shrank.
/// A bloom ring's radius, as a fraction of the box: out from
/// [`ORB_BLOOM_MIN`] to [`ORB_BLOOM_MAX`] once per period.
/// A bloom ring's opacity: full as it leaves the centre, gone by the edge —
/// squared, so it holds its brightness through the middle of the travel and
/// then goes quickly, which is what keeps the ring from looking like a
/// dissolving circle.
/// Linear interpolation.
/// A cell's phase, given the loader's raw phase and the cell's index.
/// Cosine pulse: 0 at phase 0, 1 at phase 0.5, back to 0 at phase 1.
/// Pulse loader cell opacity for a phase: 0.08 → 1 → 0.08.
/// Pulse loader cell scale for a phase: 0.9 → 1 → 0.9.
/// Gradient-spin cell opacity for a local phase `t` (0..1 of the period),
/// ported from the `gradient-spin-pulse` keyframes: full at the cycle
/// start, easing down to `dim` by 45%, resting at `dim` until 92%, then rising
/// back to full — the per-cell phase offset sweeps this pulse across the grid.
/// The phase offset of a `(row, col)` cell in the 3×3 gradient spinner: the
/// pulse enters at the bottom edge and converges toward the top-centre cell, so
/// the wave reads as travelling upward.