# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Test Commands ```bash # Build everything gradle build # Run all tests gradle test # Run tests for a specific module gradle :parser:test gradle :layout:test gradle :renderer-pdf:test gradle :app:test # Run a single test class gradle :parser:test --tests ChordProParserTest # Run a single test method gradle :parser:test --tests "ChordProParserTest.parse complete song" # Build and run CLI gradle :cli:run --args="build -d /path/to/project" gradle :cli:run --args="validate -d /path/to/project" # Launch GUI gradle :gui:run ``` Requires Java 21 (configured in `gradle.properties`). Kotlin 2.1.10, Gradle 9.3.1. ## Architecture **Pipeline:** Parse → Validate → Measure → Paginate → Render `SongbookPipeline` (in `app`) orchestrates the full flow: 1. `ConfigParser` reads `songbook.yaml` → `BookConfig` 2. `ChordProParser` reads `.chopro`/`.cho`/`.crd` files → `Song` objects 3. `ForewordParser` reads optional `foreword.txt` → `Foreword` (if configured) 4. `Validator` checks config and songs 5. `MeasurementEngine` calculates each song's height in mm using `FontMetrics` 6. `TocGenerator` estimates TOC page count and creates entries 7. `PaginationEngine` arranges songs into pages (greedy spread packing) 8. `PdfBookRenderer` generates the PDF via OpenPDF **Module dependency graph:** ``` model ← parser model ← layout model ← renderer-pdf parser, layout, renderer-pdf ← app app ← cli (Clikt) app, parser ← gui (Compose Desktop) ``` `model` is the foundation with no dependencies — all data classes, the `FontMetrics` interface, and the `BookRenderer` interface live here. The `FontMetrics` abstraction decouples layout from rendering: `PdfFontMetrics` is the real implementation (in renderer-pdf), `StubFontMetrics` is used in layout tests. **Pagination constraint:** Songs spanning 2 pages must start on a left (even) page. The `PaginationEngine` inserts filler images or blank pages to enforce this. ## Key Types - `Song` → sections → `SongLine` → `LineSegment(chord?, text)` — chord is placed above the text segment. Also has `aliases`, `lyricist`, `composer`, `key`, `tags`, `notes: List`, `references: Map` (bookId → page), `capo` - `SongLine` — holds `segments` plus optional `imagePath` (when set, the line is an inline image) - `Foreword` — `quote`, `paragraphs`, `signatures` — parsed from a plain-text file - `PageContent` — sealed class: `SongPage`, `FillerImage`, `BlankPage`, `ForewordPage` - `SectionType` — enum: `VERSE`, `CHORUS`, `BRIDGE`, `REPEAT` - `BookConfig` — top-level config with `FontsConfig`, `LayoutConfig`, `TocConfig`, `ForewordConfig`, `ReferenceBook` list. `FontSpec.file` supports custom font files. `LayoutConfig.metadataLabels` (`"abbreviated"` or `"german"`) and `metadataPosition` (`"top"` or `"bottom"`) control metadata rendering - `BuildResult` — returned by `SongbookPipeline.build()` with success/errors/counts ## Song Format ChordPro-compatible `.chopro`/`.cho`/`.crd` files: directives in `{braces}`, chords in `[brackets]` inline with lyrics, comments with `#`. See `songs/` for examples. **Metadata directives:** `{title: }` / `{t: }`, `{alias: }`, `{lyricist: }`, `{composer: }`, `{key: }`, `{tags: }`, `{note: }`, `{capo: }` **Section directives:** `{start_of_verse}` / `{sov}`, `{end_of_verse}` / `{eov}`, `{start_of_chorus}` / `{soc}`, `{end_of_chorus}` / `{eoc}`, `{start_of_repeat}` / `{sor}`, `{end_of_repeat}` / `{eor}`. Section starts accept an optional label. `{chorus}` inserts a chorus reference, `{repeat}` sets a repeat label. **Notes block:** `{start_of_notes}` / `{son}` … `{end_of_notes}` / `{eon}` — multi-paragraph rich-text notes rendered at the end of a song. **Inline image:** `{image: path}` — embeds an image within a song section. **Reference:** `{ref: bookId pageNumber}` — cross-reference to a page in another songbook (configured in `reference_books`). ## Configuration `songbook.yaml` at the project root. Key options beyond the basics: - `fonts..file` — path to a custom font file (TTF/OTF) for any font role (`lyrics`, `chords`, `title`, `metadata`, `toc`) - `layout.metadata_labels` — `"abbreviated"` (M:/T:) or `"german"` (Worte:/Weise:) - `layout.metadata_position` — `"top"` (after title) or `"bottom"` (bottom of last page) - `toc.highlight_column` — abbreviation of the reference-book column to highlight (e.g. `"CL"`) - `foreword.file` — path to a foreword text file (default `./foreword.txt`) - `reference_books` — list of `{id, name, abbreviation}` for cross-reference columns in the TOC - `songs.order` — `"alphabetical"` or `"manual"` (file-system order) ## Test Patterns Tests use `kotlin.test` annotations with Kotest assertions (`shouldBe`, `shouldHaveSize`, etc.) on JUnit 5. Layout tests use `StubFontMetrics` to avoid PDF font dependencies. App integration tests create temp directories with song files and config. ## Package All code under `de.pfadfinder.songbook.*` — subpackages match module names (`.model`, `.parser`, `.layout`, `.renderer.pdf`, `.app`, `.cli`, `.gui`).