diff --git a/CHANGELOG.md b/CHANGELOG.md index 17eb8177..c495bec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,21 @@ follow semantic versioning; release dates are ISO 8601. ### Documentation +- **The template-authoring guide describes the packages that exist.** It told a + contributor to put a new family under `templates..v2` in exactly five + sub-packages, one of them a per-family `theme/` — a layout 2.0 replaced. A family has + `data` / `components` / `widgets` / `presets`, the cosmetic tokens are the shared + `BrandTheme`, and the rule about not editing the v1 surface outlived that surface. The + contributing guide had the same problem in one paragraph, routing new template code + into `templates.builtins` and `templates.support`; both are gone, so the instruction + produced code that does not compile. Package names join the retired-surface guard, + which until now could only see types. +- **Three documents stop pointing at things that are not there.** The extension guide + sent a reader to a snapshot-test directory that does not exist, the layout-snapshot + page listed two tests among six that are not in the repository, and the docs index + still carried a rework warning for a page reworked a major release ago. The roadmap + asked for a warning on dropped DOCX content that the backend already logs; what it is + really asking for — a mode that refuses instead of dropping — now says so. - **The example catalogue stops offering a type that ships nowhere.** Its entry-point table introduced the cover letter as a `BusinessTheme.modern()` document, and `BusinessTheme` is a record local to the examples module — a reader adding the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f47981ff..ff75325d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -120,7 +120,7 @@ The 2.0 GA shipped, so the branches now hold their long-term roles: - `render-docx/` — module **graph-compose-render-docx** Semantic exporter `DocxSemanticBackend` (Apache POI based), under `com.demcha.compose.document.backend.semantic.docx` - `templates/src/main/java/com/demcha/compose/document/templates/*` — module **graph-compose-templates** - Built-in templates (CV, cover letter, invoice, proposal, weekly schedule), DTOs, themes, registries, and scene composition helpers + Template families (CV, cover letter, invoice, proposal), their data records, the shared `BrandTheme`, and the widgets they compose from. Schedule data records live here too; the schedule document is composed by an example rather than by a template. - `core/src/main/java/com/demcha/compose/document/showcase` `FontShowcase` (bundled-font preview renderer) — stays in the core engine - `core/src/main/java/com/demcha/compose/engine/*` @@ -176,12 +176,13 @@ template feature. The rules: - Layout integration for a new node is a `NodeDefinition` registered with `NodeRegistry`. See `BuiltInNodeDefinitions` for the established pattern. -- Built-in templates in `...document.templates.builtins` stay thin - public facades over reusable scene composers in - `...document.templates.support`. Keep PDF-only setup in the document - session/backend layer rather than inside template composers, and do - not import `PDDocument`, `PDPage`, `PDRectangle`, or low-level PDF - composer types into scene composer classes. +- A template family lives under `...document.templates.` with + `data` / `components` / `widgets` / `presets`; cosmetic tokens are the + shared `BrandTheme` in `...document.templates.core.theme`, never a + package of the family's own. Keep PDF-only setup in the document + session/backend layer rather than inside a preset, and do not import + `PDDocument`, `PDPage`, `PDRectangle` or other low-level PDF types into + template code. - Public template contracts are compose-first: prefer `compose(DocumentSession, ...)`. New README snippets, runnable examples, and integration docs must show `compose(...)` rather than @@ -275,9 +276,9 @@ There is one template authoring pattern, whether you are adding a new family or a new preset inside an existing one: the layered architecture documented in [**docs/templates/v2-layered/contributor-guide.md**](./docs/templates/v2-layered/contributor-guide.md). -Five sub-packages (`data/` / `theme/` / `components/` / `widgets/` -/ `presets/`), each with a clear contract, over the shared -`templates.core` layer. +A family has `presets/` and adds `data/`, `components/` and `widgets/` +as it needs them, each with a clear contract, over the shared +`templates.core` layer that owns the theme. - Every preset is a `public final class` — no inheritance — with a `create(BrandTheme)` factory returning `DocumentTemplate`, plus a diff --git a/ROADMAP.md b/ROADMAP.md index 98fa0d4d..a51cf188 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -37,7 +37,7 @@ Committed internal direction for the post-2.0 line: refactors, scale work, and t Not committed. Reflects current thinking; priorities may shift based on user feedback and adoption signals. - **PPTX beyond beta.** Graduate the fixed-layout PPTX backend from `@Beta` to stable and close the remaining fidelity gaps — true vector clipping instead of the raster fallback ([#413](https://github.com/DemchaAV/GraphCompose/issues/413)), exact numeric dash arrays, and distinct per-corner radii. -- **DOCX visibility for unsupported nodes.** Make currently-silent skips (`shape`, `line`, `ellipse`, `barcode`) loud — minimum a warn log, ideally a strict-mode flag that fails instead of dropping content silently. +- **A strict mode for DOCX.** Dropping a node the format cannot carry (`shape`, `line`, `ellipse`, `barcode`) now warns, so the loss is at least visible in the log. What is missing is the option to refuse: a flag that fails the export instead of silently producing a document with content gone. - **Block-level alignment for fixed-size flow children.** Paths, images, layer stacks, shape containers and barcodes currently left-align in a flow; centring one means wrapping it in a full-width `ShapeContainer` just to use its CENTER anchor. Add a per-node horizontal align (left / centre / right — the `margin: auto` / `align(center)` analogue) so a fixed box can place itself in the flow directly. Surfaced by the v1.8 SVG icon-gallery and feature-catalog work. - **Backend-neutral layout measurement.** Decouple measurement from PDFBox-specific resources so non-PDF backends do not pull PDFBox into the dependency graph. - **DOCX maturity.** Either expand DOCX coverage toward PDF parity, or move DOCX behind an explicitly experimental flag. diff --git a/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java b/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java index 0ba64a6d..a97156d5 100644 --- a/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java +++ b/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java @@ -29,13 +29,14 @@ class CanonicalSurfaceGuardTest { "ModuleSummary"); /** - * Types the 2.0 line removed. Every one is absent from every {@code src/main} - * tree, so a contributor-facing document naming one is teaching code that cannot - * compile — which is how the template-authoring and engine-primitive sections of - * CONTRIBUTING went stale for a full release cycle. + * Names the 2.0 line removed — types, and the packages that held them. Every one is + * absent from every {@code src/main} tree, so a contributor-facing document naming + * one is teaching code that cannot compile — which is how the template-authoring + * and engine-primitive sections of CONTRIBUTING went stale for a full release + * cycle, and how two dead package names outlived the types that lived in them. * *

