119 lines
7.1 KiB
Markdown
119 lines
7.1 KiB
Markdown
# AGENTS.md
|
||
|
||
Guidance for AI agents working in this repo. Read this before making changes.
|
||
|
||
## Project at a Glance
|
||
|
||
- **What it is:** An offline-first PWA helper for the German tabletop RPG _Das Schwarze Auge 4.1_ (DSA 4.1). Covers character sheet, melee/ranged combat, spells, and import/export.
|
||
- **Stack:** SvelteKit 2 (SPA via `adapter-static`) + Svelte 5 + TypeScript (strict) + Vite 6.
|
||
- **Persistence:** IndexedDB via Dexie (`src/lib/storage/db.ts`); meta key/value table tracks the active character.
|
||
- **Validation:** Zod schemas in `src/lib/schema/`.
|
||
- **PWA:** `@vite-pwa/sveltekit` with autoUpdate; manifest in `vite.config.ts`.
|
||
- **Language:** UI, copy, identifiers, and most code comments are **German**. Preserve German wording (`Eigenschaften`, `Talente`, `Sonderfertigkeiten`, `Vorteile`, `Nachteile`, attribute keys like `MU/KL/IN/CH/FF/GE/KO/KK`, …) – don't translate domain terms.
|
||
|
||
## Commands
|
||
|
||
| Task | Command |
|
||
| ----------------- | ---------------- |
|
||
| Install deps | `npm install` |
|
||
| Dev server | `npm run dev` |
|
||
| Production build | `npm run build` |
|
||
| Preview build | `npm run preview`|
|
||
| Type / svelte check | `npm run check` |
|
||
| Lint | `npm run lint` |
|
||
| Format | `npm run format` |
|
||
| Run tests (CI) | `npm test` |
|
||
| Watch tests | `npm run test:watch` |
|
||
| Regenerate icons | `npm run icons` |
|
||
|
||
After substantive edits, run `npm run check` and `npm test` before declaring done.
|
||
|
||
## Repository Layout
|
||
|
||
```
|
||
src/
|
||
app.css, app.html, app.d.ts
|
||
lib/
|
||
characters/ # Character defaults / factories (newCharacter)
|
||
components/ # Shared Svelte components (e.g. DiceRoller)
|
||
engine/ # Pure derivation: AT/PA/FK basis, LeP, melee, ranged, probe, talent-check, spell
|
||
import/ # Helden-Software importer (placeholder)
|
||
rules/ # DSA 4.1 rule tables: attributes, races, talents, weapons, SFs, Vor-/Nachteile
|
||
schema/ # Zod schemas + schema version / migration
|
||
storage/ # Dexie DB, repo (CRUD), io (JSON/YAML export/import)
|
||
routes/
|
||
+layout.{svelte,ts}, +page.svelte
|
||
characters/[id]/{sheet,combat/melee,combat/ranged,spells}/+page.svelte
|
||
settings/+page.svelte
|
||
tests/
|
||
setup.ts # vitest setup (uses fake-indexeddb)
|
||
engine/ # Unit tests for derivation, ranged, io
|
||
scripts/
|
||
write-placeholder-icons.mjs # runs on postinstall + `npm run icons`
|
||
static/
|
||
favicon.svg, icons/
|
||
```
|
||
|
||
Use SvelteKit aliases: `$lib/...` (resolves to `src/lib`) and `$app/...` for kit internals. Don't introduce relative `../../lib` imports.
|
||
|
||
## Conventions
|
||
|
||
### Formatting & Linting
|
||
|
||
- Prettier: **tabs**, single quotes, no trailing commas, `printWidth: 100`. `*.svelte` uses the Svelte parser via `prettier-plugin-svelte`.
|
||
- ESLint flat config (`eslint.config.js`) extends `@eslint/js`, `typescript-eslint`, and `eslint-plugin-svelte`. `build/`, `.svelte-kit/`, and `node_modules/` are ignored.
|
||
- Run `npm run format` and `npm run lint` before finishing a change.
|
||
|
||
### TypeScript
|
||
|
||
- `strict: true` in `tsconfig.json` (extends `.svelte-kit/tsconfig.json`). `allowJs` and `checkJs` are on – stray `.js` files are type-checked too.
|
||
- Prefer `import type { ... }` for type-only imports.
|
||
- No `any`. Use Zod inference (`z.infer<typeof schema>`) for domain types; the canonical `Character` type lives in `src/lib/schema/character.ts`.
|
||
|
||
### Svelte 5
|
||
|
||
- Components are Svelte 5. Use the new runes where appropriate (`$state`, `$derived`, `$effect`, `$props`); don't reintroduce legacy `export let` style for new code.
|
||
- `svelte.config.js` filters the `a11y_label_has_associated_control` warning and the ESLint config relaxes it because fields use visual labels. Keep that pattern – don't add `for`/`id` plumbing just to silence warnings.
|
||
|
||
### Domain Rules (read carefully before touching `engine/` or `rules/`)
|
||
|
||
- Derived values follow DSA 4.1 formulas in `src/lib/engine/derived.ts`. They are pure functions of `Character`; don't mutate the input.
|
||
- Attribute access goes through `effectiveAttr(char, key)` (= `startwert + mod`). Don't read raw values directly when computing derived stats.
|
||
- Race modifiers come from `getRaceDef(char.meta.rasse)` (`src/lib/rules/races.ts`). New races must provide `lep_mod`, `asp_mod`, `aup_mod`, `mr_mod` so the engine keeps working.
|
||
- Sonderfertigkeiten / Vor- / Nachteile each have a typed table in `src/lib/rules/*.ts` and a getter (`getSonderfertigkeit`, …). Add new entries to the table; don't hard-code IDs in engine code.
|
||
|
||
### Schema & Migrations
|
||
|
||
- `CURRENT_SCHEMA_VERSION` lives in `src/lib/schema/version.ts`. Every persisted `Character` carries `schemaVersion` (a `z.literal`).
|
||
- When you change the character schema in a breaking way:
|
||
1. Bump `CURRENT_SCHEMA_VERSION`.
|
||
2. Update `migrateCharacter` in `src/lib/schema/version.ts` to migrate old payloads forward.
|
||
3. Update / add tests in `tests/engine/io.test.ts` (or a new test) covering the migration path.
|
||
- Importers (`src/lib/storage/io.ts`, `src/lib/import/helden.ts`) must run input through `migrateCharacter` before `parseCharacter`.
|
||
|
||
### Storage
|
||
|
||
- All DB access goes through `src/lib/storage/repo.ts`. Don't `import { db }` from components – use `listCharacters`, `getCharacter`, `saveCharacter`, `deleteCharacter`, and the active-character helpers.
|
||
- The Dexie schema is in `src/lib/storage/db.ts`. Adding a new store or index requires bumping the Dexie `version(N)` and providing an upgrade function.
|
||
|
||
## Testing
|
||
|
||
- Test runner: Vitest (`npm test` / `npm run test:watch`). Config lives in `vite.config.ts` (`test.include` + `tests/setup.ts`).
|
||
- `tests/setup.ts` wires up `fake-indexeddb` so Dexie works in Node. Don't import real browser globals in tests.
|
||
- Add tests next to existing ones under `tests/engine/` for any new derivation, rules logic, or schema migration. Keep them deterministic (no real time/random unless seeded).
|
||
- When changing `engine/derived.ts`, re-check `tests/engine/derived.test.ts` because it pins the exact DSA 4.1 rounding (`Math.round` for AT/PA/FK/INI/MR, `Math.floor` for LeP base).
|
||
|
||
## Build & PWA Notes
|
||
|
||
- SPA mode: `adapter-static` with `fallback: 'index.html'` and `strict: false`. Don't introduce server-only endpoints (`+server.ts`, `+page.server.ts`) – the app must build as a static bundle.
|
||
- Workbox precaches `client/**/*.{js,css,ico,png,svg,webp,woff,woff2}`. If you add new asset types that should be cached offline, extend `workbox.globPatterns` in `vite.config.ts`.
|
||
- `postinstall` runs `scripts/write-placeholder-icons.mjs` to generate placeholder PNGs. Real production icons should replace `static/icons/icon-192.png`, `icon-512.png`, and `maskable-512.png`.
|
||
|
||
## Things to Avoid
|
||
|
||
- Don't add server-rendered routes, server hooks, or anything that breaks `adapter-static`.
|
||
- Don't bypass Zod – every external payload (import, future API) must go through `parseCharacter` (after migration).
|
||
- Don't hardcode rule data inside components; put it in `src/lib/rules/` so it can be reused and tested.
|
||
- Don't switch indentation, quote style, or trailing-comma policy – Prettier owns formatting.
|
||
- Don't translate German domain vocabulary or rename attribute keys; existing data and tests depend on them.
|