Skip to content

Recording HUD

In review
Live · web

Recording HUD: two variants, one shared live meter & controls

Recording, 0:08

With logo: the canonical default, Nockerl is listening

Recording, 0:08

No logo: explicit opt-out for tightly embedded chrome

Phase

State: Recording · 0:08. The HUD is live & token-colored.

On Voice (macOS) this floating command pill is the top-of-window surface. Voice is a menu-bar utility with no title bar, so the pill (not a TopBar) is its brand-anchored header: the mark, a divider, and the morphing status content in a capsule with the 1.5pt cyan outline. Android and web carry a real top bar; Voice uses this pill.

// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform; this
// is the intended @dizyx/nockerl-react API, a single controlled overlay component.
// The level meter geometry/math matches the native apps exactly.)
export function DictationOverlay({ rec }: { rec: RecorderState }) {
return (
<RecordingHud
phase={rec.phase} // 'recording' | 'paused' | 'transcribing' | 'result' | 'error'
levels={rec.levels} // number[] (0…1), one per meter bar
elapsed={rec.elapsedSeconds} // drives the m:ss mono timer
pasted={rec.pasted} // result phase: true → "Pasted", false → "Copied to clipboard"
error={rec.error} // shown in the error row when phase === 'error'
/>
);
}
PropTypeDefaultDescription
phase *'recording' | 'paused' | 'transcribing' | 'result' | 'error'Which surface the HUD shows. recording animates the meter; paused freezes it; transcribing shows the indeterminate spinner; result shows the success check + "Pasted"/"Copied to clipboard"; error shows the warning row.
levels *number[]Audio levels (0…1), one per meter bar (5 to match native). Mapped through the same ln(raw+1)/ln(32768) curve; only bar height animates.
elapsed *numberElapsed seconds, rendered m:ss in the font.family.mono token, tabular, in the warm record color.
pastedbooleanFor phase === "result": true → check + "Pasted"; false → clipboard + "Copied to clipboard"; omit → generic "Done". Mirrors Voice result(pasted: Bool). The mark carries the earned brand cyan (not green), a genuine success beat.
dismissingbooleanPlays the result dismiss. After the ~1.8s success beat the pill shoots straight down and fades (constant height; width already shrunk to the check + word). Motion, eyeball-gated; VRT captures the resting still only.
errorstringMessage shown in the error row (color + icon + text, never color alone) when phase === "error".

Stop / pause / cancel are real, keyboard-operable icon buttons (each with an aria-label); the meter play/pause is a separate control. The surface carries role="status" with an off-screen live label so the phase + elapsed are announced without re-announcing on every meter frame.

Cross-platform drift (this surface lives natively in Voice + Android)

The recording HUD is canonical on both Voice (RecordingHUD.swift) and Android (VoiceRecordingUI.kt); web is derived. The meter geometry + the ln(raw+1)/ln(32768) level math are identical across all three. Honest divergences:

  • Controls: neither native HUD ships an explicit stop / pause / cancel trio. Voice’s panel is non-interactive (ignoresMouseEvents, since it must never steal the ⌘V paste); Android surfaces only an inline Cancel (its onStop is wired but not drawn). The demo’s icon-button controls (incl. pause/resume) are an original web affordance built from the icon-button vocabulary, not yet a native API.
  • Result phase. The three platforms converged here: a success row now ships on web too, a checkmark (pasted) / clipboard (copied) mark in the earned brand cyan + “Pasted” / “Copied to clipboard”, then a shoot-down dismiss after the ~1.8s beat (constant height, width-only morph). Web phase gains result; the mark is cyan (not green) to keep the HUD monochromatic-cyan. Voice’s result(pasted:) is the value-exact source; Compose mirrors it.
  • Hint pill: Voice also has a transient hint(message) phase (a ⌘ glyph + message: the launch “ready” / hotkey prompt, auto-hides 4.5s). It’s app-level guidance, not a recording-lifecycle state, so it stays Voice-side and is not part of the shared phase set.
  • Container radius: both apps use the full stadium (pill) for the floating HUD, a deliberate exception to the 12px control radius (it reads as chrome, like the input bar). The error border swaps to the warm warning token on both.
  • Timer color: the elapsed clock is the warm record color while recording (error token), a status signal, not a decorative accent.