Imported from jamditis/audiobud (
AGENTS.md). Install upstream withnpx skills add jamditis/audiobud. Copyright stays with the author.
AGENTS.md
This file provides guidance to AI coding assistants working with code in this repository.
Development Commands
Prerequisites:
Core Development:
# Install dependencies
bun install
# Run in development mode
bun run tauri dev
# If cmake error on macOS:
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev
# Build for production
bun run tauri build
# Frontend only development
bun run dev # Start Vite dev server
bun run build # Build frontend (TypeScript + Vite)
bun run preview # Preview built frontend
Linting and Formatting (run before committing):
bun run lint # ESLint for frontend
bun run lint:fix # ESLint with auto-fix
bun run format # Prettier + cargo fmt
bun run format:check # Check formatting without changes
bun run format:frontend # Prettier only
bun run format:backend # cargo fmt only
Model Setup (Required for Development):
mkdir -p src-tauri/resources/models
curl -L -o src-tauri/resources/models/silero_vad_v4.onnx https://github.com/jamditis/audiobud/releases/download/model-assets-v1/silero_vad_v4.onnx
For detailed platform-specific build setup, see BUILD.md.
Architecture Overview
AudioBud is a cross-platform desktop speech-to-text application built with Tauri 2.x (Rust backend + React/TypeScript frontend).
Backend Structure (src-tauri/src/)
lib.rs- Main entry point, Tauri setup, manager initializationmanagers/- Core business logic:audio.rs- Audio recording and device managementmodel.rs- Model downloading and managementtranscription.rs- Speech-to-text processing pipelinehistory.rs- Transcription history storage
audio_toolkit/- Low-level audio processing:audio/- Device enumeration, recording, resamplingvad/- Voice Activity Detection (Silero VAD)
commands/- Tauri command handlers for frontend communicationcli.rs- CLI argument definitions (clap derive)shortcut/- Global keyboard shortcut handling, including the genericupdate_settingcommandsettings.rs- Application settings management:apply_setting_valuetype-checks a JSON value againstAppSettings,update_settingpersists it and runs that key's declared side effects, and a process-wide cache backsget_settingsso reads don't hit the store plugin every timeoverlay.rs- Recording overlay window (platform-specific)signal_handle.rs-send_transcription_input()reusable functionutils.rs- Platform detection helpersoutput_target.rs+output_target/backend.rs- Experimental target lock: pin transcript delivery to a chosen window (Windows, off by default). The platform-independent lock/unlock state machine, window-identity re-validation, and self-window exclusion live inoutput_target.rs; the focus-borrow paste (save foreground, activate the pinned window, paste, restore) is Windows-only, inbackend.rswindow_picker.rs+window_picker/backend.rs- Experimental one-shot window picker: route a single transcript to a chosen window without locking (Windows, off by default). Same split asoutput_target: platform-independent candidate filtering and pick lifecycle inwindow_picker.rs, window enumeration and the picker UI inbackend.rsoutput_profile.rs- Per-application output profiles: which profile applies to a delivery, and what that delivery's paste method, auto-submit, and clipboard handling become as a result. Profiles are hand-configured only and never written back into settingsdictation_context.rs- Per-dictation context: the output target and other delivery intent are captured once at recording start and carried unchanged to paste time, rather than re-read from live settingsdelivery_queue.rs- Bounded FIFO coordination for finished transcripts waiting on the delivery workerdelivery_worker.rs- The dedicated thread deliveries (pastes) run on, so a long paste -- especially a pinned target's foreground switch -- never blocks the overlay or tray. Panics in one delivery are caught so they can't take down the worker
Frontend Structure (src/)
App.tsx- Main component with onboarding flowcomponents/- React UI components:settings/- Settings UImodel-selector/- Model management interfaceonboarding/- First-run experienceoverlay/- Recording overlay UIupdate-checker/- App update notificationsshared/,ui/,icons/,footer/- Shared components
hooks/useSettings.ts- Settings state management hookstores/settingsStore.ts- Zustand store for settingsbindings.ts- Auto-generated Tauri type bindings (via tauri-specta)overlay/- Recording overlay window entry pointlib/types.ts- Shared TypeScript type definitions
Key Architecture Patterns
Manager Pattern: Core functionality organized into managers (Audio, Model, Transcription) initialized at startup and managed via Tauri state.
Command-Event Architecture: Frontend → Backend via Tauri commands; Backend → Frontend via events.
Pipeline Processing: Audio → VAD → Whisper/Parakeet → Text output → Clipboard/Paste
State Flow: Zustand → Tauri Command → Rust State → Persistence (tauri-plugin-store)
Technology Stack
Core Libraries:
whisper-rs- Local Whisper inference with GPU accelerationcpal- Cross-platform audio I/Ovad-rs- Voice Activity Detectionrdev- Global keyboard shortcutsrubato- Audio resamplingrodio- Audio playback for feedback sounds
Application Flow
- Initialization: App starts minimized to tray, loads settings, initializes managers
- Model Setup: First-run downloads preferred Whisper model (Small/Medium/Turbo/Large)
- Recording: Global shortcut triggers audio recording with VAD filtering
- Processing: Audio sent to Whisper model for transcription
- Output: Text pasted to active application via system clipboard
Settings System
Settings are stored using Tauri's store plugin with reactive updates:
- Keyboard shortcuts (configurable, supports push-to-talk)
- Audio devices (microphone/output selection)
- Model preferences (Small/Medium/Turbo/Large Whisper variants)
- Audio feedback and translation options
- Output targeting (target lock, output profiles) and delivery options
A single generic update_setting(key, value) command replaced roughly 33 bespoke per-setting commands. It type-checks the incoming value against AppSettings, persists it, and then runs that key's side effects from a declared table -- so writes are fallible (a failed persist is reported instead of silently applied) and always persist before their effects run. A few settings that need to prompt the user first (paste_method, external_script_path) or that live outside AppSettings keep their own dedicated commands.
Single Instance Architecture
The app enforces single instance behavior — launching when already running brings the settings window to front rather than creating a new process. Remote control flags (--toggle-transcription, etc.) work by launching a second instance that sends args to the running instance via tauri_plugin_single_instance, then exits.
Internationalization (i18n)
All user-facing strings must use i18next translations. ESLint enforces this (no hardcoded strings in JSX).
Adding new text:
- Add key to
src/i18n/locales/en/translation.json - Use in component:
const { t } = useTranslation(); t('key.path')
File structure:
src/i18n/
├── index.ts # i18n setup
├── languages.ts # Language metadata
└── locales/
├── en/translation.json # English (source)
├── de/, es/, fr/, ja/, ru/, zh/, ...
└── ...
For translation contribution guidelines, see CONTRIBUTING_TRANSLATIONS.md.
Code Style
Rust:
- Run
cargo fmtandcargo clippybefore committing - Handle errors explicitly (avoid unwrap in production)
- Use descriptive names, add doc comments for public APIs
TypeScript/React:
- Strict TypeScript, avoid
anytypes - Functional components with hooks
- Tailwind CSS for styling
- Path aliases:
@/→./src/
CLI Parameters
AudioBud supports command-line parameters on its retained platforms for integration with scripts, window managers, and autostart configurations.
Implementation: cli.rs (definitions), main.rs (parsing), lib.rs (applying), signal_handle.rs (shared logic)
| Flag | Description |
|---|---|
--toggle-transcription |
Toggle recording on/off on a running instance |
--toggle-post-process |
Toggle recording with post-processing on/off |
--cancel |
Cancel the current operation on a running instance |
--start-hidden |
Launch without showing the main window (tray icon visible) |
--no-tray |
Launch without system tray (closing window quits the app) |
--debug |
Enable debug mode with verbose (Trace) logging |
Key design decisions:
- CLI flags are runtime-only overrides — they do NOT modify persisted settings
- Remote control flags work via
tauri_plugin_single_instance: second instance sends args, then exits send_transcription_input()insignal_handle.rsis shared between signal handlers and CLI
Debug Mode
Access debug features: Cmd+Shift+D (macOS) or Ctrl+Shift+D (Windows)
Platform notes
- Windows x64: Validated public target. Release installers are signed, and local transcription uses Vulkan and DirectML acceleration.
- Apple Silicon macOS: v0.6.0 release-candidate target. Local transcription uses Metal, and microphone and Accessibility permissions are required. The public release does not include an Intel Mac artifact.
- Intel Mac: Inherited and unvalidated source path. No Intel artifact is planned.
- Linux and Nix: Retired from the maintained application scope. Do not restore Linux backends, bundles, or Nix hooks without a new approved validation proposal. See platform support. Linux-hosted frontend checks do not imply application support.
Troubleshooting
See the Troubleshooting section in README.md.
GitHub workflow for AI coding assistants
Before opening any PR or issue in this repo: read the relevant template file and fill in every section it lists. A generic Summary/Test-plan layout is not a substitute for the template.
- Opening a PR: Read
.github/PULL_REQUEST_TEMPLATE.mdand complete each section (Before submitting, Description, Related issues, Testing). If you skip a checklist item, say why in that section. - Opening an issue: Read
.github/ISSUE_TEMPLATE/. Usebug_report.mdfor bugs. AudioBud has GitHub Discussions disabled, so feature requests are filed as issues with theenhancementlabel (blank issues are enabled — see.github/ISSUE_TEMPLATE/config.yml). - Translations: Follow CONTRIBUTING_TRANSLATIONS.md.
- Full contributor workflow: CONTRIBUTING.md.
Commits: Use conventional commit prefixes (feat:, fix:, docs:, refactor:, chore:). Focus the message on why, not what.
Partner center submission notes
- For release-blocking Windows delivery fixes, test ordinary and target-locked delivery separately, then rebuild and install the candidate before opening a PR.
- For user-approved metadata-only release corrections, bypass the full PR review gate and avoid redundant CI reruns; verify the diff and final release SHA.
- On the Partner Center package validation page, expanded validation sections may have been opened by the user. Do not infer that automation expanded them or that their helper text is the final validation result. Wait for the overall package validation run to leave
In progressbefore deciding whether follow-up work is needed. - Joe's AudioBud workflow is press once to start recording, then press again to stop and send the transcript. Do not describe his workflow as "hold the hotkey." If docs need to describe default app behavior, verify the current
push_to_talkdefault first.
Commits and attribution
No AI attribution in commits, PR bodies, issues, docs, or code. .claude/settings.json disables automatic session links and blanks the commit and PR attribution strings. Web and Remote Control sessions can otherwise add that metadata by default. The setting lives in the repo rather than ~/.claude/settings.json because cloud sessions clone the repo and never read user-level config. Don't reintroduce any of it by hand.
No Co-authored-by trailers of any kind, including Joe's own aliases.
Git identity — set before committing, in every worktree and every agent session:
git config user.name "Joe Amditis"
git config user.email "6799804+jamditis@users.noreply.github.com"
Any other author email either trips GitHub's email-privacy push block (GH007) or makes a squash merge inject a Co-authored-by line into the merge body.