State Management (Zustand & Immer)
Relevant source files
The following files were used as context for generating this wiki page:
State management in mausVoice follows a "TypeScript is the Brain" architectural pattern. The application utilizes a single Zustand store combined with Immer to maintain a single source of truth for the entire application state. This centralized approach ensures that business logic remains within the TypeScript layer while the Rust layer acts primarily as a capability API.
The state management system is responsible for orchestrating data flow between user interface events, business logic actions, and underlying data repositories that resolve to local SQLite storage or AI providers.
Sources: llms.txt:23-31, CLAUDE.md:21-25, apps/desktop/README.md:43-46
Architectural Pattern
The application implements a unidirectional data flow where React components interact with the Zustand store primarily through Actions. These actions encapsulate business logic, compose utility functions, and call into Repositories to handle persistence or external service interactions.
Data Flow Overview
The following diagram illustrates the flow from a user event to state update and persistence:
The flow ensures that TypeScript owns all decision-making processes.
Sources: llms.txt:23-31, CLAUDE.md:26-28, apps/desktop/README.md:43-46
Components of State Management
The system is divided into several logical layers to separate concerns and ensure maintainability.
| Component | Responsibility | Location |
|---|---|---|
| Zustand Store | The single source of truth for application data. | src/state/ |
| Immer | Enables immutable state updates using mutable-style syntax. | apps/desktop/package.json |
| Actions | Orchestrate business logic and state transitions. | src/actions/ |
| Repositories | Handle data access (SQLite, AI providers). | src/repos/ |
| Hooks | Provide reactive access to state for UI components. | src/hooks/ |
Sources: llms.txt:90-93, CLAUDE.md:26-28, apps/desktop/README.md:32-41
Implementation Details
Zustand & Immer Integration
The application uses a single Zustand store, often structured into slices, and utilizes Immer to simplify complex state updates. By using Immer, developers can write logic that looks like it is mutating state, while Immer ensures a new immutable state object is produced.
// Example of accessing state in a component
const activeToneId = useAppStore((state) => {
return state.userPrefs?.activeToneId ?? null;
});
// Example of triggering an action that updates state
const handleActiveToneChange = useCallback((toneId: string | null) => {
void setActiveTone(toneId);
}, []);
Sources: apps/desktop/src/components/styling/AppStylingLayout.tsx:24-33, llms.txt:90-93
Repository Interaction
Repositories (/src/repos/) define interfaces (e.g., BaseRepo) and provide local implementations (e.g., LocalRepo). They serve as the bridge between the Zustand actions and the Tauri command layer. Data entering or leaving the Tauri boundary is often converted using helper functions like toLocalXxx() or fromLocalXxx().
Sources: CLAUDE.md:26-30, AGENTS.md:26-30
State Persistence and Repositories
In the mausVoice architecture, the state is often persisted to a local SQLite database or synchronized with AI providers. Repositories are the designated layer for this resolution.
- Local Storage: Resolved via SQLite migrations located in
src-tauri/src/db/migrations/. - AI Providers: Repositories resolve to either local (Whisper) or cloud-backed (Deepgram, Groq) providers.
- Encryption: Sensitive data like API keys are stored encrypted on disk using XChaCha20-Poly1305, managed through the repository layer.
Sources: llms.txt:59-66, CLAUDE.md:29-33, AGENTS.md:29-33
Summary
State management in mausVoice centralizes business logic within a TypeScript-based Zustand store enhanced by Immer. This architecture adheres to the principle that "Rust is the API, TypeScript is the Brain," ensuring native capabilities are managed by Rust while the application's state transitions and decision logic remain reactive and maintainable in the frontend layer.
Sources: llms.txt:23-28, apps/desktop/README.md:43-46