diff options
| author | Solstice <solstice@local> | 2026-06-09 00:52:52 -0700 |
|---|---|---|
| committer | Solstice <solstice@local> | 2026-06-09 00:52:52 -0700 |
| commit | 3019f7ffda7d7c82cfd8b31ea7397b0ab528ec65 (patch) | |
| tree | d10073c6223faf003212da50aa4c4c7b7e1d3082 /src/components/AmbientControl.tsx | |
| parent | c973d48c41169240e3f53769804696fd0d352a09 (diff) | |
feat: ambient sound engine and volume controls
Diffstat (limited to 'src/components/AmbientControl.tsx')
| -rw-r--r-- | src/components/AmbientControl.tsx | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/components/AmbientControl.tsx b/src/components/AmbientControl.tsx new file mode 100644 index 0000000..5b5017a --- /dev/null +++ b/src/components/AmbientControl.tsx @@ -0,0 +1,98 @@ +import { ChangeEvent } from 'react'; +import { useAudioStore, type AmbientSound } from '../store/audioStore'; + +const SOUND_OPTIONS: Array<{ value: AmbientSound; label: string }> = [ + { value: 'none', label: 'Silent' }, + { value: 'rain', label: 'Rain' }, + { value: 'cafe', label: 'Cafe' }, + { value: 'white_noise', label: 'White Noise' }, +]; + +export function AmbientControl() { + const { available, sound, volume, setSound, setVolume } = useAudioStore(); + + const handleSoundChange = async (event: ChangeEvent<HTMLSelectElement>) => { + await setSound(event.target.value as AmbientSound); + }; + + const handleVolumeChange = async (event: ChangeEvent<HTMLInputElement>) => { + await setVolume(Number(event.target.value)); + }; + + return ( + <div + style={{ + display: 'flex', + alignItems: 'center', + gap: '12px', + padding: '8px 10px', + border: '1px solid var(--line-2)', + borderRadius: 'var(--r-3)', + background: 'rgba(17, 21, 28, 0.85)', + boxShadow: 'var(--shadow-1)', + }} + > + <div style={{ display: 'flex', flexDirection: 'column', gap: '2px', minWidth: '90px' }}> + <span className="eyebrow" style={{ letterSpacing: '0.14em' }}> + Ambient + </span> + <span + style={{ + fontFamily: 'var(--font-sans)', + fontSize: '12px', + color: available ? 'var(--fg-3)' : 'var(--negative)', + }} + > + {available ? 'Looping background audio' : 'Audio unavailable'} + </span> + </div> + + <select + value={sound} + onChange={handleSoundChange} + disabled={!available} + style={{ + fontFamily: 'var(--font-sans)', + fontSize: '13px', + color: available ? 'var(--fg-1)' : 'var(--fg-4)', + background: 'var(--ink-3)', + border: '1px solid var(--line-2)', + borderRadius: 'var(--r-2)', + padding: '6px 10px', + outline: 'none', + minWidth: '132px', + }} + > + {SOUND_OPTIONS.map((option) => ( + <option key={option.value} value={option.value}> + {option.label} + </option> + ))} + </select> + + <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}> + <input + type="range" + min={0} + max={1} + step={0.05} + value={volume} + onChange={handleVolumeChange} + disabled={!available} + style={{ width: '112px', accentColor: 'var(--brass)' }} + /> + <span + style={{ + fontFamily: 'var(--font-mono)', + fontSize: '12px', + color: available ? 'var(--fg-2)' : 'var(--fg-4)', + width: '36px', + textAlign: 'right', + }} + > + {Math.round(volume * 100)}% + </span> + </div> + </div> + ); +} |
