Frontend Architecture
Tauri WebView + Vite multi-page React with Feature-Sliced Design
The WinSTT frontend is a Vite multi-page React 19 renderer running inside Tauri's WebView (WebView2 on Windows). Each Tauri window loads its own HTML entry; there is no router. The renderer holds no model or socket code — it reaches the Rust backend through Tauri commands and events.
One generated, typed boundary
The renderer reaches Rust through the tauri-specta-generated client in src/bindings.ts.
Every call site calls commands.* directly, so a Rust signature change breaks the
TypeScript build.
Process Architecture
Rust backend
All of the application's work lives in Rust (src-tauri/): audio capture, VAD, STT/TTS
inference, the LLM and cloud integrations, global hotkey, system tray, window management, and
settings/history persistence. See the backend architecture.
The native boundary
Renderer → Rust calls go through the generated commands.* client in src/bindings.ts.
Rust → renderer pushes go the other way as events: their names live once as Rust consts and
are mirrored for the renderer in src/shared/api/native-events.ts, with
src/shared/api/native-boundary.ts owning the Tauri listen/invoke boundary itself.
src/shared/api/ipc-client.ts is a re-export barrel over those modules.
Renderer (multi-window Vite)
Each Tauri window loads its own HTML entry — index.html for the main pill, windows/<page>.html
for the rest — and mounts one React tree from src/entries/<page>.tsx:
| Window | HTML entry | React entry |
|---|---|---|
| Main pill | index.html | entries/main.tsx |
| Settings | windows/settings.html | entries/settings.tsx |
| Model picker | windows/model-picker.html | entries/model-picker.tsx |
| History | windows/history.html | entries/history.tsx |
| Overlay | windows/overlay.html | entries/overlay.tsx |
| Tray menu | windows/tray-menu.html | entries/tray-menu.tsx |
| Onboarding | windows/onboarding.html | entries/onboarding.tsx |
Feature-Sliced Design Layers
src/
app/ Bootstrap, providers (IpcProvider, intl, stores), global styles
views/ One slice per window (main, settings, overlay, history, …)
widgets/ Composite blocks combining features and entities
features/ Reusable user interactions (push-to-talk, model-download, …)
entities/ Business domain objects (model-catalog, transcription, setting, …)
shared/ Foundation: api (ipc-client, adapter, bindings), ui, lib, config
i18n/ Locale messages (use-intl)
entries/ One .tsx per window (createRoot + render <View />)Import Contract
One direction only
Each layer may import only from the layers below it — never sideways, never upward. shared
sits at the bottom and depends on nothing; app sits at the top and may reach any layer.
| Layer | May import from |
|---|---|
app | all layers |
views | widgets, features, entities, shared |
widgets | features, entities, shared |
features | entities, shared |
entities | shared |
shared | nothing |
State Management
| Tool | Purpose |
|---|---|
| Zustand | Renderer client state (settings, model catalog, transcription) |
| Tauri commands + events | Source-of-truth data layer (no HTTP, no TanStack Query, no Redux) |
Settings are hydrated from winstt_get_settings; the update-settings feature listens for
the settings:changed event and fans out hot-swap commands (model reload, VAD slider,
autostart) so most changes apply without a restart.
Technology Stack
| Concern | Choice |
|---|---|
| Shell | Tauri 2 (WebView2 on Windows) |
| Package manager | Bun |
| Build tool | Vite 8 (renderer) + Cargo/Tauri (backend) |
| Language | React 19 + TypeScript (React Compiler in prod) |
| Linter / type check | Biome (via Ultracite) + tsc |
| Test runner | bun test |
| UI components | @base-ui/react (Base UI by MUI) |
| Styling | Tailwind CSS v4 + clsx + tailwind-merge + class-variance-authority |
| Icons | @hugeicons/react + @hugeicons/core-free-icons |
| Animation | motion (Framer Motion v12) |
| Forms | native <form> + useState + Zod safeParse (no form library) |
| i18n | use-intl (20 locales) |
LLM runs in the backend
Talking to Ollama/OpenRouter happens in the backend (LlmManager, ollama-rs + reqwest),
so the renderer ships no AI SDK — it just invokes process_text / process_transform.
Related
Backend architecture
Where audio capture, VAD, STT/TTS inference, and the LLM integrations actually run.
IPC commands
The typed command invokers the renderer uses to reach Rust — no HTTP, no sockets.
Events
The backend-to-renderer event stream that drives live previews and hot-swaps.
Dev setup
Clone, install with Bun, and run the multi-window renderer against the Rust backend.