diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..13fb52e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# Agent Instructions + +Sokko G est une webapp React + Vite + Sass centrée sur des toolboxes locales et des pages de jeu. + +## Références projet + +Avant de modifier un outil toolbox, le stockage IndexedDB, l'import/export ou les formats de données, lire : + +- `docs/STORAGE_SCHEMA.md` +- `docs/FEATURE_CHECKLIST.md` + +Après ce type de modification, mettre à jour ces documents si le format ou le workflow change. + +## Contenu éditable + +Le contenu éditorial du site doit rester dans : + +- `website/public/data/site.json` +- `website/public/data//...` + +Ne pas réintroduire de fallback massif type `DEFAULT_SITE_CONTENT` dans le code React. `site.json` est la source de vérité et `npm run check` doit échouer si le contenu requis est invalide. + +## Outils toolbox + +Chaque outil toolbox doit rester dans son propre fichier dans : + +```text +website/src/features/toolboxes/modules/ +``` + +Quand un outil est ajouté ou modifié : + +- déclarer l'outil dans `website/src/features/toolboxes/modules/index.jsx` ; +- ajouter les textes dans `website/public/data/site.json` ; +- mettre à jour la validation dans `tests/helpers/data-validation.mjs` ; +- mettre à jour la structure IndexedDB dans `docs/STORAGE_SCHEMA.md` si nécessaire ; +- vérifier l'affichage page toolbox et panneau latéral. + +## Style + +Le style est en Sass dans `website/src/styles/`. + +Privilégier : + +- les tokens existants ; +- les classes de boutons existantes ; +- les patterns visuels déjà présents pour les cards, panels, badges, scrollbars et hovers. + +Éviter les styles isolés qui ne réutilisent pas le thème. + +## Validation + +Avant de terminer une modification significative, lancer : + +```bash +npm run check +``` + +Le check couvre la génération des index de listes, les tests et le build de production. + +## Pré-push check + +Quand l'utilisateur indique qu'il va push, faire une passe globale avant de conclure. + +Vérifier et corriger si nécessaire : + +- code mort, imports inutilisés, fonctions inutilisées, styles devenus obsolètes ; +- duplication de code ou de style pouvant être factorisée sans complexifier le projet ; +- composants qui utilisent des classes trop spécifiques alors qu'un pattern général existe déjà ; +- incohérences entre boutons, badges, cards, panels, hovers, focus states et scrollbars ; +- cohérence entre `website/public/data/site.json`, les validations et les textes utilisés ; +- cohérence entre `docs/STORAGE_SCHEMA.md` et les normalisations IndexedDB réelles ; +- import/export des toolboxes quand la structure de données a changé ; +- champs inutiles persistés dans IndexedDB ou dans les exports ; +- fichiers générés attendus, notamment les index de listes ; +- optimisations simples de performance : recalculs au render, listeners non nettoyés, observers, effets CSS lourds appliqués en masse. + +Utiliser en priorité : + +```bash +rg +git status --short +git diff --stat +npm run check +``` + +Ne pas faire de refactor large sans bénéfice clair. Garder les corrections pré-push ciblées, vérifiables et faciles à relire. diff --git a/docs/FEATURE_CHECKLIST.md b/docs/FEATURE_CHECKLIST.md new file mode 100644 index 0000000..73a1d70 --- /dev/null +++ b/docs/FEATURE_CHECKLIST.md @@ -0,0 +1,60 @@ +# Feature Checklist + +Checklist à suivre lors de l'ajout ou de la modification d'un outil, d'une page jeu ou d'un format de données. + +## Outil Toolbox + +- Ajouter ou modifier le composant dans `website/src/features/toolboxes/modules/`. +- Déclarer l'outil dans `website/src/features/toolboxes/modules/index.jsx`. +- Ajouter les textes nécessaires dans `website/public/data/site.json`. +- Mettre à jour la validation de `site.json` dans `tests/helpers/data-validation.mjs`. +- Mettre à jour la normalisation et le stockage compact dans `website/src/main.jsx`. +- Mettre à jour `docs/STORAGE_SCHEMA.md`. +- Vérifier l'import et l'export si l'outil stocke des données. +- Vérifier l'affichage dans la page toolbox complète. +- Vérifier l'affichage dans le panneau latéral. +- Vérifier le mode une colonne et deux colonnes. +- Vérifier le quota de stockage si l'outil manipule des données lourdes. + +## Page Jeu + +- Ajouter les données dans `website/public/data/`. +- Ajouter les images dans `website/public/static/img/games//`. +- Déclarer le jeu dans `website/public/data/site.json`. +- Vérifier la page `/games`. +- Vérifier la page `/games/`. +- Vérifier le bouton toolbox et le panneau latéral. +- Vérifier le breadcrumb. +- Vérifier les textes avec retours à la ligne. + +## Listes de jeu + +- Ajouter la liste JSON dans `website/public/data//lists/`. +- Lancer `npm run lists:index`. +- Vérifier que `index.json` est régénéré. +- Vérifier que `nom` est présent sur chaque élément. +- Vérifier que `description` et `quantite` restent facultatifs. +- Vérifier la copie au format checklist. +- Vérifier la création directe de checklist quand une toolbox est associée. + +## Style + +- Réutiliser les tokens existants avant d'ajouter une nouvelle couleur ou taille. +- Réutiliser les classes de boutons existantes. +- Vérifier les boutons icon, danger, primary et secondary. +- Vérifier les cartes, panels et scrollbars. +- Vérifier les états hover/focus. +- Vérifier mobile et desktop quand le changement touche la mise en page. + +## Validation avant push + +```bash +npm run check +``` + +Le check doit passer avant de pousser : + +- syntaxe serveur et Vite ; +- génération des index de listes ; +- tests Node ; +- build de production. diff --git a/docs/STORAGE_SCHEMA.md b/docs/STORAGE_SCHEMA.md new file mode 100644 index 0000000..c3156c1 --- /dev/null +++ b/docs/STORAGE_SCHEMA.md @@ -0,0 +1,313 @@ +# Storage Schema + +Ce document décrit les données persistées dans IndexedDB pour les toolboxes. +Il doit être mis à jour à chaque ajout ou modification d'outil. + +## IndexedDB + +Base : `sokkog` + +Object stores : + +- `kv` : données globales simples. +- `modules` : données des outils, séparées par toolbox et par module. + +Clés principales dans `kv` : + +```json +{ + "toolboxes": [], + "links": {}, + "setting:toolboxOrder": [], + "setting:storageBudgetBytes": 536870912 +} +``` + +Entrée type dans `modules` : + +```json +{ + "key": "tbx1:mod1", + "data": {} +} +``` + +## Toolbox + +Les toolboxes sont stockées dans `kv/toolboxes`. + +```json +{ + "id": "tbx1", + "name": "Monster Hunter Wilds", + "gameId": "mhwilds", + "icon": "/static/img/toolbox-icons/toolbox.png", + "modules": [ + { + "id": "mod1", + "type": "checklist", + "title": "Armures" + } + ], + "layout": "two", + "updatedAt": "2026-07-25T12:00:00.000Z" +} +``` + +Notes : + +- `gameId` est vide pour une toolbox libre. +- `icon` est utilisé uniquement pour les toolboxes libres. +- `layout` vaut généralement `one` ou `two`. +- Les données lourdes des outils ne sont pas stockées dans la toolbox, mais dans `modules`. + +## Liens Jeu / Toolbox + +Les associations entre pages jeux et toolboxes sont stockées dans `kv/links`. + +```json +{ + "mhwilds": "tbx1", + "diablo4": "tbx2" +} +``` + +## Outil Bloc Notes + +Type : `notepad` + +```json +{ + "text": "Notes rapides..." +} +``` + +Stockage compact : + +- si `text` est vide, l'entrée de module peut être supprimée. + +## Outil Checklist + +Type : `checklist` + +Format simple, sans catégorie : + +```json +{ + "items": [ + { + "id": "item1", + "label": "Potion", + "qtyTarget": 10, + "qtyCurrent": 3 + } + ] +} +``` + +Format avec catégories : + +```json +{ + "hideCompletedSections": true, + "hideCompletedSectionsFully": false, + "sections": [ + { + "id": "section1", + "title": "Rey Dau alpha", + "hideWhenComplete": false, + "collapsed": false, + "items": [ + { + "id": "item1", + "label": "Casque", + "qtyTarget": 1, + "qtyCurrent": 0 + } + ] + } + ] +} +``` + +Stockage compact : + +- `qtyTarget` est omis si la valeur vaut `1`. +- `qtyCurrent` est omis si la valeur vaut `0`. +- `hideCompletedSections` est omis si `false`. +- `hideCompletedSectionsFully` est omis si `false`. +- `title` est omis si vide. +- `hideWhenComplete` est omis si non défini. +- `collapsed` est omis si `false`. + +## Outil Images + +Type : `screenshots` + +```json +{ + "shots": [ + { + "id": "shot1", + "label": "Map zone nord", + "dataUrl": "data:image/webp;base64,..." + } + ] +} +``` + +Stockage compact : + +- `label` est facultatif. +- `label` est omis si vide. +- `createdAt` n'est pas stocké. +- Le nom de fichier original n'est pas stocké. + +## Outil Annotation d'images + +Type : `imageAnnotation` + +```json +{ + "image": "data:image/webp;base64,...", + "markers": [ + { + "id": "marker1", + "x": 42.5, + "y": 68, + "label": "Entrée" + } + ] +} +``` + +Stockage compact : + +- un seul visuel par outil. +- `x` et `y` sont des pourcentages entre `0` et `100`. +- `label` est facultatif. +- `label` est omis si vide. + +## Outil Liens + +Type : `links` + +```json +{ + "links": [ + { + "id": "link1", + "title": "Build rogue", + "url": "https://example.com/build" + } + ] +} +``` + +Stockage compact : + +- `title` est facultatif. +- `title` est omis si vide. +- `url` est obligatoire. + +## Outil Compteurs + +Type : `counters` + +```json +{ + "counters": [ + { + "id": "counter1", + "label": "Victoire", + "value": 12 + }, + { + "id": "counter2", + "label": "Défaite", + "value": -2 + } + ] +} +``` + +Notes : + +- `value` est numérique. +- Les valeurs négatives sont acceptées. +- Les compteurs sans libellé ne sont pas conservés. + +## Outil Calculateur + +Type : `calculator` + +```json +{ + "scrollResults": true, + "entries": [ + { + "id": "calc1", + "label": "Lingots de fer", + "value": 100 + }, + { + "id": "calc2", + "parentId": "calc1", + "label": "Minerais de fer", + "value": 500 + } + ] +} +``` + +Stockage compact : + +- `scrollResults` est omis si `false`. +- `parentId` est omis si vide. +- `label` peut être vide. +- `value` est numérique. +- Si un `parentId` ne pointe vers aucune entrée existante, il est réinitialisé à vide. + +## Export + +L'export d'une toolbox remappe les identifiants pour produire des IDs courts et indépendants. + +Exemple : + +```json +{ + "toolbox": { + "id": "t1", + "name": "MHW", + "modules": [ + { + "id": "m1", + "type": "checklist", + "title": "Armures" + } + ], + "updatedAt": "2026-07-25T12:00:00.000Z" + }, + "modules": { + "m1": { + "items": [ + { + "id": "i1", + "label": "Casque" + } + ] + } + } +} +``` + +## A maintenir à chaque update + +Quand un outil change de structure : + +- mettre à jour ce document ; +- mettre à jour les normalisations dans `website/src/main.jsx` ; +- mettre à jour les textes dans `website/public/data/site.json` si nécessaire ; +- mettre à jour la validation dans `tests/helpers/data-validation.mjs` si nécessaire ; +- vérifier import/export ; +- lancer `npm run check`. diff --git a/tests/helpers/data-validation.mjs b/tests/helpers/data-validation.mjs index dfd78c4..71802d0 100644 --- a/tests/helpers/data-validation.mjs +++ b/tests/helpers/data-validation.mjs @@ -106,6 +106,8 @@ export function validateSiteContent(site) { "toolboxes.modules.screenshots.pastePlaceholder", "toolboxes.modules.screenshots.pasteAriaLabel", "toolboxes.modules.screenshots.imageAlt", + "toolboxes.modules.screenshots.labelPlaceholder", + "toolboxes.modules.screenshots.labelAriaLabel", "toolboxes.modules.screenshots.previewAriaLabel", "toolboxes.modules.screenshots.annotateAriaLabel", "toolboxes.modules.screenshots.annotateTitle", diff --git a/tests/static-smoke.test.mjs b/tests/static-smoke.test.mjs index fa4f858..b8a6c99 100644 --- a/tests/static-smoke.test.mjs +++ b/tests/static-smoke.test.mjs @@ -10,6 +10,19 @@ test("vite entrypoint loads the react application", async () => { assert.match(html, /\/src\/main\.jsx/); }); +test("project documentation tracks storage and feature updates", async () => { + const storageSchema = await readFile("docs/STORAGE_SCHEMA.md", "utf8"); + const featureChecklist = await readFile("docs/FEATURE_CHECKLIST.md", "utf8"); + + assert.match(storageSchema, /IndexedDB/); + assert.match(storageSchema, /Outil Checklist/); + assert.match(storageSchema, /Outil Images/); + assert.match(storageSchema, /Outil Annotation d'images/); + assert.match(storageSchema, /Outil Calculateur/); + assert.match(featureChecklist, /Mettre à jour `docs\/STORAGE_SCHEMA\.md`/); + assert.match(featureChecklist, /npm run check/); +}); + test("react application defines the expected local toolbox primitives", async () => { const source = await readFile("website/src/main.jsx", "utf8"); const styles = await readFile("website/src/styles/main.scss", "utf8"); diff --git a/website/public/data/site.json b/website/public/data/site.json index 7d48659..6287250 100644 --- a/website/public/data/site.json +++ b/website/public/data/site.json @@ -224,6 +224,8 @@ "pastePlaceholder": "Coller une image ici", "pasteAriaLabel": "Coller une image depuis le presse-papiers", "imageAlt": "Image", + "labelPlaceholder": "Libellé de l'image", + "labelAriaLabel": "Libellé de l'image", "previewAriaLabel": "Agrandir l'image", "annotateAriaLabel": "Annoter l'image", "annotateTitle": "Annoter", diff --git a/website/src/features/toolboxes/modules/ScreenshotsModule.jsx b/website/src/features/toolboxes/modules/ScreenshotsModule.jsx index a1d9d5f..189f7dd 100644 --- a/website/src/features/toolboxes/modules/ScreenshotsModule.jsx +++ b/website/src/features/toolboxes/modules/ScreenshotsModule.jsx @@ -13,6 +13,12 @@ export function ScreenshotsModule({ toolboxId, moduleId, context, editing }) { } } + function updateShot(shotId, patch) { + context.setModuleData(toolboxId, moduleId, { + shots: data.shots.map((shot) => shot.id === shotId ? { ...shot, ...patch } : shot) + }); + } + return ( <> {editing && ( @@ -66,6 +72,17 @@ export function ScreenshotsModule({ toolboxId, moduleId, context, editing }) { + {editing ? ( + updateShot(shot.id, { label: event.target.value })} + aria-label={textContent.labelAriaLabel || "Libellé de l'image"} + /> + ) : shot.label ? ( +
{shot.label}
+ ) : null}
- )} - + {shot.label && {shot.label}} +
+ {!canAnnotate && !hasMarkers && ( + + )} + +