|
| 1 | +# Contributing |
| 2 | + |
| 3 | +Thanks for looking. This repo is small on purpose, and most useful contributions are small too: a |
| 4 | +song, a voice, a grammar case, a doc fix. This page is the setup and the recipes. |
| 5 | + |
| 6 | +## Setup |
| 7 | + |
| 8 | +You need **Node 22+** and the [Tish](https://github.com/tishlang/tish) compiler, which comes in as a |
| 9 | +dev dependency. Chrome or Chromium is only needed for the WAV renderer. |
| 10 | + |
| 11 | +```bash |
| 12 | +git clone https://github.com/spacedevin/deck |
| 13 | +cd deck |
| 14 | +npm install |
| 15 | +npm test |
| 16 | +``` |
| 17 | + |
| 18 | +`npm test` builds the language package, runs the API and grammar suite with a 100% line-coverage |
| 19 | +gate, the conformance corpus, every song in `docs/EXAMPLES.md`, and the Tish and JS smoke tests. |
| 20 | + |
| 21 | +The workspaces have their own tests: |
| 22 | + |
| 23 | +```bash |
| 24 | +npm test -w @spacedevin/deck-player |
| 25 | +npm run build -w @spacedevin/deck-synths |
| 26 | +``` |
| 27 | + |
| 28 | +And the docs site, which is how you preview any markdown change: |
| 29 | + |
| 30 | +```bash |
| 31 | +npm run site:serve # http://localhost:4321 |
| 32 | +``` |
| 33 | + |
| 34 | +## How the repo is laid out |
| 35 | + |
| 36 | +| Path | Package | Owns | |
| 37 | +|---|---|---| |
| 38 | +| `/` | `@spacedevin/deck` | the language: tokenize, parse, registries, highlight. **No audio.** | |
| 39 | +| `packages/synths/` | `@spacedevin/deck-synths` | the 33 voices and the dispatch that picks one | |
| 40 | +| `packages/player/` | `@spacedevin/deck-player` | Song IR, defaults and clamps, transport, offline render, `<deck-player>` | |
| 41 | +| `conformance/` | — | the parse contract every implementation is checked against | |
| 42 | +| `crate/` | `deckfile` | **generated** from `src/` by `npm run build:rust`; never edit by hand | |
| 43 | +| `site/` | — | the docs-site generator; markdown is read in place from the paths above | |
| 44 | + |
| 45 | +Dependencies point one way: player → synths → deck. Each package has an `AGENTS.md` saying what |
| 46 | +belongs in it and what does not. Read the one for the package you're touching; the boundaries are |
| 47 | +the thing this repo cares most about. |
| 48 | + |
| 49 | +Tish is the source language and it has gotchas: there is **no `class` syntax** (the build emits JS |
| 50 | +that doesn't parse), and `undefined` is not a value under `tish run`. `packages/player/AGENTS.md` |
| 51 | +explains why the custom element is plain JS for that reason. |
| 52 | + |
| 53 | +## Commit messages |
| 54 | + |
| 55 | +Releases are cut by [sem](https://github.com/tishlang/sem) from Conventional Commits, so the type |
| 56 | +you pick decides whether a version ships: |
| 57 | + |
| 58 | +| Type | Effect | |
| 59 | +|---|---| |
| 60 | +| `feat:` | minor release | |
| 61 | +| `fix:`, `perf:` | patch release | |
| 62 | +| `feat!:` or a `BREAKING CHANGE:` footer | major release | |
| 63 | +| `docs:`, `chore:`, `ci:`, `test:`, `refactor:` | no release | |
| 64 | + |
| 65 | +Scope with the package when it helps: `feat(synths): …`, `fix(player): …`, `docs(examples): …`. |
| 66 | +A green `main` cuts a prerelease with all three tarballs; promoting it publishes to npm and crates.io. |
| 67 | +The PR title becomes the squash commit, so write it as the commit. |
| 68 | + |
| 69 | +## Recipes |
| 70 | + |
| 71 | +### Add a song to the examples |
| 72 | + |
| 73 | +1. Add a `deck` fenced block to `docs/EXAMPLES.md` under the right heading, with a sentence saying |
| 74 | + what it demonstrates. |
| 75 | +2. `npm run test:examples` — every block must parse without errors and produce at least one |
| 76 | + sounding channel. That is also the rule the site uses to decide whether to show a play button. |
| 77 | +3. `npm run site:serve` and press play on it. |
| 78 | + |
| 79 | +Grammar-reference snippets with `<placeholders>` belong in `docs/DECK_GRAMMAR.md`; complete songs |
| 80 | +belong in `docs/EXAMPLES.md`. |
| 81 | + |
| 82 | +### Add a voice |
| 83 | + |
| 84 | +1. Create `packages/synths/src/<Name>.tish` exporting `play<Name>(ctx, bus, t, midi, vel, durSec, ch, bendSemis)`. |
| 85 | + Build a short-lived Web Audio subgraph, connect its last node to `bus.input`, and disconnect the |
| 86 | + nodes once the tail has passed (the existing voices schedule that themselves; look at |
| 87 | + `GameBoyDmg.tish` for the shape). A voice may instead return `{ stopTime, disconnects }` and let |
| 88 | + the player prune it per step. |
| 89 | +2. Register it: an entry in `src/Registry.tish` (id, label, description, default `generatorParams`) |
| 90 | + and a case in `src/Dispatch.tish`. Param aliases or a `gen_block` dialect go in `src/DeckIds.tish`. |
| 91 | +3. Seed anything random. Two renders of one song must be identical. |
| 92 | +4. Add a song for it to `docs/EXAMPLES.md` (recipe above) and a line to the voices table in |
| 93 | + `packages/synths/README.md`. |
| 94 | +5. `npm test` and `npm test -w @spacedevin/deck-player`. |
| 95 | + |
| 96 | +### Change the grammar |
| 97 | + |
| 98 | +1. Change `src/deckfile/*.tish`. The parser is parse-only: no defaults, no clamping, no range checks. |
| 99 | + Those are host policy and belong in the player. |
| 100 | +2. Update `docs/DECK_GRAMMAR.md` — it is the canonical reference — and `docs/AST.md` if the shape changed. |
| 101 | +3. Regenerate the corpus with `npm run conformance:update` and **review the diff**. A new case means |
| 102 | + every profile in `conformance/profiles.json` must say where it stands. |
| 103 | +4. `npm run test:rust` to confirm the Rust emit still agrees. |
| 104 | +5. Keywords for highlighting live in `src/deckfile/Highlight.tish`; the site picks them up from there. |
| 105 | + |
| 106 | +### Add or fix a doc page |
| 107 | + |
| 108 | +The site is a view over the markdown already in the repo. Drop a `.md` under `docs/`, |
| 109 | +`packages/player/` or `packages/synths/` and it appears in the nav, in `llms.txt` and in |
| 110 | +`llms-full.txt` on the next build. There is no route to register. Don't add YAML frontmatter to a |
| 111 | +file that ships in an npm tarball (`README.md`, `AGENTS.md`); npm renders it as a stray heading. |
| 112 | +Use the per-section title override in `site/build.mjs` instead. |
| 113 | + |
| 114 | +## Pull requests |
| 115 | + |
| 116 | +- Keep a PR to one package where you can; the template asks which. |
| 117 | +- Tests pass, the conformance diff is reviewed if you touched the parser, and the docs say what the |
| 118 | + code now does. |
| 119 | +- No CHANGELOG edits: the release notes are generated from the commits. |
| 120 | + |
| 121 | +## Reporting a bug |
| 122 | + |
| 123 | +Open an [issue](https://github.com/spacedevin/deck/issues/new/choose). The most useful bug report |
| 124 | +is the smallest `.deck` that shows it, plus what you expected to hear or parse. If the parsers |
| 125 | +disagree with each other, that is a conformance case waiting to be written. |
0 commit comments