From 9f54bc62bd447f143d5477167c6dff2547fbc83e Mon Sep 17 00:00:00 2001 From: Samuel Prevost Date: Fri, 1 May 2026 00:14:20 +0200 Subject: [PATCH] fix(crop): persist on unmount + handle image-load failure Two follow-up fixes flagged by the post-merge review: - persist() on `onUnmounted` so a mid-drag navigation (Back button, hot-reload) doesn't lose the in-progress crop. The local refs already hold the latest coords; we just need to flush them through to the store + cache before the component goes away. - Add `el.onerror` to the image load and a small fallback UI offering Back-to-Deskew. Without it, a corrupt deskew blob would leave the canvas permanently blank with no way to recover short of refresh. --- src/components/CropViewer.vue | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/components/CropViewer.vue b/src/components/CropViewer.vue index ec3ee9f..480b53a 100644 --- a/src/components/CropViewer.vue +++ b/src/components/CropViewer.vue @@ -32,6 +32,11 @@ const overlayRef = ref(null) const img = ref(null) const imgUrl = ref(null) +// Set when the deskew blob fails to decode — corrupt result, OOM in the +// decoder, or unsupported format. We surface a small fallback UI so the +// user can navigate back to step 4 and retry instead of staring at a +// permanently blank canvas. +const loadError = ref(false) // Rotation in degrees. Crop fractions of the rotated bbox. const rotationDeg = ref(0) @@ -73,10 +78,14 @@ function persist() { function loadImage(url: string) { const el = new Image() el.onload = () => { + loadError.value = false img.value = el fitToContainer() redraw() } + el.onerror = () => { + loadError.value = true + } el.src = url } @@ -392,6 +401,11 @@ onMounted(() => { }) onUnmounted(() => { + // If the user navigated away mid-drag (e.g. tapped Back during a corner + // pull), the local refs hold the latest crop coords but `persist()` only + // fires on `pointerup`. Flush here so cache + store match what was on + // screen. + persist() resizeObs?.disconnect() if (imgUrl.value) URL.revokeObjectURL(imgUrl.value) }) @@ -500,6 +514,24 @@ function next() { @pointerup="onPointerUp" @pointercancel="onPointerUp" /> +
+

+ Failed to load the deskew result. +

+

+ The image blob couldn't be decoded. Re-run the + perspective correction in the previous step. +

+ +