StatusDock
The voice control at the bottom center of the HUD: a push-to-talk button between two waveform
meters, with the current assistant state underneath. It is driven by a single AppOrbState.
import { StatusDock } from 'jarvis-react-ui';
Basic
StatusDock is position: fixed, centered horizontally 24px above the bottom edge, with
z-index: 25. Render it once near the root of your app, or pass it to the dock slot of
HUDShell. onPTT fires when the button is clicked; update your state
there. In this demo the frame contains it; in an app it sits at the bottom of the viewport.
import { useState } from 'react';
import { StatusDock } from 'jarvis-react-ui';
import type { AppOrbState } from 'jarvis-react-ui';
export default function Basic() {
const [state, setState] = useState<AppOrbState>('idle');
return (
<StatusDock
state={state}
onPTT={() => setState((s) => (s === 'idle' ? 'listening' : 'idle'))}
/>
);
}
States
state takes an AppOrbState, the same value you pass to the orb. Every state except idle starts
the WaveformMeters and marks the
PushToTalkButton as active. The text below comes from
StatusLabel: READY when idle, otherwise listening...,
thinking..., speaking..., working... or follow-up....
import { useState } from 'react';
import { Button, StatusDock } from 'jarvis-react-ui';
import type { AppOrbState } from 'jarvis-react-ui';
const STATES: AppOrbState[] = ['idle', 'listening', 'thinking', 'speaking', 'working'];
export default function States() {
const [state, setState] = useState<AppOrbState>('thinking');
return (
<>
<div className="flex flex-wrap justify-center gap-2 self-start">
{STATES.map((s) => (
<Button
key={s}
size="sm"
variant={state === s ? 'primary' : 'ghost'}
onClick={() => setState(s)}
>
{s}
</Button>
))}
</div>
<StatusDock state={state} />
</>
);
}
import { CssOrb, StatusDock } from 'jarvis-react-ui';
import type { AppOrbState } from 'jarvis-react-ui';
function Assistant({ state, onTalk }: { state: AppOrbState; onTalk: () => void }) {
return (
<>
<CssOrb state={state} />
<StatusDock state={state} onPTT={onTalk} />
</>
);
}
Localization
labels replaces the status texts per state; states you leave out keep their built-in text.
pttLabel renames the built-in button and becomes its accessible name, and brand replaces the
J A R V I S line under the status.
import { useState } from 'react';
import { StatusDock } from 'jarvis-react-ui';
const LABELS = {
idle: 'BEREIT',
listening: 'hört zu …',
thinking: 'denkt nach …',
speaking: 'spricht …',
follow_up: 'Rückfrage …',
working: 'arbeitet …',
};
export default function Localized() {
const [talking, setTalking] = useState(false);
return (
<StatusDock
state={talking ? 'listening' : 'idle'}
labels={LABELS}
pttLabel="Sprechen"
onPTT={() => setTalking((v) => !v)}
/>
);
}
Custom button
ptt replaces the built-in button. Pass your own PushToTalkButton to change its icon or other
props; onPTT and pttLabel are then unused, so wire the click on your button.
import { useState } from 'react';
import { Radio } from 'lucide-react';
import { PushToTalkButton, StatusDock } from 'jarvis-react-ui';
export default function CustomPtt() {
const [live, setLive] = useState(false);
return (
<StatusDock
state={live ? 'speaking' : 'idle'}
ptt={
<PushToTalkButton
active={live}
aria-label="Broadcast"
onClick={() => setLive((v) => !v)}
>
<Radio size={24} strokeWidth={1.8} aria-hidden="true" />
</PushToTalkButton>
}
/>
);
}
Sound
The built-in push-to-talk button plays hover_button on hover and click on click through the
nearest SfxProvider. The meters and the label are silent.
Accessibility
- The push-to-talk button is a native
<button>namedPush to talk(orpttLabel) witharia-pressedset whilestateis notidle. It works with Enter and Space. - The waveform meters are
aria-hidden. - The state text is a polite live region (
role="status"), so screen readers announce state changes such aslistening...without interrupting.
API
| Prop | Type | Default | Description |
|---|---|---|---|
state (required) |
'idle' | 'listening' | 'thinking' | 'speaking' | 'follow_up' | 'working' |
Current assistant state. Every state except idle runs the waveform meters and marks the push-to-talk button as active; the StatusLabel shows the matching text, and screen readers announce each change (the label is a polite live region). |
|
brand |
string |
'J A R V I S' |
Brand text shown in small, widely spaced letters below the status. |
className |
string |
Additional class names for the root <div>, which is fixed to the bottom center. |
|
labels |
Partial<Record<AppOrbState, string>> |
Custom status texts per state for the built-in StatusLabel, e.g. for localization; states you leave out keep their built-in text (READY, listening..., thinking..., speaking..., follow-up..., working...). |
|
onPTT |
(() => void) |
Called when the built-in push-to-talk button is clicked; unused when ptt is set. |
|
ptt |
ReactNode |
Replaces the built-in PushToTalkButton between the two meters, e.g. with a customized one. | |
pttLabel |
string |
'Push to talk' |
Accessible name of the built-in push-to-talk button, e.g. for localization; unused when ptt is set. |
Type AppOrbState
Full app-layer orb state — a superset of OrbState that includes
states handled above the base state machine.
Components and hooks should use AppOrbState for their props / return values.
type AppOrbState = OrbState | 'working';
Type OrbState
States that drive the JARVIS orb visual and audio state machine. Single source of truth — copied from src/types.ts.
type OrbState = 'idle' | 'listening' | 'thinking' | 'speaking' | 'follow_up';