14 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 :
gameIdest vide pour une toolbox libre.iconest utilisé uniquement pour les toolboxes libres.layoutvaut généralementoneoutwo.- 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 ; htmlest nettoyé avec une allowlist de balises et de couleurs ;textconserve une version texte pour les exports et les fallbacks ;updatedAtest omis si vide ou invalide ;drawingModevauttemporarypar défaut et est omis dans ce cas ;drawingsest stocké uniquement sidrawingModevautpermanentet 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 :
qtyTargetest omis si la valeur vaut1.qtyCurrentest omis si la valeur vaut0.hideCompletedSectionsest omis sifalse.hideCompletedSectionsFullyest omis sifalse.titleest omis si vide.hideWhenCompleteest omis si non défini.collapsedest omis sifalse.
Outil Images
Type : images
{
"images": [
{
"id": "image1",
"label": "Map zone nord",
"dataUrl": "data:image/webp;base64,..."
}
]
}
Stockage compact :
labelest facultatif.labelest omis si vide.createdAtn'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.
xetysont des pourcentages entre0et100.labelest facultatif.labelest omis si vide.drawingsest facultatif et omis si aucun trait valide n'existe.- les points des dessins sont stockés en pourcentages entre
0et100pour 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
sessionStorageet ne modifie pasdrawingsdans 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, avecdurationMsettargetAt.daily_time: occurrence ponctuelle à une heure précise, avectimeau formatHH:MM:SSettargetAt.time_pattern: prochaine occurrence automatique correspondant à un patternHH:MM:SS, où chaque segment peut valoirX.interval: prochaine occurrence automatique toutes les X millisecondes, avecintervalMsetanchorAt.
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 :
activeTabest omis si la valeur vautstopwatch.scrollResultsest omis sifalse.sortResultsest omis sifalse.stopwatchest omis tant que le chrono est à zéro et qu'aucune étape n'est enregistrée.elapsedMsest omis si0.startedAtest omis si le chrono n'est pas lancé.lapsest omis tant qu'aucune étape n'est enregistrée.countdownsest omis tant qu'aucun compte à rebours n'est configuré.countdowns[].alertModevautoff,visibleousite, et est omis sioff.- 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_patternetinterval). 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 :
typevautunique,dailyouweekly.weeklyResetDaysuit les jours JavaScript :0dimanche,1lundi, ...,6samedi.resetTimeest global à l'outil et vautHH:MM.hideCompletedmasque les tâches effectuées à l'affichage, ainsi que les catégories dont toutes les tâches sont effectuées.weeklyResetDaysur une tâche hebdo surcharge le jour global.categorysur 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.
categoryOrderstocke l'ordre d'affichage des catégories utilisées. Les catégories absentes des tâches sont retirées à la normalisation.collapsedCategoriesstocke les noms des catégories repliées.relationsstocke les parents directionnels.fromTaskIddésigne la tâche enfant,toTaskIdson parent.- une tâche ne conserve qu'un seul parent.
prerequisite: truemarque 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 :
weeklyResetDayglobal est omis si la valeur vaut1.resetTimeest omis si la valeur vaut00:00.lastResetAtest omis si la valeur vaut0.hideCompletedest omis sifalse.descriptionest omis si vide.categoryest omis si vide ou si la tâche a un parent.categoryOrderest omis si aucune catégorie n'est utilisée.collapsedCategoriesest omis si aucune catégorie n'est repliée.checkedetcheckedAtsont omis si la tâche n'est pas effectuée.weeklyResetDayunitaire est omis si non défini.relationsest omis si vide.relations[].prerequisiteest omis sifalse.
Outil Liens
Type : links
{
"links": [
{
"id": "link1",
"title": "Build rogue",
"url": "https://example.com/build"
}
]
}
Stockage compact :
titleest facultatif.titleest omis si vide.urlest obligatoire.
Outil Compteurs
Type : counters
{
"counters": [
{
"id": "counter1",
"label": "Victoire",
"value": 12
},
{
"id": "counter2",
"label": "Défaite",
"value": -2
}
]
}
Notes :
valueest numérique.- Les valeurs négatives sont acceptées.
- Les compteurs sans libellé ne sont pas conservés.
Outil Combos
Type : combos
{
"device": "playstation",
"combos": [
{
"id": "combo0",
"name": "Anti air",
"device": "playstation",
"inputs": [
[
{ "kind": "direction", "value": "down" },
{ "kind": "button", "value": "triangle" }
]
]
}
],
"categories": [
{
"id": "category1",
"title": "Neutral",
"collapsed": false,
"combos": [
{
"id": "combo1",
"name": "Hadoken",
"device": "playstation",
"inputs": [
[{ "kind": "direction", "value": "down" }],
[{ "kind": "direction", "value": "down-forward" }],
[
{ "kind": "direction", "value": "forward" },
{ "kind": "button", "value": "cross" }
]
]
}
]
}
]
}
Notes :
devicevautplaystation,xbox,switch,n64oukeyboardMouse.combos[].devicepeut figer le périphérique de rendu d'un combo, afin qu'il conserve ses couleurs si l'outil change de périphérique.inputsest une liste d'étapes successives.- chaque étape contient une ou plusieurs entrées simultanées.
kindvautbutton,direction,keyoumouse.combosà la racine contient les combos sans catégorie.- les catégories restent locales à l'outil ; l'utilisateur peut ajouter plusieurs outils Combos pour séparer des usages.
Stockage compact :
deviceest omis si la valeur vautplaystation.combosest omis si aucun combo sans catégorie n'existe.categoriesconserve uniquement les catégories avec au moins un combo valide.- les combos sans nom et sans touche sont supprimés.
collapsedest omis sifalse.- les titres de catégorie et noms de combo sont limités à 80 caractères.
- chaque combo conserve au maximum 64 étapes.
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 :
scrollResultsest omis sifalse.parentIdest omis si vide.labelpeut être vide.valueest numérique.- Si un
parentIdne pointe vers aucune entrée existante, il est réinitialisé à vide.
Outil Tableau
Type : table
{
"rows": 12,
"columns": 8,
"cells": {
"A1": "Texte libre",
"B1": "100",
"C1": "=A1+B1",
"D1": "=(B1+20)/2"
},
"rowLabels": {
"0": "Boss",
"1": "Phase 2"
},
"columnLabels": {
"0": "Item",
"1": "Quantité"
}
}
Stockage compact :
rowsest omis si la valeur vaut10.columnsest omis si la valeur vaut6.cellsconserve uniquement les cellules non vides et dans les limites du tableau.rowsest limité entre1et50.columnsest limité entre1et20.- Les valeurs de cellules sont stockées sous forme de texte brut, formules incluses.
- Les résultats calculés ne sont pas persistés.
rowLabelsetcolumnLabelsconservent uniquement les intitulés visuels personnalisés.- Les intitulés personnalisés n'impactent pas les références de formule, qui restent basées sur les adresses
A1,C2, etc.
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.jsonsi nécessaire ; - mettre à jour la validation dans
tests/helpers/data-validation.mjssi nécessaire ; - vérifier import/export ;
- lancer
npm run check.