Commands, tooling, and workflows for developing the MT music player.
# Install runtimes
mise install
# Copy environment configuration (Last.fm API keys are optional)
cp .env.example .env
# Install dependencies
task deno:installWhen using agent orchestrators like Conductor or
Superset, each agent works in an isolated git worktree.
Git-untracked files (.env, node_modules/) are not copied into new worktrees
and must be restored via a setup script.
# Create a worktree on a new branch
git worktree add ../mt-feature -b feature-branch
# Enter the worktree
cd ../mt-feature
# Symlink .env from the parent repo (single source of truth)
ln -sf /path/to/mt/.env .env
# Install dependencies
task deno:installConductor exposes $CONDUCTOR_ROOT_PATH pointing to the parent repo.
Add a conductor.json at the repo root:
{
"scripts": {
"setup": "ln -sf \"$CONDUCTOR_ROOT_PATH/.env\" .env && deno install --node-modules-dir=auto --frozen",
"run": "deno run -A npm:@tauri-apps/cli dev",
"archive": ""
}
}Or for more control, create bin/conductor-setup:
#!/bin/sh
set -e
cd "$(dirname "$0")"/..
if [ -z "$CONDUCTOR_ROOT_PATH" ]; then
echo "Not running in Conductor, skipping workspace setup."
exit 0
fi
# Symlink .env from parent repo
if [ -f "$CONDUCTOR_ROOT_PATH/.env" ]; then
ln -sf "$CONDUCTOR_ROOT_PATH/.env" .env
else
echo "Warning: $CONDUCTOR_ROOT_PATH/.env not found."
echo "Create it from .env.example: cp .env.example $CONDUCTOR_ROOT_PATH/.env"
fi
# Install frontend dependencies
deno install --node-modules-dir=auto --frozenSuperset exposes $SUPERSET_ROOT_PATH pointing to the parent repo.
It creates a config.json under .superset/:
{
"setup": [
"ln -sf \"$SUPERSET_ROOT_PATH/.env\" .env && deno install --node-modules-dir=auto --frozen"
],
"teardown": []
}# Development mode with hot-reload
task tauri:dev
# Build the application
task buildThe project uses Taskfile for orchestrating workflows.
# Development
task lint # Run Rust and JS linters
task format # Run Rust and JS formatters
task test # Run Rust and JS tests
task test:e2e # Run Playwright E2E tests
task pre-commit # Run pre-commit hooks
# Building
task build # Build Tauri app for current arch
task build:arm64 # Build for Apple Silicon (arm64)
task build:x64 # Build for Intel (x86_64)
task build:timings # Analyze build performance bottlenecks (opens HTML report)
# Utilities
task install # Install project dependencies via devboxtask tauri:dev # Run Tauri in development mode
task tauri:dev:mcp # Run Tauri dev with MCP bridge for AI agent debugging
task tauri:build # Build Tauri app for current architecture
task tauri:build:arm64 # Build Tauri app for Apple Silicon
task tauri:build:x64 # Build Tauri app for Intel
task tauri:info # Show Tauri build configuration
task tauri:doctor # Check Tauri environment
task tauri:clean # Clean Tauri build artifactstask deno:install # Install dependencies via Deno (8x faster than npm ci)
task deno:lint # Run Deno linter
task deno:format # Format JS/TS code
task deno:test # Run Vitest unit/property tests
task deno:test:e2e # Run Playwright E2E tests
task deno:dev # Run Vite dev server via Deno
task deno:build # Build frontend via Deno (2.2x faster than npm)
task deno:clean # Clean deno artifactsWhen running task build, the following happens automatically:
deno:install- Install frontend dependenciescargo:install-sccache- Ensure sccache is available for build cachingtauri:build- Build Rust backend and bundle with frontend
# Install dependencies
deno install --node-modules-dir=auto --frozen # Frontend (8x faster than npm ci)
cargo build # Rust backend
# Fast syntax/type checking (no binary output, 2-3x faster than build)
cargo check --manifest-path crates/mt-tauri/Cargo.toml
cargo check --all-features
# Linting
deno lint # Frontend (Deno linter)
cargo clippy --workspace # Rust
# Formatting
deno fmt # Frontend (Deno formatter)
deno fmt --check # Check without changes
cargo fmt --all # Rust
# Tests
cargo nextest run --workspace # Rust (falls back to cargo test)
cd app/frontend && npx vitest run # Vitest unit/property tests
cd app/frontend && npx playwright test # Playwright E2E
# Pre-commit hooks
pre-commit run --all-files
# Clean build artifacts
cargo clean
rm -rf node_modules dist- Vite provides instant HMR for frontend changes
- Tauri dev mode auto-rebuilds Rust backend on changes
- Frontend changes reflect immediately without full app restart
- Backend changes trigger incremental rebuild and app restart
Audio playback only works in Tauri. When running the frontend in a standalone browser:
window.__TAURI__is undefined- Audio playback commands silently fail
- Use browser mode only for UI/styling work, not playback testing
- For playback testing, always use
task tauri:dev
| Package | Purpose |
|---|---|
| Tauri | Desktop application framework |
| Deno | Package manager and runtime |
| basecoat | Design system (Tailwind CSS) |
| Alpine.js | Lightweight reactive framework |
| Tailwind CSS | Utility-first CSS |
| Vitest | Unit and property testing |
| fast-check | Property-based testing |
| Playwright | E2E testing |
| Vite | Build tool and dev server |
| Crate | Purpose |
|---|---|
| tauri | Native system integration |
| rodio/symphonia | Audio playback |
| souvlaki | Media key integration |
| lofty | Audio metadata parsing |
| rusqlite | SQLite database |
| r2d2 | Database connection pooling |
| serde | Serialization |
| tokio | Async runtime |
| rayon | Parallel iteration |
| tracing | Structured logging |
# Frontend
npm install package-name
npm install --save-dev package-name
# Backend
cargo add crate-name
cargo add --dev crate-nameconsole.log('[Action]', 'play_track', { trackId, trackName });
console.error('[Error]', 'Failed to load track', { error, trackId });
console.debug('[IPC]', 'invoke', { command: 'play_track', args });use log::{info, warn, error, debug};
#[tauri::command]
fn play_track(track_id: String) -> Result<(), String> {
info!("Playing track: {}", track_id);
Ok(())
}Log levels: trace, debug, info, warn, error. Visible in terminal during cargo tauri dev.
- rust-analyzer: LSP for IDE integration
- cargo-watch: Auto-rebuild on changes:
cargo watch -x build - clippy: Rust linter:
cargo clippy - rustfmt: Rust formatter:
cargo fmt - cargo-expand: View macro expansions:
cargo expand
Convert screen recording to AVIF for README:
ffmpeg -i input.mp4 -c:v libsvtav1 -vf scale=-1:720 -crf 30 demo.avif# PNG frames at source resolution
ffmpeg -i video.mp4 -vf "fps=15" /tmp/frames/frame_%03d.png
# Downscaled to 720p
ffmpeg -i video.mp4 -vf "fps=15,scale=-1:720" /tmp/frames/frame_%03d.png
# Specific time range (2s to 5s)
ffmpeg -i video.mp4 -ss 2 -to 5 -vf "fps=15" /tmp/frames/frame_%03d.png
# JPEG for smaller files
ffmpeg -i video.mp4 -vf "fps=15" -qscale:v 2 /tmp/frames/frame_%03d.jpg