// Rôle : fournit l'outil annotation d'images avec marqueurs éditables et échange texte. import { useCallback, useEffect, useRef, useState } from "react"; import { Icon } from "../../../components/Icon.jsx"; import { Tooltip } from "../../../components/Tooltip.jsx"; import { DrawingOverlay } from "./DrawingOverlay.jsx"; import { TextExchangeActions } from "./TextExchangeActions.jsx"; function markerLabel(index, marker, textContent) { return marker.label || `${textContent.markerPrefix || "Marqueur"} ${index + 1}`; } export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, textExchangeOpen }) { const data = context.normalizeImageAnnotationData(context.getModuleData(toolboxId, moduleId, { image: "", markers: [] })); const textContent = context.moduleText?.imageAnnotation || {}; const notify = context.notify; const previewMediaRef = useRef(null); const temporaryDrawingsDirtyRef = useRef(false); const sessionStorageFailureRef = useRef(""); const sessionKey = `sokkog:image-annotation-drawings:${toolboxId}:${moduleId}`; const [dragOver, setDragOver] = useState(false); const [drawingMode, setDrawingMode] = useState("permanent"); const [temporaryDrawings, setTemporaryDrawings] = useState({ strokes: [] }); const markers = data.markers; const drawings = drawingMode === "permanent" ? data.drawings || { strokes: [] } : temporaryDrawings; const pastePlaceholder = textContent.pastePlaceholder || "Coller une image ici"; const getPreviewSurfaceSize = useCallback((element) => ({ width: element?.clientWidth || 0, height: element?.clientHeight || 0 }), []); useEffect(() => { temporaryDrawingsDirtyRef.current = false; sessionStorageFailureRef.current = ""; try { const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "{\"strokes\":[]}"); setTemporaryDrawings(stored?.strokes ? stored : { strokes: [] }); } catch { setTemporaryDrawings({ strokes: [] }); } }, [sessionKey]); useEffect(() => { if (drawingMode !== "temporary") return; if (!temporaryDrawingsDirtyRef.current) return; const serializedDrawings = JSON.stringify(temporaryDrawings); try { sessionStorage.setItem(sessionKey, serializedDrawings); temporaryDrawingsDirtyRef.current = false; sessionStorageFailureRef.current = ""; } catch { const failureKey = `${sessionKey}:${serializedDrawings}`; if (sessionStorageFailureRef.current === failureKey) return; sessionStorageFailureRef.current = failureKey; notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées."); } }, [drawingMode, notify, sessionKey, temporaryDrawings, textContent.sessionStorageError]); function save(nextData) { context.setModuleData(toolboxId, moduleId, nextData); } async function setImageFromFiles(files) { const file = [...files].find((item) => item?.type?.startsWith("image/")); if (!file) return; save({ image: await context.compressImageFile(file), markers: [], drawings: { strokes: [] } }); setTemporaryDrawings({ strokes: [] }); setDragOver(false); } function removeImage() { save({ image: "", markers: [], drawings: { strokes: [] } }); setTemporaryDrawings({ strokes: [] }); } return (
{textContent.emptyImage || "Ajoutez une image pour commencer l'annotation."}
)}