Progress HUD
A long job, with the body saying what is happening and the ring saying how far along it is.
See it
One session reporting a fraction to the arbiter. AgentHud reads the state, the dial and the tip from the same result, so the three cannot describe different jobs. Reaching the end sends celebrate, an event the arbiter never drops.
Until the slider moves the arbiter has heard nothing, so AgentHud draws an idle character with no dial — a HUD is a decoration on a page that has to render before any agent has connected. useHudSpec('progress', { value }) gives the same protection to a host assembling a spec by hand: an undefined fraction on the first render becomes null, and hud={null} is a character with no dial.
When to use it
A long job — an export, an index build, a batch of uploads — and a character beside it that has something to show for the whole duration. Two layers do the work. The body says what the agent is doing: working, or progress when the job is a measured thing. The ring outside the body says what it knows: a fraction, a ceiling, a sync state. They are separate on purpose, so a character can be working and showing 83% at once without the two competing for the same pixels.
Components
CharacterMark—hud={...}, aHudSpecthe host already has the numbers for.HudPlate— the figure as text beside the dial, in a polite live region, because a ring cannot say 83%.AgentHud— the arbiter's current state, dial and caption as one component, so a dial from one session cannot sit beside a face from another.useHudSpec— build a spec from live values and getnullwhen they do not hold up yet.AgentStateProvider— where the numbers arrive.
How the states move
The progress state's belt is indeterminate: a ring that fills and resets on a four-second cycle, meaning "a measured thing is underway". The measurement itself always goes in the HUD, whose progress ring is the value. Use the working body with a progress dial when the fraction is what matters, and the progress body when all you can say is that it is moving.
What each state looks like
| State | Eyes | Mouth | Effect | Id | HUD | |
|---|---|---|---|---|---|---|
workingWorking | dotDown → dot → dotDown | none | arc | 1632 | gauge | |
progressProgress | dot | none | progress | 47 | progress | |
uploadingUploading | dotUp → dot | none | rise | 45 | sync | |
loadingLoading | dot → dotLook | none | spinner | 0536 | — | |
celebrateCelebrating | star → archWide → wink → arch | grin | sparkle | 2533 | kpi | |
alertingAlerting | dotWide → dot | o | bang | 34 | — |
On the eyes and line faces, four of these hand their body to a belt. Because a HUD is drawn in the ring outside the body, a belt state and a dial coexist: the body collapses to its core, the belt orbits it, and the dial sits further out still.
Six dials
Six dials for measured things. progress and gauge are a fraction against a ceiling; context counts cells rather than filling an arc, because the last few slots are the ones that matter; balance shows the magnitude and leaves the figure to the plate; kpi spark-lines a series; sync is a lifecycle. Every one is a pure function of (spec, time), so a paused character, a server render and a screenshot agree.