fix(vwb): image plein écran — calcul dimensions JS explicite (fix définitif)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped

Cause racine : max-width/max-height CSS ne font pas GRANDIR une image.
Fix : calcul explicite width/height en JS via Math.min(ratio).
min-height:0 sur le conteneur flex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-04-21 12:19:30 +02:00
parent 8ce63fcba2
commit 3e227d28ad
2 changed files with 54 additions and 16 deletions

View File

@@ -412,6 +412,11 @@ function FullscreenSelector({
// Scale de l'image affichee par rapport a l'image naturelle // Scale de l'image affichee par rapport a l'image naturelle
const [imageScale, setImageScale] = useState({ x: 1, y: 1 }); const [imageScale, setImageScale] = useState({ x: 1, y: 1 });
// Dimensions explicites de l'image (calculees pour remplir le conteneur en contain)
// Resout le bug "timbre poste" : max-width/max-height seuls ne font PAS grandir
// une image plus petite que le conteneur. On calcule la taille explicitement.
const [imgDisplaySize, setImgDisplaySize] = useState<{ width: number; height: number } | null>(null);
// ── Detection automatique des elements UI ── // ── Detection automatique des elements UI ──
useEffect(() => { useEffect(() => {
const runDetection = async () => { const runDetection = async () => {
@@ -433,24 +438,46 @@ function FullscreenSelector({
runDetection(); runDetection();
}, [capture.screenshot_base64]); }, [capture.screenshot_base64]);
// ── Calcul de imageRect et imageScale ── // ── Calcul des dimensions image + imageRect + imageScale ──
// Appele au chargement de l'image ET au redimensionnement de la fenetre. // Appele au chargement de l'image ET au redimensionnement de la fenetre.
// Calcule la taille "contain" (remplir le conteneur en gardant le ratio)
// et l'applique comme width/height explicite sur l'img.
const recalcImageRect = useCallback(() => { const recalcImageRect = useCallback(() => {
if (!imgRef.current || !contentRef.current) return; if (!imgRef.current || !contentRef.current) return;
const containerRect = contentRef.current.getBoundingClientRect(); const containerRect = contentRef.current.getBoundingClientRect();
const imgBounds = imgRef.current.getBoundingClientRect(); const natW = imgRef.current.naturalWidth;
const natH = imgRef.current.naturalHeight;
setImageRect({ if (natW > 0 && natH > 0) {
left: imgBounds.left - containerRect.left, // Calcul "contain" : ratio le plus contraignant
top: imgBounds.top - containerRect.top, const scale = Math.min(
width: imgBounds.width, containerRect.width / natW,
height: imgBounds.height, containerRect.height / natH
}); );
const displayW = Math.round(natW * scale);
const displayH = Math.round(natH * scale);
setImgDisplaySize({ width: displayW, height: displayH });
}
setImageScale({ // Apres le render avec les nouvelles dimensions, recalculer imageRect
x: imgBounds.width / imgRef.current.naturalWidth, // On utilise requestAnimationFrame pour lire le layout apres le paint
y: imgBounds.height / imgRef.current.naturalHeight, requestAnimationFrame(() => {
if (!imgRef.current || !contentRef.current) return;
const cRect = contentRef.current.getBoundingClientRect();
const imgBounds = imgRef.current.getBoundingClientRect();
setImageRect({
left: imgBounds.left - cRect.left,
top: imgBounds.top - cRect.top,
width: imgBounds.width,
height: imgBounds.height,
});
setImageScale({
x: imgBounds.width / imgRef.current.naturalWidth,
y: imgBounds.height / imgRef.current.naturalHeight,
});
}); });
}, []); }, []);
@@ -601,7 +628,7 @@ function FullscreenSelector({
<button onClick={onClose}>Fermer (Echap)</button> <button onClick={onClose}>Fermer (Echap)</button>
</div> </div>
{/* Zone de contenu : flex center, l'image se dimensionne via max-width/max-height */} {/* Zone de contenu : flex center, l'image se dimensionne via imgDisplaySize (contain calcule en JS) */}
<div <div
className="fullscreen-content" className="fullscreen-content"
ref={contentRef} ref={contentRef}
@@ -615,7 +642,13 @@ function FullscreenSelector({
alt="Capture plein ecran" alt="Capture plein ecran"
draggable={false} draggable={false}
onLoad={handleImageLoad} onLoad={handleImageLoad}
style={{ maxWidth: '100%', maxHeight: 'calc(100vh - 70px)', objectFit: 'contain' }} style={imgDisplaySize ? {
width: imgDisplaySize.width,
height: imgDisplaySize.height,
} : {
maxWidth: '100%',
maxHeight: '100%',
}}
/> />
{/* Overlay : positionne exactement sur l'image via imageRect. {/* Overlay : positionne exactement sur l'image via imageRect.

View File

@@ -1085,12 +1085,17 @@ body {
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
/* min-height: 0 — indispensable pour que flex:1 fonctionne dans un
conteneur flex column. Sans ca, min-height:auto empeche le conteneur
de se dimensionner correctement et l'image reste minuscule. */
min-height: 0;
} }
.fullscreen-content img { .fullscreen-content img {
max-width: 100%; /* Les dimensions width/height sont calculees en JS (contain manuel)
max-height: calc(100vh - 70px); pour garantir que l'image remplit le conteneur quel que soit sa
object-fit: contain; taille naturelle (y compris thumbnails 320x240 de la bibliotheque). */
display: block;
cursor: crosshair; cursor: crosshair;
} }