@ibanzajoe/uploader 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/errors.ts","../src/core/chunk.ts","../src/core/client.ts","../src/core/transform.ts","../src/react/PickerOverlay.tsx","../src/react/usePicker.ts","../src/react/ImageEditor.tsx","../src/react/editImage.ts","../src/react/DropPane.tsx"],"sourcesContent":["/**\n * @uploader/react — headless upload client + React picker.\n *\n * Exports the headless core client and React picker components/hook.\n * Import styles separately: `import '@uploader/react/styles.css'`\n */\n\nexport { UploaderClient } from './core/index.js'\nexport {\n resize,\n crop,\n rotate,\n flip,\n flop,\n quality,\n output,\n transformUrl,\n} from './core/index.js'\nexport type {\n FileResult,\n PickerResponse,\n PolicySpec,\n UploaderClientOptions,\n UploadOptions,\n UploadAllOptions,\n TransformOp,\n TransformOpName,\n TransformChain,\n ResizeParams,\n CropParams,\n RotateParams,\n QualityParams,\n OutputParams,\n TransformUrlOptions,\n} from './core/index.js'\n\n// React picker (requires react/react-dom peer deps)\nexport { PickerOverlay } from './react/PickerOverlay.js'\nexport type { PickerOverlayProps } from './react/PickerOverlay.js'\n\nexport { DropPane } from './react/DropPane.js'\nexport type { DropPaneProps } from './react/DropPane.js'\n\nexport { usePicker } from './react/usePicker.js'\nexport type { UsePickerOptions } from './react/usePicker.js'\nexport type { UsePickerResult } from './react/types.js'\n\n// In-picker image editor (rotate / flip / crop / resize before upload)\nexport { ImageEditor } from './react/ImageEditor.js'\nexport type { ImageEditorProps } from './react/ImageEditor.js'\nexport { editImage } from './react/editImage.js'\nexport type { EditImageOptions, Flip, CropArea } from './react/editImage.js'\n\nexport type {\n PickerFile,\n PickerFileState,\n PickerOptions,\n SharedPickerProps,\n} from './react/types.js'\n","/**\n * Typed errors for UploaderClient.\n *\n * UploaderError is thrown by upload / uploadAll on any non-retried failure.\n * The caller can narrow on `err.code` for structured handling.\n */\n\nexport type UploaderErrorCode =\n | 'ABORTED' // AbortSignal fired\n | 'NETWORK_ERROR' // fetch() threw (no response)\n | 'SERVER_ERROR' // 5xx after all retries exhausted\n | 'CLIENT_ERROR' // 4xx (not retried)\n | 'INVALID_RESPONSE' // response body did not match expected shape\n\nexport class UploaderError extends Error {\n readonly code: UploaderErrorCode\n /** HTTP status code when available (undefined for ABORTED / NETWORK_ERROR). */\n readonly statusCode?: number\n\n constructor(code: UploaderErrorCode, message: string, statusCode?: number) {\n super(message)\n this.name = 'UploaderError'\n this.code = code\n this.statusCode = statusCode\n }\n}\n","/**\n * Chunk planner for multipart uploads.\n *\n * Decides single-shot vs multipart by comparing the file size against\n * MULTIPART_THRESHOLD. For multipart files, slices the Blob into parts of\n * `chunkSize` bytes.\n */\n\n/** Files ≤ this size use single-shot POST /api/store. */\nexport const MULTIPART_THRESHOLD = 5 * 1024 * 1024 // 5 MB\n\n/** Default part size for multipart uploads. */\nexport const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024 // 5 MB\n\nexport type ChunkPlan =\n | { mode: 'single' }\n | { mode: 'multipart'; parts: Blob[]; partSize: number }\n\n/**\n * Build a chunk plan for a file.\n *\n * @param file The File or Blob to upload.\n * @param chunkSize Desired part size in bytes (default DEFAULT_CHUNK_SIZE).\n * @returns A plan describing whether to use single-shot or multipart.\n */\nexport function planChunks(file: File | Blob, chunkSize = DEFAULT_CHUNK_SIZE): ChunkPlan {\n if (file.size <= MULTIPART_THRESHOLD) {\n return { mode: 'single' }\n }\n\n const parts: Blob[] = []\n let offset = 0\n while (offset < file.size) {\n parts.push(file.slice(offset, offset + chunkSize))\n offset += chunkSize\n }\n return { mode: 'multipart', parts, partSize: chunkSize }\n}\n","/**\n * UploaderClient — headless upload client.\n *\n * Chooses single-shot (POST /api/store, multipart/form-data) vs multipart\n * (start/part/complete) based on file size relative to MULTIPART_THRESHOLD\n * (5 MB). Parts are retried individually with exponential backoff. Progress\n * is emitted as a 0–100 integer. AbortSignal cancels in-flight work.\n */\n\nimport type {\n FileResult,\n UploadStartResponse,\n UploadPartResponse,\n UploaderClientOptions,\n UploadAllOptions,\n UploadOptions,\n} from './types.js'\nimport { UploaderError } from './errors.js'\nimport { planChunks } from './chunk.js'\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst MAX_RETRIES = 3\nconst RETRY_BASE_MS = 200\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Sleep for `ms` milliseconds, resolving early if signal fires. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new UploaderError('ABORTED', 'Upload aborted'))\n return\n }\n const timer = setTimeout(resolve, ms)\n signal?.addEventListener('abort', () => {\n clearTimeout(timer)\n reject(new UploaderError('ABORTED', 'Upload aborted'))\n }, { once: true })\n })\n}\n\n/** Throw UploaderError(ABORTED) if signal has already fired. */\nfunction checkAbort(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw new UploaderError('ABORTED', 'Upload aborted')\n }\n}\n\n/**\n * Fetch with retry on network errors and 5xx responses.\n * 4xx responses are not retried — they surface immediately as CLIENT_ERROR.\n */\nasync function fetchWithRetry(\n url: string,\n init: RequestInit,\n signal?: AbortSignal,\n maxRetries = MAX_RETRIES,\n): Promise<Response> {\n let lastErr: unknown\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n checkAbort(signal)\n try {\n const res = await fetch(url, { ...init, signal })\n if (res.status >= 400 && res.status < 500) {\n // Client error — do not retry\n const body = await res.text().catch(() => '')\n throw new UploaderError(\n 'CLIENT_ERROR',\n `HTTP ${res.status}: ${body}`,\n res.status,\n )\n }\n if (res.status >= 500) {\n // Server error — retry with backoff\n lastErr = new UploaderError(\n 'SERVER_ERROR',\n `HTTP ${res.status}`,\n res.status,\n )\n if (attempt < maxRetries - 1) {\n await sleep(RETRY_BASE_MS * 2 ** attempt, signal)\n }\n continue\n }\n return res\n } catch (err) {\n if (err instanceof UploaderError) {\n if (err.code === 'CLIENT_ERROR' || err.code === 'ABORTED') throw err\n lastErr = err\n } else {\n // fetch() threw (network failure, CORS, etc.)\n lastErr = new UploaderError(\n 'NETWORK_ERROR',\n err instanceof Error ? err.message : String(err),\n )\n }\n if (attempt < maxRetries - 1) {\n await sleep(RETRY_BASE_MS * 2 ** attempt, signal)\n }\n }\n }\n throw lastErr\n}\n\n// ─── UploaderClient ────────────────────────────────────────────────────────────\n\nexport class UploaderClient {\n readonly apikey: string\n readonly apiUrl: string\n readonly security: UploaderClientOptions['security']\n\n constructor(options: UploaderClientOptions) {\n this.apikey = options.apikey\n this.apiUrl = (options.apiUrl ?? 'https://api.uploaderhq.io').replace(/\\/$/, '')\n this.security = options.security\n }\n\n // ─── Auth headers ──────────────────────────────────────────────────────────\n\n /**\n * Build the auth headers shared by all requests.\n * Attaches the API key and, when present, the signed policy pair.\n */\n #authHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'X-Uploader-Key': this.apikey,\n }\n if (this.security) {\n headers['X-Uploader-Policy'] = this.security.policy\n headers['X-Uploader-Signature'] = this.security.signature\n }\n return headers\n }\n\n // ─── Single-shot upload ────────────────────────────────────────────────────\n\n /**\n * POST /api/store — multipart/form-data for files ≤ MULTIPART_THRESHOLD.\n */\n async #uploadSingleShot(\n file: File | Blob,\n opts: UploadOptions,\n ): Promise<FileResult> {\n const { onProgress, filename, signal } = opts\n\n onProgress?.(0)\n checkAbort(signal)\n\n const form = new FormData()\n form.append('file', file, filename ?? (file instanceof File ? file.name : 'upload'))\n if (filename) form.append('filename', filename)\n\n const res = await fetchWithRetry(\n `${this.apiUrl}/api/store`,\n {\n method: 'POST',\n headers: this.#authHeaders(),\n body: form,\n },\n signal,\n )\n\n const body = await res.json() as unknown\n onProgress?.(100)\n return this.#parseFileResult(body)\n }\n\n // ─── Multipart upload ──────────────────────────────────────────────────────\n\n /**\n * start → parts (parallel with progress) → complete for files > MULTIPART_THRESHOLD.\n */\n async #uploadMultipart(\n file: File | Blob,\n parts: Blob[],\n opts: UploadOptions,\n ): Promise<FileResult> {\n const { onProgress, filename, signal } = opts\n const name = filename ?? (file instanceof File ? file.name : 'upload')\n const mime = file instanceof File ? file.type : 'application/octet-stream'\n\n onProgress?.(0)\n checkAbort(signal)\n\n // 1. Start\n const startRes = await fetchWithRetry(\n `${this.apiUrl}/api/upload/start`,\n {\n method: 'POST',\n headers: { ...this.#authHeaders(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ filename: name, mimetype: mime, size: file.size }),\n },\n signal,\n )\n const { uploadId } = await startRes.json() as UploadStartResponse\n\n // 2. Upload parts sequentially (retried individually)\n const etags: { partNumber: number; etag: string }[] = []\n let uploadedBytes = 0\n\n for (let i = 0; i < parts.length; i++) {\n checkAbort(signal)\n const partBlob = parts[i]!\n const partNumber = i + 1\n\n const partForm = new FormData()\n partForm.append('uploadId', uploadId)\n partForm.append('partNumber', String(partNumber))\n partForm.append('part', partBlob)\n\n const partRes = await fetchWithRetry(\n `${this.apiUrl}/api/upload/part`,\n {\n method: 'POST',\n headers: this.#authHeaders(),\n body: partForm,\n },\n signal,\n )\n const { etag } = await partRes.json() as UploadPartResponse\n\n etags.push({ partNumber, etag })\n uploadedBytes += partBlob.size\n onProgress?.(Math.round((uploadedBytes / file.size) * 95)) // reserve 5% for complete\n }\n\n // 3. Complete\n checkAbort(signal)\n const completeRes = await fetchWithRetry(\n `${this.apiUrl}/api/upload/complete`,\n {\n method: 'POST',\n headers: { ...this.#authHeaders(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ uploadId, parts: etags }),\n },\n signal,\n )\n const body = await completeRes.json() as unknown\n onProgress?.(100)\n return this.#parseFileResult(body)\n }\n\n // ─── Response parser ───────────────────────────────────────────────────────\n\n #parseFileResult(body: unknown): FileResult {\n if (\n typeof body !== 'object' ||\n body === null ||\n typeof (body as Record<string, unknown>)['handle'] !== 'string' ||\n typeof (body as Record<string, unknown>)['url'] !== 'string'\n ) {\n throw new UploaderError('INVALID_RESPONSE', 'Unexpected response shape from upload API')\n }\n return body as FileResult\n }\n\n // ─── Public API ───────────────────────────────────────────────────────────\n\n /**\n * Upload a single file.\n *\n * Automatically selects single-shot vs multipart upload based on file size.\n * Emits progress via `opts.onProgress` (0–100). Respects `opts.signal` for\n * cancellation. Retries network errors and 5xx responses up to 3 times with\n * exponential backoff; 4xx errors are surfaced immediately.\n *\n * @throws {UploaderError} with code ABORTED | NETWORK_ERROR | SERVER_ERROR |\n * CLIENT_ERROR | INVALID_RESPONSE\n */\n async upload(\n file: File | Blob,\n opts: UploadOptions = {},\n ): Promise<FileResult> {\n const plan = planChunks(file, opts.chunkSize)\n\n if (plan.mode === 'single') {\n return this.#uploadSingleShot(file, opts)\n }\n return this.#uploadMultipart(file, plan.parts, opts)\n }\n\n /**\n * Upload multiple files with concurrency limiting.\n *\n * Resolves once all uploads settle (fulfilled or rejected). The returned\n * array preserves input order. `opts.concurrency` caps simultaneous\n * in-flight uploads (default 3). Each file shares the same opts\n * (including onProgress — the callback fires per-file, not aggregate).\n *\n * @returns Array of PromiseSettledResult in input order.\n */\n async uploadAll(\n files: Array<File | Blob>,\n opts: UploadAllOptions = {},\n ): Promise<PromiseSettledResult<FileResult>[]> {\n const concurrency = opts.concurrency ?? 3\n const results: PromiseSettledResult<FileResult>[] = new Array(files.length)\n\n let index = 0\n\n async function worker(client: UploaderClient): Promise<void> {\n while (index < files.length) {\n const i = index++\n const file = files[i]!\n try {\n results[i] = { status: 'fulfilled', value: await client.upload(file, opts) }\n } catch (err) {\n results[i] = { status: 'rejected', reason: err }\n }\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, files.length) }, () =>\n worker(this),\n )\n await Promise.all(workers)\n return results\n }\n}\n","/**\n * Delivery / transform URL builder.\n *\n * The API serves transformed derivatives at `GET /<chain>/<handle>`, where\n * <chain> is a slash-joined list of ops (e.g. `resize=w:200,h:200,fit:crop`).\n * These op builders + `transformUrl()` construct that URL from an uploaded file\n * handle, so consumers render an image at any size/format/quality without\n * hand-assembling URL strings.\n *\n * The op/param types and serialization mirror @uploader/shared EXACTLY — the\n * API's `parseTransformChain` is the other half of this contract, and\n * `contract.conformance.ts` asserts at typecheck time that they stay in sync.\n */\n\n// ─── Op vocabulary (mirrors @uploader/shared) ────────────────────────────────\n\nexport type TransformOpName =\n | 'resize'\n | 'crop'\n | 'rotate'\n | 'flip'\n | 'flop'\n | 'quality'\n | 'output'\n\n/** Parameters for the resize operation. */\nexport type ResizeParams = {\n w?: number\n h?: number\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside' | 'crop'\n}\n\n/** Parameters for the crop operation. */\nexport type CropParams = {\n /** \"x,y,w,h\" notation. */\n dim: string\n x?: number\n y?: number\n w?: number\n h?: number\n}\n\n/** Parameters for the rotate operation. */\nexport type RotateParams = { deg: number }\n\n/** Parameters for the quality operation. */\nexport type QualityParams = { n: number }\n\n/** Parameters for the output operation. */\nexport type OutputParams = { format: string }\n\n/** A single transform operation with its typed params. */\nexport type TransformOp =\n | { name: 'resize'; params: ResizeParams }\n | { name: 'crop'; params: CropParams }\n | { name: 'rotate'; params: RotateParams }\n | { name: 'flip'; params: Record<string, never> }\n | { name: 'flop'; params: Record<string, never> }\n | { name: 'quality'; params: QualityParams }\n | { name: 'output'; params: OutputParams }\n\n/** An ordered list of transform operations. */\nexport type TransformChain = { ops: TransformOp[] }\n\n// ─── Op builders ─────────────────────────────────────────────────────────────\n\n/** Resize to a width and/or height with an optional fit mode. */\nexport const resize = (params: ResizeParams): TransformOp => ({ name: 'resize', params })\n\n/** Crop a rectangular region: origin (x, y) and size w×h, in source pixels. */\nexport const crop = (rect: { x: number; y: number; w: number; h: number }): TransformOp => ({\n name: 'crop',\n params: { dim: `${rect.x},${rect.y},${rect.w},${rect.h}`, ...rect },\n})\n\n/** Rotate clockwise by `deg` degrees. */\nexport const rotate = (params: RotateParams): TransformOp => ({ name: 'rotate', params })\n\n/** Flip vertically (mirror top↔bottom). */\nexport const flip = (): TransformOp => ({ name: 'flip', params: {} })\n\n/** Flop horizontally (mirror left↔right). */\nexport const flop = (): TransformOp => ({ name: 'flop', params: {} })\n\n/** Set output quality 1–100 (ignored by lossless formats). */\nexport const quality = (params: QualityParams): TransformOp => ({ name: 'quality', params })\n\n/** Convert the output format, e.g. `{ format: 'webp' }`. */\nexport const output = (params: OutputParams): TransformOp => ({ name: 'output', params })\n\n// ─── Serialization (vendored from @uploader/shared, parser-compatible) ────────\n\nfunction serializeOp(op: TransformOp): string {\n switch (op.name) {\n case 'resize': {\n const parts: string[] = []\n if (op.params.w !== undefined) parts.push(`w:${op.params.w}`)\n if (op.params.h !== undefined) parts.push(`h:${op.params.h}`)\n if (op.params.fit !== undefined) parts.push(`fit:${op.params.fit}`)\n return `resize=${parts.join(',')}`\n }\n case 'crop':\n return `crop=dim:${op.params.dim}`\n case 'rotate':\n return `rotate=deg:${op.params.deg}`\n case 'flip':\n return 'flip'\n case 'flop':\n return 'flop'\n case 'quality':\n return `quality=n:${op.params.n}`\n case 'output':\n return `output=format:${op.params.format}`\n }\n}\n\nexport type TransformUrlOptions = {\n /** File handle from an upload (`FileResult.handle`). */\n handle: string\n /** Ordered transform ops to apply. Must contain at least one op. */\n ops: TransformOp[]\n /**\n * Base API URL. Defaults to '' so the URL is relative — `/<chain>/<handle>` —\n * and resolves same-origin (e.g. proxied to the API). Pass an absolute URL to\n * point at a remote API directly.\n */\n apiUrl?: string\n}\n\n/**\n * Build a delivery URL that applies `ops` to the file identified by `handle`.\n *\n * @example\n * transformUrl({\n * handle: file.handle,\n * ops: [resize({ w: 200, h: 200, fit: 'crop' }), output({ format: 'webp' })],\n * })\n * // => \"/resize=w:200,h:200,fit:crop/output=format:webp/<handle>\"\n */\nexport function transformUrl({ handle, ops, apiUrl = '' }: TransformUrlOptions): string {\n const base = apiUrl.replace(/\\/$/, '')\n const chain = ops.map(serializeOp).join('/')\n // The API's transform route requires at least one op segment before the\n // handle; for the unmodified original, use FileResult.url instead.\n return chain ? `${base}/${chain}/${handle}` : `${base}/${handle}`\n}\n","/**\n * PickerOverlay — modal file picker with drag-and-drop.\n *\n * A full-featured modal picker matching filestack's core UX:\n * - Drag-and-drop dropzone + \"Browse files\" button\n * - Multi-file queue with thumbnails/preview, per-file progress bars\n * - Remove and retry controls per file\n * - Aggregate progress bar\n * - Upload / Cancel action buttons\n * - Keyboard: ESC closes; Tab cycles within the modal (focus trap)\n * - ARIA: role=\"dialog\", aria-modal, labelled header, live regions\n * - Themeable via CSS variables (--uploader-accent, etc.)\n *\n * Opens when `open={true}`. Calls `onClose` when the user dismisses via ESC,\n * the backdrop click, or the Cancel button. `onUploadDone` fires after all\n * selected files have been uploaded.\n */\n\nimport React, {\n useRef,\n useState,\n useCallback,\n useEffect,\n useId,\n} from 'react'\nimport { usePicker } from './usePicker.js'\nimport { ImageEditor } from './ImageEditor.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport type PickerOverlayProps = SharedPickerProps & {\n /** Whether the modal is open. */\n open: boolean\n /** Called when the modal should close (ESC, backdrop, cancel). */\n onClose: () => void\n /** Additional CSS class(es) applied to the modal panel. */\n className?: string\n /** Inline styles applied to the modal panel. */\n style?: React.CSSProperties\n}\n\nexport function PickerOverlay({\n open,\n onClose,\n apikey,\n apiUrl,\n security,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n className,\n style,\n}: PickerOverlayProps) {\n const titleId = useId()\n const inputRef = useRef<HTMLInputElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const [isDragOver, setIsDragOver] = useState(false)\n const [editingId, setEditingId] = useState<string | null>(null)\n\n const { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── ESC key to close ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onCancel?.()\n onClose()\n }\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [open, onClose, onCancel])\n\n // ── Focus trap ───────────────────────────────────────────────────────────\n // When open, constrain Tab/Shift+Tab to the modal panel.\n useEffect(() => {\n if (!open || !panelRef.current) return\n\n const panel = panelRef.current\n const focusable = panel.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n if (focusable.length === 0) return\n\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n\n // Move initial focus into the panel\n first.focus()\n\n const trap = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n document.addEventListener('keydown', trap)\n return () => document.removeEventListener('keydown', trap)\n }, [open, files, editingId]) // re-run when the list or editor view changes\n\n // ── Body scroll lock ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [open])\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n const openFileDialog = useCallback(() => inputRef.current?.click(), [])\n\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n addFiles(e.target.files)\n e.target.value = ''\n }\n },\n [addFiles],\n )\n\n const handleClose = useCallback(() => {\n onCancel?.()\n onClose()\n }, [onCancel, onClose])\n\n const hasPending = files.some((f) => f.state === 'pending')\n const accept = pickerOptions?.accept?.join(',')\n const editingEntry = files.find((f) => f.id === editingId) ?? null\n\n if (!open) return null\n\n return (\n /* Backdrop — clicking outside the panel dismisses the modal */\n <div\n className=\"uploader-backdrop\"\n data-testid=\"picker-backdrop\"\n onClick={handleClose}\n >\n {/* Modal panel — stops backdrop click propagation */}\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n className={['uploader-picker-panel', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"picker-panel\"\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"uploader-picker-header\">\n <h2 id={titleId} className=\"uploader-picker-title\">\n {editingEntry ? 'Edit Image' : 'Upload Files'}\n </h2>\n <button\n type=\"button\"\n className=\"uploader-btn-close\"\n aria-label=\"Close picker\"\n onClick={handleClose}\n >\n ×\n </button>\n </div>\n\n {editingEntry ? (\n <ImageEditor\n file={editingEntry.file}\n onCancel={() => setEditingId(null)}\n onApply={(edited) => {\n editFile(editingEntry.id, edited)\n setEditingId(null)\n }}\n />\n ) : (\n <>\n {/* Drop zone */}\n <div\n className={[\n 'uploader-dropzone',\n isDragOver ? 'uploader-drag-over' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Drop files here or click to browse\"\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n onClick={openFileDialog}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openFileDialog()\n }\n }}\n >\n <span className=\"uploader-dropzone-icon\" aria-hidden=\"true\">\n ↑\n </span>\n <p className=\"uploader-dropzone-label\">\n Drag &amp; drop files here\n </p>\n <button\n type=\"button\"\n className=\"uploader-btn-browse\"\n onClick={(e) => {\n e.stopPropagation()\n openFileDialog()\n }}\n >\n Browse files\n </button>\n </div>\n\n {/* Hidden file input */}\n <input\n ref={inputRef}\n type=\"file\"\n multiple={!pickerOptions?.maxFiles || pickerOptions.maxFiles > 1}\n accept={accept}\n className=\"uploader-file-input\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={handleInputChange}\n />\n\n {/* File list */}\n {files.length > 0 && (\n <ul className=\"uploader-file-list\" role=\"list\" aria-label=\"Selected files\">\n {files.map((entry) => (\n <li key={entry.id} className={`uploader-file-item uploader-file-${entry.state}`}>\n {/* Thumbnail */}\n {entry.previewUrl && (\n <img\n src={entry.previewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"uploader-file-thumb\"\n />\n )}\n\n {/* Name + status */}\n <div className=\"uploader-file-info\">\n <span className=\"uploader-file-name\">{entry.file.name}</span>\n {entry.state === 'error' && (\n <span className=\"uploader-file-error\" role=\"alert\">\n {entry.error ?? 'Upload failed'}\n </span>\n )}\n {entry.state === 'done' && (\n <span className=\"uploader-file-done\">Uploaded</span>\n )}\n </div>\n\n {/* Per-file progress */}\n {entry.state === 'uploading' && entry.progress != null && (\n <progress\n className=\"uploader-file-progress\"\n value={entry.progress}\n max={100}\n aria-label={`${entry.file.name} upload progress`}\n />\n )}\n\n {/* Actions */}\n <div className=\"uploader-file-actions\">\n {entry.previewUrl &&\n (entry.state === 'pending' || entry.state === 'error') && (\n <button\n type=\"button\"\n className=\"uploader-btn-edit\"\n aria-label={`Edit ${entry.file.name}`}\n onClick={() => setEditingId(entry.id)}\n >\n Edit\n </button>\n )}\n {entry.state === 'error' && (\n <button\n type=\"button\"\n className=\"uploader-btn-retry\"\n aria-label={`Retry uploading ${entry.file.name}`}\n onClick={() => retryFile(entry.id)}\n >\n Retry\n </button>\n )}\n {entry.state !== 'uploading' && (\n <button\n type=\"button\"\n className=\"uploader-btn-remove\"\n aria-label={`Remove ${entry.file.name}`}\n onClick={() => removeFile(entry.id)}\n >\n ×\n </button>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n\n {/* Aggregate progress */}\n {isUploading && (\n <div className=\"uploader-aggregate-progress\" role=\"status\" aria-live=\"polite\">\n <progress value={progress} max={100} aria-label=\"Overall upload progress\" />\n <span aria-live=\"polite\">{progress}%</span>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"uploader-picker-footer\">\n {hasPending && !isUploading && (\n <button\n type=\"button\"\n className=\"uploader-btn-upload\"\n disabled={isUploading || !hasPending}\n onClick={() => void upload()}\n >\n Upload {files.filter((f) => f.state === 'pending').length} file\n {files.filter((f) => f.state === 'pending').length !== 1 ? 's' : ''}\n </button>\n )}\n {isDone && (\n <button\n type=\"button\"\n className=\"uploader-btn-done\"\n onClick={onClose}\n >\n Done\n </button>\n )}\n {!isDone && (\n <button\n type=\"button\"\n className=\"uploader-btn-cancel\"\n disabled={isUploading}\n onClick={handleClose}\n >\n Cancel\n </button>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n","/**\n * usePicker — headless hook driving UploaderClient for the React picker.\n *\n * Manages the file queue, per-file upload state, progress aggregation, and\n * result collection. Components (PickerOverlay, DropPane) call this hook and\n * render its state; custom UIs can use it directly for fully bespoke pickers.\n *\n * Lifecycle:\n * addFiles() → files are queued (state: \"pending\")\n * upload() → each pending file is passed to client.upload() in order;\n * state transitions pending → uploading → done/error\n * onUploadDone fires once all files have settled\n */\n\nimport { useState, useCallback, useRef } from 'react'\nimport { UploaderClient } from '../core/client.js'\nimport type { UploaderClientOptions } from '../core/types.js'\nimport type { PickerFile, PickerOptions, UsePickerResult } from './types.js'\nimport type { PickerResponse } from '../core/types.js'\n\n// ─── Counter for stable keys ──────────────────────────────────────────────────\n\nlet _idCounter = 0\nfunction nextId(): string {\n return `pf-${++_idCounter}`\n}\n\n// ─── usePicker ────────────────────────────────────────────────────────────────\n\nexport type UsePickerOptions = UploaderClientOptions & {\n pickerOptions?: PickerOptions\n onUploadDone?: (response: PickerResponse) => void\n onFileUploadFinished?: (file: import('../core/types.js').FileResult) => void\n onFileUploadFailed?: (file: File, error: Error) => void\n}\n\nexport function usePicker(opts: UsePickerOptions): UsePickerResult {\n const [files, setFiles] = useState<PickerFile[]>([])\n\n // Client ref — stable across renders; recreated only when apikey/apiUrl/security changes.\n // NOTE: In the MVP stub (T23) client.upload() throws; real logic is added in T24.\n const clientRef = useRef<UploaderClient | null>(null)\n if (\n !clientRef.current ||\n clientRef.current.apikey !== opts.apikey ||\n clientRef.current.apiUrl !== (opts.apiUrl ?? 'https://api.uploaderhq.io')\n ) {\n clientRef.current = new UploaderClient({\n apikey: opts.apikey,\n apiUrl: opts.apiUrl,\n security: opts.security,\n })\n }\n\n const addFiles = useCallback(\n (incoming: FileList | File[]) => {\n const list = Array.from(incoming)\n const { maxFiles, maxSize } = opts.pickerOptions ?? {}\n\n setFiles((prev) => {\n const remaining = maxFiles != null ? maxFiles - prev.length : Infinity\n const toAdd = list.slice(0, remaining).filter((f) => {\n if (maxSize != null && f.size > maxSize) return false\n return true\n })\n\n const newEntries: PickerFile[] = toAdd.map((f) => ({\n file: f,\n id: nextId(),\n state: 'pending',\n progress: null,\n previewUrl: f.type.startsWith('image/') ? URL.createObjectURL(f) : undefined,\n }))\n\n return [...prev, ...newEntries]\n })\n },\n [opts.pickerOptions],\n )\n\n const removeFile = useCallback((id: string) => {\n setFiles((prev) => {\n const entry = prev.find((f) => f.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n return prev.filter((f) => f.id !== id)\n })\n }, [])\n\n // Replace a queued file's bytes with an edited version (from ImageEditor),\n // regenerating its preview and resetting it to a fresh pending state.\n const editFile = useCallback((id: string, newFile: File) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n if (f.previewUrl) URL.revokeObjectURL(f.previewUrl)\n return {\n ...f,\n file: newFile,\n state: 'pending',\n progress: null,\n error: undefined,\n result: undefined,\n previewUrl: newFile.type.startsWith('image/') ? URL.createObjectURL(newFile) : undefined,\n }\n }),\n )\n }, [])\n\n const retryFile = useCallback((id: string) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === id ? { ...f, state: 'pending', progress: null, error: undefined } : f,\n ),\n )\n }, [])\n\n const upload = useCallback(async () => {\n const client = clientRef.current!\n const snapshot = files.filter((f) => f.state === 'pending')\n\n const uploaded: import('../core/types.js').FileResult[] = []\n const failed: import('../core/types.js').FileResult[] = []\n\n for (const entry of snapshot) {\n // Mark uploading\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, state: 'uploading', progress: 0 } : f)),\n )\n\n try {\n const result = await client.upload(entry.file, {\n onProgress: (pct) => {\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, progress: pct } : f)),\n )\n },\n })\n\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id ? { ...f, state: 'done', progress: 100, result } : f,\n ),\n )\n\n opts.onFileUploadFinished?.(result)\n uploaded.push(result)\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id\n ? { ...f, state: 'error', progress: null, error: error.message }\n : f,\n ),\n )\n opts.onFileUploadFailed?.(entry.file, error)\n // Push a placeholder into failed — no FileResult since upload didn't complete\n failed.push({\n handle: '',\n url: '',\n filename: entry.file.name,\n mimetype: entry.file.type,\n size: entry.file.size,\n status: 'Stored',\n })\n }\n }\n\n if (snapshot.length > 0) {\n opts.onUploadDone?.({ filesUploaded: uploaded, filesFailed: failed })\n }\n }, [files, opts])\n\n // Aggregate progress: mean of uploading files; 100 when all done, 0 when no files\n const activeFiles = files.filter((f) => f.state === 'uploading' || f.state === 'done')\n const progress =\n activeFiles.length === 0\n ? 0\n : Math.round(\n activeFiles.reduce((sum, f) => sum + (f.progress ?? 0), 0) / activeFiles.length,\n )\n\n const isUploading = files.some((f) => f.state === 'uploading')\n const isDone =\n files.length > 0 && files.every((f) => f.state === 'done' || f.state === 'error')\n\n return { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone }\n}\n","/**\n * ImageEditor — in-picker, pre-upload image editor.\n *\n * Interaction model: the crop box is FIXED in the center and only resizable\n * (drag a corner — it grows/shrinks symmetrically). The user positions the\n * IMAGE under it by dragging to pan and scrolling to zoom (zoom toward the\n * cursor). Plus rotate 90° / flip / circular crop / output resize.\n *\n * Hand-rolled (no crop library): the image is transformed by translate+scale in\n * the viewport; on Apply the crop box is inverse-mapped to source pixels and\n * editImage produces the final File. Rotation/flip are baked into a working\n * image first so the crop math stays axis-aligned.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as RPointerEvent } from 'react'\nimport { editImage } from './editImage.js'\n\nconst clampNum = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n))\nconst MIN_CROP = 48 // px, in viewport space\nconst MAX_ZOOM = 8 // relative to the fit scale\n\nconst ASPECTS: Array<{ label: string; value: number | undefined }> = [\n { label: 'Free', value: undefined },\n { label: '1:1', value: 1 },\n { label: '4:3', value: 4 / 3 },\n { label: '16:9', value: 16 / 9 },\n { label: '3:2', value: 3 / 2 },\n]\n\ntype Corner = 'nw' | 'ne' | 'sw' | 'se'\nconst HANDLES: Array<{ id: Corner; pos: CSSProperties }> = [\n { id: 'nw', pos: { left: -7, top: -7, cursor: 'nwse-resize' } },\n { id: 'ne', pos: { right: -7, top: -7, cursor: 'nesw-resize' } },\n { id: 'sw', pos: { left: -7, bottom: -7, cursor: 'nesw-resize' } },\n { id: 'se', pos: { right: -7, bottom: -7, cursor: 'nwse-resize' } },\n]\n\nexport type ImageEditorProps = {\n /** The image File to edit. */\n file: File\n /** Called with the edited File when the user clicks Apply. */\n onApply: (edited: File) => void\n /** Called when the user cancels editing. */\n onCancel: () => void\n /** Output MIME type. Defaults to the source type (when re-encodable) else png. */\n outputType?: string\n /** Quality 0–1 for lossy output. Default 0.92. */\n quality?: number\n /** Default \"max longest side\" for the resize control. Default 1920. */\n maxDimensionDefault?: number\n className?: string\n style?: CSSProperties\n}\n\nexport function ImageEditor({\n file,\n onApply,\n onCancel,\n outputType,\n quality,\n maxDimensionDefault = 1920,\n className,\n style,\n}: ImageEditorProps) {\n const [rotation, setRotation] = useState(0)\n const [flip, setFlip] = useState({ horizontal: false, vertical: false })\n const [aspect, setAspect] = useState<number | undefined>(undefined)\n const [cropShape, setCropShape] = useState<'rect' | 'round'>('rect')\n const [resizeEnabled, setResizeEnabled] = useState(false)\n const [maxDimension, setMaxDimension] = useState(maxDimensionDefault)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Working image = original with rotation/flip baked in (axis-aligned).\n const [workingFile, setWorkingFile] = useState<File>(file)\n useEffect(() => {\n let cancelled = false\n if (rotation === 0 && !flip.horizontal && !flip.vertical) {\n setWorkingFile(file)\n return\n }\n editImage(file, { rotation, flip })\n .then((f) => {\n if (!cancelled) setWorkingFile(f)\n })\n .catch(() => {\n if (!cancelled) setError('Failed to render the rotated preview')\n })\n return () => {\n cancelled = true\n }\n }, [file, rotation, flip])\n\n // Object URL for the working image (effect-based for StrictMode safety).\n const [workingUrl, setWorkingUrl] = useState('')\n useEffect(() => {\n const url = URL.createObjectURL(workingFile)\n setWorkingUrl(url)\n return () => URL.revokeObjectURL(url)\n }, [workingFile])\n\n const viewportRef = useRef<HTMLDivElement>(null)\n const [vp, setVp] = useState({ w: 0, h: 0 }) // viewport size, px\n const [nat, setNat] = useState({ w: 0, h: 0 }) // working image natural size, px\n // Image transform: viewportPoint = (x,y) + scale * naturalPoint, origin 0,0.\n const [view, setView] = useState({ scale: 1, x: 0, y: 0 })\n const [crop, setCrop] = useState({ w: 0, h: 0 }) // centered crop box, viewport px\n\n const ready = nat.w > 0 && vp.w > 0\n\n // Measure the viewport.\n useEffect(() => {\n const el = viewportRef.current\n if (!el || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const r = el.getBoundingClientRect()\n setVp({ w: r.width, h: r.height })\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Clamp a desired (scale, x, y) for the given crop: image must cover the crop,\n // and scale stays within [coverMin, fit*MAX_ZOOM].\n const normalize = useCallback(\n (scaleRaw: number, xRaw: number, yRaw: number, cw: number, ch: number) => {\n if (!nat.w || !vp.w) return { scale: scaleRaw, x: xRaw, y: yRaw }\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const coverMin = Math.max(cw / nat.w, ch / nat.h)\n const scale = clampNum(scaleRaw, coverMin, Math.max(fitS, coverMin) * MAX_ZOOM)\n const cbX = (vp.w - cw) / 2\n const cbY = (vp.h - ch) / 2\n const x = clampNum(xRaw, cbX + cw - scale * nat.w, cbX)\n const y = clampNum(yRaw, cbY + ch - scale * nat.h, cbY)\n return { scale, x, y }\n },\n [nat, vp],\n )\n\n // Initialize/reset view + crop when the image or viewport changes.\n useEffect(() => {\n if (!nat.w || !vp.w) return\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const baseline = Math.min(vp.w, vp.h) * 0.7\n let cw = baseline\n let ch = baseline\n if (cropShape === 'round') {\n cw = ch = baseline\n } else if (aspect) {\n if (aspect >= 1) ch = baseline / aspect\n else cw = baseline * aspect\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView(normalize(fitS, (vp.w - fitS * nat.w) / 2, (vp.h - fitS * nat.h) / 2, cw, ch))\n // Only re-init on a new image / viewport resize.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [nat, vp])\n\n // Adjust the crop to a new aspect/shape (keep the current view).\n useEffect(() => {\n if (!nat.w || !vp.w) return\n let cw = crop.w\n let ch = crop.h\n if (cropShape === 'round') {\n cw = ch = Math.min(cw, ch)\n } else if (aspect) {\n ch = cw / aspect\n if (ch > vp.h) {\n ch = vp.h\n cw = ch * aspect\n }\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [aspect, cropShape])\n\n const onImageLoad = useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {\n setNat({ w: e.currentTarget.naturalWidth, h: e.currentTarget.naturalHeight })\n }, [])\n\n // Wheel zoom toward the cursor (native non-passive listener to preventDefault).\n useEffect(() => {\n const el = viewportRef.current\n if (!el) return\n const onWheel = (e: WheelEvent) => {\n e.preventDefault()\n const rect = el.getBoundingClientRect()\n const fx = e.clientX - rect.left\n const fy = e.clientY - rect.top\n const factor = Math.exp(-e.deltaY * 0.0015)\n setView((v) => {\n const next = normalize(v.scale * factor, v.x, v.y, crop.w, crop.h)\n const ratio = next.scale / v.scale\n // keep the cursor point fixed, then re-clamp pan\n return normalize(next.scale, fx - ratio * (fx - v.x), fy - ratio * (fy - v.y), crop.w, crop.h)\n })\n }\n el.addEventListener('wheel', onWheel, { passive: false })\n return () => el.removeEventListener('wheel', onWheel)\n }, [normalize, crop.w, crop.h])\n\n // Pointer drag: pan the image, or resize the crop from a corner handle.\n const dragRef = useRef<\n | { mode: 'pan'; downX: number; downY: number; startX: number; startY: number }\n | { mode: 'resize'; corner: Corner }\n | null\n >(null)\n\n const onPointerDown = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n if (!ready) return\n const handle = (e.target as HTMLElement).dataset.handle as Corner | undefined\n e.currentTarget.setPointerCapture(e.pointerId)\n if (handle) {\n dragRef.current = { mode: 'resize', corner: handle }\n } else {\n dragRef.current = { mode: 'pan', downX: e.clientX, downY: e.clientY, startX: view.x, startY: view.y }\n }\n },\n [ready, view.x, view.y],\n )\n\n const onPointerMove = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current\n if (!d) return\n const rect = e.currentTarget.getBoundingClientRect()\n if (d.mode === 'pan') {\n setView((v) => normalize(v.scale, d.startX + (e.clientX - d.downX), d.startY + (e.clientY - d.downY), crop.w, crop.h))\n } else {\n const hx = Math.abs(e.clientX - rect.left - rect.width / 2)\n const hy = Math.abs(e.clientY - rect.top - rect.height / 2)\n let cw: number\n let ch: number\n if (cropShape === 'round') {\n cw = ch = 2 * Math.max(hx, hy)\n } else if (aspect) {\n cw = Math.max(2 * hx, 2 * hy * aspect)\n ch = cw / aspect\n } else {\n cw = 2 * hx\n ch = 2 * hy\n }\n cw = clampNum(cw, MIN_CROP, rect.width)\n ch = clampNum(ch, MIN_CROP, rect.height)\n if (cropShape === 'round') cw = ch = Math.min(cw, ch)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n }\n },\n [normalize, crop.w, crop.h, cropShape, aspect],\n )\n\n const onPointerUp = useCallback((e: RPointerEvent<HTMLDivElement>) => {\n dragRef.current = null\n if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId)\n }, [])\n\n const handleApply = useCallback(async () => {\n if (!ready) {\n onApply(workingFile)\n return\n }\n setBusy(true)\n setError(null)\n try {\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n // Inverse of viewportPoint = view + scale*natural.\n const region = {\n x: (cbX - view.x) / view.scale,\n y: (cbY - view.y) / view.scale,\n width: crop.w / view.scale,\n height: crop.h / view.scale,\n }\n const edited = await editImage(workingFile, {\n crop: region,\n circle: cropShape === 'round',\n maxDimension: resizeEnabled ? maxDimension : undefined,\n outputType,\n quality,\n })\n onApply(edited)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to apply edits')\n } finally {\n setBusy(false)\n }\n }, [ready, vp, crop, view, workingFile, cropShape, resizeEnabled, maxDimension, outputType, quality, onApply])\n\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n\n return (\n <div\n className={['uploader-editor', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"image-editor\"\n >\n <div\n ref={viewportRef}\n className=\"uploader-editor-canvas\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n {workingUrl && (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img\n src={workingUrl}\n alt=\"Edit preview\"\n onLoad={onImageLoad}\n draggable={false}\n className=\"uploader-editor-image\"\n style={{\n width: nat.w || undefined,\n height: nat.h || undefined,\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,\n opacity: ready ? 1 : 0,\n }}\n />\n )}\n {ready && (\n <div\n className={`uploader-editor-cropbox${cropShape === 'round' ? ' uploader-editor-cropbox-round' : ''}`}\n style={{ left: cbX, top: cbY, width: crop.w, height: crop.h }}\n >\n {HANDLES.map((h) => (\n <span key={h.id} data-handle={h.id} className=\"uploader-editor-handle\" style={h.pos} />\n ))}\n </div>\n )}\n </div>\n <p className=\"uploader-editor-hint\">Drag the image to move it · scroll to zoom · drag a corner to resize the crop.</p>\n\n <div className=\"uploader-editor-controls\">\n {/* Rotate / flip */}\n <div className=\"uploader-editor-row\">\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate left\"\n onClick={() => setRotation((r) => (r - 90 + 360) % 360)}\n >\n ⟲\n </button>\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate right\"\n onClick={() => setRotation((r) => (r + 90) % 360)}\n >\n ⟳\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.horizontal ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.horizontal}\n aria-label=\"Flip horizontal\"\n onClick={() => setFlip((f) => ({ ...f, horizontal: !f.horizontal }))}\n >\n ⇋\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.vertical ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.vertical}\n aria-label=\"Flip vertical\"\n onClick={() => setFlip((f) => ({ ...f, vertical: !f.vertical }))}\n >\n ⇅\n </button>\n </div>\n\n {/* Shape */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Shape</span>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'rect' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => setCropShape('rect')}\n >\n Rectangle\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'round' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => {\n setCropShape('round')\n setAspect(1)\n }}\n >\n Circle\n </button>\n </div>\n\n {/* Aspect ratio (locked to 1:1 for circles) */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Ratio</span>\n {ASPECTS.map((a) => (\n <button\n key={a.label}\n type=\"button\"\n disabled={cropShape === 'round'}\n className={`uploader-editor-chip${\n cropShape === 'rect' && aspect === a.value ? ' uploader-editor-chip-active' : ''\n }`}\n onClick={() => setAspect(a.value)}\n >\n {a.label}\n </button>\n ))}\n </div>\n\n {/* Resize */}\n <div className=\"uploader-editor-row\">\n <label className=\"uploader-editor-label\">\n <input\n type=\"checkbox\"\n checked={resizeEnabled}\n onChange={(e) => setResizeEnabled(e.target.checked)}\n />{' '}\n Resize to\n </label>\n <input\n type=\"number\"\n min={16}\n value={maxDimension}\n disabled={!resizeEnabled}\n aria-label=\"Maximum longest side in pixels\"\n className=\"uploader-editor-number\"\n onChange={(e) => setMaxDimension(Number(e.target.value))}\n />\n <span className=\"uploader-editor-label\">px (longest side)</span>\n </div>\n\n {error && (\n <div className=\"uploader-editor-error\" role=\"alert\">\n {error}\n </div>\n )}\n </div>\n\n <div className=\"uploader-editor-actions\">\n <button type=\"button\" className=\"uploader-btn-cancel\" onClick={onCancel} disabled={busy}>\n Cancel\n </button>\n <button type=\"button\" className=\"uploader-btn-upload\" onClick={handleApply} disabled={busy}>\n {busy ? 'Applying…' : 'Apply'}\n </button>\n </div>\n </div>\n )\n}\n","/**\n * editImage — bake pre-upload edits (rotate / flip / crop / resize) into an\n * image File using a <canvas>, returning a NEW File. Browser-only.\n *\n * The crop rectangle is in the pixel space of the rotated+flipped image (the\n * order is: rotate, then flip, then crop, then resize). ImageEditor bakes\n * rotation/flip into a working image first and passes a crop with rotation=0, so\n * the crop coordinates come straight from the on-screen selection.\n */\n\nexport type Flip = { horizontal: boolean; vertical: boolean }\nexport type CropArea = { x: number; y: number; width: number; height: number }\n\nexport type EditImageOptions = {\n /** Clockwise rotation in degrees. */\n rotation?: number\n /** Mirror horizontally / vertically. */\n flip?: Flip\n /** Crop rectangle in the rotated+flipped image's natural pixels. */\n crop?: CropArea\n /** If set, scale the result down so its longest side is at most this many pixels. */\n maxDimension?: number\n /** Mask the result to a circle/ellipse (transparent corners). Forces PNG output. */\n circle?: boolean\n /** Output MIME type. Defaults to the source type when re-encodable, else image/png. */\n outputType?: string\n /** Quality 0–1 for lossy formats (jpeg/webp). Default 0.92. */\n quality?: number\n /** Override the output filename. Defaults to the source name with a matching extension. */\n fileName?: string\n}\n\nconst ENCODABLE = new Set(['image/jpeg', 'image/png', 'image/webp'])\n\n/** Map an output MIME type to a file extension. */\nexport function mimeToExtension(mime: string): string {\n switch (mime) {\n case 'image/jpeg':\n return 'jpg'\n case 'image/png':\n return 'png'\n case 'image/webp':\n return 'webp'\n default:\n return 'png'\n }\n}\n\n/** Choose the output MIME: caller override, else the source type if re-encodable, else png. */\nexport function pickOutputType(sourceType: string, override?: string): string {\n if (override) return override\n return ENCODABLE.has(sourceType) ? sourceType : 'image/png'\n}\n\n/** Size of the axis-aligned bounding box after rotating a w×h box by `deg` degrees. */\nexport function rotatedBoundingBox(\n width: number,\n height: number,\n deg: number,\n): { width: number; height: number } {\n const rad = (deg * Math.PI) / 180\n return {\n width: Math.abs(Math.cos(rad) * width) + Math.abs(Math.sin(rad) * height),\n height: Math.abs(Math.sin(rad) * width) + Math.abs(Math.cos(rad) * height),\n }\n}\n\n/** Scale factor to fit (w,h) within `maxDimension` on the longest side; never upscales (≤1). */\nexport function fitScale(width: number, height: number, maxDimension?: number): number {\n if (!maxDimension) return 1\n const longest = Math.max(width, height)\n return longest > maxDimension ? maxDimension / longest : 1\n}\n\n/** Replace a filename's extension (keeps the base name). */\nexport function withExtension(name: string, ext: string): string {\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n return `${base}.${ext}`\n}\n\nfunction loadImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = () => reject(new Error('Failed to load image for editing'))\n img.src = url\n })\n}\n\nexport async function editImage(file: File, opts: EditImageOptions = {}): Promise<File> {\n const {\n rotation = 0,\n flip = { horizontal: false, vertical: false },\n crop,\n maxDimension,\n } = opts\n\n const url = URL.createObjectURL(file)\n try {\n const image = await loadImage(url)\n const iw = image.naturalWidth || image.width\n const ih = image.naturalHeight || image.height\n\n // 1) Draw the rotated + flipped image onto a bounding-box canvas.\n const box = rotatedBoundingBox(iw, ih, rotation)\n const boxCanvas = document.createElement('canvas')\n boxCanvas.width = Math.max(1, Math.round(box.width))\n boxCanvas.height = Math.max(1, Math.round(box.height))\n const boxCtx = boxCanvas.getContext('2d')\n if (!boxCtx) throw new Error('Canvas 2D context unavailable')\n boxCtx.translate(boxCanvas.width / 2, boxCanvas.height / 2)\n boxCtx.rotate((rotation * Math.PI) / 180)\n boxCtx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1)\n boxCtx.drawImage(image, -iw / 2, -ih / 2)\n\n // 2) Crop (default: the whole bounding box).\n const region: CropArea = crop ?? {\n x: 0,\n y: 0,\n width: boxCanvas.width,\n height: boxCanvas.height,\n }\n const scale = fitScale(region.width, region.height, maxDimension)\n const outW = Math.max(1, Math.round(region.width * scale))\n const outH = Math.max(1, Math.round(region.height * scale))\n\n const outCanvas = document.createElement('canvas')\n outCanvas.width = outW\n outCanvas.height = outH\n const outCtx = outCanvas.getContext('2d')\n if (!outCtx) throw new Error('Canvas 2D context unavailable')\n // Circular crop: clip to the inscribed ellipse so the corners stay transparent.\n if (opts.circle) {\n outCtx.beginPath()\n outCtx.ellipse(outW / 2, outH / 2, outW / 2, outH / 2, 0, 0, Math.PI * 2)\n outCtx.clip()\n }\n outCtx.drawImage(boxCanvas, region.x, region.y, region.width, region.height, 0, 0, outW, outH)\n\n // 3) Encode. A circular crop needs an alpha channel, so always PNG.\n const outputType = opts.circle ? 'image/png' : pickOutputType(file.type, opts.outputType)\n const quality = opts.quality ?? 0.92\n const blob: Blob = await new Promise((resolve, reject) => {\n outCanvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error('Canvas toBlob returned null'))),\n outputType,\n quality,\n )\n })\n\n const name = opts.fileName ?? withExtension(file.name, mimeToExtension(outputType))\n return new File([blob], name, { type: outputType, lastModified: Date.now() })\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n","/**\n * DropPane — inline drag-and-drop upload zone.\n *\n * A non-modal file drop target that renders in-place. Suitable for embedding\n * directly in a page (e.g. a form upload field) rather than as a modal overlay.\n *\n * Uses usePicker() internally; exposes the same filestack-parity callbacks as\n * PickerOverlay. Renders a file list with progress and remove/retry controls\n * below the drop zone.\n */\n\nimport React, { useRef, useState, useCallback, useId } from 'react'\nimport { usePicker } from './usePicker.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport type DropPaneProps = SharedPickerProps & {\n /** Additional CSS class(es) applied to the root element. */\n className?: string\n /** Inline styles applied to the root element. */\n style?: React.CSSProperties\n}\n\nexport function DropPane({\n apikey,\n apiUrl,\n security,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n className,\n style,\n}: DropPaneProps) {\n const inputId = useId()\n const inputRef = useRef<HTMLInputElement>(null)\n const [isDragOver, setIsDragOver] = useState(false)\n\n const { files, addFiles, removeFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n // ── File input handler ───────────────────────────────────────────────────\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n addFiles(e.target.files)\n // Reset input so the same file can be re-selected after removal\n e.target.value = ''\n }\n },\n [addFiles],\n )\n\n const openFileDialog = useCallback(() => {\n inputRef.current?.click()\n }, [])\n\n const hasPending = files.some((f) => f.state === 'pending')\n const accept = pickerOptions?.accept?.join(',')\n\n return (\n <div\n className={['uploader-drop-pane', isDragOver ? 'uploader-drag-over' : '', className ?? '']\n .filter(Boolean)\n .join(' ')}\n style={style}\n data-testid=\"drop-pane\"\n >\n {/* Drop zone */}\n <div\n className=\"uploader-dropzone\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Drop files here or click to browse\"\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n onClick={openFileDialog}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openFileDialog()\n }\n }}\n >\n <span className=\"uploader-dropzone-icon\" aria-hidden=\"true\">\n ↑\n </span>\n <span className=\"uploader-dropzone-label\">\n Drag &amp; drop files here or{' '}\n <label htmlFor={inputId} className=\"uploader-browse-link\">\n browse\n </label>\n </span>\n </div>\n\n {/* Hidden file input */}\n <input\n id={inputId}\n ref={inputRef}\n type=\"file\"\n multiple={!pickerOptions?.maxFiles || pickerOptions.maxFiles > 1}\n accept={accept}\n className=\"uploader-file-input\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={handleInputChange}\n />\n\n {/* File list */}\n {files.length > 0 && (\n <ul className=\"uploader-file-list\" role=\"list\" aria-label=\"Selected files\">\n {files.map((entry) => (\n <li key={entry.id} className={`uploader-file-item uploader-file-${entry.state}`}>\n {/* Thumbnail */}\n {entry.previewUrl && (\n <img\n src={entry.previewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"uploader-file-thumb\"\n />\n )}\n\n {/* Name + status */}\n <div className=\"uploader-file-info\">\n <span className=\"uploader-file-name\">{entry.file.name}</span>\n {entry.state === 'error' && (\n <span className=\"uploader-file-error\" role=\"alert\">\n {entry.error ?? 'Upload failed'}\n </span>\n )}\n {entry.state === 'done' && (\n <span className=\"uploader-file-done\">Uploaded</span>\n )}\n </div>\n\n {/* Progress bar */}\n {entry.state === 'uploading' && entry.progress != null && (\n <progress\n className=\"uploader-file-progress\"\n value={entry.progress}\n max={100}\n aria-label={`${entry.file.name} upload progress`}\n />\n )}\n\n {/* Actions */}\n <div className=\"uploader-file-actions\">\n {entry.state === 'error' && (\n <button\n type=\"button\"\n className=\"uploader-btn-retry\"\n aria-label={`Retry uploading ${entry.file.name}`}\n onClick={() => retryFile(entry.id)}\n >\n Retry\n </button>\n )}\n {entry.state !== 'uploading' && (\n <button\n type=\"button\"\n className=\"uploader-btn-remove\"\n aria-label={`Remove ${entry.file.name}`}\n onClick={() => removeFile(entry.id)}\n >\n ×\n </button>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n\n {/* Aggregate progress */}\n {isUploading && (\n <div className=\"uploader-aggregate-progress\" role=\"status\" aria-live=\"polite\">\n <progress value={progress} max={100} aria-label=\"Overall upload progress\" />\n <span>{progress}%</span>\n </div>\n )}\n\n {/* Footer actions */}\n <div className=\"uploader-actions\">\n {hasPending && !isUploading && (\n <button\n type=\"button\"\n className=\"uploader-btn-upload\"\n disabled={isUploading}\n onClick={() => void upload()}\n >\n Upload\n </button>\n )}\n {!isDone && !isUploading && (\n <button\n type=\"button\"\n className=\"uploader-btn-cancel\"\n onClick={onCancel}\n >\n Cancel\n </button>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","flip","crop","quality","quality","flip","crop","cbX","cbY","import_jsx_runtime","import_react","import_jsx_runtime"]}
@@ -0,0 +1,208 @@
1
+ import { P as PickerResponse, F as FileResult, h as UploaderClientOptions } from './transform-ybCOCWBG.cjs';
2
+ export { C as CropParams, O as OutputParams, a as PolicySpec, Q as QualityParams, R as ResizeParams, b as RotateParams, T as TransformChain, c as TransformOp, d as TransformOpName, e as TransformUrlOptions, U as UploadAllOptions, f as UploadOptions, g as UploaderClient, i as crop, j as flip, k as flop, o as output, q as quality, r as resize, l as rotate, t as transformUrl } from './transform-ybCOCWBG.cjs';
3
+ import * as React from 'react';
4
+ import React__default, { CSSProperties } from 'react';
5
+
6
+ /**
7
+ * React picker prop and state types.
8
+ *
9
+ * Designed for filestack-parity naming so existing filestack integrations can
10
+ * migrate with minimal changes. All callback names match filestack's onUploadDone /
11
+ * onFileUploadFinished / onFileUploadFailed / onCancel surface.
12
+ */
13
+
14
+ /** State of a single file entry in the picker queue. */
15
+ type PickerFileState = 'pending' | 'uploading' | 'done' | 'error';
16
+ /** A file in the picker's queue with upload state. */
17
+ type PickerFile = {
18
+ /** Browser File object. */
19
+ file: File;
20
+ /** Stable key for React list rendering. */
21
+ id: string;
22
+ /** Current upload state. */
23
+ state: PickerFileState;
24
+ /** Upload progress 0–100, or null while pending. */
25
+ progress: number | null;
26
+ /** Result after successful upload. */
27
+ result?: FileResult;
28
+ /** Error message if upload failed. */
29
+ error?: string;
30
+ /** Object URL for image preview; revoked on removal. */
31
+ previewUrl?: string;
32
+ };
33
+ /** File type/size constraints and source settings for the picker. */
34
+ type PickerOptions = {
35
+ /**
36
+ * MIME types or extensions to accept (e.g. ['image/*', '.pdf']).
37
+ * Maps to the <input accept="..."> attribute.
38
+ */
39
+ accept?: string[];
40
+ /** Maximum number of files selectable in one session. Default: unlimited. */
41
+ maxFiles?: number;
42
+ /** Maximum file size in bytes. Files above this are rejected at selection. */
43
+ maxSize?: number;
44
+ /**
45
+ * Upload sources. Currently only 'local_file_system' is supported (MVP).
46
+ * Extension point for webcam/URL/cloud pickers.
47
+ */
48
+ fromSources?: Array<'local_file_system'>;
49
+ };
50
+ /** Props shared by PickerOverlay and DropPane. */
51
+ type SharedPickerProps = {
52
+ /** Public API key (pk_...). */
53
+ apikey: string;
54
+ /** Base URL of the upload API. */
55
+ apiUrl?: string;
56
+ /** Optional signed security policy. */
57
+ security?: {
58
+ policy: string;
59
+ signature: string;
60
+ };
61
+ /** File constraint and source options. */
62
+ pickerOptions?: PickerOptions;
63
+ /** Called when all uploads complete. */
64
+ onUploadDone?: (response: PickerResponse) => void;
65
+ /** Called when a single file finishes uploading. */
66
+ onFileUploadFinished?: (file: FileResult) => void;
67
+ /** Called when a single file upload fails. */
68
+ onFileUploadFailed?: (file: File, error: Error) => void;
69
+ /** Called when the user cancels / closes without uploading. */
70
+ onCancel?: () => void;
71
+ };
72
+ /** State and actions returned by usePicker. */
73
+ type UsePickerResult = {
74
+ /** Current file queue. */
75
+ files: PickerFile[];
76
+ /** Add files to the queue. Respects maxFiles and maxSize. */
77
+ addFiles: (incoming: FileList | File[]) => void;
78
+ /** Remove a file from the queue by id. */
79
+ removeFile: (id: string) => void;
80
+ /** Replace a queued file's bytes with an edited version (resets it to pending). */
81
+ editFile: (id: string, newFile: File) => void;
82
+ /** Retry a failed file upload. */
83
+ retryFile: (id: string) => void;
84
+ /** Start uploading all pending files. */
85
+ upload: () => Promise<void>;
86
+ /** Aggregate progress 0–100 across all uploading files. */
87
+ progress: number;
88
+ /** True while any file is uploading. */
89
+ isUploading: boolean;
90
+ /** True once all files have finished (done or error). */
91
+ isDone: boolean;
92
+ };
93
+
94
+ /**
95
+ * PickerOverlay — modal file picker with drag-and-drop.
96
+ *
97
+ * A full-featured modal picker matching filestack's core UX:
98
+ * - Drag-and-drop dropzone + "Browse files" button
99
+ * - Multi-file queue with thumbnails/preview, per-file progress bars
100
+ * - Remove and retry controls per file
101
+ * - Aggregate progress bar
102
+ * - Upload / Cancel action buttons
103
+ * - Keyboard: ESC closes; Tab cycles within the modal (focus trap)
104
+ * - ARIA: role="dialog", aria-modal, labelled header, live regions
105
+ * - Themeable via CSS variables (--uploader-accent, etc.)
106
+ *
107
+ * Opens when `open={true}`. Calls `onClose` when the user dismisses via ESC,
108
+ * the backdrop click, or the Cancel button. `onUploadDone` fires after all
109
+ * selected files have been uploaded.
110
+ */
111
+
112
+ type PickerOverlayProps = SharedPickerProps & {
113
+ /** Whether the modal is open. */
114
+ open: boolean;
115
+ /** Called when the modal should close (ESC, backdrop, cancel). */
116
+ onClose: () => void;
117
+ /** Additional CSS class(es) applied to the modal panel. */
118
+ className?: string;
119
+ /** Inline styles applied to the modal panel. */
120
+ style?: React__default.CSSProperties;
121
+ };
122
+ declare function PickerOverlay({ open, onClose, apikey, apiUrl, security, pickerOptions, onUploadDone, onFileUploadFinished, onFileUploadFailed, onCancel, className, style, }: PickerOverlayProps): React__default.JSX.Element | null;
123
+
124
+ /**
125
+ * DropPane — inline drag-and-drop upload zone.
126
+ *
127
+ * A non-modal file drop target that renders in-place. Suitable for embedding
128
+ * directly in a page (e.g. a form upload field) rather than as a modal overlay.
129
+ *
130
+ * Uses usePicker() internally; exposes the same filestack-parity callbacks as
131
+ * PickerOverlay. Renders a file list with progress and remove/retry controls
132
+ * below the drop zone.
133
+ */
134
+
135
+ type DropPaneProps = SharedPickerProps & {
136
+ /** Additional CSS class(es) applied to the root element. */
137
+ className?: string;
138
+ /** Inline styles applied to the root element. */
139
+ style?: React__default.CSSProperties;
140
+ };
141
+ declare function DropPane({ apikey, apiUrl, security, pickerOptions, onUploadDone, onFileUploadFinished, onFileUploadFailed, onCancel, className, style, }: DropPaneProps): React__default.JSX.Element;
142
+
143
+ type UsePickerOptions = UploaderClientOptions & {
144
+ pickerOptions?: PickerOptions;
145
+ onUploadDone?: (response: PickerResponse) => void;
146
+ onFileUploadFinished?: (file: FileResult) => void;
147
+ onFileUploadFailed?: (file: File, error: Error) => void;
148
+ };
149
+ declare function usePicker(opts: UsePickerOptions): UsePickerResult;
150
+
151
+ type ImageEditorProps = {
152
+ /** The image File to edit. */
153
+ file: File;
154
+ /** Called with the edited File when the user clicks Apply. */
155
+ onApply: (edited: File) => void;
156
+ /** Called when the user cancels editing. */
157
+ onCancel: () => void;
158
+ /** Output MIME type. Defaults to the source type (when re-encodable) else png. */
159
+ outputType?: string;
160
+ /** Quality 0–1 for lossy output. Default 0.92. */
161
+ quality?: number;
162
+ /** Default "max longest side" for the resize control. Default 1920. */
163
+ maxDimensionDefault?: number;
164
+ className?: string;
165
+ style?: CSSProperties;
166
+ };
167
+ declare function ImageEditor({ file, onApply, onCancel, outputType, quality, maxDimensionDefault, className, style, }: ImageEditorProps): React.JSX.Element;
168
+
169
+ /**
170
+ * editImage — bake pre-upload edits (rotate / flip / crop / resize) into an
171
+ * image File using a <canvas>, returning a NEW File. Browser-only.
172
+ *
173
+ * The crop rectangle is in the pixel space of the rotated+flipped image (the
174
+ * order is: rotate, then flip, then crop, then resize). ImageEditor bakes
175
+ * rotation/flip into a working image first and passes a crop with rotation=0, so
176
+ * the crop coordinates come straight from the on-screen selection.
177
+ */
178
+ type Flip = {
179
+ horizontal: boolean;
180
+ vertical: boolean;
181
+ };
182
+ type CropArea = {
183
+ x: number;
184
+ y: number;
185
+ width: number;
186
+ height: number;
187
+ };
188
+ type EditImageOptions = {
189
+ /** Clockwise rotation in degrees. */
190
+ rotation?: number;
191
+ /** Mirror horizontally / vertically. */
192
+ flip?: Flip;
193
+ /** Crop rectangle in the rotated+flipped image's natural pixels. */
194
+ crop?: CropArea;
195
+ /** If set, scale the result down so its longest side is at most this many pixels. */
196
+ maxDimension?: number;
197
+ /** Mask the result to a circle/ellipse (transparent corners). Forces PNG output. */
198
+ circle?: boolean;
199
+ /** Output MIME type. Defaults to the source type when re-encodable, else image/png. */
200
+ outputType?: string;
201
+ /** Quality 0–1 for lossy formats (jpeg/webp). Default 0.92. */
202
+ quality?: number;
203
+ /** Override the output filename. Defaults to the source name with a matching extension. */
204
+ fileName?: string;
205
+ };
206
+ declare function editImage(file: File, opts?: EditImageOptions): Promise<File>;
207
+
208
+ export { type CropArea, DropPane, type DropPaneProps, type EditImageOptions, FileResult, type Flip, ImageEditor, type ImageEditorProps, type PickerFile, type PickerFileState, type PickerOptions, PickerOverlay, type PickerOverlayProps, PickerResponse, type SharedPickerProps, UploaderClientOptions, type UsePickerOptions, type UsePickerResult, editImage, usePicker };