Interactive image cropping component built on `react-easy-crop`, supporting drag/resize, zoom, rotation, circular (avatar) mode, and PNG export with automatic downscaling. ## Key Components | Export | Type | Description | |---|---|---| | `ImageCropper` | `React.FC` | Main cropper component | | `ImageCropperProps` | `interface` | Component prop contract | | `ImageCropperResult` | `interface` | Shape of the confirmed crop output | **Props** | Prop | Type | Default | Description | |---|---|---|---| | `src` | `string` | — | Source image URL or data URI | | `onComplete` | `(result) => void` | — | Called with PNG data URL + Blob on confirm | | `onCancel` | `() => void` | — | Called when user cancels | | `aspectRatio` | `number` | free-form | Width / height ratio | | `circular` | `boolean` | `false` | Circular overlay for avatar crops | | `maxSizePx` | `number` | `512` | Maximum exported image dimension | | `className` | `string` | — | Wrapper class override | ## Usage Example ```typescript import { ImageCropper, ImageCropperResult } from "@/components/image-cropper" function AvatarUploader({ imageUrl }: { imageUrl: string }) { const handleComplete = (result: ImageCropperResult) => { // Upload the Blob or use the data URL directly console.log(result.dataUrl) // "data:image/png;base64,..." uploadToServer(result.blob) } return ( console.log("cancelled")} circular // avatar mode — circular clip mask aspectRatio={1} // 1:1 square maxSizePx={256} // export capped at 256×256 px /> ) } ``` **Keyboard shortcuts:** `Enter` confirms the crop, `Escape` triggers `onCancel`. ## Notes - Exported images are always **PNG**; the canvas is scaled so neither dimension exceeds `maxSizePx`. - The `crossOrigin="anonymous"` attribute on the internal image loader prevents canvas tainting for cross-origin sources. - Depends on `react-easy-crop`, `lucide-react`, and shadcn/ui `Button` / `Slider` primitives. - Source: [`components/image-cropper.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/components/image-cropper.tsx)