# Contributing to Dune Weaver

Thanks for your interest in contributing to Dune Weaver! Whether it's a bug fix, a new feature, or improved docs, every contribution helps make kinetic sand tables more accessible.

If you have questions or ideas, join the [#dev channel on Discord](https://discord.com/channels/864079106424832021/1329553521032560791) or browse the existing [Issues](https://github.com/tuanchris/dune-weaver/issues).

## Development Setup

### Prerequisites

- Python 3.10+
- Node.js 18+ and npm
- Git

### Clone and install

```bash
git clone https://github.com/tuanchris/dune-weaver.git
cd dune-weaver

# Python dependencies (use nonrpi on a dev machine, full requirements.txt on a Raspberry Pi)
pip install -r requirements-nonrpi.txt

# Testing/dev extras
pip install -r requirements-dev.txt

# Frontend + root dependencies
npm install
cd frontend && npm install && cd ..
```

### Start the dev server

```bash
npm run dev
```

This runs **both** servers concurrently:

| Service  | URL                    | Notes                              |
| -------- | ---------------------- | ---------------------------------- |
| Frontend | `http://localhost:5173` | Vite dev server with hot reload   |
| Backend  | `http://localhost:8080` | FastAPI (proxied by Vite in dev)  |

Open `http://localhost:5173` in your browser. Vite proxies all API and WebSocket requests to the backend automatically.

## Project Structure

```
dune-weaver/
├── frontend/           # React 19 + TypeScript + Vite
│   ├── src/
│   │   ├── pages/      # Route-level page components
│   │   ├── components/ # Shared UI and feature components
│   │   ├── hooks/      # Custom React hooks
│   │   ├── lib/        # Utilities and API client
│   │   └── contexts/   # React contexts (multi-table, etc.)
│   └── package.json
├── modules/            # Backend modules
│   ├── core/           # Pattern/playlist managers, state, scheduling
│   ├── connection/     # Serial and WebSocket hardware communication
│   ├── led/            # WLED integration
│   └── mqtt/           # MQTT integration
├── patterns/           # .thr pattern files (theta-rho coordinates)
├── main.py             # FastAPI application entry point
└── package.json        # Root scripts (dev, build, prepare)
```

## Running Tests

### Backend (pytest)

```bash
pytest tests/unit/ -v
pytest tests/unit/ -v --cov       # with coverage
```

### Frontend (Vitest)

```bash
cd frontend && npm test           # single run
cd frontend && npm run test:watch # watch mode
cd frontend && npm run test:coverage
```

### End-to-end (Playwright)

```bash
cd frontend && npx playwright install chromium   # first time only
cd frontend && npm run test:e2e
cd frontend && npm run test:e2e:ui               # interactive UI mode
```

### Hardware Integration Tests (pytest)

Integration tests exercise real hardware — serial connections, homing, movement, and pattern execution. They are **skipped by default** and in CI.

```bash
# Run all integration tests (hardware must be connected via USB)
pytest tests/integration/ --run-hardware -v

# Run a specific suite
pytest tests/integration/test_hardware.py --run-hardware -v

# Show live output (useful for watching motor activity)
pytest tests/integration/ --run-hardware -v -s
```

| Test file | What it covers | Approx. duration |
| --------- | -------------- | ---------------- |
| `test_hardware.py` | Serial connection, homing, movement, pattern execution | ~5–10 min |
| `test_playback_controls.py` | Pause, resume, stop, skip, speed control | ~5 min |
| `test_playlist.py` | Playlist modes, clear patterns, state updates | ~5 min |

> **Safety:** These tests physically move the table. Make sure the ball path is clear and the table is powered on before running them.

## Linting

### Python — Ruff

```bash
ruff check .                # check for issues
ruff check --fix .          # auto-fix what it can
```

### Frontend — ESLint

```bash
cd frontend && npm run lint
```

### Pre-commit hook

A pre-commit hook runs Ruff on staged Python files and Vitest on staged TypeScript files automatically. It's installed when you run `npm install` at the repo root (via the `prepare` script). You can also install it manually:

```bash
cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
```

## Branch and Commit Conventions

### Branching

Create branches from `main` using these prefixes:

- `feature/` — new functionality (e.g., `feature/pattern-editor`)
- `fix/` — bug fixes (e.g., `fix/playlist-reorder`)
- `chore/` — maintenance, tooling, docs (e.g., `chore/update-deps`)

### Commit messages

We follow [Conventional Commits](https://www.conventionalcommits.org/):

```
feat(ui): add dark mode toggle to settings page
fix(backend): prevent crash when serial port disconnects
chore: update Python dependencies
```

## Adding API Endpoints

When you add a new backend endpoint, you **must** also register its path in the Vite proxy so it works during development:

1. Add the route in `main.py` (or the appropriate module).
2. Open `frontend/vite.config.ts` and add the endpoint path to the `server.proxy` object:
   ```ts
   '/my_new_endpoint': 'http://localhost:8080',
   ```
3. Restart the Vite dev server (`npm run dev`).

> **Tip:** Endpoints under the `/api` prefix are already proxied by a single rule, so prefer using `/api/...` paths for new routes.

## Submitting a Pull Request

1. **Fork** the repository and clone your fork.
2. Create a branch from `main` (see [Branch and Commit Conventions](#branch-and-commit-conventions)).
3. Make sure all tests pass and linting is clean:
   ```bash
   ruff check .
   cd frontend && npm run lint && npm test && cd ..
   pytest tests/unit/ -v
   ```
4. Push your branch to your fork and open a PR against `main` on the upstream repo.
5. Fill in a clear description of **what** changed and **why**.
6. CI will run backend tests, frontend tests, E2E tests, and Ruff lint automatically.

Thanks for helping make Dune Weaver better!
