# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```sh
cargo build --release        # build
cargo test                   # run all tests (unit + integration)
cargo test <test_name>       # run a single test by name
cargo run -- --plantuml /path/to/plantuml.jar ./diagrams  # run locally
make build                   # test + release build
make docker-build            # build Docker image
make publish                 # full release: tag, build, publish crate + image
```

Log level is controlled via `RUST_LOG` (e.g. `RUST_LOG=debug`).

## Architecture

**pumlv** is a single-binary web server (Axum/Tokio) that renders `.puml` files to SVG by shelling out to `java -jar plantuml.jar -tsvg -pipe`. The frontend is HTMX + vanilla JS served as embedded static files (`include_str!`).

**Request flow:**
1. Browser loads `/` → `index.html` with an `__INITIAL_FILE__` placeholder substituted at startup
2. `GET /api/files` → returns an HTML fragment of `<a data-path="...">` links; HTMX swaps it into the sidebar
3. User clicks a file → JS calls `GET /api/render?f=<relative-path>` → server runs PlantUML, returns SVG
4. `GET /api/events` → SSE stream; server broadcasts a `change` event whenever a `.puml` file changes on disk (via `notify` crate), and the frontend auto-reloads

**Module responsibilities:**
- `config.rs` — CLI/env arg parsing (`clap`); resolves root dir and optional initial file from positional args
- `files.rs` — `list_puml_files` (walkdir) and `resolve_safe` (path traversal guard: string check + `canonicalize` + prefix check)
- `renderer.rs` — async subprocess: reads file → pipes to `java -jar plantuml.jar -tsvg -pipe` → returns SVG string
- `watcher.rs` — wraps `notify` in a `tokio::sync::broadcast` channel; only `.puml` create/modify/remove events are forwarded
- `routes.rs` — Axum handlers wired to the above; `AppState` holds `Arc<PathBuf>` root + jar path + broadcast sender
- `main.rs` — wires everything together; `build_router` is a public fn used by integration tests

**Static assets** (`static/`) are embedded at compile time via `include_str!`. `viewer.js` handles pan/zoom/minimap/export. `htmx.min.js` is vendored.

**Security invariant:** `resolve_safe` in `files.rs` must be the single gateway for any user-supplied path before it reaches the filesystem or PlantUML. It rejects `..`, absolute paths, non-`.puml` extensions, and symlink escapes via canonicalization.

**Testing:** Unit tests live in each source module. Integration tests in `tests/integration.rs` use `tower::ServiceExt::oneshot` to drive the Axum router without binding a port; the PlantUML jar is intentionally absent so render tests verify error handling only.
