From 3019f7ffda7d7c82cfd8b31ea7397b0ab528ec65 Mon Sep 17 00:00:00 2001 From: Solstice Date: Tue, 9 Jun 2026 00:52:52 -0700 Subject: feat: ambient sound engine and volume controls --- src/store/audioStore.ts | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/store/audioStore.ts (limited to 'src/store') diff --git a/src/store/audioStore.ts b/src/store/audioStore.ts new file mode 100644 index 0000000..39cbf8b --- /dev/null +++ b/src/store/audioStore.ts @@ -0,0 +1,84 @@ +import { invoke } from '@tauri-apps/api/core'; +import { create } from 'zustand'; + +export type AmbientSound = 'none' | 'rain' | 'cafe' | 'white_noise'; + +interface AudioStatus { + available: boolean; + playing: boolean; + sound: AmbientSound | null; + volume: number; +} + +interface AudioStore { + available: boolean; + playing: boolean; + sound: AmbientSound; + volume: number; + fetchStatus: () => Promise; + setSound: (sound: AmbientSound) => Promise; + setVolume: (volume: number) => Promise; +} + +function normalizeSound(sound: AudioStatus['sound'], playing: boolean): AmbientSound { + if (!playing || sound === null) { + return 'none'; + } + + return sound; +} + +export const useAudioStore = create((set, get) => ({ + available: true, + playing: false, + sound: 'none', + volume: 0.5, + + fetchStatus: async () => { + try { + const status = await invoke('get_audio_status'); + set({ + available: status.available, + playing: status.playing, + sound: normalizeSound(status.sound, status.playing), + volume: status.volume, + }); + } catch (error) { + console.error('get_audio_status error:', error); + set({ available: false, playing: false, sound: 'none' }); + } + }, + + setSound: async (sound) => { + try { + if (sound === 'none') { + await invoke('stop_ambient'); + set({ playing: false, sound: 'none' }); + return; + } + + await invoke('play_ambient', { sound }); + set({ available: true, playing: true, sound }); + } catch (error) { + console.error('play_ambient error:', error); + set({ available: false, playing: false, sound: 'none' }); + } + }, + + setVolume: async (volume) => { + const nextVolume = Math.min(1, Math.max(0, volume)); + set({ volume: nextVolume }); + + try { + await invoke('set_ambient_volume', { volume: nextVolume }); + set({ available: true }); + } catch (error) { + console.error('set_ambient_volume error:', error); + set({ + available: false, + playing: false, + sound: get().sound, + }); + } + }, +})); -- cgit v1.3-2-g0d8e