Development¶
Setup¶
Rust (stable, 1.89 or newer), Node 22 with pnpm, just, and for the desktop app WebKitGTK 4.1,
libayatana-appindicator and librsvg. On Fedora-based systems (also Bazzite and Silverblue) a toolbox
keeps the host untouched:
git clone https://github.com/firsttris/pd200x-control && cd pd200x-control
bash packaging/toolbox.sh # creates the toolbox "pd200x-dev"
toolbox enter pd200x-dev
just setup # pnpm install
just # lint and every test
On Debian/Ubuntu: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev
libayatana-appindicator3-dev pipewire-bin.
just recipes¶
| Recipe | |
|---|---|
just dev |
the desktop app with hot reload, against the real microphone |
just dev-sim |
the same against a simulated microphone (PD200X_SIMULATOR=1) |
just dev-mock |
only the frontend in the browser, against the in-memory mock backend |
just lint |
cargo fmt --check, clippy with -D warnings, Biome, TypeScript |
just test |
Rust tests, Vitest with coverage, Playwright |
just bindings |
regenerate app/src/lib/bindings.ts after changing a Tauri command |
just screenshots |
the pictures in docs/ from the mock backend's demo data |
just docs |
this documentation on http://127.0.0.1:8000 |
just build / just install |
CLI and AppImage; install puts them into ~/.local/bin with a desktop entry |
Layout¶
| Path | |
|---|---|
crates/core |
protocol, device, level meter, presets, PipeWire, calibration, recording, settings, simulator |
crates/cli |
the pd200x command |
app/src-tauri |
Tauri commands (commands.rs), the app logic over the core (hub.rs), the tray |
app/src |
React frontend: routes/ (pages), components/, lib/ (bindings, queries, EQ math, mock backend) |
app/messages |
the German and English texts (Paraglide JS) |
packaging |
udev rule, desktop entry, icon, toolbox script |
docs |
this documentation and its pictures |
How the pieces fit is on How it works.
Tests¶
- Rust: the protocol, value encodings, presets and preset detection, calibration, WAV analysis
and settings as unit tests; the device against the simulator (
sim.rs, the sameTransporttrait as the real hidraw device); the CLI end to end against the simulator with its own config folder; the app's hub with a fake PipeWire and fakepw-record/pw-play. - Vitest: the EQ curve against known biquad responses, number formatting, the optimistic snapshot updates, the mock backend, and every page clicked through against the mock backend.
- Playwright: the frontend in Chromium (and WebKit in CI, like Tauri's WebKitGTK) against the mock backend: level meter, dragging sliders and EQ points, the level finder, profiles, light, the test recording, English and German.
- The bindings test fails when
bindings.tsno longer matches the Rust commands.
The mock backend (app/src/lib/mock/backend.ts) must behave like hub.rs: what one does, the other
does too. Presets, defaults and limits come from Rust as constants in the bindings, so neither side
repeats them.
Translations¶
Every text the UI shows is in app/messages/de.json and app/messages/en.json; components call
m.key(). A test fails when the two files do not have the same keys. Numbers are formatted for the
UI language (−6,4 dB / −6.4 dB).
Screenshots¶
The pictures in docs/ come from app/scripts/screenshots.mjs: the built frontend against the mock
backend, English UI, a fixed clock. After a change to the look run Actions → Update screenshots →
Run workflow; it takes them in the official Playwright image and commits the ones that changed.
just screenshots does the same locally.
Releases¶
Actions → Bump version raises the version in Cargo.toml and app/package.json, tags vX.Y.Z
and starts release.yml, which builds the CLI, the AppImage, the .deb and the .rpm and creates
the GitHub release with checksums.