Contributing
Layout
src-tauri/— the Rust backend (Tauri 2 + Tokio).src/— the React 19 + TypeScript renderer.scripts/— small dev/build helpers (the a11y audit lives here).
The docs site you're reading is under docs/ and ships as a VitePress build deployed to GitHub Pages.
Prerequisites
- Rust stable (whatever
rust-toolchainresolves to — currently no pinned version, just stable). - Node LTS (20.x or newer).
- macOS / Linux / Windows all build. Linux needs the Tauri system deps (
libwebkit2gtk-4.1-dev,libappindicator3-dev,librsvg2-dev,patchelf,libxss-dev).
Running the dev app
npm install # first time
npm run tauri dev # starts Vite + cargo, opens the appThe Tauri dev server hot-reloads both the React UI and (via cargo's watcher) the Rust code. Quitting the app stops the dev server.
Tests
# Rust (244 tests at the time of writing)
cargo test --manifest-path src-tauri/Cargo.toml --lib
# Frontend unit tests (vitest, 101 tests)
npm test
# Accessibility audit — full Vite build + Puppeteer + axe-core,
# every tab × light & dark scheme (14 audits total)
npm run audit:a11yLints and formatting
CI enforces all four. Run them locally before pushing:
cargo fmt --manifest-path src-tauri/Cargo.toml --check
cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
npx tsc --noEmitThe cargo step also runs cargo doc --no-deps with -D warnings so broken intra-doc links fail the build.
Branch / PR workflow
- Branch off
mainfor every change. PRs land via Squash & merge. - Feature branches use prefixes:
feat/...,fix/...,refactor/...,docs/...,chore/.... - Test plans in PR descriptions are not required — list verification steps in the body instead (what you ran, what you changed, what's new).
- CI runs on PR open and on every push to a PR branch: frontend job (tsc + vitest + a11y) and a Rust matrix (macOS / Ubuntu / Windows; fmt + clippy + test + doc).
Adding a Tauri command
Every IPC entry-point is a #[tauri::command] somewhere under src-tauri/src/scheduler/commands/. The pattern:
- Add the function to the right submodule (
settings.rs,breaks.rs,profiles.rs, etc.). - Add a
///doc comment — at minimum: what it does, what events it emits, what errors it returns. - Register it in
lib.rsinside thetauri::generate_handler![...]macro. - If the renderer needs to call it, add a wrapper in the relevant
views/settings/hooks/use-*.tsso the call stays out of components.
See the IPC contract for the existing surface.
Adding a setting
- Add the field to the Rust
Settingsstruct insrc-tauri/src/scheduler/settings.rs, with a sensible default in theDefaultimpl. - Add the matching field to the TS
SchedulerSettingstype insrc/views/settings/types.ts. - Render a control on the relevant tab under
src/views/settings/tabs/. - If you're adding a new field that the scheduler should react to, wire it into
run_loop.rsand add a test.
The serde defaults make missing fields harmless on older settings.json files, but the TS type drift is not enforced by anything yet (see #13 for the parity-test idea).
Adding a break suppression / guard
The 1Hz loop in src-tauri/src/scheduler/run_loop.rs consults each guard in order. To add one:
- Add the detection module (e.g.
dnd.rs,camera.rs,video.rs) or extend an existing one. - Add the
GuardReasonvariant insrc-tauri/src/stats.rs. - Hook the check into
run_loopbefore the fire-decision; if active, reset the per-kind timers, log vialog_suppressions, andcontinue. - Add a setting that gates it (see "Adding a setting" above) and surface it on the Quiet tab.
Filing issues
Use the GitHub issue tracker. Include the diagnostics report from Settings → About → Copy diagnostics report — it's a redacted snapshot of your settings, session stats, and the last 50 KB of logs.