- README : fonctionnalités, installation, build, tests (302 + 20 E2E), couverture 94%, workflow mise à jour latest.yml, changelog v1.1.6 - CLAUDE.md : règles de collaboration, architecture, conventions - Plans de conception : DDD, electron-updater, accouplement, toast Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
6.6 KiB
Markdown
115 lines
6.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Règles de collaboration
|
|
|
|
- **Toujours répondre et écrire en français**
|
|
- **Ne jamais modifier les fichiers de tests sans le signaler explicitement** à l'utilisateur avant de le faire
|
|
- **Toujours écrire des tests** (unitaires + fonctionnels/régression) lors de toute correction de bug ou ajout de feature — les tests font partie de la livraison, pas une étape optionnelle
|
|
- **Toujours mettre à jour `algorithmes.md`** quand un algorithme ou une formule de calcul change
|
|
- **Ne jamais utiliser de taux fixe pour les jauges** — toujours utiliser `gainedIn` / `timeToGain` avec traversée des tiers (erreur historique du monolithe)
|
|
- **Signaler explicitement** tout changement qui touche aux snapshots du timer (`snapGauges`, `snapStats`, `gaugeRecharges`) car cela peut affecter tous les calculs en cours de session
|
|
- **Les tests ne tournent pas en WSL** — toujours rappeler à l'utilisateur de lancer `npm test` depuis un terminal Windows
|
|
|
|
## Project Overview
|
|
|
|
Minuteur Dragodinde is a Windows desktop app (Electron + Vite + TypeScript) for managing Dragodinde breeding timers in Dofus 3. French-language UI.
|
|
|
|
## Commands
|
|
|
|
- **Dev**: `npm start` (runs Vite dev server + Electron via `vite-plugin-electron`)
|
|
- **Build**: `npm run build` (Vite build + electron-builder → NSIS installer in `dist/`)
|
|
- **Test**: `npm test` (Vitest, run from Windows — native bindings incompatible with WSL)
|
|
- **Test watch**: `npm run test:watch`
|
|
- **Build scripts**: `build.bat` (auto-elevates to admin) or `build.ps1` for Windows
|
|
|
|
## Architecture
|
|
|
|
**DDD hexagonal architecture** with Vite + TypeScript. Four layers:
|
|
|
|
```
|
|
src/
|
|
domain/ — Entities, value objects, domain services (pure TS, no deps)
|
|
application/ — Command handlers, query handlers, CommandBus/QueryBus
|
|
infrastructure/ — LocalStorageRepository, ElectronNotification, WebAudioAlarm
|
|
presentation/ — Components, helpers, UIState, entry point (index.ts)
|
|
```
|
|
|
|
### Domain layer
|
|
|
|
- **`entities/Enclos.ts`** — Enclos entity with `TimerData` (includes `gaugeRecharges: Record<string, GaugeRecharge[]>`)
|
|
- **`entities/Dragodinde.ts`** — DD entity with stats, targets, `levelTarget`, `sereniteTarget`
|
|
- **`services/GaugeCalculator.ts`** — Core pure functions: `gainedIn`, `timeToGain`, `gaugeAfter`, `elapsed`, `computeGaugeState`
|
|
- **`value-objects/GaugeType.ts`** — `GAUGE_DEFS`, `STAT_DEFS`, `DEFAULT_TARGETS` (mangeoire default = 100, but UI uses 200 when `levelTarget` is null)
|
|
- **`value-objects/XpTable.ts`** — `xpForLevel`, `levelFromXp`
|
|
- **`value-objects/Tier.ts`** — `tierNum`, `tierRate`
|
|
|
|
### Application layer
|
|
|
|
Commands: `StartTimer`, `StopTimer`, `CompleteTimer`, `ResetTimer`, `CreateEnclos`, `DeleteEnclos`, `AddDragodinde`, `RemoveDragodinde`, `UpdateGauge` (toggle + level), `RechargeGauge`, `RegisterAccouplement`, `UpdateSettings`, `ResetStats`, `ReorderEnclos`, `UpdateWorkflow`, `DeleteWorkflow`, `EnclosActions` (clear, rename, reset-timer), `DragodindeActions` (rename, update-stat, seren-target, level-target, reorder)
|
|
|
|
Queries: `GetDashboard`, `GetEnclosDetail`, `GetTimerState`, `GetBreedingOptions`, `GetReapproTree`, `GetInventaire`, `GetSettings`, `GetWorkflows`
|
|
|
|
### Presentation layer
|
|
|
|
- **`components/App.ts`** — Root component, animation loop (`requestAnimationFrame`), CommandBus/QueryBus wiring
|
|
- **`components/EnclosView.ts`** — Main timer view: gauge inputs (with `input` listener for real-time updates), DD grid, "Alarme dans" display
|
|
- **`components/DragodindeCard.ts`** — Per-DD card: stat pills, targets (all with `input` listeners for real-time preview)
|
|
- **`helpers/gauge-live.ts`** — All live calculation helpers: `computeGaugeLive`, `enclosGaugeCurGl`, `enclosGlobalState`, `calcSerenEtaLive`, `calcLevelEtaLive`
|
|
|
|
### Key data flows
|
|
|
|
**Timer start** (fresh): snapshots `gaugeLevels` → `snapGauges`, DD stats → `snapStats`, clears `gaugeRecharges`
|
|
**Timer resume** (from pause): accumulates `pausedMs += now - pausedAt`, preserves snapshots
|
|
**Gauge recharge** (during timer): pushes `{ atSec, level }` to `gaugeRecharges[gid]`, updates `gaugeLevels[gid]`
|
|
**Live display**: `requestAnimationFrame` loop → `enclosGlobalState(enc)` → per-DD `computeGaugeLive` → DOM updates
|
|
|
|
### Timer calculation core — `computeGaugeState`
|
|
|
|
The central calculation function. Handles:
|
|
- Multiple gauge recharges during a session (segments between each recharge)
|
|
- Freeze at absolute stat cap (sérénité ±5000, end/mat/amour 20000, niveau 200)
|
|
- Returns `{ gained, curGl, effectiveEl }`
|
|
|
|
**Pre-start preview**: `enclosGlobalState` computes even when timer not started (`started = false`), using current `gaugeLevels` as `startGl` and `el = 0`. "Alarme dans" updates in real-time as the user types gauge values and DD targets.
|
|
|
|
**XP gauge (mangeoire)**: when the target is unreachable in a single gauge drain, countdown shows drain time instead of `∞`. Formula: `timeToGain(curGl, Math.min(xpRestante, curGl))` — consistent with "Vide en" display.
|
|
|
|
**Default level target**: when `dd.levelTarget === null`, the XP gauge defaults to targeting level 200.
|
|
|
|
### IPC channels
|
|
|
|
Main→Renderer: `app-version`, `play-alarm-sound`, `update-available`, `update-downloading`, `update-progress`, `update-ready`, `update-error`
|
|
Renderer→Main: `trigger-alarm`, `show-notification`, `send-ntfy`, `focus-window`, `install-update`, `get-version`
|
|
|
|
### Auto-update flow
|
|
|
|
Checks Gitea Releases API on startup (after 3s) and hourly. Downloads NSIS Setup .exe to temp, launches with `/S` (silent), then quits app.
|
|
|
|
### Dev vs packaged mode
|
|
|
|
When `!app.isPackaged`, userData is stored in `MinuteurDragodinde-DEV` (isolated from installed app) and a DEV badge is injected into the UI.
|
|
|
|
## Tests
|
|
|
|
```
|
|
tests/
|
|
unit/domain/ — GaugeCalculator, Enclos, Dragodinde, XpTable, etc.
|
|
unit/application/ — commands.test.ts, queries.test.ts, CommandBus.test.ts
|
|
unit/infrastructure/
|
|
functional/ — breeding-workflow, enclos-management, timer-workflow
|
|
regression/ — gauge-tier-calculation, gauge-recharge, xp-timer-display, etc.
|
|
```
|
|
|
|
Vitest with path aliases (`@domain`, `@application`, `@infrastructure`, `@presentation`). Run with `npm test` from Windows.
|
|
|
|
## Key conventions
|
|
|
|
- All UI text is in French
|
|
- TypeScript strict mode — no `any` unless unavoidable
|
|
- Immutable domain entities (functions return new objects)
|
|
- Commands mutate shared `state` object directly (no Redux-style reducers)
|
|
- `repo.save(state)` called after every state mutation
|
|
- CSS uses custom properties in `:root` (dark purple/gaming aesthetic), component styles in `src/presentation/styles/`
|