Skip to content

Latest commit

 

History

History
184 lines (135 loc) · 8.69 KB

File metadata and controls

184 lines (135 loc) · 8.69 KB

Development

Guide to setting up the environment and working on RuVox.

Requirements

  • Linux (X11 or Wayland). macOS/Windows are not supported.
  • Nix (recommended) — provides a fully reproducible environment: Rust toolchain, Node, pnpm, Python 3.12, uv, libmpv, webkit2gtk, and Tauri's system libraries.
  • Without Nix — you'll have to manually install Rust stable + Node 20 + Python 3.12 + system deps (see buildInputs in nix/devshell.nix: webkitgtk_4_1, libsoup_3, gtk3, libmpv, pipewire/pulseaudio, libappindicator-gtk3, librsvg, pkg-config).

Environment

# Interactive shell
nix develop
pnpm install
pnpm tauri dev

# Or run a single command without entering the shell
nix develop -c pnpm install
nix develop -c pnpm tauri dev

Important: all commands (cargo, pnpm, uv, tauri, ruff, pytest) are only available inside nix develop. Don't run commands from an "already open" nix develop session after editing nix/devshell.nix — shellHook (including XDG_DATA_DIRS, GIO_EXTRA_MODULES, WEBKIT_DISABLE_DMABUF_RENDERER) is only executed when entering the shell. Each nix develop -c "..." forks a fresh subshell and gets the up-to-date env.

Project structure

/
├── src/                    # React + TypeScript frontend (Vite + Mantine 8)
│   ├── components/         # AppShell, QueueList, Player, TextViewer, icons
│   ├── dialogs/            # PreviewDialog (FF 1.1), Settings
│   ├── lib/                # tauri.ts (typed wrappers), markdown, html, mermaid, wordHighlight, errors
│   └── stores/             # Zustand store selectedEntry
├── src-tauri/              # Rust backend
│   ├── src/
│   │   ├── pipeline/       # Normalization: tracked_text, normalizers/, html_extractor, constants
│   │   ├── storage/        # JSON history + audio files (schema in storage/schema.rs)
│   │   ├── tts/            # TTS engines: piper, silero_native, ttsd subprocess manager
│   │   ├── player/         # tauri-plugin-mpv wrapper (ensure_mpv_alive, seek-suppress)
│   │   ├── commands/       # Tauri commands (#[tauri::command])
│   │   ├── tray/           # System tray (close-to-tray, "Выход")
│   │   ├── state.rs        # AppState
│   │   └── lib.rs          # Tauri::Builder entry point
│   └── tests/
│       ├── fixtures/pipeline/  # Golden fixtures (37 cases × 3 files)
│       └── golden.rs           # Golden integration test
├── ttsd/                   # Python subprocess (Silero TTS sidecar)
│   ├── pyproject.toml
│   └── ttsd/
│       ├── silero.py       # SileroEngine: load, synthesize
│       ├── timestamps.py   # Word timestamp estimation
│       ├── protocol.py     # Request/response types
│       └── main.py         # Main stdin→stdout JSON loop
├── silero-native/          # Native Silero v5 engine (ONNX Runtime, no Python)
├── docs/                   # Documentation (this directory)
├── scripts/                # Utilities (launch-prod, rebuild_prod)
├── nix/
│   └── devshell.nix        # Nix dev environment (Rust + Node + Python + Tauri deps)
└── flake.nix               # Flake (for nix build .#ruvox and nix develop)

Commands

Run

nix develop -c pnpm tauri dev               # dev mode with hot reload
nix build .#ruvox && ./result/bin/ruvox     # production binary

Tests

nix develop -c cargo test --manifest-path src-tauri/Cargo.toml                  # all Rust tests
nix develop -c cargo test --manifest-path src-tauri/Cargo.toml --test golden    # golden tests only
nix develop -c pnpm typecheck                                                   # TypeScript strict
nix develop -c bash -c "cd ttsd && uv run python -m pytest"                     # Python subprocess

Production build

nix build .#ruvox
./result/bin/ruvox

.#ruvox builds the slim Tauri release binary (Piper + native Silero), wraps it via wrapProgram (runtime LD_LIBRARY_PATH + GIO_EXTRA_MODULES), and puts mpv in PATH. The .#ruvox-with-silero variant additionally links ttsd (the Python Silero subprocess) into PATH.

