- Renommage complet dans package.json, main.ts, UI, tray, notifications - GUID NSIS fixe pour mise à jour propre (pas de doublon d'installation) - Migration automatique des données depuis %APPDATA%\Minuteur Dragodinde\ - Rétrocompatibilité import : backups 'minuteur-dragodinde' toujours acceptés - Mise à jour README changelog, CLAUDE.md, docs, maquettes, page ntfy Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.7 KiB
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.mdquand un algorithme ou une formule de calcul change - Ne jamais utiliser de taux fixe pour les jauges — toujours utiliser
gainedIn/timeToGainavec 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 testdepuis un terminal Windows
Project Overview
Obsidienne (anciennement "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 viavite-plugin-electron) - Build:
npm run build(Vite build + electron-builder → NSIS installer indist/) - 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) orbuild.ps1for 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 withTimerData(includesgaugeRecharges: Record<string, GaugeRecharge[]>)entities/Dragodinde.ts— DD entity with stats, targets,levelTarget,sereniteTargetservices/GaugeCalculator.ts— Core pure functions:gainedIn,timeToGain,gaugeAfter,elapsed,computeGaugeStatevalue-objects/GaugeType.ts—GAUGE_DEFS,STAT_DEFS,DEFAULT_TARGETS(mangeoire default = 100, but UI uses 200 whenlevelTargetis null)value-objects/XpTable.ts—xpForLevel,levelFromXpvalue-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 wiringcomponents/EnclosView.ts— Main timer view: gauge inputs (withinputlistener for real-time updates), DD grid, "Alarme dans" displaycomponents/DragodindeCard.ts— Per-DD card: stat pills, targets (all withinputlisteners 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 Obsidienne-DEV (isolated from installed app) and a DEV badge is injected into the UI. On first packaged launch, data is auto-migrated from the old Minuteur Dragodinde folder.
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
anyunless unavoidable - Immutable domain entities (functions return new objects)
- Commands mutate shared
stateobject 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 insrc/presentation/styles/