Sound setting
ProThe person's control for sound: "Sounds: Off / When I'm away / Always", the events as checkboxes with when each is raised and the visible cue it comes with (a sound is never the only signal), and a Play button for each that plays the brand's file or says no sound is set. It stores nothing: the choice goes back through onValueChange, and the app keeps it and passes it on as useAttention's sound.enabled, sound.when and sound.events. Nothing is picked until the person picks it: without a value it shows Off and no events. Turning sounds on selects nothing for the person: the control says "Choose the events to hear" until an event is ticked, and the first tick is a click, which is when the browser lets audio play, so that event plays once as a preview. A Play button whose file can't be loaded says so beside the event. The sound files are the brand's --cd-sound-<event> tokens; the sounds item installs a starter set.
<SoundSetting value={settings.sound} onValueChange={(sound) => setSettings({ ...settings, sound })} />Version 5.2.0· other versions
This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).
- 5.1.0
- 5.0.0
- 4.5.0
- 4.4.0
- 4.3.0
- 4.2.0
- 4.1.0
- 4.0.2
- 4.0.1
- 4.0.0
- 3.0.0
- 2.10.0
- 2.9.0
- 2.8.0
- 2.7.0
- 2.6.2
- 2.6.1
- 2.6.0
- 2.5.1
- 2.5.0
- 2.4.0
Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0 and 2.3.0: the page came later, or had another name then.
Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.
Preview
Loading the preview
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/sound-setting
Brings in: button, locale, locked, sound, tokens, use-attention, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import * as React from "react"
import { SoundSetting, soundOff, type SoundChoice } from "@/components/candor/sound-setting"
import { useAttention } from "@/lib/candor/use-attention"
import { soundEvents } from "@/lib/candor/sound"
import { Button } from "@/components/candor/ui/button"
/*
* Sounds are off until the person picks "When I'm away" or "Always"; that click is also what lets the browser play
* audio, so the first event plays once as a preview. The choice lives here, in the app (the kit stores none of it),
* and goes to useAttention as `sound`. In an app the files come from your brand file (`--cd-sound-needs-you:
* "/sounds/needs-you.mp3"`); this example sets the same names on the page while it is mounted, which is where the
* `sounds` starter set puts its files. Without them, Play says no sound is set.
*/
export default function Example() {
const [choice, setChoice] = React.useState<SoundChoice>(soundOff)
React.useLayoutEffect(() => {
for (const e of soundEvents) document.documentElement.style.setProperty(`--cd-sound-${e}`, `"/sounds/${e}.mp3"`)
return () => {
for (const e of soundEvents) document.documentElement.style.removeProperty(`--cd-sound-${e}`)
}
}, [])
const attention = useAttention({ sound: { enabled: choice.mode !== "off", when: choice.mode === "away" ? "away" : "always", events: choice.events } })
return (
<div className="flex max-w-xl flex-col items-start gap-3 text-sm">
<SoundSetting value={choice} onValueChange={setChoice} className="w-full" />
<Button size="sm" variant="outline" onClick={() => attention.signal({ kind: "approval", title: "Approve: raise the export batch size" })}>
An approval arrives
</Button>
<p className="text-xs text-muted-foreground">
The sound is never the only signal: the count here ({attention.count}) and the “(n)” in the page title say the same while you are away.
</p>
</div>
)
}<SoundSetting>
| Prop | Type | Default | Description |
|---|---|---|---|
| value | SoundChoice | undefined | soundOff | The person's choice as the app last stored it. Left out, the control shows Off with nothing chosen. |
| onValueChangerequired | (next: SoundChoice) => void | — | Called with the new choice when the person picks a mode or ticks an event. |
| events | readonly SoundEvent[] | undefined | soundEvents | The events this app offers; all five when left out. |
| className | string | undefined | — | Extra classes for the outer element. |
Values
soundOffThe state before the person has chosen anything: sounds off, no event picked.
SoundChoiceStrings
The fixed words, in English by default. Change any of them under soundSetting in <LocaleProvider strings={{ … }}>; the table is exported as soundSettingStrings.
| Key | English |
|---|---|
| label | Sounds |
| modeLegend | Sounds |
| modeOff | Off |
| modeAway | When I'm away |
| modeAlways | Always |
| eventsLegend | Play a sound when |
| turnOnFirst | Turn sounds on to choose which ones. |
| chooseEvents | Choose the events to hear. |
| loadFailed | The sound file couldn't be loaded. |
| play | Play |
| playEvent | Play the sound for: {event} |
| noSound | No sound set for this event |
| alsoShownAs | Also shown as: {cue} |
| needs-you | Something needs me |
| needs-you.when | A new approval, question or choice waits on me |
| needs-you.cue | the prompt or row, the (n) in the page title, a notification |
| finished | A run I started finished |
| finished.when | A run I started has finished |
| finished.cue | a toast, the result, the badge |
| failed | A run failed |
| failed.when | A run failed, or a guardrail stopped it |
| failed.cue | an error that stays on screen, the way to recover, a notice |
| reply | A person replied |
| reply.when | A person, not an agent, replied in a thread I'm in |
| reply.cue | the message and the badge |
| silent | A run has gone quiet |
| silent.when | A run I started hasn't been heard from for longer than it should |
| silent.cue | “No update since…” |
Types
- SoundMode
- When sounds play: never, only while the window isn't in front of the person, or whether it is or not.
"off" | "away" | "always"- SoundChoice
- What the person chose. The kit stores none of it: the app keeps it and passes it to `useAttention`'s `sound` option.
{ /** Whether and when sounds play. */ mode: SoundMode; /** The events the person left on. */ events: readonly SoundEvent[]; }
Types from other items
The props above take these. Each is defined by another item; import it from the path shown.
- SoundEvent from @/lib/candor/sound
- The five things the app may make a sound for. A closed list: `useAttention` plays each for the `AttentionKind` of the same name (`needs-you` for `approval`).
"needs-you" | "finished" | "failed" | "reply" | "silent"