sokko-g/docs/STORAGE_SCHEMA.md
Shinuwa de1b8a5638
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
improve notepad features and add drawing canvas
2026-07-29 13:51:42 +02:00

12 KiB

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 :

{
  "toolboxes": [],
  "links": {},
  "setting:toolboxOrder": [],
  "setting:storageBudgetBytes": 536870912,
  "setting:toastPosition": "right"
}

Entrée type dans modules :

{
  "key": "tbx1:mod1",
  "data": {}
}

Toolbox

Les toolboxes sont stockées dans kv/toolboxes.

{
  "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.

{
  "mhwilds": "tbx1",
  "diablo4": "tbx2"
}

Outil Bloc Notes

Type : notepad

{
  "html": "<p>Notes <strong>rapides</strong>...</p>",
  "text": "Notes rapides...",
  "updatedAt": "2026-07-29T12:00:00.000Z",
  "drawingMode": "permanent",
  "drawings": {
    "strokes": [
      {
        "id": "stroke1",
        "color": "#f6c453",
        "width": 4,
        "points": [
          { "x": 12.5, "y": 48 },
          { "x": 24, "y": 52.5 }
        ]
      }
    ]
  }
}

Stockage compact :

  • l'ancien format { "text": "..." } reste accepté et est converti en HTML riche au prochain enregistrement ;
  • html est nettoyé avec une allowlist de balises et de couleurs ;
  • text conserve une version texte pour les exports et les fallbacks ;
  • updatedAt est omis si vide ou invalide ;
  • drawingMode vaut temporary par défaut et est omis dans ce cas ;
  • drawings est stocké uniquement si drawingMode vaut permanent et qu'au moins un trait valide existe ;
  • les dessins temporaires du bloc-notes sont conservés dans sessionStorage, pas dans IndexedDB ;
  • si le texte, la date et les dessins permanents sont vides, l'entrée de module peut être supprimée.

Outil Checklist

Type : checklist

Format simple, sans catégorie :

{
  "items": [
    {
      "id": "item1",
      "label": "Potion",
      "qtyTarget": 10,
      "qtyCurrent": 3
    }
  ]
}

Format avec catégories :

{
  "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 : images

{
  "images": [
    {
      "id": "image1",
      "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

{
  "image": "data:image/webp;base64,...",
  "markers": [
    {
      "id": "marker1",
      "x": 42.5,
      "y": 68,
      "label": "Entrée"
    }
  ],
  "drawings": {
    "strokes": [
      {
        "id": "stroke1",
        "color": "#f6c453",
        "width": 4,
        "points": [
          { "x": 18.5, "y": 32 },
          { "x": 24, "y": 40.5 }
        ]
      }
    ]
  }
}

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.
  • drawings est facultatif et omis si aucun trait valide n'existe.
  • les points des dessins sont stockés en pourcentages entre 0 et 100 pour rester alignés en aperçu réduit et en plein écran.
  • les dessins sont éditables uniquement depuis la modale plein écran ; l'aperçu réduit les affiche en lecture seule.
  • le mode temporaire des dessins d'image utilise sessionStorage et ne modifie pas drawings dans IndexedDB tant que l'utilisateur ne repasse pas en stockage permanent.

Outil Timer

Type : timer

Structure initiale :

{
  "activeTab": "stopwatch",
  "scrollResults": true,
  "sortResults": true,
  "stopwatch": {
    "elapsedMs": 12000,
    "startedAt": 1760000000000,
    "laps": [
      {
        "id": "timer1",
        "label": "Phase 1",
        "elapsedMs": 9000
      }
    ]
  },
  "countdowns": [
    {
      "id": "timer2",
      "label": "Boss",
      "type": "daily_time",
      "alertMode": "visible",
      "time": "12:03:00",
      "targetAt": 1760000000000
    },
    {
      "id": "timer3",
      "label": "Event",
      "type": "time_pattern",
      "pattern": "X:45:00"
    },
    {
      "id": "timer4",
      "label": "Event",
      "type": "interval",
      "intervalMs": 1800000,
      "anchorAt": 1760000000000
    }
  ]
}

Types de compte à rebours :

  • duration : durée ponctuelle, avec durationMs et targetAt.
  • daily_time : occurrence ponctuelle à une heure précise, avec time au format HH:MM:SS et targetAt.
  • time_pattern : prochaine occurrence automatique correspondant à un pattern HH:MM:SS, où chaque segment peut valoir X.
  • interval : prochaine occurrence automatique toutes les X millisecondes, avec intervalMs et anchorAt.

Pour time_pattern, un segment X situé après un segment plus large fixé cible le premier instant de l'occurrence. Exemple : X:24:X cible hh:24:00 et se répète toutes les heures.

Stockage compact :

  • activeTab est omis si la valeur vaut stopwatch.
  • scrollResults est omis si false.
  • sortResults est omis si false.
  • stopwatch est omis tant que le chrono est à zéro et qu'aucune étape n'est enregistrée.
  • elapsedMs est omis si 0.
  • startedAt est omis si le chrono n'est pas lancé.
  • laps est omis tant qu'aucune étape n'est enregistrée.
  • countdowns est omis tant qu'aucun compte à rebours n'est configuré.
  • countdowns[].alertMode vaut off, visible ou site, et est omis si off.
  • L'activation d'une alerte est bloquée dans l'interface si la fréquence configurée est inférieure à 5 minutes pour les comptes à rebours récurrents (time_pattern et interval). Le blocage ne dépend pas du temps restant avant la prochaine échéance.

Outil Task Planner

Type : taskPlanner

{
  "weeklyResetDay": 1,
  "resetTime": "00:00",
  "lastResetAt": 1760000000000,
  "hideCompleted": true,
  "categoryOrder": ["Raid", "Farm"],
  "collapsedCategories": ["Farm"],
  "tasks": [
    {
      "id": "task1",
      "title": "Quête 1",
      "description": "Préparer la route.",
      "category": "Raid",
      "type": "daily",
      "checked": true,
      "checkedAt": 1760000000000
    },
    {
      "id": "task2",
      "title": "Quête 2",
      "type": "weekly",
      "weeklyResetDay": 3
    }
  ],
  "relations": [
    {
      "id": "relation1",
      "fromTaskId": "task1",
      "toTaskId": "task2"
    },
    {
      "id": "relation2",
      "fromTaskId": "task2",
      "toTaskId": "task1",
      "prerequisite": true
    }
  ]
}

Notes :

  • type vaut unique, daily ou weekly.
  • weeklyResetDay suit les jours JavaScript : 0 dimanche, 1 lundi, ..., 6 samedi.
  • resetTime est global à l'outil et vaut HH:MM.
  • hideCompleted masque les tâches effectuées à l'affichage, ainsi que les catégories dont toutes les tâches sont effectuées.
  • weeklyResetDay sur une tâche hebdo surcharge le jour global.
  • category sur une tâche racine est facultatif. Les tâches enfants héritent de la catégorie effective de leur parent et ne stockent pas de catégorie propre.
  • Les tâches catégorisées sont regroupées par catégorie au niveau racine.
  • categoryOrder stocke l'ordre d'affichage des catégories utilisées. Les catégories absentes des tâches sont retirées à la normalisation.
  • collapsedCategories stocke les noms des catégories repliées.
  • relations stocke les parents directionnels. fromTaskId désigne la tâche enfant, toTaskId son parent.
  • une tâche ne conserve qu'un seul parent.
  • prerequisite: true marque aussi ce parent comme pré requis.
  • Les pré requis sont non bloquants : ils affichent un avertissement si le parent requis n'est pas effectué.
  • Le drag & drop conserve l'ordre manuel, et peut aussi ajouter une tâche racine à une catégorie existante ou la sortir vers les tâches sans catégorie.

Stockage compact :

  • weeklyResetDay global est omis si la valeur vaut 1.
  • resetTime est omis si la valeur vaut 00:00.
  • lastResetAt est omis si la valeur vaut 0.
  • hideCompleted est omis si false.
  • description est omis si vide.
  • category est omis si vide ou si la tâche a un parent.
  • categoryOrder est omis si aucune catégorie n'est utilisée.
  • collapsedCategories est omis si aucune catégorie n'est repliée.
  • checked et checkedAt sont omis si la tâche n'est pas effectuée.
  • weeklyResetDay unitaire est omis si non défini.
  • relations est omis si vide.
  • relations[].prerequisite est omis si false.

Outil Liens

Type : links

{
  "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

{
  "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

{
  "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 :

{
  "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/features/toolboxes/storage/toolboxStorage.js ;
  • 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.