7.1 KiB
7.1 KiB
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/sveltekitwith autoUpdate; manifest invite.config.ts. - Language: UI, copy, identifiers, and most code comments are German. Preserve German wording (
Eigenschaften,Talente,Sonderfertigkeiten,Vorteile,Nachteile, attribute keys likeMU/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.*.svelteuses the Svelte parser viaprettier-plugin-svelte. - ESLint flat config (
eslint.config.js) extends@eslint/js,typescript-eslint, andeslint-plugin-svelte.build/,.svelte-kit/, andnode_modules/are ignored. - Run
npm run formatandnpm run lintbefore finishing a change.
TypeScript
strict: trueintsconfig.json(extends.svelte-kit/tsconfig.json).allowJsandcheckJsare on – stray.jsfiles are type-checked too.- Prefer
import type { ... }for type-only imports. - No
any. Use Zod inference (z.infer<typeof schema>) for domain types; the canonicalCharactertype lives insrc/lib/schema/character.ts.
Svelte 5
- Components are Svelte 5. Use the new runes where appropriate (
$state,$derived,$effect,$props); don't reintroduce legacyexport letstyle for new code. svelte.config.jsfilters thea11y_label_has_associated_controlwarning and the ESLint config relaxes it because fields use visual labels. Keep that pattern – don't addfor/idplumbing 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 ofCharacter; 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 providelep_mod,asp_mod,aup_mod,mr_modso the engine keeps working. - Sonderfertigkeiten / Vor- / Nachteile each have a typed table in
src/lib/rules/*.tsand a getter (getSonderfertigkeit, …). Add new entries to the table; don't hard-code IDs in engine code.
Schema & Migrations
CURRENT_SCHEMA_VERSIONlives insrc/lib/schema/version.ts. Every persistedCharactercarriesschemaVersion(az.literal).- When you change the character schema in a breaking way:
- Bump
CURRENT_SCHEMA_VERSION. - Update
migrateCharacterinsrc/lib/schema/version.tsto migrate old payloads forward. - Update / add tests in
tests/engine/io.test.ts(or a new test) covering the migration path.
- Bump
- Importers (
src/lib/storage/io.ts,src/lib/import/helden.ts) must run input throughmigrateCharacterbeforeparseCharacter.
Storage
- All DB access goes through
src/lib/storage/repo.ts. Don'timport { db }from components – uselistCharacters,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 Dexieversion(N)and providing an upgrade function.
Testing
- Test runner: Vitest (
npm test/npm run test:watch). Config lives invite.config.ts(test.include+tests/setup.ts). tests/setup.tswires upfake-indexeddbso 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-checktests/engine/derived.test.tsbecause it pins the exact DSA 4.1 rounding (Math.roundfor AT/PA/FK/INI/MR,Math.floorfor LeP base).
Build & PWA Notes
- SPA mode:
adapter-staticwithfallback: 'index.html'andstrict: 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, extendworkbox.globPatternsinvite.config.ts. postinstallrunsscripts/write-placeholder-icons.mjsto generate placeholder PNGs. Real production icons should replacestatic/icons/icon-192.png,icon-512.png, andmaskable-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.