Thanks for your interest in contributing to Vox! Here's how to get started.
Requires: cmake β needed to build the whisper.cpp binary on first install.
git clone https://github.com/app-vox/vox.git
cd vox
make install # installs npm deps + builds whisper.cpp
make dev # starts the dev server (also: make start)
make help # list all available make targetsRun all checks locally:
npm run typecheck
npm run lint
npm testUse Conventional Commits format:
type(scope): description
Types: feat, fix, refactor, chore, docs, test, perf, style
Examples:
feat(audio): add noise gate filterfix(shortcuts): prevent double-fire on togglerefactor(llm): extract retry logic
- Fork the repository
- Create a feature branch (
git checkout -b feat/my-feature) - Make your changes
- Ensure all checks pass
- Open a pull request against
main
Keep PRs focused β one feature or fix per PR.
MegaLinter runs automatically on every pull request and push to main. It is a required check β PRs cannot merge if it fails.
- Security: secret detection (Gitleaks, Secretlint), vulnerability scanning (Trivy)
- Code quality: ESLint (JS/TS), JSON validation, YAML linting, Markdown linting
npx mega-linter-runner --flavor javascriptReports are saved to the megalinter-reports/ directory (git-ignored).
When running npm run dev, a Dev Panel tab appears in the sidebar. It displays all runtime states and lets you override them (fake an update, revoke permissions, simulate offline, etc.) to test UI behavior without real conditions. Overrides are automatically cleared on app startup.
This panel is completely excluded from production builds. All entry points use import.meta.env.DEV guards with React.lazy() dynamic imports, so Vite's dead-code elimination removes the entire module tree (panel, store, styles) during npm run build. Zero dev bytes ship to users.
If you add new dev-only code, follow the same pattern: gate behind import.meta.env.DEV and use lazy(() => import(...)) β never static imports.
When introducing a new piece of shared state (used across multiple components), add it to the Dev Panel in src/renderer/components/dev/DevPanel.tsx. If the state is renderer-side and affects the UI, also add an override for it in the DevOverrides interface (src/renderer/stores/dev-overrides-store.ts) and wire it into the consuming component via useDevOverrideValue.
- TypeScript strict mode
- Code, comments, and documentation in English
- No unused imports or variables (enforced by ESLint)
Renovate Bot automatically manages npm dependency updates. It creates pull requests for outdated packages, grouped by category (Electron, React, AWS SDK, etc.).
- Dependency Dashboard: A GitHub issue tracks all pending, rate-limited, and ignored updates.
- Automerge: Patch updates and low-risk devDependency changes merge automatically when CI passes.
- Native modules (
whisper-node,koffi,uiohook-napi): These are never automerged and include a manual testing checklist β verify audio recording, shortcuts, and builds on macOS (arm64 and x64) and Windows. - Major updates: Always require manual review and are separated into individual PRs.
See renovate.json for the full configuration.
Use the Bug Report issue template.
Use the Feature Request issue template.