Esta página se publica en inglés y en ucraniano; abajo está la versión en inglés.
Launcher button and characters
For developers, in code: the three launcher types, the label and container, the five characters and their colour slots, auto-contrast, and where the widget sits.
The launcher is the thing in the corner. It is also the character: when the widget opens, the same orb stays where it is and the conversation appears beside it — there is one orb, never a second copy.
In the panel: the character and its colours are on Widget → Appearance → Orb / character, the button on Widget → Launcher (Widget settings). This page is the same in code.
Three launcher types
launcher.type |
What is drawn | Notes |
|---|---|---|
orb (default) |
the animated character, skin voiceCharacter |
launcher.value is ignored; the skin is set by voiceCharacter |
image |
the image at launcher.value, raster or SVG |
one graphic, open and closed alike; no character is drawn — the companion’s aura and controls take the character’s colours |
none |
nothing while closed | headless: your own button calls Shukai.open() — Headless |
launcher.label — a non-blank label turns the launcher into a pill with the text beside the
character, and is also its aria-label; the colour is the theme token launcher-color.
launcher.container — elevated (fill and shadow), flat (fill only), bare (transparent, the
character floats; the panel’s default for a new widget). launcherAttention — off, subtle
or prominent — sets how the closed launcher draws the eye and whether the greeting teaser
appears; off is neither.
The five characters
voiceCharacter picks the skin. Each has colourable parts («slots»), set by the owner in the panel
or by a page through orbColours: { slot: colour } or the CSS variable. The body colour is shared
by every skin: voiceOrbColor, or the primary colour when blank — always drawn through a derived
palette that is clamped for contrast, so a stored colour is never painted exactly as given, and a
grey stays grey in every state.
| Skin | Slots | Default | CSS variable |
|---|---|---|---|
robot2 (Robot 2.0) |
visor |
auto-contrast, tail #1b2130 |
--sa-orb-face |
eyes |
auto-contrast, tail #fff |
--sa-orb-eye |
|
mouth |
follows the eyes where readable, else auto | --sa-orb-mouth |
|
kozak (Kozak) |
visor, eyes |
auto-contrast | --sa-orb-kozak-visor, -eyes |
hair (forelock), moustache |
#1C1B2E |
--sa-orb-kozak-hair, -moustache |
|
earring |
#F5B800 |
--sa-orb-kozak-earring |
|
fox |
muzzle, eyewhite |
#FFFFFF |
--sa-orb-fox-muzzle, -eyewhite |
nose, mouth |
the state’s ink | --sa-orb-fox-nose, -mouth |
|
pupils |
#0E1726 |
--sa-orb-fox-pupils |
|
monster |
mouth |
the state’s ink | --sa-orb-monster-mouth |
pupils |
#0E1726 |
--sa-orb-monster-pupils |
|
eyewhite |
#FFFFFF |
--sa-orb-monster-eyewhite |
|
mic |
grille |
#FFFFFF |
--sa-orb-mic-grille |
bars |
the state’s colour | --sa-orb-mic-bars |
The Kozak has no mouth: its moustache moves when it speaks. A slot that follows the state (a fox’s nose, the microphone’s bars) keeps changing with the state until a colour is set, and then holds it in every state. An unknown skin id draws Robot 2.0.
Every fill reads var(--sa-orb-<skin>-<slot>, var(--sa-orb-<skin>-<slot>-auto, <fallback>)) (Robot 2.0
keeps its public names, --sa-orb-face / -eye / -mouth), so
an explicit value — the owner’s, a page’s CSS, orbColours — wins over the auto pick by CSS
fallback order with no detection code.
Auto-contrast
With no visor or eyes set, the widget picks a pair that can be seen on the body colour: a dark
visor wherever it reaches 3:1 against the body, otherwise the more contrasting of dark and light;
eyes and the badge ink are whichever of white and #1b2130 contrasts more. Set only one of the
two and the other is chosen to contrast with it. The mouth of Robot 2.0 is drawn on the body, not
the visor, so it follows the eyes only where that ink can be seen on the body and is otherwise
picked to stand out. Computed once, at mount, from the served colours.
States the character shows
Idle, listening, thinking, speaking; headphones while a voice conversation is live; a raised
hand when a proactive finding is waiting; a one-shot happy or confused after an outcome; an
offline look when a turn failed; dormant after 30 seconds. Each state has its own colour pair
(--sa-orb-<state> / -ink) you may override from page CSS.
Where the widget sits
The owner’s position is a corner. A visitor may drag the launcher (6 px tells a drag from a
click); a release within 24 px of an edge snaps flush to the 20 px inset; the result is stored for
that visitor with no expiry and clamped on resize or rotation, so a rotated phone puts the widget
back where they left it. The column of bubbles and the card open towards the side with room,
measured live. On a phone the widget slides half aside while the page scrolls and lifts above an
on-screen keyboard — neither is ever persisted.
While a page action reveals an element under the widget, the whole widget steps aside to the opposite edge for a moment and returns; there is no keyboard alternative to dragging.