From 91d99d6ca47858721359c2dc7d052ca9316c5a9c Mon Sep 17 00:00:00 2001 From: Shinuwa Date: Tue, 25 Aug 2026 19:17:19 +0200 Subject: [PATCH] Update memo layout and library import examples --- DESIGN_SYSTEM.md | 1 + website/public/data/site.json | 64 +++++++++++++------ .../toolboxes/modules/MemosModule.jsx | 26 +++++++- .../toolboxes/modules/textImport/basic.js | 4 +- .../toolboxes/modules/textImport/planning.js | 3 +- website/src/styles/toolboxes/_memos.scss | 16 +++-- 6 files changed, 86 insertions(+), 28 deletions(-) diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md index 011b0a7..3618c86 100644 --- a/DESIGN_SYSTEM.md +++ b/DESIGN_SYSTEM.md @@ -307,6 +307,7 @@ Regles UI : - cartes compactes, non imbriquees, avec accent couleur localise ; - edition directe du texte court ; +- zone de texte en hauteur automatique, sans resize manuel ni scrollbar interne ; - palette de couleurs compacte reprenant les swatches du bloc note ; - la creation de memo est masquee par defaut et s'ouvre via le bouton `+` du header d'outil ; - réorganisation des cartes par poignée de drag & drop compacte ; diff --git a/website/public/data/site.json b/website/public/data/site.json index ff5cd3f..ff66601 100644 --- a/website/public/data/site.json +++ b/website/public/data/site.json @@ -118,7 +118,7 @@ ], "importFormat": { "text": "Chaque ligne crée un mémo. Le suffixe color=#f6c453 conserve la couleur de la carte.", - "example": "- Sauvegarder avant le boss | color=#f6c453\n- Acheter des potions | color=#22d3ee" + "example": "- Pour la reproduction, combine Pierre Stase + Nœud Destin pour transmettre nature et IV. | color=#f6c453\n- Les IV obtenus par Hyper Training ne sont pas transmis aux oeufs. | color=#8b5cf6\n- Pour une équipe Distorsion, vise 0 IV en Vitesse sur les Pokémon lents. | color=#22d3ee\n- Utilise les objets Pouvoir pour cibler précisément les EV à entraîner. | color=#d946ef" } }, "notepad": { @@ -262,7 +262,7 @@ ], "importFormat": { "text": "Le Bloc Note utilise des pages avec blocs HTML nettoyés, puis une ligne dessins en JSON compact si nécessaire.", - "example": "@notepad\n@page: Page 1\n@html\n

Obtenir Mew :

\n@endhtml\n@drawingMode: permanent\n@drawings: {\"strokes\":[...]}" + "example": "@notepad\n@page: Infos: Obtenir Mew\n@html\n

Obtenir Mew :


\n@endhtml\n@drawingMode: permanent\n@drawings: {\"strokes\":[...]}\n@page: Infos: Obtenir Celebi\n@html\n

Obtenir Celebi :



\n@endhtml\n@drawingMode: permanent\n@drawings: {\"strokes\":[...]}" } }, "checklist": { @@ -618,7 +618,7 @@ ], "importFormat": { "text": "Le format contient l'image en base64, puis les marqueurs et les dessins permanents.", - "example": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABA...\n@marker: 63.85,49.46,Niv. 1-20\n@marker: 47.65,31.93,Niv. 10-20\n@marker: 55.92,41.03,Niv. 20-30\n@drawings: {\"strokes\":[...]}" + "example": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABA...\n@marker: 63.849429339290076,49.45757170456234,Niv. 1-20\n@marker: 47.654532128023455,31.930025688385026,Niv. 10-20\n@marker: 55.91951415308366,41.03347982081821,Niv. 20-30\n@marker: 68.87543192209696,28.397341995202,Niv. 30-40\n@marker: 54.6909357439531,19.97325011145787,Niv. 30-50\n@marker: 40.95319535094761,50.136933953251386,Niv. 30-50\n@marker: 42.51684059893198,25.136403201494595,Niv. 50-60\n@marker: 32.46483543331821,70.51780141392268,Niv. 60-70\n@marker: 46.425953718892885,80.70823514425832,Niv. 60-70\n@marker: 36.59732644584831,9.91868883086003,Niv. 70-80\n@drawings: {\"strokes\":[...]}" } }, "links": { @@ -689,6 +689,16 @@ "icon": "copy" } }, + { + "group": "Formule", + "icon": "parent", + "title": "Parent", + "text": "Insère la valeur du parent dans la formule.", + "preview": { + "kind": "iconButton", + "icon": "parent" + } + }, { "group": "Contrôles", "icon": "edit", @@ -740,7 +750,7 @@ ], "importFormat": { "text": "Une ligne par résultat. L'indentation de deux espaces conserve les liens parent/enfant.", - "example": "Lingot de champignite: 100\n Lingot de chlorophyte: 100\n Champignon luisant: 1500\nLingot de chlorophyte: 100\n Minerai de chlorophyte: 500" + "example": "Lingot de champignite: 100\n Lingot de chlorophyte: 100 | formula=base\n Champignon luisant: 1500 | formula=base*15\nLingot de chlorophyte: 100\n Minerai de chlorophyte: 500 | formula=base*5" } }, "goalTree": { @@ -773,6 +783,16 @@ "icon": "edit" } }, + { + "group": "Formule", + "icon": "parent", + "title": "Parent", + "text": "Insère la valeur du parent dans une quantité ou formule.", + "preview": { + "kind": "iconButton", + "icon": "parent" + } + }, { "group": "Objectifs", "icon": "trash", @@ -815,7 +835,7 @@ ], "importFormat": { "text": "Une ligne par nœud avec indentation de deux espaces. Utilisez xN pour une quantité simple ou = base * N pour une formule.", - "example": "- Breuvage de rage x10\n - Élixir de destruction = base * 3\n - Poudre de flamme x5\n - Sève de cèdre x7\n - Catalyseur magique x10" + "example": "@lastResetAt: 1787263200000\n- Potion d'invincibilité\n - Potion de vie\n - Ingredients | kind=checklist | items=%5B%7B%22id%22%3A%22xn2okr5%22%2C%22label%22%3A%22Herbe%20magique%22%2C%22formula%22%3A%22base%22%2C%22qtyTarget%22%3A10%2C%22qtyCurrent%22%3A7%7D%2C%7B%22id%22%3A%22xm5h5uu%22%2C%22label%22%3A%22Eau%20rajeunissante%22%2C%22formula%22%3A%22base%22%2C%22qtyTarget%22%3A10%2C%22qtyCurrent%22%3A5%7D%5D\n - Potion de résistance\n - Ingredients supp. | kind=checklist | items=%5B%7B%22id%22%3A%22x3mlygm%22%2C%22label%22%3A%22Poudre%20magique%22%2C%22formula%22%3A%22base%22%2C%22qtyTarget%22%3A10%7D%2C%7B%22id%22%3A%22x1dqx6w%22%2C%22label%22%3A%22Herbe%20magique%22%2C%22formula%22%3A%22base*2%22%2C%22qtyTarget%22%3A20%7D%5D\n- Parler a l'alchimiste | collapsed=true\n - Acheter une station d'alchimie | collapsed=true\n - Apprendre la recette de potion d'invicibilité | collapsed=true\n - S'inscrire à l'academie d'alchimie | collapsed=true" } }, "table": { @@ -1026,7 +1046,7 @@ ], "importFormat": { "text": "Les étapes et comptes à rebours sont exportés en lignes lisibles. Les échéances sont recalculées à l'import.", - "example": "@activeTab: countdown\nlap | label=Fosse 110 | elapsedMs=1046475\nlap | label=Fosse 55 | elapsedMs=50019\ncountdown | label=Fin de vague infernale | type=time_pattern | alertMode=off | pattern=X:55:X\ncountdown | label=Boss Mondial | type=daily_time | alertMode=off | time=14:00:00\ncountdown | label=Legion | type=interval | alertMode=off | autoRefresh=true | intervalMs=1800000 | startMode=now\ncountdown | label=Durée | type=duration | alertMode=off | durationMs=1800000" + "example": "@activeTab: countdown\nlap | label=Fosse 110 | elapsedMs=1046475\nlap | label=Fosse 55 | elapsedMs=50019\ncountdown | label=Fin de vague infernale | type=time_pattern | pattern=X:55:X\ncountdown | label=Boss Mondial | type=daily_time | time=14:00:00\ncountdown | label=Legion | type=interval | autoRefresh=true | intervalMs=1800000 | startMode=now\ncountdown | label=Durée | type=duration | durationMs=1800000" } }, "calendar": { @@ -1193,7 +1213,7 @@ ], "importFormat": { "text": "Les catégories commencent par #. Les tâches utilisent leur type et l'indentation de deux espaces crée les sous-tâches. Les états cochés ne sont pas repris.", - "example": "@hideCompleted: true\n# To craft\n- unique Essence de liqueur\n- unique Bière\n- unique Vinaigre\n- weekly Faire les boss du Sanctuaire Noir (Groupe)\n @weeklyResetDay: 0\n- weekly Faire les boss du Sanctuaire Noir (Solo)\n @weeklyResetDay: 0\n- unique Ameliorer le bateau\n - daily Faire du troc\n @prerequisite: true" + "example": "@hideCompleted: true\n# To craft\n- unique Essence de liqueur\n- unique Bière\n- unique Vinaigre\n- weekly Faire les boss du Sanctuaire Noir (Groupe)\n @weeklyResetDay: 0\n- weekly Faire les boss du Sanctuaire Noir (Solo)\n @weeklyResetDay: 0\n- weekly Faire la fosse aux immortels\n- unique Ameliorer le bateau\n - daily Faire du troc\n @prerequisite: true" } }, "combos": { @@ -1375,7 +1395,7 @@ ], "importFormat": { "text": "Le format est découpé par type, équipement et sous-sections Stats, Sockets et Craft.", - "example": "# Casque | icon=helmet\n## Nu Udra γ | icon=helmet | active=true\nStats\n | shield | Défense | 68\n | shield | Resistance au feu | 5\n | shield | Resistance a l'eau | -4\nSockets\nJoyau antivirus | shape=jewel | color=violet\n bonus | | sword | Antivirus | 1\nCraft\n@obtain: Avoir un certificat Alpha supreme\n## Nu Udra γ | icon=chest-armor | active=true\nStats\n | shield | Défense | 68\nSockets\nJoyau Attaque | shape=jewel | color=red\n bonus | | sword | Attaque | 2" + "example": "# Casque | icon=helmet\n## Nu Udra γ | icon=helmet | active=true\nStats\n | shield | Défense | 68\n | shield | Resistance au feu | 5\n | shield | Resistance a l'eau | -4\n | shield | Resistance à la foudre | 0\n | shield | Resistance à la glace | 1\n | shield | Resistance au dragon | 0\nSockets\nJoyau antivirus | shape=jewel | color=violet\n bonus | | sword | Antivirus | 1\nJoyau antivirus | shape=jewel | color=violet\n bonus | | sword | Antivirus | 1\nJoyau antivirus | shape=jewel | color=violet\n bonus | | sword | Antivirus | 1\nCraft\n@obtain: Avoir un certificat Alpha supreme\n## Nu Udra γ | icon=chest-armor | active=true\nStats\n | shield | Défense | 68\n | shield | Resistance au feu | 5\n | shield | Resistance a l'eau | -4\n | shield | Resistance à la foudre | 0\n | shield | Resistance à la glace | 1\n | shield | Resistance au dragon | 0\nSockets\nJoyau Mise a mort | shape=jewel | color=indigo\n bonus | | sword | Mise a mort | 1\nJoyau Attaque | shape=jewel | color=red\n bonus | | sword | Attaque | 2" } } }, @@ -1568,28 +1588,34 @@ "toolsLibraryLink": "Voir la librairie d'outils", "toolGroups": [ { - "icon": "notepad", + "icon": "memos", "title": "Notes et suivi", - "text": "Écrire des rappels, suivre des objectifs, organiser une routine ou préparer une liste de choses à faire.", - "examples": ["Notes", "Checklists", "Tâches"] + "text": "Écrire des rappels courts, garder un bloc note multi-pages, suivre des objectifs simples ou compter des ressources.", + "examples": ["Memos", "Bloc Note", "Checklist", "Compteurs"] }, { "icon": "picture", "title": "Médias et repères visuels", - "text": "Conserver des captures, cartes, images de référence ou annotations visibles pendant la session.", - "examples": ["Images", "Annotations", "Cartes"] + "text": "Conserver des captures, cartes, images annotées ou liens utiles visibles pendant la session.", + "examples": ["Images", "Annotation d'images", "Liens"] }, { "icon": "table", "title": "Données et calculs", - "text": "Comparer des valeurs, compter des ressources, préparer des tableaux ou calculer des besoins de craft.", - "examples": ["Compteurs", "Tableaux", "Calculs"] + "text": "Comparer des valeurs, préparer des tableaux, calculer des besoins de craft ou structurer des objectifs.", + "examples": ["Calculateur", "Tableau", "Arbre d'objectifs"] }, { "icon": "clock", "title": "Temps et actions", - "text": "Gérer des timers, routines, rotations, commandes ou séquences à reproduire sans perdre le fil.", - "examples": ["Timers", "Combos", "Routines"] + "text": "Gérer des timers, calendriers, routines et tâches récurrentes sans perdre le fil.", + "examples": ["Timer", "Calendrier", "Planificateur de tâches"] + }, + { + "icon": "controller", + "title": "Builds et commandes", + "text": "Documenter des builds, organiser des équipements et conserver des séquences de commandes.", + "examples": ["Combos", "Planificateur d'équipements"] } ], "contribute": { @@ -1785,7 +1811,7 @@ "tickerLabel": "Mémos", "hideTickerTitle": "Masquer la bande", "showTickerTitle": "Afficher la bande", - "importPlaceholder": "- Mémo prioritaire | color=#f6c453\n- Mémo simple | color=#22d3ee", + "importPlaceholder": "- Pour la reproduction, combine Pierre Stase + Nœud Destin pour transmettre nature et IV. | color=#f6c453\n- Les IV obtenus par Hyper Training ne sont pas transmis aux oeufs. | color=#8b5cf6\n- Pour une équipe Distorsion, vise 0 IV en Vitesse sur les Pokémon lents. | color=#22d3ee\n- Utilise les objets Pouvoir pour cibler précisément les EV à entraîner. | color=#d946ef", "importOpenButton": "Importer du texte", "exportOpenButton": "Exporter en format texte", "importModalTitle": "Importer des mémos", @@ -2040,7 +2066,7 @@ "itemCheckedLabel": "État", "itemTotalLabel": "Total", "itemTotalPlaceholder": "base", - "itemTotalHelp": "Saisissez un nombre ou une formule avec base pour référencer la cible du parent.", + "itemTotalHelp": "Saisissez un nombre ou une formule avec 'base' pour référencer la cible du parent.", "prerequisitesSectionTitle": "Prérequis supplémentaires", "contentTabHint": "Le contenu se modifie dans l'onglet Contenu.", "stepContentUnavailable": "Les étapes n'ont pas de contenu interne.", diff --git a/website/src/features/toolboxes/modules/MemosModule.jsx b/website/src/features/toolboxes/modules/MemosModule.jsx index 3b7a1f7..9f1dcb0 100644 --- a/website/src/features/toolboxes/modules/MemosModule.jsx +++ b/website/src/features/toolboxes/modules/MemosModule.jsx @@ -2,7 +2,7 @@ import { CompactDropdown } from "../../../components/CompactDropdown.jsx"; import { Icon } from "../../../components/Icon.jsx"; import { moveItem, useGroupedReorder } from "../../../hooks/useGroupedReorder.js"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { TextExchangeActions } from "./TextExchangeActions.jsx"; import { TEXT_COLORS } from "./notepadUtils.js"; @@ -11,6 +11,7 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang const data = useMemo(() => context.normalizeMemosData(storedData), [context, storedData]); const textContent = context.moduleText?.memos || {}; const tickerItems = data.tickerEnabled ? data.items : []; + const textareaRefs = useRef(new Map()); const [drafts, setDrafts] = useState({}); const { itemReorder, @@ -32,6 +33,16 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang setDrafts(Object.fromEntries(data.items.map((item) => [item.id, item.text]))); }, [data.items]); + useLayoutEffect(() => { + data.items.forEach((item) => resizeMemoTextarea(textareaRefs.current.get(item.id))); + }, [data.items, drafts]); + + function resizeMemoTextarea(element) { + if (!element) return; + element.style.height = "auto"; + element.style.height = `${element.scrollHeight}px`; + } + function save(nextData) { context.setModuleData(toolboxId, moduleId, nextData, "memos"); } @@ -133,10 +144,21 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang