@webmusic/ui/recorder
mountRecorder() renders a required record toggle, an optional playback toggle, optional
format-specific export buttons, a compact input-level bar, and a status line. It paints
RecorderState; the caller remains responsible for media permission, capture, playback, file
creation, and persistence.
Use meter for continuous level or spectrum monitoring without a
capture workflow, or mixer for channel gain and routing controls.
The <score-recorder> and
<audio-recorder> elements compose this
presenter; mount it directly around a custom recorder state machine.
Press Record to run an asynchronous demo capture; choose exportFormats to add working download callbacks.
Import
Section titled “Import”import {mountRecorder} from '@webmusic/ui/recorder';
const handle = mountRecorder(host, { snapshot: () => store.recorder, toggleRecording: () => recorder.toggle(), subscribe: (notify) => store.subscribe(notify),});Async capture commands
Section titled “Async capture commands”Every command may return a promise. The presenter invokes it without owning a competing state
machine, then repaints after settlement. A synchronous throw or rejection from the current command goes
to onError; an older command can refresh accepted state but cannot report over a newer command.
Settlement after destruction or replacement is ignored. Errors from onError are contained.
Publish intermediate busy, recording, playing, level, and status snapshots through
subscribe() when the UI must change while work is pending. The common binding ownership and
same-host lifecycle are documented in the
shared presenter shape.
togglePlayback controls whether the Play button exists. Export buttons require both nonempty
exportFormats and an export(format) binding. busy or disabled blocks all commands.
Playback and export additionally use canPlay and canExport; an active take remains stoppable
even when canPlay becomes false. The presenter borrows all recorder state and resources.
API
mountRecorder(host, binding, options?) accepts an HTMLElement or ShadowRoot and returns a
RecorderHandle. The level is clamped to 0…1; omitting it hides the visual meter.
State
| Member | Kind | Description |
|---|---|---|
recording |
boolean |
Required. Selects Record versus Stop text, label, pressed state, and generated recording status. |
busy? |
boolean |
Strict true blocks every command and sets root/record aria-busy. |
playing? |
boolean |
Strict true applies the playback modifier, pressed state, Stop text and generated playback status. |
level? |
number |
Normalized input level written to --wui-recorder-level. Omission hides the meter. |
recordedCount? |
number |
Count printed by the generated ● recording… <count> status; defaults there to 0. |
takeCount? |
number |
Count printed by generated captured/playback status. When explicit availability is absent, any defined value enables play and export, including 0. |
status? |
string |
Overrides every generated status string. |
canPlay? |
boolean |
Explicit playback availability. When absent, falls back to whether takeCount is defined. |
canExport? |
boolean |
Explicit availability for every export button. When absent, falls back to whether takeCount is defined. |
disabled? |
boolean |
Strict true blocks all commands and applies root aria-disabled. |
Without status, the fallback order is recording count, playing-take text, captured-take text
when takeCount is defined, then Ready. Finite counts become nonnegative integers; invalid
counts are omitted. Finite levels clamp to 0–1 and invalid supplied levels paint as 0.
Binding
| Member | Kind | Description |
|---|---|---|
snapshot() |
() => RecorderState |
Required pull port read on mount, notification, command fulfillment, and explicit update(). Errors are sent to onError and leave the last successful paint. |
toggleRecording() |
() => void | Promise<void> |
Required command called by the record toggle in both directions. |
togglePlayback?() |
() => void | Promise<void> |
Optional command. Its presence creates the playback toggle. |
export?(format) |
(string) => void | Promise<void> |
Optional command receiving the clicked exportFormats id. |
subscribe?(notify) |
(notify: () => void) => () => void |
Optional subscription. Its disposer is called during destroy or same-host replacement. |
Options
| Member | Kind | Default | Description |
|---|---|---|---|
exportFormats |
readonly RecorderExportFormat[] |
[] |
Snapshotted {id, label} strings. With export(), creates one button per format; its id reaches the command and its label names the download. |
label |
string |
Recorder |
Accessible root group name. |
recordLabel / stopRecordingLabel |
string |
Record / Stop recording |
Accessible record-toggle names. |
playLabel |
string |
Play take |
Accessible playback-toggle name. |
stopPlaybackLabel |
string |
playLabel |
Optional name while playing. Set it explicitly, for example to Stop playback, to enable a distinct stop label. |
meterLabel |
string |
Input level |
Accessible progressbar name. |
exportsLabel |
string |
Export take |
Accessible export-group name. |
statusLabel |
string |
Recorder status |
Accessible live-status name. |
classNames / parts |
RecorderClassNames / RecorderParts |
{} |
Add classes or part tokens for root, record, play, meter, level, exports, export, and status, preserving canonical hooks. |
onError |
(error: unknown) => void |
— | Receives snapshot/current-command errors and errors retiring a prior mount. Callback faults are contained. Current mount setup and final cleanup failures can throw after best-effort rollback or release. |
stylesheet |
boolean |
true |
Installs the presenter’s stylesheet unless explicitly false, for a light-DOM host that cannot inject CSS into its page. |
Handle
| Member | Kind | Description |
|---|---|---|
element |
HTMLElement |
Presenter-owned .wui-recorder root. |
update() |
() => void |
Re-reads state and repaints every control; becomes a no-op after destruction. |
destroy() |
() => void |
Idempotently retires commands, removes listeners, disposes the subscription and removes root/style. Attempts every cleanup before rethrowing the first failure; retains borrowed recorder resources. |
Accessibility. The root is a named group. Record and playback are native buttons whose
pressed state follows capture/playback. Recording has a dynamic accessible name; playback
uses playLabel unless an explicit stopPlaybackLabel supplies its playing name. Export buttons
are named Download <label>.
The optional input level uses role="progressbar", bounds 0–1 and a percentage value text;
its decorative fill is hidden from accessibility. Status is a named, atomic, polite live region.
Busy/disabled state blocks every native command; keyboard focus remains visible.
Styling
Use the shared surface and palette tokens for
background (including transparent), foreground, accent, outer border and corner radius.
Presenter-specific tokens below take precedence within their documented roles.
The installed stylesheet resolves presenter-specific properties through shared --wm-* tokens
and then literals. --wui-recorder-level is presenter-owned output written on the level node, not
a theme input. The mount installs recorderStyle by default; set stylesheet: false to omit it
and supply the exported stylesheet or your own CSS from the host. Recorder has no classNames
or parts option; canonical classes, fixed compatibility classes, and canonical parts are always
installed.
Controls and the export group wrap within the host width. Export buttons have independent spacing and retain their full labels; long labels and status text can break across lines. The optional level meter stays hidden when the binding supplies no level. Width changes use CSS and do not remount the recorder or alter recording/playback state.
Tokens
| Property | Falls back to | Default | Controls |
|---|---|---|---|
--wm-recorder-padding |
literal | .6rem |
Root-inset compatibility fallback. |
--wm-recorder-height |
--wm-control-size → --wui-row-h |
2rem |
Height of every button and of the level bar, so the row reads as one strip. |
--wm-recorder-meter-width |
literal | 90px |
Level bar width. Its height follows the control size above. |
--wm-recorder-border |
--wm-border |
#d8d8d8 |
Root border. |
--wm-control-border |
--wm-border |
#d8d8d8 |
Shared non-primary control border fallback. |
--wm-border |
literal | #d8d8d8 |
Shared border fallback. |
--wm-recorder-background |
--wm-surface |
#fff |
Root background. |
--wm-surface |
literal fallback | #fff |
Shared root and button-surface fallback. |
--wm-recorder-text |
--wm-foreground |
#222 |
Root and inherited button text. |
--wm-foreground |
contextual literal | #222 text / #666 muted |
Shared foreground fallback. |
--wm-font-family |
--wm-font |
system-ui, sans-serif |
Root and button font family; --wm-font remains a compatibility fallback. |
--wm-recorder-radius |
--wm-control-radius |
0 |
Root and inherited button corners. |
--wm-control-radius |
literal fallback | 0 |
Shared corner fallback. |
--wm-recorder-button-border |
--wm-control-border → --wm-border |
#d8d8d8 |
Record, playback, and export button border. |
--wm-recorder-button |
--wm-surface |
#fff |
Unpressed button background. |
--wm-recorder-accent |
literal | #c0392b |
Pressed record background and level fill. |
--wm-recorder-accent-text |
literal | #fff |
Pressed record text. |
--wm-recorder-track |
literal | #ddd |
Compact level track background. |
--wm-recorder-status-font |
.76rem var(--wm-font-mono, ui-monospace, monospace) |
.76rem ui-monospace, monospace |
Complete status font shorthand. |
--wm-font-mono |
literal | ui-monospace, monospace |
Status font family when no complete shorthand is supplied. |
--wm-focus |
--wm-focus-ring |
Highlight |
2px keyboard-focus outline on record, playback and export buttons. |
--wm-recorder-muted |
--wm-foreground-muted |
#666 |
Status text color. |
--wm-foreground-muted |
literal fallback | #666 |
Shared muted-text fallback. |
Classes
| Node | Canonical class | Present when | Fixed compatibility class |
|---|---|---|---|
| Root | .wui-recorder |
Always | .wrap |
| Any action | .wui-recorder__button |
Record, playback, and every export | Format id is also a class on export buttons |
| Record | .wui-recorder__record |
Always | .rec; .armed while recording |
| Playback | .wui-recorder__play |
togglePlayback exists |
.play; .on while playing |
| Meter track | .wui-recorder__meter |
Always, hidden when level is absent | .meter |
| Level fill | .wui-recorder__level |
Always | .level |
| Export container | .wui-recorder__exports |
Export binding and formats exist | — |
| Status | .wui-recorder__status |
Always | .status |
Parts
| Node | Canonical part | Present when |
|---|---|---|
| Root | root |
Always |
| Record button | record |
Always |
| Playback button | play |
togglePlayback exists |
| Meter track | meter |
Always, possibly hidden |
| Level fill | level |
Always |
| Export group | exports |
Export binding and formats exist |
| Export button | export |
One per configured export format |
| Status | status |
Always |
classNames adds classes beside these canonical classes. parts adds tokens beside each
canonical part, with duplicate tokens removed. Accessible names use the corresponding label options.