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 (task 2623), 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.

// macOS / iOS: SwiftUI, from the NockerlDesign package (the packaged pill
// aligned VALUE-EXACT to the web canon: logo → gray divider → the ONLY warm dot →
// theme-ink mono timer → cyan meter, cyan pill). Driven by `phase`; the app's
// NSPanel still owns window presentation, this is the pill it hosts.
import NockerlDesign
NockerlRecordingHUD(phase: .recording, elapsedLabel: "0:07", levels: levels) // pulsing dot + mono timer + live cyan meter
NockerlRecordingHUD(phase: .paused, elapsedLabel: "0:41", levels: frozen) // dimmed dot + muted timer/meter + "Paused"
NockerlRecordingHUD(phase: .transcribing) // indeterminate spinner + "Transcribing…"
NockerlRecordingHUD(phase: .error, errorMessage: "Mic unavailable") // warn triangle + text; border → statusWarning
// showBrand:false drops the logo+divider (embedded chrome); animate:false freezes
// the pulse for snapshots; animateBars opts into the ~80ms per-bar ease.

RecordingHUD is an imperative @MainActor controller: you call phase methods, not pass props. The 5-bar Equalizer + PulsingDot are file-private views.

StyleTypeDefaultDescription
showRecording()funcEnters the recording phase: resets the 5-sample level ring + the elapsed clock, starts the 250ms ticker, and presents the panel.
updateLevel(_:)func (Float)Pushes one audio level (0…1) into the ring buffer. Each bar maps via ln(raw+1)/ln(32768) (raw = level·32767), clamped to [0.08, 1].
showTranscribing()funcSwaps the pill content to an indeterminate ProgressView + "Transcribing…"; stops the ticker.
showResult(pasted:)func (Bool)Success row: checkmark.circle.fill "Pasted" or doc.on.clipboard "Copied to clipboard". Auto-hides after 1.8s.
showError(_:)func (String)Error row: a warning triangle (warm) + the message (≤4 lines, ≤380pt). The pill border turns warning. Auto-hides after 8s.
EqualizerView (private)5 capsule bars, 3pt wide, 3pt gap, 20pt tall; bar height eases over 0.08s. The brand cyan accent fill.