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> named Push to talk (or pttLabel) with aria-pressed set while state is not idle. 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 as listening... 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';