Files
2FATest/AGENTS.md
T
pitfriedrich 3e209dfcbc
CI / Build & Test (pull_request) Successful in 1m6s
Add custom Solstice theme with a dark/light toggle
Introduces a custom Aura theme variant (amber daylight / aurora cyan
midnight, via -light/-dark token pairs) and a header toggle button to
switch between them at runtime, using the Vaadin 25 ColorScheme API
(Page#setColorScheme, @ColorScheme) rather than the older Lumo-only
theme-attribute pattern. The choice is persisted in a cookie so it
survives reloads and future sessions without any client-side script,
and is restored flash-free on the next visit via an
IndexHtmlRequestListener.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNL5syWGwM7KnFcuZeEKQ1
2026-08-08 15:47:25 +02:00

56 lines
5.8 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
A Spring Boot + Vaadin Flow project (Vaadin 25, Spring Boot 4, Java 25). UI is built entirely in server-side Java — no HTML/JS/TypeScript to write for views. Data layer is Spring Data JPA over H2 (file/in-memory, dev only).
## Commands
```bash
./mvnw spring-boot:run # run the app (dev), http://localhost:8080
./mvnw test # run all tests
./mvnw test -Dtest=TaskServiceTest # single test class
./mvnw test -Dtest=TaskServiceTest#tasks_are_validated_before_they_are_stored # single test method
./mvnw package # production build -> target/*.jar
java -jar target/*.jar # run the built jar
```
No system Maven is required; always use the `./mvnw` wrapper. There is no separate lint command configured.
Live hotswap (edit Java, see changes without restart) requires launching via the Vaadin IDE plugin (IntelliJ/VS Code/Eclipse) instead of `spring-boot:run` — not available from the CLI.
Port defaults to 8080 (override via `server.port` in `src/main/resources/application.properties` or the `PORT` env var).
## Architecture
**Feature-package structure**: code is organized by feature/domain under `com.example.<feature>`, not by technical layer. Each feature package is self-contained: entity, repository, service, and a `ui` sub-package for its views. `com.example.examplefeature` is the template feature to copy/replace — its `package-info.java` has a `TODO Remove this package once you have added real features`.
**Package visibility convention**: types are package-private by default (e.g. `TaskRepository`, `TaskService`, `TaskListView` are not `public`). Only export a type outside its feature package when another package actually needs it. Follow this pattern for new features.
**Null-safety**: packages are annotated `@NullMarked` (JSpecify) in their `package-info.java`, so all types are non-null by default; use `@Nullable` explicitly where null is allowed (e.g. `Task.dueDate`). Apply `@NullMarked` to any new feature package.
**Layout/routing**: `MainLayout` (`com.example.base.ui`, annotated `@Layout`) is the shared `AppLayout` shell (drawer nav + header/footer) applied automatically to routed views. Side-nav entries are auto-discovered via `@Menu` on `@Route` view classes (see `TaskListView`) — no manual registration needed. `ViewTitle` is a shared composite for the per-view title bar (drawer toggle + heading).
**Data access pattern**: `Slice<T>` (via `findAllBy(Pageable)`) is preferred over `Page<T>` for grid data providers, since `Slice` avoids the extra `COUNT` query — see the comment in `TaskRepository`. Vaadin `Grid` is wired to Spring Data via `VaadinSpringDataHelpers.toSpringPageRequest(query)` in an in-memory/lazy `DataProvider` callback (see `TaskListView`).
**Entities**: JPA entities use `@GeneratedValue(strategy = GenerationType.SEQUENCE)`, expose validation in setters (throwing `IllegalArgumentException`) rather than via bean-validation annotations, and implement `equals`/`hashCode` based on ID only (with a fixed `hashCode`, per the pattern in `Task`).
**Schema management**: `spring.jpa.hibernate.ddl-auto=update` is used for local dev convenience only. This is explicitly not appropriate for production — the app is meant to move to Flyway (or similar) for real schema migrations before shipping.
**Frontend theming**: `Application` sets the app-shell config (`@Push`, `Aura` theme via `@StyleSheet(Aura.STYLESHEET)`, plus custom `styles.css`/`view-title.css`/`solstice.css` under `src/main/resources/META-INF/resources/`). No separate frontend build step is needed for typical UI work since Flow generates the client bundle; the `vaadin-maven-plugin` `build-frontend` goal runs as part of the Maven build.
**Custom theme ("Solstice") and dark/light mode**: `solstice.css` overrides Aura's `-light`/`-dark` suffixed design tokens (e.g. `--aura-accent-color-light/-dark`, `--aura-background-color-light/-dark`) rather than the unsuffixed ones — those are read-only, computed via the native CSS `light-dark()` function from the pair. Everything else should keep consuming the unsuffixed tokens (`--aura-accent-color`, `--vaadin-text-color`, ...) so it follows whichever scheme is active automatically. Switching schemes at runtime uses the Vaadin 25 `ColorScheme` API (`Application` declares `@ColorScheme(ColorScheme.Value.SYSTEM)` as the default; `Page#setColorScheme(...)` changes it at runtime) — do not use the older Lumo-only `setAttribute("theme", ...)` pattern. The feature lives in `com.example.base.ui`: `ThemePreference` (session-scoped) holds the current choice and applies it; `ThemeToggle` is the header button; `ThemeInitializer` restores the choice from a cookie (`ThemeCookie`) on every request/UI-init, both for a flash-free reload and to seed a fresh session, since a plain `@ColorScheme` annotation can't read per-visitor state on its own.
## Testing
Two distinct test styles are used:
- **Service/integration tests** (`TaskServiceTest`): `@SpringBootTest(webEnvironment = MOCK)` + `@Transactional` (each test rolls back), asserting against the JPA layer directly.
- **UI/browserless tests** (`TaskListViewTest`): extend `SpringBrowserlessTest` (from `com.vaadin:browserless-test-spring`), which renders and interacts with actual Vaadin components server-side without a real browser. Use `navigate(ViewClass.class)`, `test(component)` for interactions/assertions, and `$(ComponentClass.class)` for component lookup queries. Views expose package-private fields (e.g. `taskGrid`, `description`, `createBtn`) specifically so tests in the same package can drive them directly.
## MCP
`.mcp.json` configures a Vaadin docs MCP server (`https://mcp.vaadin.com/docs`) and a Playwright MCP server — both available for querying live Vaadin component/API docs and browser automation if needed.