fix: free-form cropping in image lightbox (#737)#739
Merged
Conversation
Replace react-easy-crop with react-advanced-cropper. react-easy-crop is a pan/zoom-over-a-fixed-aspect-frame cropper with no draggable free-form crop box, so omitting `aspect` only reverted CropOverlay to the library's 4:3 default — it could never be free-form. CropOverlay (image lightbox) now uses a RectangleStencil with no aspectRatio, giving a resizable-in-any-direction crop box. Coordinates are read from the cropper (image pixels) and converted to the existing fraction-based ImageCrop. The Glaze Import Tool swatch crop stays square (1:1) — that is intentional product design from the original feature (#146, "rotatable square crop box"; square WebP swatch output feeding the OCR label pipeline), not a bug. It is migrated to the same library but keeps aspectRatio={1} and the CropSquare model, so the OCR/render pipeline is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 tasks
- GlazeImportCropStage: replace the leftover react-easy-crop cropEditor reducer (crop/zoom were dead after the migration) with a single rotation useState. - Skip the onChange commit + OCR-state reset when the clamped crop is unchanged, since react-advanced-cropper fires onChange continuously. - Drop the no-op className="cropper" from both croppers (sizing is via style). - Add a CropOverlay Storybook story that renders the real (unmocked) react-advanced-cropper as an integration check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Both crop UIs locked to a fixed aspect ratio with no free-form resize (#737). Root cause:
react-easy-cropis a pan/zoom-over-a-fixed-aspect-frame cropper — it has no draggable free-form crop box at all, so omittingaspectmerely revertedCropOverlayto the library's4/3default.Fix
Replace
react-easy-cropwithreact-advanced-cropper(native free-form resizable stencil).CropOverlay(image lightbox):RectangleStencilwith noaspectRatio→ resizable in any direction. Crop coordinates (image px) are converted to the existing fraction-basedImageCrop; no backend change.GlazeImportCropStage): migrated to the same library but kept square (aspectRatio={1}). The square swatch is intentional product design from the original feature (Workflow for creating public glaze types and glaze combinations from local test-tile images #146 — "rotatable square crop box", square WebP output feeding the OCR label pipeline), not a bug. TheCropSquare/OCR/render pipeline is unchanged.Regression Tests
CropOverlay.test.tsx— asserts no fixedaspectRatioon the stencil and that a non-square crop round-trips throughonSavewith independentwidth/height.GlazeImportCropStage.test.tsx— asserts the stencil stays locked toaspectRatio === 1and crop geometry updates viaonChange.ImageLightbox.test.tsx— mock updated toreact-advanced-cropper.Verification
bazel test //web:web_test— 37/37 pass.bazel build --config=lint //web/...— eslint + tsc clean.bazel build //web:web_lib //web:web_prod_lib— dev and prod bundles resolve the new package.Closes #737