# Contributing This is a one-person project, but issues and PRs are very welcome - fixes, docs and artwork most of all! | You have | Where it goes | | --- | --- | | A fix, a doc change, a bird you've cut | **A PR.** No issue needed. Ref an existing if relevant. | | Something is broken | **A [bug report](https://github.com/arnegiacomo/fugleramme/issues/new/choose)** | | An idea, a feature request, a setup question, showcase or anything else | **[Discussions](https://github.com/arnegiacomo/fugleramme/discussions)** | Never opened a PR before? [First Contributions](https://github.com/firstcontributions/first-contributions) walks through it step by step, and the [good first issues](https://github.com/arnegiacomo/fugleramme/labels/good%20first%20issue) are a good place to start. I'll add a few from time to time. Want to take one on? Comment on it first, so nobody else picks the same issue as you. The one thing worth asking about first is a big feature or change - start it in Discussions so you don't spend a weekend on something that's already half-designed or deliberately out of scope. ## Running it without a Pi Everything except the panel, the buttons and the mic runs on a workstation: ```bash uv sync uv run fugleramme-fake-detector # stand-in BirdNET-Go on :8090 uv run fugleramme-dev # :8080, restarts on save ``` Kiosk on `http://localhost:8080/`, admin on `/admin`. `uv run fugleramme-frame --preview out.png` renders the collage once and exits. The fake serves the same `/api/v2` endpoints the frame reads, over generated detections; `--auth` and `--down` reproduce a locked-down and an unreachable station. With `--birdweather` it is a BirdWeather station instead, at `http://127.0.0.1:8090/stations/1` on the Detector tab. To work against a real one instead, pass its address: `uv run fugleramme-dev --detector http://birdnet.local:8090`. `uv run fugleramme-check` says whether a detector answers everything the frame needs, and is the first thing to run when a page comes up empty. > [!NOTE] > I can only test on the hardware I have. If a change touches the panel, the > buttons or the install scripts, say in the PR what you ran it on. ## Before you open a PR CI runs these (good idea to run them yourself first): ```bash uv run ruff format uv run ruff check uv run mypy uv run pytest -q ``` If you touched `pyproject.toml`, commit `uv.lock` with it - CI installs with `uv sync --locked` and fails on a drifted lock. Whether you write the code yourself or use an LLM is up to you, as long as you can explain, understand and be accountable for it. ## Commit messages Conventional commits (with the issue number if applicable): ``` feat: #23 add a mic-less display mode docs: fix the passepartout measurements ``` The type decides what the next release bumps: | Type | Bump | | --- | --- | | `feat` | minor | | `fix`, `perf` | patch | | anything else (e.g. `docs`) | none | PRs are squashed, so the title is the message that counts. **Artwork is `chore`, not `fix`** - `chore(assets): add Sturnus unicolor`. A new bird isn't a change to the software, so it rides along in the next release without bumping the version. ## Artwork Half the point of this project is showing off amazing public-domain natural-history illustration, so every bird has to be cut from a real plate. Nothing AI-generated, and no scan its source says was restored or upscaled with AI. Retouching uses conventional tools - masking, levels, cloning the plate's own strokes - whether you or an agent runs them. A generative image model may only fill a small, masked spot of actual damage, like a hole or a stain, and the PR must disclose it. If the damage is too big for that, choose another plate. | Fine | Not fine | |---|---| | Cloning or healing a spot from the plate's own strokes | Handing a whole scan to Gemini or gpt-image to "fix" | | Rebuilding a twig hidden behind a foot from the plate's own strokes | Filling in behind a removed bird | | A mask from SAM or rembg | Upscaling with Topaz or Real-ESRGAN | | A masked generative fill over a small hole, disclosed | Redrawing texture or feathers | **A bird missing from `classic`** is the usual one - [Species coverage](https://arnegiacomo.dev/fugleramme/species/) shows the current coverage. Check the [open PRs](https://github.com/arnegiacomo/fugleramme/pulls) first, in case someone is already cutting it. Then cut it and open a PR. If you'd rather point at a plate than cut it yourself, open a [Missing bird](https://github.com/arnegiacomo/fugleramme/issues/new/choose) issue instead. **A whole new style** is the nicest thing you can contribute: its own folder under `assets/artwork/`, its own `ATTRIBUTION.md`, picked from the admin page. **Keep artwork PRs small** - around five birds each. Small PRs merge quicker and need fewer rounds of review, which is easier for both the contributor and the reviewer. If you've cut more, please split them into batches, and open the next one once the last has merged. I want to keep the bar for contributing low, and small PRs are help with that. Some things no tool can check, so they're what an artwork PR gets read for: whether the licensing is A-ok, the illustration looks good and fits the styles, and whether the cut-out blends nicely on the page. The rest is mechanical. `assets/artwork/custom/README.md` covers the file itself: a transparent WebP or PNG, named for the scientific name exactly as BirdNET-Go emits it (`assets/birdnet_labels_v2.4.txt`), with `-2`, `-3` for more of the same bird. A name that isn't an existing label fails the test suite. After cutting a bird, use the artwork tool to add it as an asset: ```bash uv run python tools/add_bird.py ~/Desktop/bird.png ``` It searches BirdNET species and existing artist/source keys, assigns the next variant filename, and updates the style manifest. `fzf` provides live search when installed. See [Adding artwork](docs/adding-artwork.md). `--preview` renders your cut-out on the page's paper before it is written, which is the quickest way to see whether the halo blends: ```bash uv run python tools/add_bird.py ~/Desktop/bird.png --preview /tmp/bird.png --dry-run ``` > [!WARNING] > Look at every cut yourself at full size before opening the PR. An agent's own checks passing isn't a review. > > ```bash > uv run python tools/preview_plates.py /tmp/preview assets/artwork/classic/birds/your-asset.webp > ``` Then, for the style folder it lands in: - **The licensing has to work.** Public domain, or terms compatible with the folder's own. `classic` is CC BY-SA 4.0. - **The folder's `manifest.json` names each file's source and links its plate** (`"bird.webp": {"source": "gould", "url": ...}`). A new file means a new entry. - **`ATTRIBUTION.md` gives each person one entry and one manifest key** (``Manifest key: `gould`.``), naming their works and each work's terms. A new person means a new entry - the test suite fails a manifest key no entry names. - **An entry describes works, not plates.** For each work: title, creators, dates, where the scans came from, terms. No volume or plate numbers, no per-plate engravers or printers - the manifest already links the plate page, which carries all of that. A bird from a work already listed touches `manifest.json` alone, and a new work by someone already listed goes in their entry under their key, so don't add a second key for the same person. ## Docs `docs/` is the manual for someone building and living with a frame. Internals and mechanism don't belong there. The install guide is written from macOS. If you set yours up from Linux or Windows, a PR extending it is very welcome. ## Licensing The code is MIT and contributions come in under the same licence - there's no CLA. For artwork, you're telling me you have the right to contribute the images.