Deliberately absent: {@code BusinessTheme} survives as an examples-local - * theme helper used by twenty-one example sources, and {@code PptxSemanticBackend} + * theme helper the example catalogue still uses, and {@code PptxSemanticBackend} * still ships beside the fixed-layout PPTX backend. Forbidding either would fail * the build on text that is correct.

*/ @@ -49,7 +50,19 @@ class CanonicalSurfaceGuardTest { "Breakable", "hasRender(", "CvSpec", - "CvBuilder"); + "CvBuilder", + // Package names, not types. The retired-type list could not catch a + // contributing guide that routed new template code into + // `templates.builtins` and `templates.support` — two packages the 2.0 + // split left behind and nothing has occupied since, so the instruction + // read as current and produced code that does not compile. Both spellings + // are listed: a doc names a package either as a dotted coordinate or as the + // path it lives at, and the sentence that carried this one for a release + // used the path. + "templates.builtins", + "templates.support", + "templates/builtins/", + "templates/support/"); /** * Types a contributor must not be pointed at when told how to build against the @@ -211,9 +224,9 @@ void contributorFacingDocsShouldNotNameSurfaceRetiredIn2_0() throws IOException } assertThat(violations) - .describedAs("these documents name a type 2.0 removed, so anyone following " - + "them writes code that does not compile. A document whose purpose " - + "is to record the removal belongs under one of %s.", + .describedAs("these documents name a type or package 2.0 removed, so anyone " + + "following them writes code that does not compile. A document whose " + + "purpose is to record the removal belongs under one of %s.", HISTORICAL_RECORD_PREFIXES) .isEmpty(); } diff --git a/docs/README.md b/docs/README.md index efc6ff57..74f6930d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,8 +34,8 @@ back here. - **[troubleshooting.md](troubleshooting.md)** — symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples. ### Templates -- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end. ⚠️ Being reworked for the layered `ModernInvoice` / `ModernProposal` surface. -- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `theme` / `components` / `widgets` / `presets`. +- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end, on the layered `ModernInvoice` / `ModernProposal` surface. +- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `components` / `widgets` / `presets` per family, over the shared `templates.core.theme`. - **[templates/v1-classic/](templates/v1-classic/)** — 🗄️ archived: the classic spec/builder/presets surface removed in 2.0; kept for pre-2.0 callers. ### Output backends @@ -107,7 +107,7 @@ it does. - **[adr/0012-nested-list-evolution.md](adr/0012-nested-list-evolution.md)** — nested list rendering evolution. - **[adr/0013-composed-table-cell.md](adr/0013-composed-table-cell.md)** — composed table cell model. - **[adr/0014-controlled-absolute-placement.md](adr/0014-controlled-absolute-placement.md)** — controlled absolute placement strategy. -- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `cv.v2` / `coverletter.v2` authoring model (current standard); supersedes the preset/builder portion of 0011. +- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `templates.cv` / `templates.coverletter` authoring model (current standard); supersedes the preset/builder portion of 0011. > **ADR numbering gap (0005–0010)** is intentional — those numbers > were reserved during a v1.5 restructure that landed under ADR 0011 diff --git a/docs/contributing/extension-guide.md b/docs/contributing/extension-guide.md index 082e9524..02546114 100644 --- a/docs/contributing/extension-guide.md +++ b/docs/contributing/extension-guide.md @@ -212,9 +212,9 @@ Set-up per node: The [`LayoutSnapshotAssertions`](../../testing/src/main/java/com/demcha/compose/testing/layout/LayoutSnapshotAssertions.java) -helper class wraps the diff machinery; existing snapshot tests under -`core/src/test/java/com/demcha/compose/document/templates/builtins/` -(e.g. `BuiltInTemplateLayoutSnapshotTest`) show the call site shape. +helper class wraps the diff machinery; the snapshot tests in the `qa` +module (`ChartLayoutSnapshotTest`, `ShapeContainerLayoutSnapshotTest`) +show the call site shape. ## Reading the source by responsibility diff --git a/docs/operations/layout-snapshot-testing.md b/docs/operations/layout-snapshot-testing.md index 518a04e0..b1afdf30 100644 --- a/docs/operations/layout-snapshot-testing.md +++ b/docs/operations/layout-snapshot-testing.md @@ -340,8 +340,6 @@ Prioritize documents that are most sensitive to layout regressions: ## Examples in this repository -- `RepositoryShowcaseRenderTest` -- `TablePaginationIntegrationTest` - `FontShowcaseLayoutSnapshotTest` - `ChartLayoutSnapshotTest` - `ShapeContainerLayoutSnapshotTest` diff --git a/docs/templates/v2-layered/README.md b/docs/templates/v2-layered/README.md index 36a988b5..00cd3ccc 100644 --- a/docs/templates/v2-layered/README.md +++ b/docs/templates/v2-layered/README.md @@ -52,20 +52,19 @@ inline DSL, and how to ship a new preset as ~150 lines that anyone can read end-to-end. ### 🛠 You're adding a new template family to the library -You're a GraphCompose maintainer or contributor. You want to bring -invoice-v2, cover-letter-v2, or a new document type onto the same -5-layer pattern that CV uses. +You're a GraphCompose maintainer or contributor. You want to bring a +new document type onto the same layering the shipped families use. → **[contributor-guide.md](contributor-guide.md)** -You'll get the package convention (`/v2/data` / -`theme` / `components` / `widgets` / `presets`), naming rules, test -expectations, doc expectations, and a worked checklist for a new -template family from empty folder to merged PR. +You'll get the package convention (`/data` / `components` / +`widgets` / `presets` over the shared `templates.core.theme`), naming +rules, test expectations, doc expectations, and a worked checklist for a +new template family from empty folder to merged PR. --- -## The 5-layer pattern at a glance +## The layering at a glance ``` presets/ composition: data + theme + widgets → DocumentTemplate @@ -74,10 +73,11 @@ presets/ composition: data + theme + widgets → DocumentTemplate widgets/ LEGO bricks: Headline, Subheadline, ContactLine, SectionHeader, … │ delegate to read tokens from ▼ ▼ -components/ internal renderers + primitives theme/ palette - typography - spacing - decoration +components/ internal renderers + primitives templates.core.theme + BrandTheme: palette + typography + spacing + decoration │ render ▼ data/ records describing what to render (no styling) @@ -95,16 +95,17 @@ The detailed contract for each layer is in ## What this pattern is *not* -- ❌ **Not a migration mandate.** Existing v1 templates - (`cv/spec`, `cv/builder`, `cv/presets`) continue to work and - ship. The layered pattern is for **new** templates and major - rewrites. +- ❌ **Not one shape for every family.** `presets/` is the only + package a family always has. CV carries all four; cover letter has no + `widgets/`; invoice and proposal are presets alone, reading data + records from the shared `templates.data.`. Add a layer when + the family needs one. - ❌ **Not a framework with magic.** Every file is plain Java records + static helpers. No reflection, no annotations, no codegen. - ❌ **Not coupled to CV.** The pattern is domain-agnostic; CV is - just the first family migrated. Invoice or cover-letter would - use the same five folders with their own data shapes inside. + just the family that uses every layer, which is why it is the + reference implementation. - ❌ **Not a UI framework.** No state, no events, no lifecycle. Templates render static PDFs from immutable data. diff --git a/docs/templates/v2-layered/authoring-presets.md b/docs/templates/v2-layered/authoring-presets.md index 0ee2259a..a54739ac 100644 --- a/docs/templates/v2-layered/authoring-presets.md +++ b/docs/templates/v2-layered/authoring-presets.md @@ -384,7 +384,7 @@ When you do add a new widget: A new preset needs at least: -1. **Smoke test** in `core/src/test/.../cv/presets/MyPresetSmokeTest.java`: +1. **Smoke test** in `qa/src/test/.../cv/presets/MyPresetSmokeTest.java`: - `exposes_stable_identity` — checks `id()` and `displayName()` - `default_factory_renders` — calls `create().compose(...)` with a full sample document, asserts `session.roots()` is non-empty diff --git a/docs/templates/v2-layered/contributor-guide.md b/docs/templates/v2-layered/contributor-guide.md index aab4da0f..e0049dcb 100644 --- a/docs/templates/v2-layered/contributor-guide.md +++ b/docs/templates/v2-layered/contributor-guide.md @@ -1,9 +1,9 @@ # Contributor Guide — add a new template family You're a GraphCompose contributor and you want to bring a brand-new -document type onto the layered architecture — invoice-v2, -cover-letter-v2, report-v2, anything that isn't CV. This doc is -your checklist + convention reference. +document type onto the layered architecture — a report, a +statement, anything the shipped families do not already cover. This +doc is your checklist + convention reference. It assumes you've read [quickstart.md](quickstart.md) and [authoring-presets.md](authoring-presets.md) — those explain *why* @@ -13,7 +13,7 @@ the layered pattern looks the way it does. ## Table of contents -1. [The 5-layer convention](#the-5-layer-convention) +1. [The layer convention](#the-5-layer-convention) 2. [Package map for a new family](#package-map-for-a-new-family) 3. [Naming rules](#naming-rules) 4. [Worked walkthrough — `invoice`](#worked-walkthrough) @@ -26,30 +26,35 @@ the layered pattern looks the way it does. --- -## The 5-layer convention +## The layer convention Every new template family lives under -`com.demcha.compose.document.templates..v2` with **exactly -five sub-packages**: +`com.demcha.compose.document.templates.` with these +sub-packages: ``` -/v2/ +/ ├── data/ records describing what's on the page -├── theme/ cosmetic tokens (palette, typography, spacing, decoration) ├── components/ internal renderers + low-level primitives -├── widgets/ reusable visual LEGO bricks +├── widgets/ reusable visual LEGO bricks — omit if the family has none └── presets/ composition: data + theme + widgets → DocumentTemplate ``` +Cosmetic tokens are **not** per-family. `templates.core.theme` holds +`BrandTheme` and its parts (`Palette`, `Typography`, `Spacing`, +`Decoration`), and every family reads from it — that is what keeps two +presets from two families looking like one product. A family that needs +a token nobody else has adds it there, not to a package of its own. + Each layer's contract: | Layer | Contract | |---|---| | `data/` | Pure records. Zero dependencies on rendering, theming, or DSL. Sealed hierarchy for section / block subtypes. | -| `theme/` | Records + factories. No rendering logic. Aggregate root is `Theme(palette, typography, spacing, decoration)`. | +| `templates.core.theme` | Shared, not per-family. `BrandTheme(palette, typography, spacing, decoration)` plus its parts. Records + factories, no rendering logic. | | `components/` | Static helpers. Take `(host, data, theme)`. No statics holding state. No magic numbers — read tokens from theme. | -| `widgets/` | Static helpers. Named factory methods per visual variant. Compose internally from `components/`. | -| `presets/` | One `public final class` per visual style. Two factories: `create()` and `create(Theme)`. Inner `Template` implements `DocumentTemplate<Document>`. | +| `widgets/` | Static helpers. Named factory methods per visual variant. Compose internally from `components/`. Optional — the cover-letter family has none. | +| `presets/` | One `public final class` per visual style. Two factories: `create()` and `create(BrandTheme)`. Inner `Template` implements `DocumentTemplate<Document>`. | **The convention is the same regardless of domain.** A cv has identity + sections; an invoice has parties + line items + totals; a @@ -61,10 +66,14 @@ data differs; the *layering* is identical. ## Package map for a new family -Mirror the CV v2 layout. Concrete example for invoice: +The fullest shape, which is CV's. A family takes the layers it needs and +no more: cover letter has no `widgets/`, and the shipped invoice and +proposal families are `presets/` alone, reading their records from the +shared `templates.data.`. The tree below is an invoice-shaped +domain drawn at full size, so every layer has something in it: ``` -templates/src/main/java/com/demcha/compose/document/templates/invoice/ +templates/src/main/java/com/demcha/compose/document/templates// ├── package-info.java ← ASCII diagram + 4-step walkthrough ├── AUTHORS.md ← recipe cookbook ├── data/ @@ -79,13 +88,6 @@ templates/src/main/java/com/demcha/compose/document/templates/invoice/ │ ├── TotalsSection.java ← concrete subtype │ ├── NotesSection.java ← concrete subtype │ └── Slot.java ← if multi-column variants needed -├── theme/ -│ ├── package-info.java -│ ├── InvoicePalette.java ← ink / muted / rule / accent -│ ├── InvoiceTypography.java ← scale -│ ├── InvoiceSpacing.java ← margins, gaps -│ ├── InvoiceDecoration.java ← row separators, totals divider -│ └── InvoiceTheme.java ← aggregate + factories ├── components/ │ ├── package-info.java │ ├── ParagraphPrimitive.java ← internal — package-private @@ -106,8 +108,8 @@ templates/src/main/java/com/demcha/compose/document/templates/invoice/ └── (more as added) ``` -The structure is **identical** to cv — only the records inside -differ. +That is cv's structure with invoice records inside. A smaller family +keeps `presets/` and drops the rest. --- @@ -116,13 +118,13 @@ differ. - **Family prefix** on top-level records to avoid name collisions. CV uses `CvName`, `CvIdentity`, `CvSection`. Invoice should use - `InvoiceParty`, `InvoiceLine`, `InvoiceTheme`. Cover letter: - `CoverLetterRecipient`, `CoverLetterTheme`. Etc. + `InvoiceParty`, `InvoiceLine`. Cover letter: + `CoverLetterRecipient`, `CoverLetterBody`. Etc. - **`Document`** for the root record. (`CvDocument`, `InvoiceDocument`, `CoverLetterDocument`.) - **`Section` (sealed)** for the body content hierarchy. -- **`Theme(palette, typography, spacing, decoration)`** for - the aggregate theme record. +- **No `Theme`.** The aggregate theme record is the shared + `BrandTheme`; a family names a factory on it, not a type of its own. - **Widgets** are domain-specific verbs / nouns describing the visual: `Headline`, `ContactLine`, `SectionHeader` (CV); `Letterhead`, `LineTable`, `TotalsCard` (invoice). Don't try to @@ -162,23 +164,16 @@ Sealed `InvoiceSection` lists every body shape an invoice can have. Concrete subtypes (`LineItemsSection`, `TotalsSection`, …) are records carrying the section data. -### 2. `theme/` second (no rendering, just tokens) +### 2. the theme second (there is nothing to write) + +Pick the `BrandTheme` factory the family should default to, or add +one beside the others in `templates.core.theme` if none fits. The +record is shared, so a token added for an invoice is available to a +CV — which is the point, and also the reason to think before adding +one. ```java -public record InvoiceTheme( - InvoicePalette palette, - InvoiceTypography typography, - InvoiceSpacing spacing, - InvoiceDecoration decoration) { - - public static InvoiceTheme classic() { - return new InvoiceTheme( - InvoicePalette.classic(), - InvoiceTypography.classic(), - InvoiceSpacing.classic(), - InvoiceDecoration.classic()); - } -} +BrandTheme theme = BrandTheme.invoiceModern(); ``` ### 3. `components/` third (low-level renderers consume data + theme) @@ -188,7 +183,7 @@ public final class LineRowRenderer { private LineRowRenderer() {} public static void render(SectionBuilder host, InvoiceLine line, - InvoiceTheme theme) { + BrandTheme theme) { // … DSL calls reading theme tokens … } } @@ -205,7 +200,7 @@ public final class LineTable { private LineTable() {} public static void render(SectionBuilder host, - List lines, InvoiceTheme theme) { + List lines, BrandTheme theme) { for (InvoiceLine line : lines) { LineRowRenderer.render(host, line, theme); } @@ -227,10 +222,10 @@ public final class ClassicInvoice { private ClassicInvoice() {} public static DocumentTemplate create() { - return create(InvoiceTheme.classic()); + return create(BrandTheme.invoiceModern()); } - public static DocumentTemplate create(InvoiceTheme theme) { + public static DocumentTemplate create(BrandTheme theme) { return new Template(theme); } @@ -250,15 +245,15 @@ Mirror the CV preset shape exactly. A new template family PR ships with: -- [ ] **5 packages** under `/v2/` populated per convention +- [ ] **The sub-packages** under `/` populated per convention - [ ] **At least 1 reference preset** that renders a sample document - [ ] **`AUTHORS.md`** in the family root (recipe cookbook — copy the cv one as starting structure) - [ ] **`package-info.java`** at family root + each sub-package - [ ] **Sample fixture** in `ExampleDataFactory.sampleDocumentV2()` -- [ ] **Example runner** in `examples/.../templates//v2/` +- [ ] **Example runner** under `examples/.../templates//` - [ ] **Smoke tests** per the checklist below -- [ ] **No edits** to `engine/`, `dsl/`, or v1 `/` surface +- [ ] **No edits** to `engine/` or `dsl/` --- @@ -270,7 +265,6 @@ Minimum test coverage matching CV v2: | Test class | What it asserts | |---|---| | `DocumentTest` | Builder rejects null / blank required fields; valid build succeeds | -| `ThemeTest` | All factories produce valid themes; deprecated constructors (if any) wrap correctly | | `SmokeTest` | `id()`, `displayName()`, default-factory render, custom-theme render | | `SectionDispatcherTest` *(optional)* | Each sealed subtype routes correctly | | `WidgetSmokeTest` | Each public widget variant renders without throwing | @@ -301,7 +295,7 @@ git commit -m "test: refresh visual baselines after " ``` **Where baselines live:** -`core/src/test/resources/visual-baselines/-v2-layered/-page-N.png` +`qa/src/test/resources/visual-baselines/-v2-layered/-page-N.png` One PNG per page per preset. Pages overflow naturally — a 2-page preset gets `-page-0.png` and `-page-1.png`. @@ -331,7 +325,7 @@ baseline so a reviewer can see exactly what changed before deciding to re-bless or fix. **Reference**: see -`core/src/test/java/com/demcha/compose/document/templates/cv/presets/CvV2VisualParityTest.java` +`qa/src/test/java/com/demcha/compose/document/templates/cv/presets/CvV2VisualParityTest.java` — a 200-line drop-in template you can copy for a new family. --- @@ -339,13 +333,13 @@ to re-bless or fix. ## Doc checklist -- [ ] `/v2/package-info.java` — ASCII diagram of the 5 - layers, plus a 4-step "how to author a document" walkthrough - (copy the cv one's structure). -- [ ] `/v2/AUTHORS.md` — recipe cookbook. At least: +- [ ] `/package-info.java` — ASCII diagram of the layers, + plus a 4-step "how to author a document" walkthrough (copy the + cv one's structure). +- [ ] `/AUTHORS.md` — recipe cookbook. At least: change a glyph, change colours, add a new section subtype, conditional sections. -- [ ] `/v2//package-info.java` — each +- [ ] `//package-info.java` — each sub-package gets a paragraph explaining its role. - [ ] **Update [`docs/templates/v2-layered/README.md`](README.md)** to list the new family in the "implementations" section. @@ -359,9 +353,9 @@ to re-bless or fix. `document/engine`, `document/node`, `document/style`). If your family needs an engine feature that doesn't exist, that's a separate prerequisite PR. -- ❌ **Don't edit v1 surface** for the same family. They coexist. - Mark v1 `@Deprecated` only after the v2 surface is feature-complete - and shipped — that's a follow-up PR. +- ❌ **Don't add a theme package to your family.** Tokens live in + `templates.core.theme` so families stay visually consistent; a + private palette is how that consistency is lost one family at a time. - ❌ **Don't fork widgets across families.** If invoice needs a `Headline`, write `templates/invoice/widgets/Letterhead.java` — an invoice letterhead has different needs from a CV name @@ -389,13 +383,13 @@ A new template family PR is reviewed against: 1. **Layer discipline** — do data / theme / components / widgets / presets each obey their contract? 2. **Test coverage** — smoke tests for every public surface; - builder validation tests; theme factory tests. + builder validation tests. 3. **Doc completeness** — `package-info.java` everywhere, `AUTHORS.md` with at least 4 recipes, root README updated. 4. **Visual signature** — render the reference preset, attach the PDF to the PR description, eyeball-validate it matches the intent. -5. **No engine / v1 edits** — additive only. +5. **No engine edits** — additive only. Expected size: ~1500-2500 lines of new code for a fresh family. Compare to cv baseline (PR #45) which was 2082 lines including diff --git a/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java b/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java index 6385003c..85b0e5c3 100644 --- a/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java +++ b/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java @@ -45,7 +45,7 @@ * GraphCompose source tree calls.

* *

See {@code docs/recipes/streaming.md} and the existing - * {@code BuiltInTemplateLayoutSnapshotTest} for the production + * {@code ChartLayoutSnapshotTest} for the production * pattern.

* * @author Artem Demchyshyn