42 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",
"setting:autoBackup": {
"enabled": false,
"directoryHandle": null,
"lastBackup": ""
}
}
setting:autoBackup conserve le FileSystemDirectoryHandle choisi par l'utilisateur
et le dernier timestamp de réussite. Le handle n'est jamais inclus dans les exports.
Les sauvegardes utilisent le format de l'export global, sont nommées avec la date
locale du navigateur et conservent les dix fichiers sokkog-global-YYYY-MM-DD.json
les plus récents. La vérification au démarrage ne demande pas de permission et ne
crée pas les jours manquants lorsque l'application était fermée. Une nouvelle
sauvegarde automatique est ignorée si son contenu est identique à la version la
plus récente, en excluant uniquement l'horodatage d'export. Si le navigateur ne
conserve pas l'autorisation du dossier entre deux sessions, l'interface propose
une réautorisation explicite ; le démarrage n'ouvre aucune demande de permission.
Entrée type dans modules :
{
"key": "tbx1:mod1",
"data": {}
}
Toolbox
Ordre et confirmation des écritures
Le démarrage attend la lecture du registre, des liens, des modules hors médias et de la position des notifications avant d'ouvrir l'interface. Un échec laisse le stockage non prêt : les méthodes d'écriture refusent les opérations, et la création, l'import et l'export ne peuvent pas utiliser un état vide incomplet. Le bouton de reprise relance uniquement les lectures, sans supprimer de données. Une ouverture IndexedDB échouée n'est pas conservée dans le cache de connexion. Les erreurs de lecture sont distinctes des erreurs d'écriture et ne sont pas présentées systématiquement comme un dépassement de quota.
Dans une instance de l'application, le registre, les liens, les modules et la position des notifications partagent une file d'écritures. L'interface présente les modifications en attente ; en cas d'échec, elle est reconstruite depuis le dernier état confirmé, puis les opérations restantes sont réappliquées.
La création, l'association, l'import et la suppression d'une toolbox utilisent
une transaction commune aux stores kv et modules. Les imports ajoutent leur
contenu à l'état courant au moment de l'exécution et annoncent leur succès après
la fin de transaction. Une erreur synchrone pendant sa préparation déclenche
l'abandon de la transaction. Les écritures ordinaires d'un module restent ciblées
sur sa clé et ne réécrivent pas les images des autres outils.
Les données des outils images et imageAnnotation sont lues à leur première
ouverture, en page complète ou dans le panneau latéral. Les minuteurs et calendriers
restent chargés au démarrage pour assurer les alertes globales. Les couvertures,
incluses dans le registre des toolboxes, restent également chargées au démarrage.
Un marqueur uniquement en mémoire distingue un média non lu d'un outil vide ; il
n'est jamais persisté ni exporté. Les transactions de snapshot conservent les
entrées non lues, sauf celles explicitement supprimées de l'état courant.
Une lecture tardive ne réintroduit pas un outil supprimé entre-temps.
Pendant la lecture, le contenu de l'outil est bloqué. Un échec affiche une reprise locale sans empêcher les autres outils de fonctionner ni autoriser l'écrasement du média non lu. Après ouverture, les médias restent en mémoire jusqu'au rechargement de l'application ; il n'y a pas d'éviction à la fermeture.
Les exports attendent les écritures précédentes dans la même file et lisent les médias manquants depuis l'état confirmé, sans les ajouter au cache de l'interface. Si cette lecture échoue, aucun fichier partiel n'est téléchargé. Les formats IndexedDB et import/export sont inchangés.
Cette coordination est locale à l'instance React ; elle ne synchronise pas l'édition entre plusieurs onglets.
Les estimations de quota demandées après sauvegarde sont regroupées sur une fenêtre de 250 ms, avec une seule estimation active. Une écriture intervenant pendant le calcul déclenche une nouvelle estimation. Les sauvegardes ne sont pas retardées ; en cas d'estimation indisponible, le dernier quota connu est conservé.
Les toolboxes sont stockées dans kv/toolboxes.
{
"id": "tbx1",
"name": "Monster Hunter Wilds",
"gameId": "mhwilds",
"icon": "/static/img/toolbox-icons/toolbox.png",
"customCover": "data:image/webp;base64,...",
"modules": [
{
"id": "mod1",
"type": "checklist",
"title": "Armures",
"collapsed": true
}
],
"moduleColumns": 2,
"moduleOrder": {
"one": ["mod1"],
"two": [["mod1"], []]
},
"tutorial": true,
"updatedAt": "2026-07-25T12:00:00.000Z"
}
Notes :
gameIdest vide pour une toolbox libre.iconest utilisé uniquement pour les toolboxes libres.customCoverest une image rectangulaire optionnelle utilisée comme cover de card pour les toolboxes libres. Elle est stockée en data URL image après recadrage, sans conserver le fichier original.moduleColumnsvaut1ou2et contrôle l'affichage de la page toolbox complète ; le panneau latéral utilise toujours l'ordre une colonne.moduleOrder.onestocke l'ordre du mode une colonne.moduleOrder.twostocke l'ordre explicite des colonnes gauche et droite en mode deux colonnes.- Chaque identifiant d'outil présent dans
modulesdoit apparaître exactement une fois dansmoduleOrder.oneet exactement une fois dans l'une des deux colonnes demoduleOrder.two. - Si une ancienne toolbox n'a pas encore
moduleOrder.two, les outils sont répartis automatiquement depuis l'ordre une colonne avec une moitié en colonne gauche et une moitié en colonne droite. - Les données lourdes des outils ne sont pas stockées dans la toolbox, mais dans
modules. modules[].collapsedvauttruelorsque seul l'en-tête de l'outil est affiché ; il est omis lorsque l'outil est déplié.tutorialest un indicateur temporaire réservé aux toolboxes créées depuis la prise en main. Il déclenche les premiers repères à l'ouverture, puis il est retiré automatiquement du stockage.
Liens Jeu / Toolbox
Les associations entre pages jeux et toolboxes sont stockées dans kv/links.
{
"mhwilds": "tbx1",
"diablo4": "tbx2"
}
Outil Bloc Note
Type : notepad
{
"activePageId": "page1",
"pages": [
{
"id": "page1",
"title": "Page 1",
"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 mono-page
{ "html": "...", "text": "..." }reste accepté et est converti enpages[0]au prochain enregistrement ; activePageIdest omis si la première page stockée est active ;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-note sont conservés dans
sessionStoragepar toolbox, outil et page, pas dans IndexedDB ; - si toutes les pages sont vides et que seule la page par défaut reste présente, l'entrée de module peut être supprimée.
Outil Mémos
Type : memos
{
"tickerEnabled": true,
"items": [
{
"id": "memo1",
"text": "Penser à sauvegarder avant le boss",
"color": "#f6c453",
"updatedAt": "2026-08-21T12:00:00.000Z"
}
]
}
Stockage compact :
tickerEnabledvauttruepar défaut et est omis dans ce cas ;updatedAtest omis si vide ou invalide ;- les mémos sans texte sont ignorés ;
- si aucun mémo n'est valide et que la bande est active par défaut, l'entrée de module peut être supprimée.
Outil Checklist
Association temporaire aux guides
Les pages Aniimo, monstres MHW, faune MHW et affixes Diablo peuvent refléter une checklist de la toolbox actuellement associée au jeu. Chaque checklist compatible affiche un toggle de lien à côté du « + » de son en-tête dans le panneau latéral. Il ouvre une section masquée par défaut en haut du contenu : bouton toggle d'association/dissociation et switch de masquage, actif seulement pour la liste associée. Associer une autre liste remplace la précédente et conserve le réglage de masquage ; dissocier réaffiche tous les éléments. Replier les commandes ne modifie pas l'association ; cet état d'affichage reste local au composant. Cette liaison ne modifie ni les données de checklist ni les liens persistants jeu/toolbox.
sessionStorage["sokkog:guide-checklists"] contient un objet indexé par
aniimo/aniimos, mhwilds/monsters, mhwilds/endemic ou diablo4/affixes.
Chaque entrée contient { toolboxId, moduleId, hideCompleted }. Aucune liaison
n'est choisie automatiquement. La navigation et le rechargement la conservent ;
la dissociation, la suppression du module ou le changement de toolbox la retirent.
Sans accès au stockage de session, l'état reste en mémoire dans l'application.
Ces associations ne sont pas exportées et ne se synchronisent pas entre onglets.
La reconnaissance compare les libellés complets sans tenir compte de la casse,
des accents ni des espaces répétés. Une checklist partielle suffit, avec au moins
une correspondance dans toutes les données du guide, indépendamment des filtres.
Les lignes étrangères sont ignorées. Une correspondance est terminée lorsque
toutes ses lignes atteignent leur propre qtyTarget ; les doublons ne sont pas
additionnés. Renommer une ligne peut supprimer sa correspondance. Une liste encore
sélectionnée mais sans correspondance reste liée, avec un message explicite.
Une checkbox sur la carte d'un guide peut modifier la progression de toutes les
lignes exactes qui lui correspondent : cocher porte qtyCurrent à qtyTarget,
décocher le remet à 0. Les données passent par le flux React/IndexedDB existant ;
aucune copie de progression n'est conservée dans l'état de session du guide.
La copie Aniimo utilise Nom:1 en affichage regroupé et Nom — Forme:1 en
affichage séparé. Les noms et formes français et anglais sont reconnus. Une ligne
sans forme couvre toutes les variantes ; une ligne spécifique à une forme prend
priorité sur la ligne générale. Le masquage des cochés précède le regroupement ;
le style d'une carte groupée dépend de la variante active. La copie et les
compteurs portent sur les résultats effectivement affichés.
Données persistantes
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.- les sections sans item sont ignorées.
Outil Planificateur D'équipements
Type : equipmentPlanner
{
"types": [
{
"id": "type1",
"title": "Armes",
"icon": "sword",
"collapsed": false
}
],
"equipments": [
{
"id": "equipment1",
"typeId": "type1",
"name": "Épée runique",
"icon": "sword",
"active": true,
"obtain": "Forge après le boss",
"characteristics": [
{
"id": "trait1",
"category": "Stats",
"icon": "sword",
"name": "Force",
"value": 12
}
],
"socketItems": [
{
"id": "socket1",
"name": "Joyau attaque",
"shape": "jewel",
"color": "yellow",
"bonuses": [
{
"id": "bonus1",
"category": "Compétences",
"name": "Attaque",
"value": 1
}
]
}
],
"socketLinks": [
{
"id": "link1",
"fromSocketItemId": "socket1",
"toSocketItemId": "socket2"
}
],
"materials": [
{
"id": "material1",
"name": "Minerai rare",
"qty": 4
}
],
"categoryOrder": ["Stats", "Compétences"],
"collapsedCategories": []
}
],
"typeOrder": ["type1"]
}
Outil Arbre D'objectifs
Type : goalTree
{
"lastResetAt": 1786226400000,
"orientation": "horizontal",
"nodes": [
{
"id": "goal1",
"parentId": "",
"title": "Breuvage de rage",
"kind": "step",
"icon": "cube",
"color": "gold",
"qtyTarget": 10,
"qtyCurrent": 0,
"formula": "",
"progressMode": "auto",
"resetType": "none",
"resetTime": "00:00",
"weeklyResetDay": 1,
"checkedAt": 0,
"collapsed": false
},
{
"id": "goal2",
"parentId": "goal1",
"parentIds": ["goal1"],
"title": "Élixir de destruction",
"kind": "step",
"icon": "flask",
"color": "cyan",
"qtyTarget": 1,
"qtyCurrent": 0,
"formula": "base * 3",
"resetType": "weekly",
"resetTime": "05:00",
"weeklyResetDay": 1,
"checkedAt": 0,
"collapsed": false
},
{
"id": "goal3",
"parentId": "goal1",
"title": "Liste de farm",
"kind": "checklist",
"items": [
{
"id": "item1",
"label": "Élément",
"formula": "1",
"qtyTarget": 1,
"qtyCurrent": 1
},
{
"id": "item2",
"label": "Faire cela",
"formula": "base",
"qtyTarget": 5,
"qtyCurrent": 2
}
],
"resetType": "none",
"resetTime": "00:00",
"weeklyResetDay": 1,
"checkedAt": 0,
"collapsed": false
}
]
}
Stockage compact :
lastResetAtest omis si vide.orientationest omis si la valeur vauthorizontal.orientationvauthorizontalouvertical.parentIdest omis pour les racines.parentIdreste le parent principal de placement dans le schéma.parentIdscontient les parents/prérequis pris en compte pour les liaisons de convergence ; il est omis si aucun prérequis supplémentaire n'existe.- les calculs
base, le placement, la suppression de branche et l'agrégation de progression restent basés surparentId. - un objectif peut donc être placé sous un parent principal tout en ayant d'autres prérequis, afin de représenter une convergence de branches.
kindest omis si la valeur vautstep.kindvautstep,textouchecklist.iconest omis si la valeur vautcube.iconvautcube,fire,flask,grass,leaf,meat,powder,tearouball.colorest omis si la valeur vautgold.colorvautgold,cyan,violet,pink,green,gray,red,orange,yellow,blueouindigo.qtyTargetest omis si la valeur vaut1.qtyCurrentest omis si la valeur vaut0.formulaest omis si vide.progressModeest utilisé uniquement par les objectifsstep; il vautautooumanual.progressModeest omis tant qu'aucun choix explicite n'a été fait; par défaut, une étape avec enfants calcule sa progression automatiquement, sauf si tous ses enfants directs ont une cible de1, auquel cas elle se comporte en mise à jour manuelle.- en
auto, une étape avec enfants calcule sa progression depuis ses enfants; enmanual, elle utilise sa propreqtyCurrent. itemsest omis si vide ou sikindvautstep.items[].labelcontient la ligne affichee dans un noeudtextouchecklist; il peut etre vide pendant l'edition.items[].formulaest utilise uniquement parchecklistpour calculer le total de la ligne ; la variablebasereference la cible calculee du parent direct.items[].qtyTargetest utilise uniquement parchecklistet omis si la valeur vaut1.items[].qtyCurrentest utilise uniquement parchecklistet omis si la valeur vaut0.resetTypeest omis si la valeur vautnone.resetTypevautnone,dailyouweekly.resetTimeest omis si la valeur vaut00:00.weeklyResetDayest omis si la valeur vaut1.checkedAtest omis si vide.collapsedest omis sifalse.- Les parents invalides ou cycliques sont ramenés à la racine pendant la normalisation.
Stockage compact :
- les types sans équipement sont ignorés ;
icondes types vautboot,chest-armor,helmet,glove,shield,sword,gun,earring,necklaceouring;icondes équipements utilise la même liste et pilote l'icône affichée sur la ligne de l'équipement ;activeest omis quand la valeur vauttrue;obtain,characteristics,socketItems,socketLinks,materials,categoryOrderetcollapsedCategoriessont omis si vides ;collapsedest omis sifalse;qtydes matériaux est normalisé à minimum1;- les caractéristiques et bonus sans nom sont ignorés ;
icondes caractéristiques directes vautswordoushield;shapevautballoujewel, avecjewelpar défaut ;colorvautred,orange,amber,yellow,lime,green,teal,cyan,blue,indigo,violet,purple,pink,rose,white,grayoublack;- les liens de sertissage invalides, dupliqués ou hors équipement sont ignorés ;
- seuls les équipements actifs contribuent au résumé.
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",
"autoRefresh": true,
"time": "12:03:00",
"targetAt": 1760000000000
},
{
"id": "timer3",
"label": "Event",
"type": "time_pattern",
"pattern": "X:45:00"
},
{
"id": "timer4",
"label": "Event",
"type": "interval",
"autoRefresh": true,
"intervalMs": 1800000,
"startMode": "time",
"startTime": "06:00:00",
"anchorAt": 1760000000000,
"targetAt": 1760001800000
}
]
}
Types de compte à rebours :
duration: durée ponctuelle, avecdurationMsettargetAt.daily_time: occurrence à une heure précise, avectimeau formatHH:MM:SS,targetAtetautoRefreshoptionnel.time_pattern: prochaine occurrence automatique correspondant à un patternHH:MM:SS, où chaque segment peut valoirX.interval: intervalle basé surintervalMs,anchorAt,targetAt,startMode,startTimeetautoRefreshoptionnel.startModevautnowoutime;startTimeest présent uniquement avectime.autoRefreshn'est conservé que si l'intervalle dure au moins 5 minutes ; les anciens intervalles sanstargetAtrestent interprétés comme répétables.
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.countdowns[].autoRefreshest omis sifalse.countdowns[].startModeetcountdowns[].startTimesont omis pour les intervalles qui démarrent à la création.- 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 Calendrier
Type : calendar
{
"initialized": true,
"viewMode": "month",
"weekStartsOn": 1,
"events": [
{
"id": "calendar1",
"title": "Raid",
"startDate": "2026-08-14",
"endDate": "2026-08-16",
"repeat": "monthly",
"monthlyRepeatBy": "weekday",
"excludedOccurrences": ["2026-09-11"],
"alertMode": "site",
"alertOffsetMinutes": [10, 5, 0],
"color": "violet"
}
]
}
Stockage compact :
viewModevautweekoumonth;weekest omis dans ce cas.initializedindique que le choix du type de calendrier a été validé dans l'outil ; il est conservé pour garder un calendrier vide utilisable.weekStartsOnvaut1par défaut et est omis dans ce cas.eventsest omis tant qu'aucun événement valide n'est configuré.- les événements sans titre ou sans cible de jour/date valide sont supprimés ;
startMinutesreste requis en hebdomadaire et vaut0par défaut en mensuel. dayIndexvaut0pour lundi,1pour mardi, jusqu'à6pour dimanche.- en calendrier mensuel,
startDateetendDatesont des dates localesYYYY-MM-DD;endDateest omis si la plage dure un seul jour. - en calendrier mensuel,
monthlyRepeatByvautmonthDayouweekday;monthDayest la valeur par défaut et est omise dans ce cas. - en calendrier mensuel,
repeatvautnone,weeklyoumonthly;monthlyest la valeur par défaut et est omise dans ce cas. repeat=weeklyest réservé aux entrées mensuelles parweekday; si une entrée parmonthDaydemandeweekly, elle est normalisée ennone.- une entrée mensuelle répétée par
monthDayn'a pas d'occurrence dans un mois où le jour demandé n'existe pas. - une entrée mensuelle répétée par
weekdayetmonthlyse répète sur le même rang de jour de semaine questartDate; par exemple le premier lundi du mois. excludedOccurrencescontient des dates localesYYYY-MM-DDcorrespondant aux occurrences supprimées individuellement.startMinutesetendMinutessont des minutes depuis00:00dans la journée ; en hebdomadaire, l'interface saisit les heures au pas de 5 minutes tout en gardant une grille visuelle de 30 minutes ; en mensuel, les champs horaires ne sont pas exposés etstartMinutesest omis s'il vaut0.endMinutesest conservé s'il produit une fin postérieure au début ; en mensuel, il peut être inférieur àstartMinutessi la fin est sur un jour ultérieur.- en hebdomadaire,
repeatn'est pas stocké dans le format cible : il est déduit derepeatable(weeklypar défaut,nonesirepeatable=false). - en hebdomadaire,
repeatablevauttruepar défaut et est omis dans ce cas ; sifalse, l'entrée est supprimée quand son échéance de la semaine courante est terminée. alertModevautoff,visibleousite, et est omis sioff.alertOffsetMinutesest une liste composée de10,5ou0(0= au début et pendant l'événement pour les reprises de session) ; il est omis si l'alerte est désactivée ou si la liste vaut[10].colorvautviolet,gold,cyan,pink,greenougray, et est omis siviolet.
Import/export texte :
- le format texte commence par
@viewMode: weekou@viewMode: month, puis une ligneevent | ...par entrée ; - en hebdomadaire, les champs
day,startetenddécrivent le jour de semaine et les heures locales (HH:mm) ; - en mensuel,
repeatBy=monthDayourepeatBy=weekdayutilisestartDate/endDateetrepeat=none|weekly|monthly, par exemplerepeatBy=weekday | repeat=monthly | startDate=2026-08-03 | endDate=2026-08-05; - l'import texte remappe toujours les identifiants d'événements.
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",
"dailyResetTime": "06:30",
"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.dailyResetTimesur une tâche quotidienne surcharge l'heure globale.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.dailyResetTimeunitaire est omis si non défini ou identique à l'heure globale.weeklyResetDayunitaire est omis si non défini.relationsest omis si vide.relations[].prerequisiteest omis sifalse.
Outil Liens
Type : links
{
"categoryOrder": ["Builds", "Guides"],
"collapsedCategories": ["Guides"],
"links": [
{
"id": "link1",
"title": "Build rogue",
"category": "Builds",
"url": "https://example.com/build"
}
]
}
Stockage compact :
titleest facultatif.titleest omis si vide.categoryest facultatif et sert à l'autocomplete de création.categoryest omis si vide.urlest obligatoire.categoryOrderstocke l'ordre d'affichage des catégories, y compris les catégories vides.collapsedCategoriesstocke les noms des catégories repliées.
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 Randomizer
Type : randomizer
{
"activeTab": "wheel",
"dice": {
"faces": 6,
"rolls": 3,
"results": [2, 5, 1],
"history": [
{
"id": "random1",
"at": "2026-08-30T12:00:00.000Z",
"results": [2, 5, 1]
}
]
},
"wheel": {
"draws": 2,
"entries": [
{
"id": "entry1",
"label": "Route farm",
"weight": 3
}
],
"results": ["Route farm"],
"history": []
},
"teams": {
"teamCount": 2,
"playerSelectionMode": "all",
"playerSelectionCount": 2,
"players": [
{
"id": "player1",
"name": "Alice",
"score": 10,
"active": true
}
],
"exclusions": [
{
"id": "exclusion1",
"playerAId": "player1",
"playerBIds": ["player2", "player3"],
"mode": "source"
}
],
"teams": [],
"history": []
}
}
Stockage compact :
activeTabest omis quand l'onglet actif estdice.dice,wheeletteamssont omis quand ils ne contiennent que leurs valeurs par défaut et aucun résultat.- l'historique de chaque onglet est limité aux 20 derniers tirages.
- les entrées de roue sans libellé sont ignorées, et
weightest borné de 1 à 999. - les joueurs sans nom sont ignorés, et
scoreest borné de 0 à 9999. activevauttruepar défaut sur un joueur.playerSelectionModevautalloucount; en modecount,playerSelectionCountindique combien de joueurs actifs sont choisis aléatoirement avant la répartition.- chaque exclusion décrit un nom principal
playerAId, une ou plusieurs ciblesplayerBIdset unmode. mode: "source"interdit uniquementplayerAIdavec chaque cible; les cibles peuvent rester ensemble.mode: "group"interdit chaque nom de la combinaison avec tous les autres noms de cette combinaison.- les exclusions invalides, dupliquées ou qui référencent un joueur absent sont ignorées.
Outil Simulateur de chance
Type : rngSimulator
{
"activeRateId": "rate1",
"rates": [
{
"id": "rate1",
"label": "Drop rare",
"rate": "2.500",
"costPerDraw": "150",
"multiplier": 50,
"occurrenceValue": 30,
"occurrenceUnit": "perHour",
"realDraws": 120
},
{
"id": "rate2",
"label": "Ultra rare",
"rate": "0.000004"
}
],
"settings": {
"simulationDraws": 1000
}
}
Stockage compact :
ratescontient les taux enregistrés du module; un taux sans libellé est ignoré.rateest une chaîne en pourcentage afin de conserver la précision saisie, par exemple"2.500"ou"0.000004".- un taux doit être strictement supérieur à
0et inférieur ou égal à100; les valeurs supérieures sont bornées à"100". costPerDrawest une chaîne décimale optionnelle, associée au taux, utilisée comme unité libre pour les calculs de coût.activeRateIdréférence un taux existant; sinon le premier taux est sélectionné.rates[].multiplierest un bonus ou malus en pourcentage associé au taux, vaut 0 par défaut et le taux effectif est borné entre 0% et 100%.rates[].occurrenceValueest numérique, positif ou nul, associé au taux, et vaut 0 par défaut.rates[].occurrenceUnitvautperHourouperDay, et vautperHourpar défaut.- les anciennes données
occurrenceUnit: "perMonth"sont converties enperDay, avecoccurrenceValuedivisé par 30. rates[].realDrawsest le total persistant de tirages réellement effectués pour ce taux, borné de 0 à 1 000 000 000.settings.simulationDrawsest borné de 1 à 1 000 000 et vaut 1000 par défaut.settingsest omis sisimulationDrawsvaut sa valeur par défaut.rates[].costPerDrawest omis si vide ou nul.rates[].multiplierest omis si sa valeur vaut 0.rates[].occurrenceValueetrates[].occurrenceUnitsont omis sioccurrenceValuevaut 0.rates[].realDrawsest omis si sa valeur vaut 0.- l'historique des simulations n'est pas persisté en IndexedDB; il est limité aux 20 dernières entrées en
sessionStorage.
Import/export texte :
@activeRateId: rate1
[rates]
- Drop rare | rate=2.500 | active=true | costPerDraw=150 | multiplier=50 | occurrenceValue=30 | occurrenceUnit=perHour | realDraws=120
[settings]
simulationDraws: 1000
Outil Combos
Type : combos
{
"device": "playstation",
"categoryOrder": ["Neutral"],
"collapsedCategories": ["Advanced"],
"combos": [
{
"id": "combo0",
"name": "Anti air",
"device": "playstation",
"inputs": [
[
{ "kind": "direction", "value": "down", "holdMs": 2000 },
{ "kind": "button", "value": "triangle", "hold": true }
]
]
},
{
"id": "combo1",
"name": "Hadoken",
"category": "Neutral",
"device": "playstation",
"inputs": [
[{ "kind": "direction", "value": "down" }],
[{ "kind": "direction", "value": "down-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.combos[].categoryest facultatif ; les catégories sont déduites automatiquement de cette valeur.inputsest une liste d'étapes successives.- chaque étape contient une ou plusieurs entrées simultanées.
kindvautbutton,direction,keyoumouse.holdest facultatif et indique qu'une entrée doit être maintenue sans durée précise.holdMsest facultatif et indique qu'une entrée doit être maintenue pendant cette durée en millisecondes ; l'interface limite la saisie à des secondes entières de1à99.categoryOrderstocke l'ordre d'affichage des catégories utilisées.collapsedCategoriesstocke les noms des catégories repliées.- 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.categoryest omise si vide.holdest omis si absent ou faux.holdMsest omis si absent ou nul.categoryOrderest omis si aucune catégorie n'est utilisée.collapsedCategoriesest omis si aucune catégorie n'est repliée.combosest omis si aucun combo valide n'existe.- les combos sans nom et sans touche sont supprimés.
- les catégories 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,
"formula": "100"
},
{
"id": "calc2",
"parentId": "calc1",
"label": "Minerais de fer",
"value": 500,
"formula": "base*5"
}
]
}
Stockage compact :
scrollResultsest omis sifalse.parentIdest omis si vide.labelpeut être vide.valueest numérique.formulaest omis si vide ; dans un calcul enfant, le motbaseréférence la valeur recalculée du parent.- 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.- les espaces saisis dans une cellule non vide sont conservés.
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"
}
]
}
}
}
Import / Export texte des outils
Les outils compatibles proposent aussi un échange par copier-coller depuis le toggle import/export de leur header. Ce format est un format UI distinct du stockage IndexedDB et de l'export JSON global.
Règles communes :
- l'import valide tout le texte avant de sauvegarder ;
- une erreur de format ne modifie pas les données existantes ;
- les IDs internes sont recréés à l'import ;
- l'identité de contenu est évaluée après normalisation ;
- les états de progression ne sont pas repris : checklist à
qtyCurrent: 0, tâches non effectuées etcheckedAt: 0.
Formats :
notepad: format structuré@notepad, blocs@page: Titre,@html/@endhtmlpour le HTML riche nettoyé, meta@drawingMode: permanentet@drawings: {...}pour les dessins permanents. Un collage texte simple reste accepté et devient une page sans mise en forme.memos: une ligne par mémo, avec préfixe-optionnel et suffixe couleur| color=#f6c453; les anciens préfixes*sont acceptés puis normalisés.checklist: sections# Catégorie, lignesItem: quantité cible.links: sections# Catégorie, lignesTitre: https://...ou URL seule; le suffixe optionnel| category=Catégoriereste accepté à l'import.counters: lignesLibellé: valeur, valeurs négatives acceptées.calculator: lignesLibellé: valeur | formula=base*5, indentation de deux espaces pour les enfants, meta@scrollResults: true. Le suffixeformula=...est optionnel et les anciens exportsLibellé: valeurrestent acceptés.table: TSV pur accepté ; l'export peut ajouter@size,@columns,@rowspuis@tsvpour préserver dimensions et intitulés.combos: sections# Catégorie, lignesNom | device=... | kind:value > kind:value+kind:value, avec suffixes[hold]ou[2s].taskPlanner: sections# Catégorie, lignes indentées- daily Titre,- weekly Titreou- unique Titre, metas@description,@dailyResetTime,@weeklyResetDay,@prerequisite.equipmentPlanner: types# Type | icon=..., équipements## Nom | icon=... | active=true, sous-sectionsStats,Sockets,Craft.images: blocs# Libellépuisdata:image/...;base64,....imageAnnotation: image base64, lignes@marker: x,y,labelet@drawings: {...}pour les dessins permanents.timer: metas globales, ligneslap | ...etcountdown | ...; les échéances temporelles sont recalculées à l'import.
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.