summaryrefslogtreecommitdiff
path: root/src/components/AmbientControl.tsx
diff options
context:
space:
mode:
authorSolstice <solstice@local>2026-06-09 00:52:52 -0700
committerSolstice <solstice@local>2026-06-09 00:52:52 -0700
commit3019f7ffda7d7c82cfd8b31ea7397b0ab528ec65 (patch)
treed10073c6223faf003212da50aa4c4c7b7e1d3082 /src/components/AmbientControl.tsx
parentc973d48c41169240e3f53769804696fd0d352a09 (diff)
feat: ambient sound engine and volume controls
Diffstat (limited to 'src/components/AmbientControl.tsx')
-rw-r--r--src/components/AmbientControl.tsx98
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>
+ );
+}