Repository navigation
Name the silent microphone on the pill - #243
Merged
Merged
Conversation
When the liveness gate fails closed, the pill said only "Error", with "The microphone didn't start." on hover — which sent users hunting for a Blurt bug when the cause was their audio setup (an aggregate whose USB interface was unplugged, leaving a silent BlackHole as the default input). The failure now carries the device name, and the pill says it: "AGGREGATE DEVICE ISN'T SENDING ANY AUDIO". - MicCaptureError.inputNeverDelivered(deviceName:), looked up only on the failure path; BlurtError drops the "Audio capture failed:" prefix for MicCaptureError, whose messages already read as sentences. - OverlayUIState.inputSilent owns the wording: pillNotice (subject + predicate) and the sentence form for VoiceOver/hover; 3 s dwell. - The overlay widens the panel for this notice only (centred, capped at 340 pt, clamped on screen), swaps under a panel fade because the window resizes a frame before SwiftUI redraws, and returns to the saved placement after. Long names truncate in the middle; the predicate never truncates and nothing shrinks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexkroman
enabled auto-merge (squash)
October 9, 2026 20:28
The new OverlayUIState.inputSilent case left three iPhone switches non-exhaustive. The iPhone has no widened pill, so it reads as an error whose message is the sentence naming the device. Also wraps an over-long .animation line for swift-format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the mic delivers only silence, Blurt's liveness gate fails closed (correctly). The pill said only ERROR, and the reason, "The microphone didn't start.", was only in a hover tooltip. Nothing on screen pointed at the actual cause, so it looked like a Blurt bug.
Seen in the field: the default input was an Audio MIDI Setup aggregate of a Focusrite Scarlett and BlackHole. With the Scarlett unplugged, only BlackHole was left in the aggregate, delivering -758 dBFS on every channel. Seven presses in a row failed, all with the same unexplained error.
Change
The pill now names the device: AGGREGATE DEVICE ISN'T SENDING ANY AUDIO.
Engine
MicCaptureError.inputNeverDelivered(deviceName:). The name is looked up (AudioInputDevices.inputName(pinnedUID:)) only on the failure path, so a successful press pays nothing extra.BlurtErrordrops the "Audio capture failed:" prefix forMicCaptureError, whose messages are already user-facing sentences. Other capture errors keep it.OverlayUIState.inputSilent(deviceName:), which owns the wording:pillNotice(subject + predicate) for the pill.errorand keeps the compact ERROR pill.Overlay (Mac shell)
withTransactiondoesn't reach this view, because the@Observablebridge is applied in a later update. A flag on the bridge carries "don't cross-fade this change" instead.NSHostingView.sizingOptions = []so the controller alone sizes the panel.Verification
swift test: 774 tests pass, including new ones for the message,pillNotice/pillText, the sentence form, and "only this notice writes on the pill".swift-format lint --strict,swiftlint --strictandcheck-invariants.shpass. The Debug app anddev-build.shboth build.swift6-concurrency-reviewer: no issues; its suggested comment on payload-blind error equality is added.macos-hig-reviewer: its truncation, shrink and edge-clamp findings are fixed.Not in this PR
The log line still says "input liveness unconfirmed … — proceeding" even though the press fails there. It's a one-word follow-up.
🤖 Generated with Claude Code