operatore: immagine del task visione in fit, pagina senza scorrimento (Refs #96)

Pannello visione overflow-hidden/min-h-0, immagine e overlay object-contain
sovrapposti nel riquadro flex-1, colonna esito con scorrimento proprio.
Test sulle classi del template.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 18:20:15 +02:00
parent 552f96bb47
commit b98d1f15f4
2 changed files with 63 additions and 6 deletions
@@ -534,9 +534,10 @@
<div x-show="isVisionTask" x-cloak <div x-show="isVisionTask" x-cloak
x-data="visionExecute()" x-data="visionExecute()"
x-init="ctx = () => ({ lot: lotNumber, serial: serialNumber, runId: productionRun ? productionRun.id : null })" x-init="ctx = () => ({ lot: lotNumber, serial: serialNumber, runId: productionRun ? productionRun.id : null })"
class="flex-1 overflow-auto p-3 flex flex-col lg:flex-row gap-3"> class="flex-1 min-h-0 overflow-hidden p-3 flex flex-col lg:flex-row gap-3">
{# #96: il pannello non scorre mai (overflow-hidden, min-h-0): l'immagine sta in fit nello spazio che resta #}
<div class="flex-1 min-w-0 flex flex-col gap-2"> <div class="flex-1 min-w-0 min-h-0 flex flex-col gap-2">
<div class="flex items-center gap-2 flex-wrap"> <div class="flex items-center gap-2 flex-wrap">
{# On a tablet this opens the camera; on a PC it picks a file. «Riprova» reuses the same input. #} {# On a tablet this opens the camera; on a PC it picks a file. «Riprova» reuses the same input. #}
<label class="btn btn-primary text-sm gap-1.5 cursor-pointer" :class="busy ? 'opacity-50 pointer-events-none' : ''"> <label class="btn btn-primary text-sm gap-1.5 cursor-pointer" :class="busy ? 'opacity-50 pointer-events-none' : ''">
@@ -558,16 +559,18 @@
<div x-show="error" class="p-2 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-sm text-measure-fail" x-text="error"></div> <div x-show="error" class="p-2 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-sm text-measure-fail" x-text="error"></div>
<div x-show="shownImage" class="relative border border-[var(--border-color)] rounded-lg overflow-hidden bg-black/5"> {# #96: riquadro flex-1 min-h-0; img e canvas object-contain sovrapposti (il canvas ha la stessa risoluzione naturale dell'immagine, quindi l'overlay resta allineato) #}
<img x-ref="img" :src="shownImage" @load="drawOverlay()" alt="" class="block w-full h-auto"> <div x-show="shownImage" class="relative flex-1 min-h-0 border border-[var(--border-color)] rounded-lg overflow-hidden bg-black/5">
<canvas x-ref="overlay" class="absolute inset-0 w-full h-full pointer-events-none"></canvas> <img x-ref="img" :src="shownImage" @load="drawOverlay()" alt="" class="absolute inset-0 w-full h-full object-contain">
<canvas x-ref="overlay" class="absolute inset-0 w-full h-full object-contain pointer-events-none"></canvas>
</div> </div>
<div x-show="!shownImage" class="text-sm text-[var(--text-muted)]"> <div x-show="!shownImage" class="text-sm text-[var(--text-muted)]">
{{ _('Nessuna immagine acquisita: premi «Acquisisci immagine».') }} {{ _('Nessuna immagine acquisita: premi «Acquisisci immagine».') }}
</div> </div>
</div> </div>
<div class="lg:w-80 shrink-0 flex flex-col gap-2" x-show="result"> {# #96: colonna esito con scorrimento proprio (solo con molte quote); sotto lg max 40% dell'altezza #}
<div class="lg:w-80 shrink-0 min-h-0 overflow-y-auto max-h-[40%] lg:max-h-none flex flex-col gap-2" x-show="result">
<div class="text-sm font-bold text-[var(--text-primary)]"> <div class="text-sm font-bold text-[var(--text-primary)]">
{{ _('Esito') }}: <span x-text="(result && result.outcome) || '-'"></span> {{ _('Esito') }}: <span x-text="(result && result.outcome) || '-'"></span>
</div> </div>
@@ -0,0 +1,54 @@
"""gitea:Adriano/TieMeasureFlow#96 - l'immagine del task visione sta in fit, senza scorrimento.
Controllo sulle classi del template: pannello overflow-hidden, img/canvas
object-contain sovrapposti, nessun h-auto sull'immagine (era la causa dello scorrimento).
"""
from pathlib import Path
TEMPLATE = Path(__file__).resolve().parents[1] / "templates" / "measure" / "task_execute.html"
def _panel():
src = TEMPLATE.read_text(encoding="utf-8")
start = src.rindex("<div", 0, src.index('x-data="visionExecute()"'))
end = src.index("MEASUREMENT TIMER BANNER")
return src[start:end]
def _tag_with(panel, marker):
# Il tag (aperto) che contiene il marker; i '>' dentro le virgolette (es. "=>") non lo chiudono.
pos = panel.index(marker)
i = panel.rindex("<", 0, pos)
quote = None
for j in range(i, len(panel)):
c = panel[j]
if quote:
quote = None if c == quote else quote
elif c in "\"'":
quote = c
elif c == ">":
return panel[i:j + 1]
raise AssertionError(marker)
def test_panel_does_not_scroll():
tag = _tag_with(_panel(), 'x-data="visionExecute()"')
assert "overflow-hidden" in tag and "min-h-0" in tag
assert "overflow-auto" not in tag
def test_image_is_object_contain_without_h_auto():
panel = _panel()
img = _tag_with(panel, 'x-ref="img"')
assert "object-contain" in img
assert "h-auto" not in img
canvas = _tag_with(panel, 'x-ref="overlay"')
assert "object-contain" in canvas # stesso fit dell'img: overlay allineato
def test_image_box_and_outcome_column_can_shrink():
panel = _panel()
box = _tag_with(panel, 'x-show="shownImage"')
assert "flex-1" in box and "min-h-0" in box
outcome = _tag_with(panel, 'x-show="result">')
assert "overflow-y-auto" in outcome and "min-h-0" in outcome