From 29226500e8d72254b31850899a9d1ef7c7ad2f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Battistella=20Nadas?= Date: Tue, 9 Jun 2026 19:38:53 +0200 Subject: [PATCH] ux(ocr): full-bleed modal on mobile, buttons hug screen edges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @media (max-width: 640px): - backdrop loses its 1 rem inset; modal goes 100vw × 100vh with no border-radius - crop modal body keeps ~20 px horizontal padding so cropper corner handles stay safely outside Android's edge-swipe back-gesture zone (the corner DRAG is what triggers it; a tap on a button at the edge doesn't) - camera viewfinder body has zero padding — video fills the whole space - footer has minimal 0.5 rem padding so the rotate / invert / cancel / check buttons hug the screen edges - iOS notch / home-indicator insets honoured via env(safe-area-inset-*) Doesn't change the camera-confirm behaviour — that's the OS-camera fallback path that fires when getUserMedia is denied for not being a secure context (i.e. http://lan-ip:8000 from the phone, not HTTPS or localhost). --- CHANGELOG.md | 1 + static/index.html | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fe4886..7729ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Camera viewfinder modal on mobile (via `navigator.mediaDevices.getUserMedia` with `facingMode: environment`) — replaces the `` flow. Tapping the shutter goes directly to the crop modal, skipping the OS-level "retake / use" confirm step. Falls back to the file picker if camera access is denied or unavailable; a "Pick from file" icon (`photo_library`) inside the camera modal also routes to the picker on demand. - Invert-colours toggle (`invert_colors`) in the crop modal. Tap to flip light-on-dark text into the dark-on-light polarity Tesseract expects. The grayscale buffer is inverted before adaptive thresholding so the binarised output is correct. +- Mobile-first modal layout (≤ 640 px): the crop and camera modals now fill the viewport (no backdrop padding, no border-radius), buttons hug the screen edges in the footer, and the camera video occupies the entire body. Body still has ~20 px horizontal padding so cropper's corner handles stay outside Android's edge-swipe back-gesture zone. iOS notch / home-bar insets honoured. ### Fixed - "Hot: …" status no longer claims "unloads in X" when the server's `keep_alive` is `-1` (Ollama returns an absurd year-4001 `expires_at`). Threshold: any expiry >7 days drops the suffix and just shows the model name. diff --git a/static/index.html b/static/index.html index cf3b714..f4220fa 100644 --- a/static/index.html +++ b/static/index.html @@ -299,6 +299,41 @@ display: block; } + /* On mobile: full-bleed modal, buttons hug the screen edges. Body padding + still ~20 px so cropper corner handles stay outside Android's edge-swipe + gesture zone — tapping a button doesn't trigger it, but dragging into + the very edge does. */ + @media (max-width: 640px) { + .modal-backdrop { padding: 0; } + .modal { + width: 100vw; + max-width: 100vw; + height: 100vh; + max-height: 100vh; + border-radius: 0; + } + .modal-header { + padding-top: max(0.75rem, env(safe-area-inset-top, 0px)); + } + .modal-body { + padding: + 1rem + max(1.25rem, env(safe-area-inset-right, 0px)) + 1rem + max(1.25rem, env(safe-area-inset-left, 0px)); + max-height: none; + } + .cam-body { padding: 0 !important; } + .cam-body video { max-height: none; } + .modal-footer { + padding: + 0.5rem + max(0.5rem, env(safe-area-inset-right, 0px)) + max(0.5rem, env(safe-area-inset-bottom, 0px)) + max(0.5rem, env(safe-area-inset-left, 0px)); + } + } + /* CodeMirror editor host — matches .output styling so input & output align */ .cm-editor { min-height: 180px;