rename: Minuteur Dragodinde → Obsidienne
- 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>
This commit is contained in:
parent
8af626dd66
commit
0c3b5e27a7
@ -14,7 +14,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Minuteur Dragodinde is a Windows desktop app (Electron + Vite + TypeScript) for managing Dragodinde breeding timers in Dofus 3. French-language UI.
|
||||
Obsidienne (anciennement "Minuteur Dragodinde") is a Windows desktop app (Electron + Vite + TypeScript) for managing Dragodinde breeding timers in Dofus 3. French-language UI.
|
||||
|
||||
## Commands
|
||||
|
||||
@ -89,7 +89,7 @@ Checks Gitea Releases API on startup (after 3s) and hourly. Downloads NSIS Setup
|
||||
|
||||
### 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.
|
||||
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
|
||||
|
||||
|
||||
16
README.md
16
README.md
@ -1,4 +1,4 @@
|
||||
# ⚔ Obsidienne — Minuteur Dragodinde (Dofus 3)
|
||||
# ⚔ Obsidienne — Élevage Dragodinde (Dofus 3)
|
||||
|
||||
Application desktop Windows de gestion d'élevage de Dragodindes pour Dofus 3.
|
||||
Construite avec **Electron + Vite + TypeScript** en architecture **DDD hexagonale**.
|
||||
@ -39,7 +39,7 @@ Construite avec **Electron + Vite + TypeScript** en architecture **DDD hexagonal
|
||||
|
||||
## Installation (utilisateurs)
|
||||
|
||||
1. Télécharger `Minuteur Dragodinde Setup x.x.x.exe` depuis la section [Releases](https://gitea.mickael-pol.fr/mickael/dd-timer/releases)
|
||||
1. Télécharger `Obsidienne Setup x.x.x.exe` depuis la section [Releases](https://gitea.mickael-pol.fr/mickael/dd-timer/releases)
|
||||
2. **Clic droit → Propriétés → cocher "Débloquer" → OK** (important, une seule fois)
|
||||
3. Double-cliquer pour lancer l'installation
|
||||
4. L'app apparaît dans le menu Démarrer et sur le Bureau
|
||||
@ -122,7 +122,7 @@ L'application utilise **electron-updater** avec un fichier `latest.yml` pour la
|
||||
# 2. Build l'application
|
||||
npm run build
|
||||
# → Génère dans dist/ :
|
||||
# - Minuteur Dragodinde Setup 1.x.x.exe
|
||||
# - Obsidienne Setup 1.x.x.exe
|
||||
# - latest.yml (version + sha512, requis par electron-updater)
|
||||
|
||||
# 3. Committer et tagger
|
||||
@ -133,7 +133,7 @@ git push && git push --tags
|
||||
|
||||
# 4. Sur Gitea : Releases → Nouvelle release → tag v1.x.x
|
||||
# Attacher les 2 fichiers :
|
||||
# - Minuteur Dragodinde Setup 1.x.x.exe
|
||||
# - Obsidienne Setup 1.x.x.exe
|
||||
# - latest.yml
|
||||
```
|
||||
|
||||
@ -236,6 +236,12 @@ src/
|
||||
- 🛡 **Nettoyage Ctrl+Z listener** — `removeEventListener` dans `destroy()` pour éviter les memory leaks
|
||||
- 🛡 **Toast stale container** — protection `isConnected` contre les conteneurs DOM détachés
|
||||
|
||||
#### Renommage
|
||||
- 🏷 **Renommage "Minuteur Dragodinde" → "Obsidienne"** — nouveau nom d'application, raccourcis, titre, tray, notifications
|
||||
- 🔄 **Migration automatique des données** — copie transparente du fichier de sauvegarde depuis l'ancien dossier `%APPDATA%\Minuteur Dragodinde\` au premier lancement
|
||||
- 🔄 **GUID NSIS fixe** — l'installeur reconnaît l'ancienne version et la remplace proprement (pas de doublon)
|
||||
- 🔄 **Rétrocompatibilité import** — les backups exportés avec `app: 'minuteur-dragodinde'` restent importables
|
||||
|
||||
#### Technique
|
||||
- ⬆ **Migration electron-updater** — vérification sha512 via `latest.yml`, installation NSIS native, restart auto
|
||||
- 🎨 **Icône Windows native** — migration `icon.png` → `icon.ico`
|
||||
@ -303,7 +309,7 @@ src/
|
||||
- 📝 **Sous-onglets par enclos** (Elevage / Historique bebes)
|
||||
- 🔧 **Mode DEV** — Donnees isolees et badge DEV visible quand lance avec `npm start`
|
||||
- ⬆ **Mise a jour automatique** via Gitea Releases avec banniere de progression dans l'interface
|
||||
- 🔧 Correction de l'identifiant applicatif (`fr.mickael-pol.minuteur-dragodinde`)
|
||||
- 🔧 Correction de l'identifiant applicatif (`fr.mickael-pol.obsidienne`)
|
||||
- 🔧 Masquage des spinners natifs sur les champs numeriques
|
||||
|
||||
### v1.0.0
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Algorithmes de calcul — Minuteur Dragodinde
|
||||
# Algorithmes de calcul — Obsidienne
|
||||
|
||||
Ce document décrit tous les algorithmes utilisés dans l'application, expliqués simplement.
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Fonctionnalités par écran — Minuteur Dragodinde
|
||||
# Fonctionnalités par écran — Obsidienne
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Minuteur Dragodinde - Notifications</title>
|
||||
<title>Obsidienne - Notifications</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0b0b14;color:#dddaf8;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
|
||||
@ -23,7 +23,7 @@ h1{font-size:1.3rem;margin-bottom:8px}
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>Minuteur Dragodinde</h1>
|
||||
<h1>Obsidienne</h1>
|
||||
<p class="sub">Notifications mobiles</p>
|
||||
|
||||
<div id="loading">
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "minuteur-dragodinde",
|
||||
"name": "obsidienne",
|
||||
"version": "1.1.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "minuteur-dragodinde",
|
||||
"name": "obsidienne",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.8.3"
|
||||
|
||||
13
package.json
13
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "minuteur-dragodinde",
|
||||
"name": "obsidienne",
|
||||
"version": "1.1.6",
|
||||
"description": "Minuteur elevage Dragodinde Dofus 3",
|
||||
"description": "Obsidienne — Minuteur d'élevage Dragodinde pour Dofus 3",
|
||||
"main": "dist-electron/main.js",
|
||||
"author": "Mickael",
|
||||
"scripts": {
|
||||
@ -14,12 +14,12 @@
|
||||
"test:e2e": "npx playwright test"
|
||||
},
|
||||
"build": {
|
||||
"appId": "fr.mickael-pol.minuteur-dragodinde",
|
||||
"appId": "fr.mickael-pol.obsidienne",
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "https://gitea.mickael-pol.fr/mickael/dd-timer/releases/download/latest"
|
||||
},
|
||||
"productName": "Minuteur Dragodinde",
|
||||
"productName": "Obsidienne",
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
@ -48,7 +48,8 @@
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "Minuteur Dragodinde",
|
||||
"shortcutName": "Obsidienne",
|
||||
"guid": "3b4a21ac-02a8-4525-a48b-988079fc75d4",
|
||||
"deleteAppDataOnUninstall": false,
|
||||
"runAfterFinish": true
|
||||
}
|
||||
@ -71,7 +72,7 @@
|
||||
"type": "git",
|
||||
"url": "https://gitea.mickael-pol.fr/mickael/dd-timer.git"
|
||||
},
|
||||
"productName": "Minuteur Dragodinde",
|
||||
"productName": "Obsidienne",
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.8.3"
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<html class="dark" lang="fr"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Minuteur Dragodinde - L'Archive d'Obsidienne</title>
|
||||
<title>Obsidienne - Dashboard</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Cinzel:wght@400;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet"/>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<html class="dark" lang="fr"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Minuteur Dragodinde - Enclos 1</title>
|
||||
<title>Obsidienne - Enclos 1</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;600;700&family=Material+Icons+Round&display=swap" rel="stylesheet"/>
|
||||
<script>
|
||||
|
||||
@ -14,10 +14,10 @@ import fs from 'fs';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
|
||||
// ─── NOM DE L'APPLICATION ─────────────────────────────────────────────────────
|
||||
app.setName('Minuteur Dragodinde');
|
||||
app.setName('Obsidienne');
|
||||
// Windows utilise l'AppUserModelId pour le nom affiché dans les notifications
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId('Minuteur Dragodinde');
|
||||
app.setAppUserModelId('Obsidienne');
|
||||
}
|
||||
|
||||
// ─── MODE DEV / E2E ────────────────────────────<E29480><E29480>─────────────────────────────
|
||||
@ -26,7 +26,23 @@ if (process.env.ELECTRON_USER_DATA_DIR) {
|
||||
app.setPath('userData', process.env.ELECTRON_USER_DATA_DIR);
|
||||
} else if (!app.isPackaged) {
|
||||
// En dev (npm start), les données sont isolées de l'app installée
|
||||
app.setPath('userData', path.join(app.getPath('appData'), 'MinuteurDragodinde-DEV'));
|
||||
app.setPath('userData', path.join(app.getPath('appData'), 'Obsidienne-DEV'));
|
||||
}
|
||||
|
||||
// ─── MIGRATION DONNÉES (ancien nom → Obsidienne) ────────────────────────────
|
||||
// Les utilisateurs de "Minuteur Dragodinde" conservent leurs données après le renommage
|
||||
if (app.isPackaged) {
|
||||
const oldDataFile = path.join(app.getPath('appData'), 'Minuteur Dragodinde', 'dd-timer-data.json');
|
||||
const newDataFile = path.join(app.getPath('userData'), 'dd-timer-data.json');
|
||||
if (!fs.existsSync(newDataFile) && fs.existsSync(oldDataFile)) {
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(newDataFile), { recursive: true });
|
||||
fs.copyFileSync(oldDataFile, newDataFile);
|
||||
console.log('Migration données: Minuteur Dragodinde → Obsidienne OK');
|
||||
} catch (e: unknown) {
|
||||
console.error('Migration données échouée:', (e as Error).message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── CONFIG GITEA ─────────────────────────────────────────────────────────────
|
||||
@ -59,7 +75,7 @@ function getAppIcon(): Electron.NativeImage {
|
||||
function createWindow(): void {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1280, height: 900, minWidth: 960, minHeight: 650,
|
||||
title: 'Minuteur Dragodinde - Dofus 3',
|
||||
title: 'Obsidienne - Dofus 3',
|
||||
icon: getAppIcon(),
|
||||
backgroundColor: '#0b0b14',
|
||||
webPreferences: {
|
||||
@ -85,7 +101,7 @@ function createWindow(): void {
|
||||
buttons: ['Minimiser', 'Quitter'],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
title: 'Minuteur Dragodinde',
|
||||
title: 'Obsidienne',
|
||||
message: 'Que souhaites-tu faire ?',
|
||||
detail: 'Minimiser garde l\'app en arriere-plan.\nLes alarmes continueront de sonner.',
|
||||
});
|
||||
@ -127,7 +143,7 @@ function createWindow(): void {
|
||||
// ─── TRAY ─────────────────────────────────────────────────────────────────────
|
||||
function createTray(): void {
|
||||
tray = new Tray(getAppIcon());
|
||||
tray.setToolTip(`Minuteur Dragodinde v${CURRENT_VERSION}`);
|
||||
tray.setToolTip(`Obsidienne v${CURRENT_VERSION}`);
|
||||
rebuildTrayMenu();
|
||||
tray.on('double-click', () => { mainWindow!.show(); mainWindow!.focus(); });
|
||||
}
|
||||
@ -135,7 +151,7 @@ function createTray(): void {
|
||||
function rebuildTrayMenu(): void {
|
||||
if (!tray) return;
|
||||
const items: Electron.MenuItemConstructorOptions[] = [
|
||||
{ label: `Minuteur Dragodinde v${CURRENT_VERSION}`, enabled: false },
|
||||
{ label: `Obsidienne v${CURRENT_VERSION}`, enabled: false },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Ouvrir', click: () => { mainWindow!.show(); mainWindow!.focus(); } },
|
||||
];
|
||||
@ -361,7 +377,7 @@ function checkForUpdates(silent = false): void {
|
||||
path: `/api/v1/repos/${GITEA_USER}/${GITEA_REPO}/releases?limit=1`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': `MinuteurDragodinde/${CURRENT_VERSION}`,
|
||||
'User-Agent': `Obsidienne/${CURRENT_VERSION}`,
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
@ -56,7 +56,7 @@ export class App {
|
||||
<header class="app-header">
|
||||
<button class="app-hamburger" id="hamburger-btn">☰</button>
|
||||
<div class="app-header-text">
|
||||
<h1 class="app-title"><span class="app-title-icon">⚔</span> Minuteur Dragodinde</h1>
|
||||
<h1 class="app-title"><span class="app-title-icon">⚔</span> Obsidienne</h1>
|
||||
<p class="app-subtitle">Dofus 3 · Gestion multi-enclos en temps réel</p>
|
||||
</div>
|
||||
<div class="app-hamburger" style="visibility:hidden;pointer-events:none;" aria-hidden="true"></div>
|
||||
|
||||
@ -301,7 +301,7 @@ export class ParametresView {
|
||||
const { ntfyTopic } = this.getSettings();
|
||||
if (!ntfyTopic) return;
|
||||
const url = `${NTFY_BASE}/${ntfyTopic}`;
|
||||
(window as any).electronAPI?.sendNtfy?.(url, 'Test alarme', 'Ceci est un test de la notification mobile Minuteur Dragodinde !');
|
||||
(window as any).electronAPI?.sendNtfy?.(url, 'Test alarme', 'Ceci est un test de la notification mobile Obsidienne !');
|
||||
}
|
||||
|
||||
/* ══ Backup / Restore ══ */
|
||||
@ -319,7 +319,7 @@ export class ParametresView {
|
||||
|
||||
const version = await api.getVersion?.() ?? 'unknown';
|
||||
const backup = {
|
||||
app: 'minuteur-dragodinde',
|
||||
app: 'obsidienne',
|
||||
version,
|
||||
exportedAt: new Date().toISOString(),
|
||||
data: JSON.parse(raw),
|
||||
@ -366,7 +366,7 @@ export class ParametresView {
|
||||
};
|
||||
|
||||
// Validation du format backup
|
||||
if (parsed.app === 'minuteur-dragodinde' && parsed.data && typeof parsed.data === 'object' && parsed.data !== null) {
|
||||
if ((parsed.app === 'obsidienne' || parsed.app === 'minuteur-dragodinde') && parsed.data && typeof parsed.data === 'object' && parsed.data !== null) {
|
||||
if (!validateEnclosData(parsed.data)) {
|
||||
Toast.show('error', 'Le backup contient des données corrompues ou incomplètes.');
|
||||
return;
|
||||
|
||||
@ -57,19 +57,19 @@ describe('Sécurité — Hardening', () => {
|
||||
describe('Validation backup — format métadonnées', () => {
|
||||
it('backup valide contient app, version, exportedAt, data', () => {
|
||||
const backup = {
|
||||
app: 'minuteur-dragodinde',
|
||||
app: 'obsidienne',
|
||||
version: '1.1.6',
|
||||
exportedAt: new Date().toISOString(),
|
||||
data: { enclos: [] },
|
||||
};
|
||||
expect(backup.app).toBe('minuteur-dragodinde');
|
||||
expect(backup.app).toBe('obsidienne');
|
||||
expect(typeof backup.version).toBe('string');
|
||||
expect(typeof backup.exportedAt).toBe('string');
|
||||
expect(backup.data).toBeDefined();
|
||||
});
|
||||
|
||||
it('data: null est détecté comme invalide', () => {
|
||||
const backup = { app: 'minuteur-dragodinde', data: null };
|
||||
const backup = { app: 'obsidienne', data: null };
|
||||
// typeof null === 'object' mais data === null doit être rejeté
|
||||
expect(backup.data === null).toBe(true);
|
||||
expect(!!(backup.data && typeof backup.data === 'object' && backup.data !== null)).toBe(false);
|
||||
|
||||
@ -112,6 +112,6 @@ describe('Validation import — validateEnclosData', () => {
|
||||
});
|
||||
|
||||
it('rejette un objet backup avec data: null', () => {
|
||||
expect(validateEnclosData({ app: 'minuteur-dragodinde', data: null })).toBe(false);
|
||||
expect(validateEnclosData({ app: 'obsidienne', data: null })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user