Improve toolbox architecture and initial bundle loading
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s

This commit is contained in:
Shinuwa 2026-08-14 09:13:53 +02:00
parent f76cddcc95
commit b221257616
34 changed files with 8358 additions and 6744 deletions

View file

@ -1,18 +1,7 @@
// Rôle : affiche la librairie des outils avec des exemples locaux non persistés.
import React, { useEffect, useMemo, useRef, useState } from "react";
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react";
import { Icon } from "../components/Icon.jsx";
import { CalculatorModule } from "../features/toolboxes/modules/CalculatorModule.jsx";
import { ChecklistModule } from "../features/toolboxes/modules/ChecklistModule.jsx";
import { CombosModule } from "../features/toolboxes/modules/CombosModule.jsx";
import { CountersModule } from "../features/toolboxes/modules/CountersModule.jsx";
import { EquipmentPlannerModule } from "../features/toolboxes/modules/EquipmentPlannerModule.jsx";
import { ImageAnnotationModule } from "../features/toolboxes/modules/ImageAnnotationModule.jsx";
import { ImagesModule } from "../features/toolboxes/modules/ImagesModule.jsx";
import { LinksModule } from "../features/toolboxes/modules/LinksModule.jsx";
import { NotepadModule } from "../features/toolboxes/modules/NotepadModule.jsx";
import { TableModule } from "../features/toolboxes/modules/TableModule.jsx";
import { TaskPlannerModule } from "../features/toolboxes/modules/TaskPlannerModule.jsx";
import { TimerModule } from "../features/toolboxes/modules/TimerModule.jsx";
import { LIBRARY_CATEGORY_DEFINITIONS, getToolboxModuleDefinition } from "../features/toolboxes/toolboxModuleRegistry.js";
import {
clampQty,
hostnameFromUrl,
@ -35,28 +24,20 @@ import {
const LIBRARY_TOOLBOX_ID = "library";
const MODULE_COMPONENTS = {
notepad: { label: "Bloc notes", mode: "Texte riche", icon: "notepad", Component: NotepadModule },
checklist: { label: "Checklist", mode: "Catégories + sans catégorie", icon: "checklist", Component: ChecklistModule, editable: true, scrollable: true },
images: { label: "Images", mode: "Média", icon: "picture", Component: ImagesModule, editable: true, scrollable: true },
links: { label: "Liens", mode: "Liste simple", icon: "link", Component: LinksModule, editable: true, scrollable: true },
counters: { label: "Compteurs", mode: "Valeurs", icon: "abacus", Component: CountersModule, editable: true, scrollable: true },
combos: { label: "Combos", mode: "Catégories", icon: "controller", Component: CombosModule, editable: true, scrollable: true },
calculator: { label: "Calculateur", mode: "Arborescence", icon: "calculator", Component: CalculatorModule },
table: { label: "Tableau", mode: "Grille + formules", icon: "table", Component: TableModule, scrollable: true },
timer: { label: "Timer", mode: "Chrono + comptes à rebours", icon: "clock", Component: TimerModule },
taskPlanner: { label: "Planificateur de tâches", mode: "Catégories + parent/enfant", icon: "tasklist", Component: TaskPlannerModule, editable: true, scrollable: true },
equipmentPlanner: { label: "Planificateur d'équipements", mode: "Build + sertissages", icon: "chest-armor", Component: EquipmentPlannerModule, editable: true, scrollable: true },
imageAnnotation: { label: "Annotation d'images", mode: "Image + marqueurs", icon: "map", Component: ImageAnnotationModule, editable: true }
notepad: lazy(() => import("../features/toolboxes/modules/NotepadModule.jsx").then((module) => ({ default: module.NotepadModule }))),
checklist: lazy(() => import("../features/toolboxes/modules/ChecklistModule.jsx").then((module) => ({ default: module.ChecklistModule }))),
images: lazy(() => import("../features/toolboxes/modules/ImagesModule.jsx").then((module) => ({ default: module.ImagesModule }))),
links: lazy(() => import("../features/toolboxes/modules/LinksModule.jsx").then((module) => ({ default: module.LinksModule }))),
counters: lazy(() => import("../features/toolboxes/modules/CountersModule.jsx").then((module) => ({ default: module.CountersModule }))),
combos: lazy(() => import("../features/toolboxes/modules/CombosModule.jsx").then((module) => ({ default: module.CombosModule }))),
calculator: lazy(() => import("../features/toolboxes/modules/CalculatorModule.jsx").then((module) => ({ default: module.CalculatorModule }))),
table: lazy(() => import("../features/toolboxes/modules/TableModule.jsx").then((module) => ({ default: module.TableModule }))),
timer: lazy(() => import("../features/toolboxes/modules/TimerModule.jsx").then((module) => ({ default: module.TimerModule }))),
taskPlanner: lazy(() => import("../features/toolboxes/modules/TaskPlannerModule.jsx").then((module) => ({ default: module.TaskPlannerModule }))),
equipmentPlanner: lazy(() => import("../features/toolboxes/modules/EquipmentPlannerModule.jsx").then((module) => ({ default: module.EquipmentPlannerModule }))),
imageAnnotation: lazy(() => import("../features/toolboxes/modules/ImageAnnotationModule.jsx").then((module) => ({ default: module.ImageAnnotationModule })))
};
const LIBRARY_CATEGORY_DEFINITIONS = [
{ key: "notesTracking", types: ["notepad", "checklist", "counters"] },
{ key: "references", types: ["images", "imageAnnotation", "links"] },
{ key: "calculationData", types: ["calculator", "table"] },
{ key: "timeRoutines", types: ["timer", "taskPlanner"] },
{ key: "buildsCommands", types: ["combos", "equipmentPlanner"] }
];
function cloneData(value) {
if (value === undefined) return {};
if (typeof structuredClone === "function") return structuredClone(value);
@ -262,7 +243,7 @@ export function LibraryPage({ siteContent, actions }) {
key={module.id}
module={module}
sourceData={libraryPayload.modules?.[module.id] || {}}
description={getToolDescription(siteContent, MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad)}
description={getToolDescription(siteContent, getToolboxModuleDefinition(module.type))}
categoryTitle={activeCategory.title}
docs={content.toolDocs?.[module.type]}
featureHeading={content.featureHeading}
@ -299,7 +280,7 @@ function LibraryDocNav({ content, categories, activeCategoryKey, onSelectCategor
}}>{category.title}</a>
<ul hidden={category.key !== activeCategoryKey}>
{category.modules.map((module) => {
const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
const definition = getToolboxModuleDefinition(module.type);
const title = module.title || definition.label || "Outil";
const anchorId = getLibraryAnchorId(module);
return (
@ -328,8 +309,8 @@ function LibraryDocNav({ content, categories, activeCategoryKey, onSelectCategor
}
function LibraryToolExample({ module, sourceData, description, categoryTitle, docs, featureHeading, advancedHeading, advancedCopyLabel, advancedCopySuccess, controlLegendHeading, context }) {
const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
const Component = definition.Component;
const definition = getToolboxModuleDefinition(module.type);
const Component = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
const [scrollable, setScrollable] = useState(false);
const [editing, setEditing] = useState(false);
const title = module.title || definition.label || "Outil";
@ -388,7 +369,9 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
</div>
</header>
<div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
<Component toolboxId={LIBRARY_TOOLBOX_ID} moduleId={module.id} context={context} editing={editing} />
<Suspense fallback={<div className="empty"><p>Chargement de l'outil...</p></div>}>
<Component toolboxId={LIBRARY_TOOLBOX_ID} moduleId={module.id} context={context} editing={editing} />
</Suspense>
</div>
</article>
</div>