First nix build run: the frontend derivation uses pnpm.fetchDeps with lib.fakeHash — Nix will fail with a hash mismatch and print the real hash; substitute it into flake.nix and rerun the build. This is the standard pnpm2nix procedure.

Code rules

General

  • Identifiers and comments are in English. User-facing strings (UI, notifications) are in Russian.
  • No emoji in code or commit messages.
  • Commit format: <type>(<module>): <short desc>, type ∈ {feat, fix, chore, refactor, docs, test, build}.
  • Forbidden: "Co-Authored-By: Claude …" or any mention of Claude in a commit.
  • Comments only when WHY is non-obvious (a hidden invariant, a workaround for a known bug). Don't comment WHAT.

Rust

  • Edition 2024 (MSRV 1.85).
  • tracing for logs, thiserror for domain errors, anyhow::Result only at boundaries.
  • unwrap is forbidden in production paths — use ? + typed errors.
  • cargo fmt and cargo clippy must be clean.

TypeScript / React

  • strict: true in tsconfig. No any unless absolutely necessary.
  • Functional components only. Don't use React.FC.
  • Hooks-first. No class components.
  • Prettier for formatting.

Mantine 8

  • Styling via CSS Modules and the classNames prop.
  • Forbidden: sx, createStyles, emotion, any Mantine 6/7 legacy.
  • Forms: @mantine/form (not react-hook-form, not Formik).
  • Notifications: @mantine/notifications.
  • Hooks: @mantine/hooks.
  • Modals: @mantine/modals (modals.openConfirmModal, etc.).

State

  • No Redux. Global state — Zustand or React context. By default — props + useState.
  • React Query is not needed — Tauri invoke fits well with useEffect + useState.

Routing

  • No router. Dialogs go through @mantine/modals or non-modal floating windows (react-rnd, see PreviewDialog).

Python (ttsd)

  • Python 3.12, uv-managed.
  • Logs to stderr, JSON requests on stdin, JSON responses on stdout.
  • ruff check and pytest must be green.

Debugging

Logs

  • Tauri (Rust) — via tracing to the process's stderr. In dev mode they're visible in the terminal where pnpm tauri dev is running.
  • ttsd — Python logs go to stderr → Rust proxies them to tracing::info!("ttsd: ...").
  • Frontend — webview DevTools (right click → Inspect Element in the application window).

Webview DevTools

In debug builds Tauri's webview enables DevTools. For prod builds you have to either explicitly allow them in tauri.conf.json or build with the devtools feature.

Reading the data directory

Storage lives in two per-user roots on Linux (on Windows both coincide with %LOCALAPPDATA%\com.ruvox.app\ — the bundle identifier dir, so the NSIS uninstaller's "Delete the application data" checkbox can remove it):

  • ~/.local/share/ruvox/history.json — list of TextEntry. You can open it manually.
  • ~/.local/share/ruvox/audio/{uuid}.opus — Ogg-Opus audio (32 kbps VOIP, mono). Plays in any modern player (mpv, VLC, browsers, ...).
  • ~/.local/share/ruvox/audio/{uuid}.timestamps.json — word timings.
  • ~/.config/ruvox/config.json — UIConfig.

Installs from before 2026-08 keep their files in ~/.cache/ruvox/; the first launch of a current build migrates them automatically (issue #222).

See Storage schema for details.

Workflow

git checkout -b feat/short-description     # separate branch
# ... edits ...
nix develop -c cargo test ...              # run tests
git commit -m "feat(<module>): <desc>"     # commit
git push -u origin feat/short-description  # push (after approval)

New features and fixes follow the standard feature-branch flow.

Git hooks (lefthook)

lefthook install runs from the devshell shellHook. Fast checks (fmt, ruff) gate the commit; heavy checks (clippy, typecheck, eslint, knip) gate the push.

Known upstream quirk: lefthook v2 computes pre-push files via git diff HEAD @{push}, and on the first push of a branch (no upstream yet) falls back to diffing against the local branch named by origin/HEAD — i.e. local main. If local main is absent (deleted, or checked out in another worktree/clone), every pre-push hook exits 128 and the push is rejected. The devshell shellHook keeps a local main ref around as a workaround — if you ever run the hooks outside nix develop and hit fatal: ambiguous argument 'main', create it manually: git branch --no-track main origin/main. Do not "fix" this with --no-verify.