The menu gains a third group with the sound-effect volume slider, reading and
writing the same stored value as the slider in settings, so the next effect
plays at the new level. The menu is bounded to the height Radix reports for it
and only the device lists scroll; the heading and the slider stay in place.
Every control is now reachable by keyboard alone. Radix swallows Tab inside
its menus so that the arrow keys walk the items; the audio menu keeps Tab
from it, letting the browser move focus from the device rows to the meter and
on to the slider, while the menu's focus trap keeps that order inside the
menu. The slider's own keys stop at the slider so the menu does not treat
them as navigation. The meter keeps its live-region text on blur: removing it
handed the focus trap an empty active element mid-Tab and pulled focus back
into the menu.
Spec: FEATURES_SPEC/2026-09_Audio_Quick_Menu.md, slice 4 — AC4, AC16, AC17,
AC20, AC21 (manual), AC22, AC27.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The meter sits at the foot of the microphone group and follows the selected
microphone. Its capture starts when the menu opens and stops when it closes,
muted or not, so the microphone is only held while the user is looking at the
level. A denied permission shows a hint in place of the meter.
Keyboard navigation marks the current menu item with a focus border; pointer
use gets the hover background only, since the menu moves DOM focus to
whatever the pointer is over.
Spec: FEATURES_SPEC/2026-09_Audio_Quick_Menu.md, slice 3 — AC9, AC10, AC12,
AC14, AC23, AC24 (manual), AC26.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
useMicrophoneLevel opens a capture of the given microphone for as long as it
is enabled, reports a smoothed 0..1 level per animation frame, and tears the
capture down on disable, device change or unmount, including while the
capture is still being acquired. A denied permission and a microphone that
cannot be opened are reported as distinct states.
AudioLevelMeter renders that state as a row of bars, focusable and announcing
its state to a screen reader only while focused, with a hint in place of the
meter when permission is denied and a greyed-out meter when the microphone is
unavailable. Nothing renders it yet; the audio menu picks it up next.
Spec: FEATURES_SPEC/2026-09_Audio_Quick_Menu.md, slice 2 — AC8, AC11, AC13,
AC15.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The chevron beside the microphone button now opens a menu headed "Audio
controls" that lists the microphones and, below a separator, the audio
outputs. Selecting either switches the device and keeps the menu open.
Where the browser offers no choice of output, or only one, the speaker
group still names the output in use as a non-selectable row.
The footer view model derives the outputs from the same MediaDevices
helper as the device lists, so the menu reaches both surfaces at once:
in a call on desktop, and before joining on every platform.
Spec: FEATURES_SPEC/2026-09_Audio_Quick_Menu.md, slice 1 — AC1, AC2, AC3,
AC5, AC6, AC7, AC25.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The spec, its product sidecar and its notes, as of the exploration branch
fkwp/feature/audio_quick_menu_exploration at 39d2ffbf where it reached status
implement, plus the drift log this implementation appended: the drift check
against a fresh main, the re-derived slicing plan, the owner's decision to
land the feature as one PR, and the findings from building it.
The commits that follow are its slices, in the order the plan gives.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FEATURES_SPEC/AGENTS.md describes how an exploration is distilled into a
feature spec and how a spec is turned into PRs against a current main; the
product spec template is the sidecar a brainstorming session fills in.
The folder is excluded from oxfmt: a product spec is immutable once a spec
leaves draft, and the process documents are prose in their own style.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codifies what is currently tribal knowledge or only discoverable by reading CI
config: the view model / view contract, the rule that nothing reads the page,
Clean Code ordering, the three test layers, and reuse-before-you-build.
The root AGENTS.md is the always-loaded minimum and routes to docs/agents/, so
an agent loads only the detail its task needs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>