Skip to content
Tier

Sound setting

Pro

The 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 })} />
Markdown
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).

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

With the Candor CLI

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

TSX · The example above
"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>

PropTypeDefaultDescription
valueSoundChoice | undefinedsoundOffThe 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.
eventsreadonly SoundEvent[] | undefinedsoundEventsThe events this app offers; all five when left out.
classNamestring | undefined—Extra classes for the outer element.

Values

soundOff

The state before the person has chosen anything: sounds off, no event picked.

SoundChoice

Strings

The fixed words, in English by default. Change any of them under soundSetting in <LocaleProvider strings={{ … }}>; the table is exported as soundSettingStrings.

KeyEnglish
labelSounds
modeLegendSounds
modeOffOff
modeAwayWhen I'm away
modeAlwaysAlways
eventsLegendPlay a sound when
turnOnFirstTurn sounds on to choose which ones.
chooseEventsChoose the events to hear.
loadFailedThe sound file couldn't be loaded.
playPlay
playEventPlay the sound for: {event}
noSoundNo sound set for this event
alsoShownAsAlso shown as: {cue}
needs-youSomething needs me
needs-you.whenA new approval, question or choice waits on me
needs-you.cuethe prompt or row, the (n) in the page title, a notification
finishedA run I started finished
finished.whenA run I started has finished
finished.cuea toast, the result, the badge
failedA run failed
failed.whenA run failed, or a guardrail stopped it
failed.cuean error that stays on screen, the way to recover, a notice
replyA person replied
reply.whenA person, not an agent, replied in a thread I'm in
reply.cuethe message and the badge
silentA run has gone quiet
silent.whenA 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"