Walkthrough surfaces
Six pieces for showing an agent product without being the product: a window frame, work in flight, starting points, a waiting screen, a hand-off and a message field.
Part of
@aora-bot/surfaces, not@aora-bot/react:pnpm add @aora-bot/surfaces @aora-bot/react, and import@aora-bot/surfaces/styles.cssafter the React stylesheet.
- Read the brief
- Draft a reply
- Check the numbers
- Open a pull request
DemoDesktop
A window frame. Onboarding has to show software without being the software, and a plain card does not read as an application — the window bar is what tells someone "this is a picture of a thing happening elsewhere" in no words at all. The frame is presentational: aria-hidden chrome around whatever the host puts in it, so the content keeps its own semantics. toolbar takes small chrome below the title bar — tabs, a fake URL.
JobBubbles
Work in flight, as words around a character. Bubbles rather than a list, because the claim is "several things at once" and a list reads as a queue. Each bubble drifts on its own period with a negative delay so they are never in phase — a ring of labels bobbing in unison looks like one object rotating. The ring uses real angles rather than a rotating container: a container that turns takes its text with it, and labels passing upside-down are unreadable. jobs defaults to four from the locale; radiusPx sets the ring.
SuggestionCards
Three starting points, each with a face. The characters are derived from the card ids rather than chosen, which is what makes the component generic: a host passing its own cards gets three distinguishable agents without picking shapes, and the same card always looks the same because the derivation is a hash, not a shuffle. Set shape and color on a card to override.
LandingWait
Ready when you are
Give it the first task and it will get going.
Approval window
The run continues on its own after this.
The screen before anything has happened. A blank state with a waiting character says the product is ready and not broken, which is the one thing an empty screen otherwise fails to say. Without waitMs the plate counts how long it has been waiting; with one it becomes a deadline, the character wears a timer dial and the plate counts down. The clock is on the plate rather than in the dial because a number is a number: the ring can show that time is passing, only text can say how much. since starts the clock from a moment the host already knows about.
HandOff
One agent handing work to another. sending and receiving are a matched pair in the engine — one leans away as the other catches — so the hand-off needs no travelling object between the two to read; the bodies already describe the direction. Three beats rather than a continuous animation, because the sequence has a meaning and an end: before, during, after. loop is opt-in, for a marketing page where nobody is waiting for it to finish.
Composer
A message field with a character that pays attention to it. Three states, driven by what the field is doing rather than by a timer: listening on focus, composing while typing, back to idle idleMs after the typing stops. A character beside an input is only worth having if it reacts to the input, and the reaction has to be immediate enough to feel like attention and quiet enough not to distract from writing. Uncontrolled unless value is given, so it drops into a demo without state around it. The props table below is generated from ComposerProps; sendButton hides the send control for a host that has its own.
API
DemoDesktop
| Prop | Type | Default | Description |
|---|---|---|---|
children | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Content of the window. |
className | string | — | — |
style | CSSProperties | — | — |
title | string | — | Text in the window's title bar. |
toolbar | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Small chrome below the title bar — tabs, a toolbar, a fake URL. |
widthPx | number | 420 | — |
JobBubbles
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
jobs | readonly string[] | — | Short task strings. Defaults to four from the locale. |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
radiusPx | number | 96 | Radius of the ring the bubbles sit on. |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
sizePx | number | — | — |
state | "idle" | "sleeping" | "drowsy" | "bored" | "waking" | "powering-down" | "listening" | "thinking" | "searching" | "reading" | "writing" | "working" | "loading" | "progress" | "radar" | "orbit" | "dictating" | "uploading" | "sending" | "receiving" | "messaging" | "waiting" | "spawning" | "composing" | "dragging" | "happy" | "excited" | "laughing" | "playful" | "proud" | "celebrate" | "bouncing" | "shy" | "sad" | "angry" | "confused" | "suspicious" | "surprised" | "scared" | "curious" | "humming" | "alerting" | "notifying" | "refusing" | 'working' | — |
style | CSSProperties | — | — |
SuggestionCards
| Prop | Type | Default | Description |
|---|---|---|---|
cards | readonly SuggestionCard[] | — | Defaults to three examples from the locale. |
className | string | — | — |
face | "cute" | "eyes" | "line" | — | — |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
onPick | ((card: SuggestionCard) => void) | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
LandingWait
| Prop | Type | Default | Description |
|---|---|---|---|
action | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Buttons under the copy. |
body | string | — | — |
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
since | number | — | Start the clock from a moment the host already knows about. |
sizePx | number | — | — |
style | CSSProperties | — | — |
title | string | — | — |
waitMs | number | — | A deadline. With one the character shows a countdown dial and the plate counts down; without one it shows how long it has been waiting. |
HandOff
| Prop | Type | Default | Description |
|---|---|---|---|
from* | AgentLike | — | Who is passing the work on. |
to* | AgentLike | — | Who is picking it up. |
beatMs | number | 1400 | Length of each beat. The full cycle is three of them. |
className | string | — | — |
face | "cute" | "eyes" | "line" | — | — |
label | string | — | What is being handed over, shown between the two. |
lineWeight | number | — | — |
loop | boolean | false | Run the beats on a loop. A single pass by default. |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
Composer
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
idleMs | number | 1200 | How long after the last keystroke the character stops composing. |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
onChange | ((value: string) => void) | — | — |
onSubmit | ((value: string) => void) | — | — |
placeholder | string | — | — |
sendButton | boolean | true | Show the send button. On by default. |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
value | string | — | — |
Next
- Onboarding — a scripted first run
- CastStage — several characters entering in turn