img-upload-kit 2.0.1 → 2.0.2
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.
- package/CHANGELOG.md +89 -89
- package/MIGRATION.md +192 -192
- package/README.md +243 -243
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/blur.cjs +3 -3
- package/dist/blur.cjs.map +1 -1
- package/dist/blur.js +2 -2
- package/dist/{chunk-XKQMCY5Z.cjs → chunk-2OVNXHTN.cjs} +1 -1
- package/dist/chunk-2OVNXHTN.cjs.map +1 -0
- package/dist/{chunk-FP5YIWG5.cjs → chunk-3ICG2SLL.cjs} +1 -1
- package/dist/chunk-3ICG2SLL.cjs.map +1 -0
- package/dist/{chunk-J6A44Y3W.js → chunk-4ASFG3KB.js} +1 -1
- package/dist/chunk-4ASFG3KB.js.map +1 -0
- package/dist/{chunk-IDLLCVGB.js → chunk-AI3JJLSA.js} +3 -3
- package/dist/chunk-AI3JJLSA.js.map +1 -0
- package/dist/{chunk-MHGQEBPW.js → chunk-CV23O4YU.js} +1 -1
- package/dist/chunk-CV23O4YU.js.map +1 -0
- package/dist/{chunk-25GYKQ6J.js → chunk-G7LLSKBV.js} +1 -1
- package/dist/chunk-G7LLSKBV.js.map +1 -0
- package/dist/{chunk-EWVIL57J.js → chunk-IB3TEDLR.js} +1 -1
- package/dist/chunk-IB3TEDLR.js.map +1 -0
- package/dist/{chunk-DFCSN3X7.cjs → chunk-KJVYWKIG.cjs} +1 -1
- package/dist/chunk-KJVYWKIG.cjs.map +1 -0
- package/dist/{chunk-34L4TEE3.cjs → chunk-NEDBIIE3.cjs} +1 -1
- package/dist/chunk-NEDBIIE3.cjs.map +1 -0
- package/dist/chunk-QHU7FUO4.cjs.map +1 -1
- package/dist/{chunk-VYVZVUNI.js → chunk-QQHZJKNA.js} +1 -1
- package/dist/chunk-QQHZJKNA.js.map +1 -0
- package/dist/{chunk-HILELQGS.js → chunk-TT5C7K3Q.js} +1 -1
- package/dist/chunk-TT5C7K3Q.js.map +1 -0
- package/dist/{chunk-TA5Y64JM.cjs → chunk-U2TWTEBY.cjs} +24 -24
- package/dist/chunk-U2TWTEBY.cjs.map +1 -0
- package/dist/{chunk-MBQJ6NVJ.cjs → chunk-WISG74VL.cjs} +1 -1
- package/dist/chunk-WISG74VL.cjs.map +1 -0
- package/dist/{chunk-LOSVFO56.cjs → chunk-YRXXQUDE.cjs} +1 -1
- package/dist/chunk-YRXXQUDE.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/headless.cjs +4 -4
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.js +3 -3
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/react.cjs +138 -138
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +5 -5
- package/dist/react.js.map +1 -1
- package/dist/server-handler.cjs +2 -2
- package/dist/server-handler.cjs.map +1 -1
- package/dist/server-handler.js +1 -1
- package/dist/server.cjs +30 -30
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/storage.cjs +9 -9
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/storage.js.map +1 -1
- package/dist/styles/animated-image.css +112 -112
- package/dist/styles/tokens.css +123 -123
- package/dist/styles.css +235 -235
- package/package.json +1 -1
- package/dist/chunk-25GYKQ6J.js.map +0 -1
- package/dist/chunk-34L4TEE3.cjs.map +0 -1
- package/dist/chunk-DFCSN3X7.cjs.map +0 -1
- package/dist/chunk-EWVIL57J.js.map +0 -1
- package/dist/chunk-FP5YIWG5.cjs.map +0 -1
- package/dist/chunk-HILELQGS.js.map +0 -1
- package/dist/chunk-IDLLCVGB.js.map +0 -1
- package/dist/chunk-J6A44Y3W.js.map +0 -1
- package/dist/chunk-LOSVFO56.cjs.map +0 -1
- package/dist/chunk-MBQJ6NVJ.cjs.map +0 -1
- package/dist/chunk-MHGQEBPW.js.map +0 -1
- package/dist/chunk-TA5Y64JM.cjs.map +0 -1
- package/dist/chunk-VYVZVUNI.js.map +0 -1
- package/dist/chunk-XKQMCY5Z.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/image-upload-kit/image-upload-kit/dist/chunk-3ICG2SLL.cjs","../src/blur/blurDataUtils.ts"],"names":[],"mappings":"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","file":"/home/runner/work/image-upload-kit/image-upload-kit/dist/chunk-3ICG2SLL.cjs","sourcesContent":[null,"import { decode as decodeBlurHash, isBlurhashValid } from 'blurhash';\nimport type { BlurData, BlurImageSource } from './types';\n\nexport interface BlurPlaceholderStyle {\n backgroundColor: string;\n backgroundImage?: string;\n backgroundSize?: string;\n backgroundPosition?: string;\n backgroundRepeat?: string;\n filter?: string;\n transform?: string;\n}\n\nfunction formatCssUrl(rawUrl: string): string {\n const escaped = rawUrl\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/[\\r\\n]/g, '');\n return `url(\"${escaped}\")`;\n}\n\nexport function getBlurData(\n image: BlurImageSource | null | undefined,\n): BlurData {\n if (!image) {\n return {\n blurDataUrl: '',\n blurColor: '#E5E7EB',\n hasBlur: false,\n };\n }\n\n return {\n blurDataUrl: image.blurDataUrl || '',\n blurColor: image.blurColor || '#E5E7EB',\n ...(image.blurHash ? { blurHash: image.blurHash } : {}),\n hasBlur: Boolean(image.blurDataUrl || image.blurHash),\n };\n}\n\nexport function getImagePropsWithBlur(\n image: BlurImageSource | null | undefined,\n) {\n if (!image?.url) {\n return null;\n }\n\n const blurData = getBlurData(image);\n\n return {\n src: image.url,\n alt: image.altText || '',\n width: image.width || 1920,\n height: image.height || 1080,\n ...(blurData.blurDataUrl && {\n placeholder: 'blur' as const,\n blurDataURL: blurData.blurDataUrl,\n }),\n };\n}\n\nexport function getBackgroundPropsWithBlur(\n image: BlurImageSource | null | undefined,\n): BlurPlaceholderStyle {\n if (!image?.url) {\n return {\n backgroundColor: '#E5E7EB',\n };\n }\n\n const blurData = getBlurData(image);\n\n return {\n backgroundColor: blurData.blurColor,\n backgroundImage: formatCssUrl(image.url),\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n backgroundRepeat: 'no-repeat',\n };\n}\n\nexport function getBlurPlaceholderStyle(\n image: BlurImageSource | null | undefined,\n): BlurPlaceholderStyle {\n const blurData = getBlurData(image);\n\n if (!blurData.blurDataUrl) {\n return {\n backgroundColor: blurData.blurColor,\n };\n }\n\n return {\n backgroundColor: blurData.blurColor,\n backgroundImage: formatCssUrl(blurData.blurDataUrl),\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n filter: 'blur(20px)',\n transform: 'scale(1.1)',\n };\n}\n\nexport function hasBlurData(\n image: BlurImageSource | null | undefined,\n): boolean {\n return Boolean(image?.blurDataUrl || image?.blurColor || image?.blurHash);\n}\n\nexport function getBulkBlurData(\n images: Array<BlurImageSource | null | undefined>,\n): BlurData[] {\n return images.map(getBlurData);\n}\n\n/**\n * Builds a responsive `srcset` descriptor for the full-resolution image URL.\n * Never includes the low-resolution LQIP placeholder in `srcset` so browsers\n * cannot accidentally select the blurred placeholder as the final image.\n */\nexport function createSrcSetWithBlur(\n image: BlurImageSource | null | undefined,\n): string {\n if (!image?.url) return '';\n if (image.width && image.width > 0) {\n return `${image.url} ${image.width}w`;\n }\n return image.url;\n}\n\n/**\n * Preloads the target image URL in `document.head` via `<link rel=\"preload\" as=\"image\">`.\n * Does not set `imagesrcset` to the LQIP data URL and deduplicates existing preload links.\n */\nexport function preloadImageWithBlur(\n image: BlurImageSource | null | undefined,\n): void {\n if (!image?.url || typeof document === 'undefined' || !document.head) return;\n\n const existingLinks = document.head.querySelectorAll(\n 'link[rel=\"preload\"][as=\"image\"]',\n );\n for (let i = 0; i < existingLinks.length; i++) {\n const el = existingLinks[i] as HTMLLinkElement;\n if (el.getAttribute('href') === image.url || el.href === image.url) {\n return;\n }\n }\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'image';\n link.href = image.url;\n document.head.appendChild(link);\n}\n\nexport function extractImageUrls(\n image: BlurImageSource | null | undefined,\n): string[] {\n if (!image?.url) return [];\n return [image.url];\n}\n\n/**\n * Browser helper: Decodes a compact BlurHash string (e.g. \"LEHV6nWB2yk8pyo0adR*.7kCMdnj\")\n * into a `data:image/png;base64,...` URL using an offscreen `<canvas>`.\n */\nexport function decodeBlurHashToDataURL(\n hash: string,\n width: number = 32,\n height: number = 32,\n punch: number = 1,\n): string | null {\n if (!hash || typeof document === 'undefined') return null;\n const validation = isBlurhashValid(hash);\n if (!validation.result) return null;\n\n try {\n const pixels = decodeBlurHash(hash, width, height, punch);\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n const imageData = ctx.createImageData(width, height);\n imageData.data.set(pixels);\n ctx.putImageData(imageData, 0, 0);\n return canvas.toDataURL('image/png');\n } catch {\n return null;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/handler/responses.ts","../src/server/handler/guards.ts","../src/server/handler/deleteRoute.ts","../src/server/handler/parseUploadForm.ts","../src/server/handler/index.ts"],"sourcesContent":["import type { UploadErrorContext } from './options';\n\nexport class UploadValidationError extends Error {\n readonly status = 400;\n constructor(message: string) {\n super(message);\n this.name = 'UploadValidationError';\n }\n}\n\nexport function isClientValidationError(error: unknown): boolean {\n if (!error) return false;\n if (error instanceof UploadValidationError) return true;\n if (\n typeof error === 'object' &&\n error !== null &&\n 'status' in error &&\n (error as { status: unknown }).status === 400\n ) {\n return true;\n }\n if (\n error instanceof Error &&\n (error.name === 'UploadValidationError' || error.name === 'ValidationError')\n ) {\n return true;\n }\n return false;\n}\n\nexport function jsonResponse(body: unknown, status: number = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nexport function handleUnexpectedError(\n error: unknown,\n ctx: UploadErrorContext,\n onError?: (error: unknown, ctx: UploadErrorContext) => void,\n): Response {\n if (isClientValidationError(error)) {\n const message =\n error instanceof Error ? error.message : 'Invalid upload request';\n return jsonResponse({ error: message }, 400);\n }\n\n if (\n typeof error === 'object' &&\n error !== null &&\n 'status' in error &&\n typeof (error as { status: unknown }).status === 'number'\n ) {\n const status = (error as { status: number }).status;\n if (status >= 400 && status < 500) {\n const message =\n error instanceof Error ? error.message : 'Request rejected';\n return jsonResponse({ error: message }, status);\n }\n if (status === 503) {\n return jsonResponse(\n { error: 'Server busy, please try again later' },\n 503,\n );\n }\n }\n\n try {\n onError?.(error, ctx);\n } catch {\n // Ignore secondary logger failures\n }\n\n return jsonResponse({ error: 'Internal server error' }, 500);\n}\n","import type { AuthorizeContext, UploadHttpHandlerOptions } from './options';\nimport { jsonResponse } from './responses';\n\nconst DEFAULT_FALLBACK_MAX_MB = 25;\nconst MIN_DEFAULT_BODY_BYTES = 1024 * 1024; // 1 MB floor when maxBodyBytes is not explicitly set\n\nexport function resolveMaxBodyBytes<TPreset extends string>(\n options: UploadHttpHandlerOptions<TPreset>,\n): number {\n if (\n typeof options.maxBodyBytes === 'number' &&\n Number.isFinite(options.maxBodyBytes) &&\n options.maxBodyBytes > 0\n ) {\n return Math.floor(options.maxBodyBytes);\n }\n\n const uploaderMaxMB =\n typeof options.uploader.getMaxSizeMB === 'function'\n ? options.uploader.getMaxSizeMB(options.allowedPresets)\n : DEFAULT_FALLBACK_MAX_MB;\n\n const computed = Math.ceil(1.25 * uploaderMaxMB * 1024 * 1024);\n return Math.max(MIN_DEFAULT_BODY_BYTES, computed);\n}\n\nfunction extractRequestOrigin(request: Request): string | null {\n const originHeader = request.headers.get('origin');\n if (originHeader && originHeader !== 'null') {\n return originHeader.trim();\n }\n\n const refererHeader = request.headers.get('referer');\n if (refererHeader) {\n try {\n return new URL(refererHeader).origin;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nexport function checkAllowedOrigin(\n request: Request,\n allowedOrigins?: string[] | ((origin: string) => boolean),\n): Response | null {\n if (!allowedOrigins) return null;\n\n const origin = extractRequestOrigin(request);\n if (!origin) {\n return jsonResponse({ error: 'Forbidden origin' }, 403);\n }\n\n let allowed = false;\n try {\n allowed =\n typeof allowedOrigins === 'function'\n ? Boolean(allowedOrigins(origin))\n : allowedOrigins.includes(origin);\n } catch {\n allowed = false;\n }\n\n if (!allowed) {\n return jsonResponse({ error: 'Forbidden origin' }, 403);\n }\n\n return null;\n}\n\nexport async function runAuthorize(\n authorize: UploadHttpHandlerOptions['authorize'],\n ctx: AuthorizeContext,\n): Promise<Response | null> {\n if (!authorize) return null;\n\n try {\n const allowed = await authorize(ctx);\n if (!allowed) {\n return jsonResponse({ error: 'Unauthorized' }, 401);\n }\n return null;\n } catch {\n return jsonResponse({ error: 'Unauthorized' }, 401);\n }\n}\n\nexport interface BoundedFormDataResult {\n formData?: FormData;\n earlyErrorResponse?: Response;\n}\n\nexport async function readBoundedFormData(\n request: Request,\n maxBodyBytes: number,\n): Promise<BoundedFormDataResult> {\n const contentLengthRaw = request.headers.get('content-length');\n if (contentLengthRaw !== null && contentLengthRaw.trim() !== '') {\n const contentLength = Number(contentLengthRaw);\n if (Number.isFinite(contentLength) && contentLength > maxBodyBytes) {\n return {\n earlyErrorResponse: jsonResponse(\n {\n error: `Payload too large: request body (${contentLength} bytes) exceeds limit of ${maxBodyBytes} bytes`,\n },\n 413,\n ),\n };\n }\n }\n\n if (!request.body) {\n try {\n const formData = await request.formData();\n return { formData };\n } catch {\n return {};\n }\n }\n\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n totalBytes += value.byteLength;\n if (totalBytes > maxBodyBytes) {\n await reader.cancel().catch(() => {});\n return {\n earlyErrorResponse: jsonResponse(\n {\n error: `Payload too large: streamed body exceeds limit of ${maxBodyBytes} bytes`,\n },\n 413,\n ),\n };\n }\n chunks.push(value);\n }\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n // Ignore release errors\n }\n }\n\n if (totalBytes === 0) {\n return {};\n }\n\n const combined = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n try {\n const formData = await new Response(combined, {\n headers: request.headers,\n }).formData();\n return { formData };\n } catch {\n return {};\n }\n}\n","import { parsePublicStorageUrl } from '../../storage/urlParser';\nimport { checkAllowedOrigin, runAuthorize } from './guards';\nimport type { UploadHttpHandlerOptions } from './options';\nimport { handleUnexpectedError, jsonResponse } from './responses';\n\nfunction parseVirtualHostedS3Bucket(url: string): {\n bucket: string;\n path: string;\n} | null {\n try {\n const parsed = new URL(url);\n const s3Match = parsed.hostname.match(\n /^([a-z0-9.-]+)\\.s3(?:[.-][a-z0-9-]+)?\\.amazonaws\\.com$/i,\n );\n if (s3Match) {\n const key = decodeURIComponent(parsed.pathname).replace(/^\\/+/, '');\n if (key) {\n return { bucket: s3Match[1], path: key };\n }\n }\n } catch {\n // Ignore invalid URLs\n }\n return null;\n}\n\nfunction resolveAllowedBuckets<TPreset extends string>(\n options: UploadHttpHandlerOptions<TPreset>,\n): Set<string> {\n if (options.allowedBuckets && options.allowedBuckets.length > 0) {\n return new Set(options.allowedBuckets);\n }\n if (typeof options.uploader.getAllowedBuckets === 'function') {\n return new Set(options.uploader.getAllowedBuckets());\n }\n if (options.uploader.defaultBucket) {\n return new Set([options.uploader.defaultBucket]);\n }\n return new Set(['images']);\n}\n\nexport function createDeleteHandler<TPreset extends string>(\n options: UploadHttpHandlerOptions<TPreset>,\n) {\n const {\n uploader,\n authorize,\n allowedOrigins,\n allowClientBucketOverride = false,\n allowedBuckets,\n onError,\n } = options;\n\n return async function DELETE(request: Request): Promise<Response> {\n try {\n const originError = checkAllowedOrigin(request, allowedOrigins);\n if (originError) return originError;\n\n const { searchParams } = new URL(request.url);\n const url = searchParams.get('url');\n const storagePath = searchParams.get('path');\n const bucketParam = searchParams.get('bucket');\n\n const allowedBucketSet = resolveAllowedBuckets(options);\n\n const parsedFromUrl = url\n ? (typeof uploader.parseUrl === 'function'\n ? uploader.parseUrl(url)\n : null) ??\n parsePublicStorageUrl(url) ??\n parseVirtualHostedS3Bucket(url)\n : null;\n\n const resolvedPath = parsedFromUrl?.path ?? storagePath ?? undefined;\n const resolvedBucket =\n parsedFromUrl?.bucket ??\n (bucketParam || uploader.defaultBucket || 'images');\n\n const authError = await runAuthorize(authorize, {\n action: 'delete',\n request,\n ...(resolvedPath ? { path: resolvedPath } : {}),\n ...(resolvedBucket ? { bucket: resolvedBucket } : {}),\n });\n if (authError) return authError;\n\n if (url) {\n if (parsedFromUrl?.bucket) {\n if (\n allowedBuckets &&\n allowedBuckets.length > 0 &&\n !allowedBuckets.includes(parsedFromUrl.bucket)\n ) {\n return jsonResponse(\n { error: `Forbidden bucket \"${parsedFromUrl.bucket}\"` },\n 403,\n );\n }\n if (\n !allowClientBucketOverride &&\n allowedBucketSet.size > 0 &&\n !allowedBucketSet.has(parsedFromUrl.bucket)\n ) {\n return jsonResponse(\n { error: `Forbidden bucket \"${parsedFromUrl.bucket}\"` },\n 403,\n );\n }\n }\n\n const deleted = await uploader.deleteByUrl(url);\n return jsonResponse({ success: deleted }, deleted ? 200 : 400);\n }\n\n if (bucketParam) {\n if (\n allowedBuckets &&\n allowedBuckets.length > 0 &&\n !allowedBuckets.includes(bucketParam)\n ) {\n return jsonResponse(\n { error: `Forbidden bucket \"${bucketParam}\"` },\n 403,\n );\n }\n if (\n !allowClientBucketOverride &&\n allowedBucketSet.size > 0 &&\n !allowedBucketSet.has(bucketParam)\n ) {\n return jsonResponse(\n { error: `Forbidden bucket \"${bucketParam}\"` },\n 403,\n );\n }\n }\n\n if (!storagePath) {\n return jsonResponse(\n { error: 'Missing required \"path\" or \"url\" query parameter' },\n 400,\n );\n }\n\n const bucket = allowClientBucketOverride\n ? bucketParam || undefined\n : undefined;\n\n await uploader.deleteImage(storagePath, bucket);\n return jsonResponse({ success: true }, 200);\n } catch (error) {\n return handleUnexpectedError(\n error,\n { action: 'delete', request },\n onError,\n );\n }\n };\n}\n","import type {\n ExplicitOutputFormat,\n ImageDimensions,\n ImageUploadOptions,\n} from '../../types';\nimport type { AuthorizeContext, UploadHttpHandlerOptions } from './options';\nimport { jsonResponse } from './responses';\n\nconst ALLOWED_OUTPUT_FORMATS = new Set(['jpeg', 'png', 'webp', 'avif']);\n\nfunction parseJsonRecord(\n raw: string | null,\n): Record<string, string> | undefined {\n if (!raw) return undefined;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const clean: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed)) {\n if (v !== undefined && v !== null) {\n clean[k] = String(v);\n }\n }\n return Object.keys(clean).length > 0 ? clean : undefined;\n }\n } catch {\n // Ignore malformed metadata JSON\n }\n return undefined;\n}\n\nexport function peekUploadAuthContext<TPreset extends string>(\n formData: FormData | undefined,\n request: Request,\n options: UploadHttpHandlerOptions<TPreset>,\n): AuthorizeContext {\n const preset =\n (formData?.get('preset') as string | null) || undefined;\n const rawBucket =\n (formData?.get('bucket') as string | null) || undefined;\n const rawFolder =\n (formData?.get('folder') as string | null) || undefined;\n const presetConfig =\n preset && typeof options.uploader.resolvePreset === 'function'\n ? options.uploader.resolvePreset(preset)\n : undefined;\n\n const bucket = options.allowClientBucketOverride\n ? rawBucket || presetConfig?.bucket || options.uploader.defaultBucket\n : presetConfig?.bucket || options.uploader.defaultBucket;\n\n const folder = options.allowClientOverrides?.folder\n ? rawFolder || presetConfig?.folder\n : presetConfig?.folder;\n\n return {\n action: 'upload',\n request,\n ...(folder ? { path: folder } : {}),\n ...(bucket ? { bucket } : {}),\n ...(preset ? { preset } : {}),\n };\n}\n\nexport interface ParsedUploadFormResult<TPreset extends string> {\n file?: File;\n uploadOptions?: ImageUploadOptions<TPreset>;\n errorResponse?: Response;\n}\n\nexport function parseUploadForm<TPreset extends string>(\n formData: FormData | undefined,\n authCtx: AuthorizeContext,\n options: UploadHttpHandlerOptions<TPreset>,\n): ParsedUploadFormResult<TPreset> {\n if (!formData) {\n return {\n errorResponse: jsonResponse({ error: 'No file provided' }, 400),\n };\n }\n\n const file = formData.get('file') as File | null;\n if (\n !file ||\n typeof file === 'string' ||\n (typeof file.size === 'number' && file.size === 0 && !file.name)\n ) {\n return {\n errorResponse: jsonResponse({ error: 'No file provided' }, 400),\n };\n }\n if (typeof file.size === 'number' && file.size === 0) {\n return {\n errorResponse: jsonResponse({ error: 'File is empty' }, 400),\n };\n }\n\n const {\n uploader,\n allowedPresets,\n allowClientOverrides,\n allowClientBucketOverride = false,\n allowedBuckets,\n } = options;\n\n const preset = (formData.get('preset') as TPreset | null) || undefined;\n if (preset) {\n if (\n allowedPresets &&\n allowedPresets.length > 0 &&\n !allowedPresets.includes(preset)\n ) {\n return {\n errorResponse: jsonResponse(\n {\n error: `Invalid preset \"${preset}\". Allowed presets: ${allowedPresets.join(', ')}`,\n },\n 400,\n ),\n };\n }\n if (\n typeof uploader.resolvePreset === 'function' &&\n !uploader.resolvePreset(preset)\n ) {\n return {\n errorResponse: jsonResponse(\n { error: `Invalid preset \"${preset}\".` },\n 400,\n ),\n };\n }\n }\n\n const presetConfig =\n preset && typeof uploader.resolvePreset === 'function'\n ? uploader.resolvePreset(preset)\n : undefined;\n\n const rawBucket = (formData.get('bucket') as string | null) || undefined;\n if (rawBucket && allowedBuckets && !allowedBuckets.includes(rawBucket)) {\n return {\n errorResponse: jsonResponse(\n { error: `Bucket \"${rawBucket}\" is not allowed` },\n 403,\n ),\n };\n }\n const bucket = allowClientBucketOverride ? rawBucket : undefined;\n\n let folder: string | undefined;\n const rawFolder = (formData.get('folder') as string | null) || undefined;\n if (rawFolder !== undefined) {\n if (allowClientOverrides?.folder === true) {\n folder = rawFolder;\n } else if (typeof allowClientOverrides?.folder === 'function') {\n const scoped = allowClientOverrides.folder(rawFolder, authCtx);\n if (scoped === null) {\n return {\n errorResponse: jsonResponse(\n { error: 'Invalid or disallowed folder' },\n 400,\n ),\n };\n }\n folder = scoped || undefined;\n }\n }\n\n const rawFilename =\n (formData.get('customFilename') as string | null) || undefined;\n const customFilename = allowClientOverrides?.filename\n ? rawFilename\n : undefined;\n\n const outputFormatRaw = formData.get('outputFormat') as string | null;\n if (outputFormatRaw && !ALLOWED_OUTPUT_FORMATS.has(outputFormatRaw)) {\n return {\n errorResponse: jsonResponse(\n {\n error: `Invalid output format \"${outputFormatRaw}\". Allowed formats: jpeg, png, webp, avif`,\n },\n 400,\n ),\n };\n }\n const outputFormat =\n allowClientOverrides?.format && outputFormatRaw\n ? (outputFormatRaw as ExplicitOutputFormat)\n : undefined;\n\n const qualityRaw = formData.get('quality') as string | null;\n let quality: number | undefined;\n if (qualityRaw !== null && qualityRaw.trim() !== '') {\n const parsedQuality = Number(qualityRaw);\n if (\n !Number.isFinite(parsedQuality) ||\n !Number.isInteger(parsedQuality) ||\n parsedQuality < 1 ||\n parsedQuality > 100\n ) {\n return {\n errorResponse: jsonResponse(\n {\n error: `Invalid quality \"${qualityRaw}\". Must be an integer between 1 and 100.`,\n },\n 400,\n ),\n };\n }\n if (allowClientOverrides?.quality) {\n quality = presetConfig?.quality\n ? Math.min(parsedQuality, presetConfig.quality)\n : parsedQuality;\n }\n }\n\n const maxWidthRaw = formData.get('maxWidth') as string | null;\n const maxHeightRaw = formData.get('maxHeight') as string | null;\n let maxDimensions: ImageDimensions | undefined;\n if (\n (maxWidthRaw !== null && maxWidthRaw.trim() !== '') ||\n (maxHeightRaw !== null && maxHeightRaw.trim() !== '')\n ) {\n const w = Number(maxWidthRaw);\n const h = Number(maxHeightRaw);\n if (!Number.isFinite(w) || !Number.isFinite(h) || w < 1 || h < 1) {\n return {\n errorResponse: jsonResponse(\n { error: 'Invalid maxWidth or maxHeight dimensions' },\n 400,\n ),\n };\n }\n if (allowClientOverrides?.dimensions) {\n const cap = presetConfig?.maxDimensions;\n maxDimensions = cap\n ? {\n width: Math.min(Math.floor(w), cap.width),\n height: Math.min(Math.floor(h), cap.height),\n }\n : { width: Math.floor(w), height: Math.floor(h) };\n }\n }\n\n const altText = (formData.get('altText') as string | null) || undefined;\n const metadata = parseJsonRecord(formData.get('metadata') as string | null);\n\n return {\n file,\n uploadOptions: {\n preset,\n folder,\n bucket,\n altText,\n customFilename,\n ...(customFilename ? { isClientFilename: true } : {}),\n outputFormat,\n quality,\n maxDimensions,\n metadata,\n signal: authCtx.request.signal,\n },\n };\n}\n","import { createDeleteHandler } from './deleteRoute';\nimport {\n checkAllowedOrigin,\n readBoundedFormData,\n resolveMaxBodyBytes,\n runAuthorize,\n} from './guards';\nimport type {\n AllowClientOverrides,\n AuthorizeContext,\n UploadErrorContext,\n UploadHttpHandlerOptions,\n} from './options';\nimport { parseUploadForm, peekUploadAuthContext } from './parseUploadForm';\nimport {\n handleUnexpectedError,\n isClientValidationError,\n jsonResponse,\n UploadValidationError,\n} from './responses';\n\nexport {\n type AllowClientOverrides,\n type AuthorizeContext,\n isClientValidationError,\n type UploadErrorContext,\n type UploadHttpHandlerOptions,\n UploadValidationError,\n};\n\n/**\n * Standard Web Fetch (`Request` -> `Response`) upload & delete route handlers.\n * Works across Next.js App Router, Remix, React Router, SvelteKit, Astro, Hono, and Node Fetch servers.\n */\nexport function createUploadHttpHandler<TPreset extends string = string>(\n options: UploadHttpHandlerOptions<TPreset>,\n) {\n const { uploader, authorize, allowedOrigins, onError } = options;\n const maxBodyBytes = resolveMaxBodyBytes(options);\n\n async function POST(request: Request): Promise<Response> {\n try {\n const originError = checkAllowedOrigin(request, allowedOrigins);\n if (originError) return originError;\n\n const { formData, earlyErrorResponse } = await readBoundedFormData(\n request,\n maxBodyBytes,\n );\n if (earlyErrorResponse) return earlyErrorResponse;\n\n const authCtx = peekUploadAuthContext(formData, request, options);\n const authError = await runAuthorize(authorize, authCtx);\n if (authError) return authError;\n\n const parsed = parseUploadForm(formData, authCtx, options);\n if (parsed.errorResponse) return parsed.errorResponse;\n\n const result = await uploader.uploadImage(\n parsed.file!,\n parsed.uploadOptions,\n );\n\n return jsonResponse(result, 200);\n } catch (error) {\n return handleUnexpectedError(\n error,\n { action: 'upload', request },\n onError,\n );\n }\n }\n\n const DELETE = createDeleteHandler(options);\n\n return { POST, DELETE };\n}\n\nexport const createUploadHandler = createUploadHttpHandler;\n"],"mappings":"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","names":[]}
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
QUALITY_PRESETS,
|
|
4
4
|
createSuccessMessage,
|
|
5
5
|
getStatusMessage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-G7LLSKBV.js";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_BLUR_COLOR,
|
|
9
9
|
STANDARD_PRESETS,
|
|
10
10
|
normalizeColorInput,
|
|
11
11
|
validateImageFile,
|
|
12
12
|
validateMultipleFiles
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TT5C7K3Q.js";
|
|
14
14
|
|
|
15
15
|
// src/react/hooks/useClipboardPaste.ts
|
|
16
16
|
import { useCallback, useEffect, useRef } from "react";
|
|
@@ -2367,4 +2367,4 @@ export {
|
|
|
2367
2367
|
HeadlessSingleUpload,
|
|
2368
2368
|
HeadlessMultiUpload
|
|
2369
2369
|
};
|
|
2370
|
-
//# sourceMappingURL=chunk-
|
|
2370
|
+
//# sourceMappingURL=chunk-AI3JJLSA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/hooks/useClipboardPaste.ts","../src/react/hooks/useDragAndDrop.ts","../src/react/hooks/useSingleImageState.ts","../src/react/context.tsx","../src/react/i18n/ar.ts","../src/react/i18n/en.ts","../src/react/i18n.ts","../src/react/utils/sessionMetaCache.ts","../src/react/hooks/singleStateHelpers.ts","../src/react/hooks/useFilePreview.ts","../src/react/hooks/useImageUpload.ts","../src/react/hooks/uploadTracker.ts","../src/react/hooks/xhrUpload.ts","../src/react/utils/slotStyles.ts","../src/react/headless/useSingleUpload.ts","../src/react/headless/useMultiUpload.ts","../src/react/hooks/useMultiImageState.ts","../src/react/hooks/uploadQueue.ts","../src/react/headless/HeadlessSingleUpload.tsx","../src/react/headless/HeadlessMultiUpload.tsx"],"sourcesContent":["import { type ClipboardEvent as ReactClipboardEvent, useCallback, useEffect, useRef } from 'react';\n\nexport type PasteTargetMode = 'dropzone' | 'document';\n\nexport interface UseClipboardPasteOptions {\n /** Enable clipboard image paste handling (defaults to `true`) */\n enabled?: boolean;\n /** Listen on the focused dropzone (`'dropzone'`) or globally on `document` (`'document'`). Defaults to `'dropzone'`. */\n pasteTarget?: PasteTargetMode;\n /** Disable paste handling when the uploader is disabled */\n disabled?: boolean;\n /** Allow pasting multiple images at once (defaults to `false` for single upload, `true` for multi upload) */\n multiple?: boolean;\n /** Callback invoked when one or more image files are pasted */\n onPasteFiles: (files: FileList) => void;\n}\n\nfunction isEditableTextTarget(target: EventTarget | null): boolean {\n if (!target || typeof HTMLElement === 'undefined' || !(target instanceof HTMLElement)) {\n return false;\n }\n const tag = target.tagName;\n if (tag === 'TEXTAREA' || target.isContentEditable) return true;\n if (tag === 'INPUT') {\n const type = (target as HTMLInputElement).type?.toLowerCase();\n return type !== 'file' && type !== 'button' && type !== 'checkbox' && type !== 'radio';\n }\n return false;\n}\n\nexport function extractClipboardImageFiles(\n clipboardData: DataTransfer | null | undefined,\n multiple: boolean,\n): File[] {\n if (!clipboardData) return [];\n\n const extracted: File[] = [];\n if (clipboardData.files && clipboardData.files.length > 0) {\n for (let i = 0; i < clipboardData.files.length; i++) {\n const file = clipboardData.files[i];\n if (file && file.type.startsWith('image/')) {\n extracted.push(file);\n }\n }\n }\n\n if (extracted.length === 0 && clipboardData.items && clipboardData.items.length > 0) {\n for (let i = 0; i < clipboardData.items.length; i++) {\n const item = clipboardData.items[i];\n if (item && item.kind === 'file' && item.type.startsWith('image/')) {\n const file = item.getAsFile();\n if (file) extracted.push(file);\n }\n }\n }\n\n if (extracted.length === 0) return [];\n return multiple ? extracted : [extracted[0]];\n}\n\nexport function toFileListLike(files: File[]): FileList {\n if (typeof DataTransfer !== 'undefined') {\n try {\n const dt = new DataTransfer();\n for (const file of files) dt.items.add(file);\n if (dt.files.length === files.length) return dt.files;\n } catch {\n // Fallback for test environments without full DataTransfer item mutation\n }\n }\n const arrayWithItem = Object.assign([...files], {\n item: (index: number) => files[index] ?? null,\n });\n return arrayWithItem as unknown as FileList;\n}\n\nexport function useClipboardPaste({\n enabled = true,\n pasteTarget = 'dropzone',\n disabled = false,\n multiple = false,\n onPasteFiles,\n}: UseClipboardPasteOptions) {\n const onPasteFilesRef = useRef(onPasteFiles);\n onPasteFilesRef.current = onPasteFiles;\n const handledNativeEventsRef = useRef<WeakSet<Event>>(new WeakSet());\n\n const processPaste = useCallback(\n (\n clipboardData: DataTransfer | null | undefined,\n target: EventTarget | null,\n nativeEvent: Event,\n preventDefault: () => void,\n ) => {\n if (!enabled || disabled) return;\n if (handledNativeEventsRef.current.has(nativeEvent)) return;\n if (isEditableTextTarget(target)) return;\n\n const files = extractClipboardImageFiles(clipboardData, multiple);\n if (files.length === 0) return;\n\n handledNativeEventsRef.current.add(nativeEvent);\n preventDefault();\n onPasteFilesRef.current(toFileListLike(files));\n },\n [enabled, disabled, multiple],\n );\n\n const onPaste = useCallback(\n (event: ReactClipboardEvent<HTMLElement>) => {\n processPaste(\n event.clipboardData,\n event.target,\n event.nativeEvent,\n () => event.preventDefault(),\n );\n },\n [processPaste],\n );\n\n useEffect(() => {\n if (!enabled || disabled || pasteTarget !== 'document' || typeof document === 'undefined') {\n return;\n }\n const handleDocumentPaste = (event: ClipboardEvent) => {\n processPaste(\n event.clipboardData,\n event.target,\n event,\n () => event.preventDefault(),\n );\n };\n document.addEventListener('paste', handleDocumentPaste);\n return () => document.removeEventListener('paste', handleDocumentPaste);\n }, [enabled, disabled, pasteTarget, processPaste]);\n\n return { onPaste };\n}\n","import { type DragEvent, useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseDragAndDropOptions {\n disabled?: boolean;\n onDrop: (files: FileList) => void;\n}\n\nexport function useDragAndDrop({ disabled, onDrop }: UseDragAndDropOptions) {\n const [isDragging, setIsDragging] = useState(false);\n const dragDepthRef = useRef(0);\n\n useEffect(() => {\n if (disabled) {\n dragDepthRef.current = 0;\n setIsDragging(false);\n }\n }, [disabled]);\n\n const handleDragEnter = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n dragDepthRef.current += 1;\n setIsDragging(true);\n },\n [disabled],\n );\n\n const handleDragLeave = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);\n if (dragDepthRef.current === 0) {\n setIsDragging(false);\n }\n }, []);\n\n const handleDragOver = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n }, []);\n\n const handleDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragDepthRef.current = 0;\n setIsDragging(false);\n\n if (disabled) return;\n\n const files = e.dataTransfer?.files;\n if (files && files.length > 0) {\n onDrop(files);\n }\n },\n [disabled, onDrop],\n );\n\n return {\n isDragging,\n dragHandlers: {\n onDragEnter: handleDragEnter,\n onDragLeave: handleDragLeave,\n onDragOver: handleDragOver,\n onDrop: handleDrop,\n },\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { QualityLevel, StandardPresetName, UploadedImageData, UploadPresetName } from '../../types';\nimport { QUALITY_PRESETS, STANDARD_PRESETS } from '../../types';\nimport { DEFAULT_BLUR_COLOR, normalizeColorInput } from '../../utils/color';\nimport { validateImageFile } from '../../utils/validators';\nimport { useImageUploadContext } from '../context';\nimport { normalizeUploadLocale, resolveTranslations } from '../i18n';\nimport type { SingleImageUploadProps } from '../types';\nimport { createUploadSessionCache, downloadImageWithFallback } from '../utils/sessionMetaCache';\nimport {\n buildSingleImagePayload,\n resolveInitialQualityLevel,\n resolveLiveAnnouncement,\n serializeSingleFormValue,\n stripUrlCacheQuery,\n} from './singleStateHelpers';\nimport { useFilePreview } from './useFilePreview';\nimport { useImageUpload } from './useImageUpload';\n\nexport { resolveLiveAnnouncement, serializeSingleFormValue };\n\nexport function useSingleImageState<TPreset extends string = UploadPresetName>(\n props: SingleImageUploadProps<TPreset>,\n) {\n const {\n value, onChange, preset, folder, bucket, metadata, endpoint, uploader,\n headers, withCredentials, locale: propLocale, translations,\n initialAltText: propInitialAltText, maxSizeMB: propMaxSizeMB,\n maxDimensions, requireAltText: propRequireAltText, required, isOpen, onClose,\n outputFormat, quality: initialQualityProp, customFilename, formValue,\n } = props;\n\n const ctx = useImageUploadContext();\n const locale = normalizeUploadLocale(propLocale || ctx.locale);\n const ui = resolveTranslations(locale, translations || ctx.translations);\n const requireAltText = propRequireAltText ?? ctx.requireAltText;\n const standardPreset = preset ? STANDARD_PRESETS[preset as StandardPresetName] : undefined;\n const maxSizeMB = propMaxSizeMB ?? standardPreset?.maxSizeMB ?? 10;\n const requiresManualCommit = Boolean(isOpen);\n const cache = useMemo(() => createUploadSessionCache(ctx.storageKeyPrefix || 'iuk'), [ctx.storageKeyPrefix]);\n\n const objVal = typeof value === 'object' && value ? value : null;\n const initPreview = typeof value === 'string' ? value : objVal?.url || null;\n const initAlt = objVal?.altText || propInitialAltText || '';\n const initColor = objVal?.blurColor || DEFAULT_BLUR_COLOR;\n\n const [preview, setPreview] = useState<string | null>(initPreview);\n const previewRef = useRef<string | null>(initPreview);\n previewRef.current = preview;\n\n const [altText, setAltText] = useState<string>(initAlt);\n const [blurColor, setBlurColor] = useState<string>(initColor);\n const [detectedBlurColor, setDetectedBlurColor] = useState<string>(initColor);\n const [blurDataUrlState, setBlurDataUrlState] = useState<string>(objVal?.blurDataUrl || '');\n const [hasColorOverride, setHasColorOverride] = useState(false);\n const [validationError, setValidationError] = useState<string>('');\n const [showMissingAltWarning, setShowMissingAltWarning] = useState(false);\n const [editingAltText, setEditingAltText] = useState(false);\n const [sizeInfo, setSizeInfo] = useState<{ original: number; optimized: number } | null>(null);\n const [uploadedData, setUploadedData] = useState<UploadedImageData | null>(objVal && 'url' in objVal ? objVal : null);\n const [isBlurModalOpen, setIsBlurModalOpen] = useState(false);\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [qualityLevel, setQualityLevel] = useState<QualityLevel>(() => resolveInitialQualityLevel(initialQualityProp, standardPreset));\n const fileInputRef = useRef<HTMLInputElement>(null);\n const lastAltRef = useRef<string>(initAlt);\n const activeUploadIdRef = useRef<string | null>(null);\n const uploadSeqRef = useRef(0);\n\n const resetColorAndErrorState = () => {\n setValidationError('');\n setHasColorOverride(false);\n setBlurColor(DEFAULT_BLUR_COLOR);\n setDetectedBlurColor(DEFAULT_BLUR_COLOR);\n setIsBlurModalOpen(false);\n };\n\n const { createPreview, revokePreview } = useFilePreview({ errorLabel: ui.failedCreatePreview });\n\n const { uploadImage, abortAll, isUploading, progress, uploadMessage, reset } = useImageUpload({\n preset, folder, bucket, metadata, outputFormat, maxDimensions, customFilename,\n quality: QUALITY_PRESETS[qualityLevel].value,\n endpoint, uploader, headers, withCredentials, locale,\n onSuccess: (data, _file, uploadId) => {\n if (uploadId && activeUploadIdRef.current && uploadId !== activeUploadIdRef.current) return;\n if (previewRef.current && previewRef.current !== data.url) revokePreview(previewRef.current);\n setPreview(data.url);\n setSizeInfo({ original: data.originalSize || 0, optimized: data.size });\n const normColor = data.blurColor || DEFAULT_BLUR_COLOR;\n setBlurColor(normColor);\n setDetectedBlurColor(normColor);\n setHasColorOverride(false);\n setBlurDataUrlState(data.blurDataUrl || '');\n const keyInfo = { url: data.url, contentHash: data.contentHash };\n cache.writeBlurMetaFor(keyInfo, { blurDataUrl: data.blurDataUrl || '', blurColor: normColor });\n const currentAlt = lastAltRef.current || data.altText || '';\n setAltText(currentAlt);\n lastAltRef.current = currentAlt;\n const payload: UploadedImageData = { ...data, altText: currentAlt, blurColor: normColor };\n setUploadedData(payload);\n cache.writeAltMetaFor(keyInfo, currentAlt);\n if (!requiresManualCommit) onChange(payload);\n },\n onError: (errorMsg, _file, uploadId) => {\n if (uploadId && activeUploadIdRef.current && uploadId !== activeUploadIdRef.current) return;\n revokePreview(previewRef.current);\n setValidationError(errorMsg);\n setPreview(null);\n setUploadedData(null);\n },\n });\n\n useEffect(() => {\n if (isOpen === false) abortAll();\n }, [isOpen, abortAll]);\n\n useEffect(() => {\n const incomingObj = typeof value === 'object' && value ? value : null;\n const nextPreview = typeof value === 'string' ? value : incomingObj?.url || null;\n const rawAlt = incomingObj?.altText || propInitialAltText || '';\n const cachedAlt = cache.readAltMetaFor({ url: incomingObj ? incomingObj.url : nextPreview, contentHash: incomingObj?.contentHash ?? null });\n const nextAlt =\n rawAlt.trim() === '' && nextPreview === preview && (lastAltRef.current.trim() !== '' || cachedAlt?.trim())\n ? lastAltRef.current.trim() || cachedAlt || rawAlt\n : rawAlt;\n const nextColor = incomingObj?.blurColor || DEFAULT_BLUR_COLOR;\n\n if (stripUrlCacheQuery(nextPreview) !== stripUrlCacheQuery(preview)) setPreview(nextPreview);\n if (nextAlt !== altText && (nextPreview !== preview || nextAlt.trim() !== '' || !altText.trim())) {\n setAltText(nextAlt);\n lastAltRef.current = nextAlt;\n }\n\n const nextIsFallback = nextColor === DEFAULT_BLUR_COLOR;\n if (nextColor !== blurColor && (nextPreview !== preview || (!hasColorOverride && (!nextIsFallback || blurColor === DEFAULT_BLUR_COLOR)))) {\n setBlurColor(nextColor);\n setDetectedBlurColor(nextColor);\n setHasColorOverride(false);\n }\n const nextBlurUrl = incomingObj?.blurDataUrl || '';\n if (nextPreview !== preview || (nextBlurUrl && nextBlurUrl !== blurDataUrlState)) {\n setBlurDataUrlState(nextBlurUrl);\n }\n\n if (nextPreview) {\n const cached = cache.readBlurMetaFor({\n url: nextPreview,\n contentHash: incomingObj?.contentHash || uploadedData?.contentHash,\n });\n if (cached?.blurDataUrl && !nextBlurUrl) setBlurDataUrlState(cached.blurDataUrl);\n if (cached?.blurColor && nextIsFallback && blurColor === DEFAULT_BLUR_COLOR) {\n setBlurColor(cached.blurColor);\n setDetectedBlurColor(cached.blurColor);\n }\n }\n\n if (!value) {\n setUploadedData(null);\n setBlurDataUrlState('');\n setSizeInfo(null);\n setSelectedFile(null);\n lastAltRef.current = propInitialAltText || '';\n if (fileInputRef.current) fileInputRef.current.value = '';\n } else if (incomingObj?.url) {\n setUploadedData((prev) => (prev?.url === incomingObj.url ? prev : incomingObj));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, propInitialAltText]);\n\n const commitIfAuto = (overrides: Partial<UploadedImageData>) => {\n if (!requiresManualCommit) {\n const payload = buildSingleImagePayload(\n { uploadedData, objVal, value, preview, altText, blurColor, blurDataUrlState },\n overrides,\n );\n if (payload) onChange(payload);\n }\n };\n\n const applyBlurColor = (color: string, options: { markOverride?: boolean } = {}) => {\n const normalized = normalizeColorInput(color);\n if (!normalized) return;\n setBlurColor(normalized);\n setHasColorOverride(options.markOverride !== false);\n setUploadedData((prev) => (prev ? { ...prev, blurColor: normalized } : prev));\n if (preview) cache.writeBlurMetaFor({ url: preview, contentHash: uploadedData?.contentHash }, { blurDataUrl: blurDataUrlState, blurColor: normalized });\n commitIfAuto({ blurColor: normalized });\n };\n\n const handleResetColor = () => {\n if (detectedBlurColor) applyBlurColor(detectedBlurColor, { markOverride: false });\n };\n\n const handleFileSelect = async (files: FileList) => {\n const file = files[0];\n if (!file) return;\n abortAll();\n setSelectedFile(file);\n resetColorAndErrorState();\n const err = validateImageFile(file, maxSizeMB, locale);\n if (err) {\n setValidationError(err);\n return;\n }\n uploadSeqRef.current += 1;\n const uploadId = `single-${uploadSeqRef.current}`;\n activeUploadIdRef.current = uploadId;\n try {\n const nextBlobUrl = await createPreview(file);\n if (activeUploadIdRef.current !== uploadId) {\n revokePreview(nextBlobUrl);\n return;\n }\n revokePreview(previewRef.current);\n setPreview(nextBlobUrl);\n setAltText('');\n lastAltRef.current = '';\n } catch {\n setValidationError(ui.failedCreatePreview);\n return;\n }\n await uploadImage(file, '', customFilename, { uploadId });\n };\n\n const handleCancelUpload = () => {\n activeUploadIdRef.current = null;\n abortAll();\n revokePreview(previewRef.current);\n setPreview(null);\n setSelectedFile(null);\n reset();\n if (fileInputRef.current) fileInputRef.current.value = '';\n };\n\n const handleRemoveTile = () => {\n handleCancelUpload();\n setAltText('');\n lastAltRef.current = '';\n setBlurDataUrlState('');\n setSizeInfo(null);\n setUploadedData(null);\n resetColorAndErrorState();\n if (!requiresManualCommit) onChange(null);\n };\n\n const handleAltTextSave = (newAltText: string) => {\n setAltText(newAltText);\n lastAltRef.current = newAltText;\n if (preview) cache.writeAltMetaFor({ url: preview, contentHash: uploadedData?.contentHash }, newAltText);\n setShowMissingAltWarning(false);\n setEditingAltText(false);\n commitIfAuto({ altText: newAltText });\n };\n\n const handleSaveImage = () => {\n if (required && !preview) {\n setValidationError(ui.requiredFieldError);\n return;\n }\n if (requireAltText && preview && !altText?.trim()) {\n setShowMissingAltWarning(true);\n setValidationError(ui.addAltBeforeSaving);\n return;\n }\n setShowMissingAltWarning(false);\n setValidationError('');\n const payload = buildSingleImagePayload({ uploadedData, objVal, value, preview, altText, blurColor, blurDataUrlState });\n if (payload) {\n lastAltRef.current = payload.altText || lastAltRef.current;\n cache.writeAltMetaFor({ url: payload.url, contentHash: payload.contentHash }, payload.altText || '');\n onChange(payload);\n }\n onClose?.();\n };\n\n const currentImageData = preview ? (uploadedData ?? objVal) : null;\n const downloadUrl = preview\n ? (uploadedData?.url ?? (typeof value === 'string' ? value : objVal?.url) ?? null)\n : null;\n const handleDownload = useCallback(async () => {\n if (downloadUrl) await downloadImageWithFallback(downloadUrl, uploadedData?.format);\n }, [downloadUrl, uploadedData?.format]);\n\n const liveAnnouncement = resolveLiveAnnouncement(isUploading, progress, Boolean(sizeInfo && !isUploading), ui);\n const serializedFormValue = serializeSingleFormValue({\n formValue, currentImageData, preview, altText, blurColor, blurDataUrlState, isUploading,\n });\n\n return {\n locale, ui, requireAltText, maxSizeMB, fileInputRef,\n preview, altText, blurColor, detectedBlurColor, blurDataUrlState,\n validationError, showMissingAltWarning, editingAltText, setEditingAltText,\n sizeInfo, uploadedData, isBlurModalOpen, setIsBlurModalOpen,\n selectedFile, qualityLevel, setQualityLevel, isUploading, progress, uploadMessage, liveAnnouncement,\n currentImageData, canDownload: Boolean(downloadUrl), serializedFormValue,\n applyBlurColor, handleResetColor, handleFileSelect, handleCancelUpload, handleRemoveTile,\n handleAltTextSave, handleSaveImage, handleDownload,\n };\n}\n","import { createContext, type ReactNode, useContext, useMemo } from 'react';\nimport type { UploadLocale } from '../types';\nimport type { UploadTranslations } from './i18n';\nimport type {\n MultiUploadClassNames,\n MultiUploadStyles,\n SingleUploadClassNames,\n SingleUploadStyles,\n} from './styling';\nimport type { CustomImageRenderer, CustomUploaderFn } from './types';\n\nexport interface ImageUploadContextValue {\n endpoint: string;\n uploader?: CustomUploaderFn;\n headers?: Record<string, string>;\n withCredentials?: boolean;\n locale?: UploadLocale;\n translations?: Partial<UploadTranslations>;\n renderImage?: CustomImageRenderer;\n storageKeyPrefix: string;\n requireAltText: boolean;\n unstyled: boolean;\n singleClassNames?: SingleUploadClassNames;\n singleStyles?: SingleUploadStyles;\n multiClassNames?: MultiUploadClassNames;\n multiStyles?: MultiUploadStyles;\n}\n\nconst DEFAULT_CONTEXT: ImageUploadContextValue = {\n endpoint: '/api/upload',\n locale: undefined,\n storageKeyPrefix: 'iuk',\n requireAltText: true,\n unstyled: false,\n};\n\nconst ImageUploadContext =\n createContext<ImageUploadContextValue>(DEFAULT_CONTEXT);\n\nexport interface ImageUploadProviderProps {\n children: ReactNode;\n /** Default server endpoint for uploads (defaults to `'/api/upload'`) */\n endpoint?: string;\n /** Custom async uploader function used across all upload components */\n uploader?: CustomUploaderFn;\n /** Custom HTTP headers attached to FormData upload requests */\n headers?: Record<string, string>;\n /** Send credentials/cookies with XHR/fetch requests */\n withCredentials?: boolean;\n /** Default UI locale (`'en' | 'ar'`) */\n locale?: UploadLocale;\n /** Global translation overrides */\n translations?: Partial<UploadTranslations>;\n /** Custom image component renderer (e.g. for custom CDN or framework `<Image />`) */\n renderImage?: CustomImageRenderer;\n /** Prefix used for sessionStorage metadata cache keys (defaults to `'iuk'`) */\n storageKeyPrefix?: string;\n /** Require alt text before saving in modal mode (defaults to `true`) */\n requireAltText?: boolean;\n /** Globally strip default `iuk-*` classes across all Single/Multi upload components */\n unstyled?: boolean;\n /** Global default slot `classNames` applied to all `<SingleImageUpload />` instances */\n singleClassNames?: SingleUploadClassNames;\n /** Global default slot `styles` applied to all `<SingleImageUpload />` instances */\n singleStyles?: SingleUploadStyles;\n /** Global default slot `classNames` applied to all `<MultiImageUpload />` instances */\n multiClassNames?: MultiUploadClassNames;\n /** Global default slot `styles` applied to all `<MultiImageUpload />` instances */\n multiStyles?: MultiUploadStyles;\n}\n\nexport function ImageUploadProvider({\n children,\n endpoint = '/api/upload',\n uploader,\n headers,\n withCredentials,\n locale,\n translations,\n renderImage,\n storageKeyPrefix = 'iuk',\n requireAltText = true,\n unstyled = false,\n singleClassNames,\n singleStyles,\n multiClassNames,\n multiStyles,\n}: ImageUploadProviderProps) {\n const value = useMemo<ImageUploadContextValue>(\n () => ({\n endpoint,\n uploader,\n headers,\n withCredentials,\n locale,\n translations,\n renderImage,\n storageKeyPrefix,\n requireAltText,\n unstyled,\n singleClassNames,\n singleStyles,\n multiClassNames,\n multiStyles,\n }),\n [\n endpoint,\n uploader,\n headers,\n withCredentials,\n locale,\n translations,\n renderImage,\n storageKeyPrefix,\n requireAltText,\n unstyled,\n singleClassNames,\n singleStyles,\n multiClassNames,\n multiStyles,\n ],\n );\n\n return (\n <ImageUploadContext.Provider value={value}>\n {children}\n </ImageUploadContext.Provider>\n );\n}\n\nexport function useImageUploadContext(): ImageUploadContextValue {\n return useContext(ImageUploadContext);\n}\n","import type { UploadTranslations } from './types';\n\nexport const arTranslations: UploadTranslations = {\n uploadWindow: 'نافذة الرفع',\n tapToResume: 'اضغط للاستئناف',\n restoreUploadWindow: 'استعادة نافذة الرفع',\n minimize: 'تصغير',\n minimizeUploader: 'تصغير نافذة الرفع',\n close: 'إغلاق',\n editPrefix: 'تعديل - {title}',\n uploadImage: 'رفع صورة',\n uploadImages: 'رفع صور',\n noValidImageFiles:\n 'لم يتم العثور على ملفات صور صالحة. يرجى اختيار صور (JPEG, PNG, WebP, AVIF, SVG).',\n selectedImagesSkipped:\n 'تم اختيار {imageCount} صورة. وتم تجاهل {skippedCount} ملف غير صوري.',\n queued: 'في الانتظار...',\n retrying: 'جاري إعادة المحاولة...',\n preparingUpload: 'جاري تجهيز الرفع...',\n uploadFailed: 'فشل الرفع',\n failedCreatePreview: 'تعذر إنشاء المعاينة',\n failedCreatePreviewFor: 'تعذر إنشاء معاينة للملف: {file}',\n selectAll: 'تحديد الكل',\n deselectAll: 'إلغاء تحديد الكل',\n removeSelected: 'حذف المحدد',\n uploading: 'جارٍ الرفع',\n uploadingCount: 'جارٍ رفع {count} صورة',\n someUploadsFailed: 'فشل رفع بعض الملفات.',\n retryFailedUploads: 'إعادة رفع الملفات الفاشلة',\n needsAltBeforeSave:\n 'يوجد {count} صورة بدون نص بديل. أضف وصفًا قبل الحفظ من زر الوصف على كل صورة.',\n networkError: 'خطأ في الشبكة أثناء رفع الصورة',\n invalidUploadResponse: 'استجابة غير صالحة من خادم الرفع',\n uploadAborted: 'تم إلغاء الرفع',\n uploadStartedLive: 'بدأ الرفع',\n uploadProgressLive: 'اكتمل الرفع بنسبة {progress}%',\n uploadCompleteLive: 'اكتمل الرفع',\n qualityTitle: 'جودة الضغط',\n qualityLevelAria: 'مستوى الجودة',\n qualitySaved: 'توفير',\n qualityRecommended: '⭐ موصى به',\n selectFilesAria: 'اختيار ملفات',\n selectMoreFilesAria: 'اختيار ملفات إضافية',\n selectFolderAria: 'اختيار مجلد',\n dropImagesHere: 'أسقط الصور هنا',\n releaseToUpload: 'اترك للإفلات والرفع',\n dragDropOrChoose: 'اسحب وأفلت الصور هنا، أو اختر طريقة الرفع من الأسفل',\n selectFiles: 'اختيار ملفات',\n selectFolder: 'اختيار مجلد',\n supportsFormats: 'الصيغ المدعومة: JPEG, PNG, WebP, AVIF, SVG',\n maxPerFile: 'الحد الأقصى {size}MB لكل ملف',\n upToSizeMB: 'حتى {size} ميجابايت',\n dropHere: 'أسقط هنا',\n addMore: 'إضافة المزيد',\n files: 'ملفات',\n folder: 'مجلد',\n withoutAltText: '{count} بدون نص بديل',\n saveImages: 'حفظ الصور',\n addAltBeforeSaving:\n 'يرجى إضافة نص بديل قبل الحفظ. اضغط زر الوصف على الصورة.',\n missingDescription: 'الوصف مفقود',\n saveImage: 'حفظ الصورة',\n downloadImage: 'تحميل الصورة',\n dragDropOrClickToUpload: 'اسحب وأفلت أو انقر للرفع',\n browseFiles: 'تصفح الملفات',\n image: 'صورة',\n processing: 'جارٍ المعالجة...',\n uploadImageAria: 'رفع صورة',\n fileInputAria: 'حقل اختيار ملف',\n uploadingAlt: 'جارٍ الرفع',\n previewAlt: 'معاينة',\n addImageDescription: 'إضافة وصف للصورة',\n describeImagePlaceholder: 'صف هذه الصورة لإمكانية الوصول...',\n characters: 'حرف',\n usedForA11ySeo: 'يُستخدم لإمكانية الوصول وتحسين الظهور في البحث',\n cancel: 'إلغاء',\n saveDescription: 'حفظ الوصف',\n selectImageAria: 'تحديد الصورة',\n editAltTextAria: 'تعديل النص البديل',\n addAltTextAria: 'إضافة نص بديل',\n addDescriptionForA11y: 'أضف وصفًا لإمكانية الوصول',\n adjustBlurColorAria: 'ضبط التمويه واللون',\n adjustBlurColorTitle: 'معاينة بيانات التمويه وضبط الألوان',\n removeImageAria: 'حذف الصورة',\n pasteHint: 'أو الصق من الحافظة',\n cancelUpload: 'إلغاء الرفع',\n retryUpload: 'إعادة رفع الصورة',\n minImagesError: 'يرجى رفع {min} صورة على الأقل.',\n requiredFieldError: 'هذا الحقل مطلوب.',\n blurModal: {\n title: 'معاينة التمويه واللون السائد',\n closeModal: 'إغلاق المعاينة',\n adjustColor: 'لون الخلفية السائد',\n clickToSample: 'انقر في أي مكان على الصورة لالتقاط لون البكسل',\n pickColorNote:\n 'اختر لونًا من الأداة، أو أدخل كود HEX، أو التقط مباشرة من الصورة',\n enterHexColor: 'أدخل كود اللون HEX',\n hexPlaceholder: 'F5F5F5',\n enablePenMode: 'التقاط لون من الصورة',\n disablePenMode: 'إيقاف الالتقاط',\n penModeActive: 'وضع الالتقاط مفعل',\n resetToDetected: 'استعادة اللون التلقائي',\n sampled: 'تم التقاط',\n livePreview: 'معاينة حية للتمويه والصورة',\n showingOriginal: 'عرض الصورة الأصلية (لا يتوفر تمويه بعد)',\n saved: 'التوفير في الحجم:',\n accessibility: 'إمكانية الوصول ومحركات البحث',\n altText: 'النص البديل للصورة (Alt Text)',\n altTextPlaceholder: 'صف محتوى الصورة لقارئات الشاشة ومحركات البحث...',\n saveAndClose: 'حفظ وإغلاق',\n },\n};\n","import type { UploadTranslations } from './types';\n\nexport const enTranslations: UploadTranslations = {\n uploadWindow: 'Upload window',\n tapToResume: 'Tap to resume',\n restoreUploadWindow: 'Restore upload window',\n minimize: 'Minimize',\n minimizeUploader: 'Minimize uploader',\n close: 'Close',\n editPrefix: 'Edit - {title}',\n uploadImage: 'Upload Image',\n uploadImages: 'Upload Images',\n noValidImageFiles:\n 'No valid image files found. Please select image files (JPEG, PNG, WebP, AVIF, SVG).',\n selectedImagesSkipped:\n '{imageCount} image{imageSuffix} selected. {skippedCount} non-image file{skippedSuffix} skipped.',\n queued: 'Queued...',\n retrying: 'Retrying...',\n preparingUpload: 'Preparing upload...',\n uploadFailed: 'Upload failed',\n failedCreatePreview: 'Failed to create preview',\n failedCreatePreviewFor: 'Failed to create preview for {file}',\n selectAll: 'Select All',\n deselectAll: 'Deselect All',\n removeSelected: 'Remove Selected',\n uploading: 'Uploading',\n uploadingCount: 'Uploading {count} image{suffix}',\n someUploadsFailed: 'Some uploads failed.',\n retryFailedUploads: 'Retry Failed Uploads',\n needsAltBeforeSave:\n '{count} image{suffix} need{verbSuffix} alt text before saving. Click the description button on each image.',\n networkError: 'Network error during image upload',\n invalidUploadResponse: 'Invalid JSON response from upload endpoint',\n uploadAborted: 'Upload cancelled',\n uploadStartedLive: 'Upload started',\n uploadProgressLive: 'Upload {progress}% complete',\n uploadCompleteLive: 'Upload complete',\n qualityTitle: 'Compression Quality',\n qualityLevelAria: 'Quality level',\n qualitySaved: 'saved',\n qualityRecommended: '⭐ Recommended',\n selectFilesAria: 'Select files',\n selectMoreFilesAria: 'Select more files',\n selectFolderAria: 'Select folder',\n dropImagesHere: 'Drop images here',\n releaseToUpload: 'Release to upload',\n dragDropOrChoose: 'Drag and drop images here, or choose an option below',\n selectFiles: 'Select Files',\n selectFolder: 'Select Folder',\n supportsFormats: 'Supports: JPEG, PNG, WebP, AVIF, SVG',\n maxPerFile: 'Max {size}MB per file',\n upToSizeMB: 'Up to {size} MB',\n dropHere: 'Drop here',\n addMore: 'Add More',\n files: 'Files',\n folder: 'Folder',\n withoutAltText: '{count} without alt text',\n saveImages: 'Save Images',\n addAltBeforeSaving:\n 'Please add alt text before saving. Click the description button on the image.',\n missingDescription: 'Missing description',\n saveImage: 'Save Image',\n downloadImage: 'Download Image',\n dragDropOrClickToUpload: 'Drag & drop or click to upload',\n browseFiles: 'Browse Files',\n image: 'Image',\n processing: 'Processing...',\n uploadImageAria: 'Upload image',\n fileInputAria: 'File input',\n uploadingAlt: 'Uploading',\n previewAlt: 'Preview',\n addImageDescription: 'Add Image Description',\n describeImagePlaceholder: 'Describe this image for accessibility...',\n characters: 'characters',\n usedForA11ySeo: 'Used for accessibility & SEO',\n cancel: 'Cancel',\n saveDescription: 'Save Description',\n selectImageAria: 'Select image',\n editAltTextAria: 'Edit alt text',\n addAltTextAria: 'Add alt text',\n addDescriptionForA11y: 'Add description for accessibility',\n adjustBlurColorAria: 'Adjust blur and color',\n adjustBlurColorTitle: 'Visualize blur data and adjust colors',\n removeImageAria: 'Remove image',\n pasteHint: 'or paste from clipboard',\n cancelUpload: 'Cancel upload',\n retryUpload: 'Retry upload',\n minImagesError: 'Please upload at least {min} image{suffix}.',\n requiredFieldError: 'This field is required.',\n blurModal: {\n title: 'Blur & Dominant Color Inspector',\n closeModal: 'Close inspector',\n adjustColor: 'Dominant Backdrop Color',\n clickToSample: 'Click anywhere on the image preview to sample a pixel color',\n pickColorNote:\n 'Use the picker, enter a HEX code, or sample directly from the image',\n enterHexColor: 'Enter HEX color',\n hexPlaceholder: 'F5F5F5',\n enablePenMode: 'Eyedropper Sample',\n disablePenMode: 'Exit Eyedropper',\n penModeActive: 'Sampling Active',\n resetToDetected: 'Reset to Detected',\n sampled: 'Sampled',\n livePreview: 'Live Placeholder & Image Preview',\n showingOriginal: 'Showing original image (blur placeholder not generated yet)',\n saved: 'Size Saved:',\n accessibility: 'Accessibility & SEO',\n altText: 'Image Alt Text',\n altTextPlaceholder:\n 'Describe this image for screen readers and search engines...',\n saveAndClose: 'Save & Close',\n },\n};\n","import type { UploadLocale } from '../types';\nimport { arTranslations } from './i18n/ar';\nimport { enTranslations } from './i18n/en';\nimport type {\n BlurModalTranslations,\n UploadTranslations,\n} from './i18n/types';\n\nexport { arTranslations, enTranslations };\nexport const DEFAULT_UPLOAD_TRANSLATIONS = enTranslations;\nexport const ARABIC_UPLOAD_TRANSLATIONS = arTranslations;\nexport type { BlurModalTranslations, UploadLocale, UploadTranslations };\n\nexport function normalizeUploadLocale(locale?: string): UploadLocale {\n if (locale) {\n return locale.toLowerCase().startsWith('ar') ? 'ar' : 'en';\n }\n if (typeof document !== 'undefined') {\n const docLang =\n document.documentElement.getAttribute('lang')?.toLowerCase() || '';\n const docDir =\n document.documentElement.getAttribute('dir')?.toLowerCase() || '';\n if (docLang.startsWith('ar') || docDir === 'rtl') {\n return 'ar';\n }\n }\n return 'en';\n}\n\nexport function selectPluralCategory(\n locale: UploadLocale,\n count: number,\n): Intl.LDMLPluralRule {\n try {\n return new Intl.PluralRules(locale).select(count);\n } catch {\n return count === 1 ? 'one' : 'other';\n }\n}\n\nexport function formatUploadingCount(\n ui: UploadTranslations,\n count: number,\n locale: UploadLocale,\n overrides?: Partial<UploadTranslations>,\n): string {\n if (\n overrides &&\n !overrides.uploadingCount &&\n (overrides.uploading || overrides.uploadImage || overrides.uploadImages)\n ) {\n const isOne = selectPluralCategory(locale, count) === 'one';\n return `${ui.uploading} ${count} ${isOne ? ui.uploadImage : ui.uploadImages}`;\n }\n const suffix = selectPluralCategory(locale, count) === 'one' ? '' : 's';\n return ui.uploadingCount\n .replace('{count}', String(count))\n .replace('{suffix}', suffix);\n}\n\nexport function formatNeedsAltMessage(\n ui: UploadTranslations,\n count: number,\n locale: UploadLocale,\n): string {\n const isOne = selectPluralCategory(locale, count) === 'one';\n return ui.needsAltBeforeSave\n .replace('{count}', String(count))\n .replace('{suffix}', isOne ? '' : 's')\n .replace('{verbSuffix}', isOne ? 's' : '');\n}\n\nexport function formatSkippedImagesMessage(\n ui: UploadTranslations,\n imageCount: number,\n skippedCount: number,\n locale: UploadLocale,\n): string {\n const imgSuffix =\n selectPluralCategory(locale, imageCount) === 'one' ? '' : 's';\n const skipSuffix =\n selectPluralCategory(locale, skippedCount) === 'one' ? '' : 's';\n return ui.selectedImagesSkipped\n .replace('{imageCount}', String(imageCount))\n .replace('{imageSuffix}', imgSuffix)\n .replace('{skippedCount}', String(skippedCount))\n .replace('{skippedSuffix}', skipSuffix);\n}\n\nexport const uploadText: Record<UploadLocale, UploadTranslations> = {\n en: enTranslations,\n ar: arTranslations,\n};\n\nexport function resolveTranslations(\n locale?: UploadLocale,\n overrides?: Partial<UploadTranslations>,\n): UploadTranslations {\n const base = uploadText[normalizeUploadLocale(locale)];\n if (!overrides) return base;\n return {\n ...base,\n ...overrides,\n blurModal: {\n ...base.blurModal,\n ...(overrides.blurModal || {}),\n },\n };\n}\n","import type { ImageOutputFormat } from '../../types';\n\nexport interface SessionMetaLookup {\n url?: string | null;\n contentHash?: string | null;\n}\n\nexport interface CachedBlurMeta {\n blurDataUrl?: string;\n blurColor?: string;\n}\n\nexport interface UploadSessionCache {\n writeAltMetaFor: (info: SessionMetaLookup, alt: string) => void;\n readAltMetaFor: (info: SessionMetaLookup) => string | null;\n writeBlurMetaFor: (info: SessionMetaLookup, meta: CachedBlurMeta) => void;\n readBlurMetaFor: (info: SessionMetaLookup) => CachedBlurMeta | null;\n}\n\nconst SIGNED_URL_QUERY_RE =\n /[?&](?:X-Amz-Signature|X-Amz-Credential|X-Amz-Algorithm|X-Goog-Signature|Signature|AWSAccessKeyId|GoogleAccessId|token|sig|sv|se)=/i;\n\nexport function isSignedStorageUrl(url: string): boolean {\n return SIGNED_URL_QUERY_RE.test(url);\n}\n\nexport function appendCacheBuster(\n url: string,\n options: { enabled?: boolean; token?: string | number } = {},\n): string {\n if (!url || !options.enabled) return url;\n if (/^(?:blob:|data:)/i.test(url) || isSignedStorageUrl(url)) return url;\n if (/[?&](?:v|t)=/i.test(url)) return url;\n const buster = options.token ?? Date.now();\n return `${url}${url.includes('?') ? '&' : '?'}t=${buster}`;\n}\n\nexport function readSessionJson<T>(key: string): T | null {\n if (typeof window === 'undefined') return null;\n try {\n const raw = window.sessionStorage.getItem(key);\n return raw ? (JSON.parse(raw) as T) : null;\n } catch {\n return null;\n }\n}\n\nexport function writeSessionJson(key: string, value: unknown): void {\n if (typeof window === 'undefined') return;\n try {\n window.sessionStorage.setItem(key, JSON.stringify(value));\n } catch {\n // Ignore storage quota errors\n }\n}\n\nexport function createUploadSessionCache(\n storagePrefix = 'iuk',\n): UploadSessionCache {\n const altKey = (id: string) => `${storagePrefix}:alt-meta:${id}`;\n const blurKey = (id: string) => `${storagePrefix}:blur-meta:${id}`;\n\n const writeAltMetaFor = (info: SessionMetaLookup, alt: string): void => {\n if (!alt?.trim()) return;\n if (info.url) writeSessionJson(altKey(info.url), alt);\n if (info.contentHash) writeSessionJson(altKey(info.contentHash), alt);\n };\n\n const readAltMetaFor = (info: SessionMetaLookup): string | null => {\n if (info.contentHash) {\n const byHash = readSessionJson<string>(altKey(info.contentHash));\n if (byHash) return byHash;\n }\n if (info.url) {\n const byUrl = readSessionJson<string>(altKey(info.url));\n if (byUrl) return byUrl;\n }\n return null;\n };\n\n const writeBlurMetaFor = (\n info: SessionMetaLookup,\n meta: CachedBlurMeta,\n ): void => {\n if (info.url) writeSessionJson(blurKey(info.url), meta);\n if (info.contentHash) writeSessionJson(blurKey(info.contentHash), meta);\n };\n\n const readBlurMetaFor = (info: SessionMetaLookup): CachedBlurMeta | null => {\n if (info.contentHash) {\n const byHash = readSessionJson<CachedBlurMeta>(blurKey(info.contentHash));\n if (byHash?.blurDataUrl || byHash?.blurColor) return byHash;\n }\n if (info.url) {\n const byUrl = readSessionJson<CachedBlurMeta>(blurKey(info.url));\n if (byUrl?.blurDataUrl || byUrl?.blurColor) return byUrl;\n }\n return null;\n };\n\n return {\n writeAltMetaFor,\n readAltMetaFor,\n writeBlurMetaFor,\n readBlurMetaFor,\n };\n}\n\nconst MIME_TO_EXT: Record<string, ImageOutputFormat | 'jpg'> = {\n 'image/avif': 'avif',\n 'image/webp': 'webp',\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/svg+xml': 'svg',\n};\n\nexport async function downloadImageWithFallback(\n url: string,\n format?: ImageOutputFormat,\n): Promise<void> {\n if (!url || typeof window === 'undefined') return;\n try {\n const fetchUrl = isSignedStorageUrl(url)\n ? url\n : appendCacheBuster(url, { enabled: true });\n const res = await fetch(fetchUrl, { cache: 'no-store' });\n if (!res.ok) {\n throw new Error(`Download failed with status ${res.status}`);\n }\n const blob = await res.blob();\n const objectUrl = URL.createObjectURL(blob);\n\n const ext: ImageOutputFormat | 'jpg' =\n format ?? MIME_TO_EXT[blob.type] ?? 'avif';\n\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.download = `image-${Date.now()}.${ext}`;\n anchor.style.display = 'none';\n document.body?.appendChild(anchor);\n anchor.click();\n anchor.remove();\n setTimeout(() => {\n try {\n URL.revokeObjectURL(objectUrl);\n } catch {\n // Ignore revocation errors\n }\n }, 1000);\n } catch {\n window.open(url, '_blank', 'noopener,noreferrer');\n }\n}\n","import type {\n QualityLevel,\n UploadedImageData,\n UploadPresetConfig,\n} from '../../types';\nimport { DEFAULT_BLUR_COLOR } from '../../utils/color';\n\nexport function resolveInitialQualityLevel(\n quality?: number,\n preset?: UploadPresetConfig,\n): QualityLevel {\n const q = quality ?? preset?.quality;\n if (!q) return preset?.recommendedQualityLevel || 'high';\n if (q <= 55) return 'low';\n if (q <= 78) return 'medium';\n return q <= 90 ? 'high' : 'maximum';\n}\n\nexport function resolveLiveAnnouncement(\n isUploading: boolean,\n progress: number,\n hasCompletedUpload: boolean,\n ui: {\n uploadStartedLive: string;\n uploadProgressLive: string;\n uploadCompleteLive: string;\n },\n): string {\n if (isUploading) {\n if (progress >= 75) return ui.uploadProgressLive.replace('{progress}', '75');\n if (progress >= 50) return ui.uploadProgressLive.replace('{progress}', '50');\n if (progress >= 25) return ui.uploadProgressLive.replace('{progress}', '25');\n return ui.uploadStartedLive;\n }\n return hasCompletedUpload ? ui.uploadCompleteLive : '';\n}\n\nexport function stripUrlCacheQuery(u: string | null): string | null {\n return u && !/^(?:blob:|data:)/i.test(u) ? u.split('?')[0] : u;\n}\n\nexport function buildSingleImagePayload(\n params: {\n uploadedData: UploadedImageData | null;\n objVal: UploadedImageData | null;\n value?: string | UploadedImageData | null;\n preview: string | null;\n altText: string;\n blurColor: string;\n blurDataUrlState: string;\n },\n overrides: Partial<UploadedImageData> = {},\n): UploadedImageData | null {\n const {\n uploadedData,\n objVal,\n value,\n preview,\n altText,\n blurColor,\n blurDataUrlState,\n } = params;\n const base: UploadedImageData | null = uploadedData\n ? { ...uploadedData }\n : objVal\n ? { ...objVal }\n : typeof value === 'string' && value.trim()\n ? {\n url: value,\n blurDataUrl: '',\n blurColor: DEFAULT_BLUR_COLOR,\n altText: '',\n width: 0,\n height: 0,\n format: 'webp',\n size: 0,\n contentHash: 'existing-image',\n uploadedAt: new Date().toISOString(),\n }\n : null;\n if (!base) return null;\n return {\n ...base,\n ...overrides,\n url: (overrides.url ?? base.url) || preview || '',\n altText: overrides.altText ?? altText ?? base.altText ?? '',\n blurColor:\n overrides.blurColor ?? blurColor ?? base.blurColor ?? DEFAULT_BLUR_COLOR,\n blurDataUrl:\n overrides.blurDataUrl ?? base.blurDataUrl ?? blurDataUrlState ?? '',\n };\n}\n\nexport function serializeSingleFormValue(params: {\n formValue?: 'url' | 'json';\n currentImageData: UploadedImageData | null;\n preview: string | null;\n altText: string;\n blurColor: string;\n blurDataUrlState: string;\n isUploading: boolean;\n}): string {\n const {\n formValue = 'url',\n currentImageData,\n preview,\n altText,\n blurColor,\n blurDataUrlState,\n isUploading,\n } = params;\n if (!preview || (isUploading && !currentImageData)) return '';\n const nonBlobPreview =\n preview && !preview.startsWith('blob:') ? preview : null;\n const resolvedUrl = currentImageData?.url || nonBlobPreview || '';\n if (!resolvedUrl) return '';\n if (formValue === 'json') {\n const payload = currentImageData\n ? {\n ...currentImageData,\n altText: altText || currentImageData.altText || '',\n blurColor: blurColor || currentImageData.blurColor || DEFAULT_BLUR_COLOR,\n }\n : {\n url: resolvedUrl,\n altText,\n blurColor: blurColor || DEFAULT_BLUR_COLOR,\n blurDataUrl: blurDataUrlState,\n };\n return JSON.stringify(payload);\n }\n return resolvedUrl;\n}\n","import { useCallback, useEffect, useRef } from 'react';\n\nexport interface UseFilePreviewOptions {\n errorLabel?: string;\n}\n\nexport function useFilePreview(options: UseFilePreviewOptions = {}) {\n const objectUrlsRef = useRef<Set<string>>(new Set());\n const errorLabel = options.errorLabel || 'Failed to read file';\n\n const revokePreview = useCallback((url?: string | null) => {\n if (!url || !url.startsWith('blob:')) return;\n objectUrlsRef.current.delete(url);\n if (typeof URL !== 'undefined' && typeof URL.revokeObjectURL === 'function') {\n try {\n URL.revokeObjectURL(url);\n } catch {\n // Ignore revocation errors\n }\n }\n }, []);\n\n const revokeAllPreviews = useCallback(() => {\n if (typeof URL !== 'undefined' && typeof URL.revokeObjectURL === 'function') {\n for (const url of objectUrlsRef.current) {\n try {\n URL.revokeObjectURL(url);\n } catch {\n // Ignore revocation errors\n }\n }\n }\n objectUrlsRef.current.clear();\n }, []);\n\n useEffect(() => {\n return () => {\n revokeAllPreviews();\n };\n }, [revokeAllPreviews]);\n\n const createPreview = useCallback(\n (file: File): Promise<string> => {\n if (\n typeof URL !== 'undefined' &&\n typeof URL.createObjectURL === 'function'\n ) {\n try {\n const objectUrl = URL.createObjectURL(file);\n objectUrlsRef.current.add(objectUrl);\n return Promise.resolve(objectUrl);\n } catch {\n // Fall through to FileReader\n }\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n } else {\n reject(new Error(errorLabel));\n }\n };\n reader.onerror = () => {\n reject(new Error(errorLabel));\n };\n reader.readAsDataURL(file);\n });\n },\n [errorLabel],\n );\n\n return { createPreview, revokePreview, revokeAllPreviews };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport type { ExplicitOutputFormat, ImageDimensions, UploadedImageData, UploadLocale } from '../../types';\nimport { createSuccessMessage, getStatusMessage } from '../../utils/formatters';\nimport { useImageUploadContext } from '../context';\nimport { normalizeUploadLocale, resolveTranslations } from '../i18n';\nimport type { CustomUploaderFn } from '../types';\nimport { createUploadTracker, type UploadTracker } from './uploadTracker';\nimport { buildUploadFormData, isUploadAbortError, uploadViaXhr } from './xhrUpload';\n\nexport interface UploadCallOptions {\n uploadId?: string;\n signal?: AbortSignal;\n}\n\nexport interface UseImageUploadOptions {\n preset?: string;\n folder?: string;\n bucket?: string;\n metadata?: Record<string, string>;\n altText?: string;\n outputFormat?: ExplicitOutputFormat;\n /** 1–100. When omitted, the server picks the preset or format default. */\n quality?: number;\n maxDimensions?: ImageDimensions;\n customFilename?: string;\n endpoint?: string;\n uploader?: CustomUploaderFn;\n headers?: Record<string, string>;\n withCredentials?: boolean;\n locale?: UploadLocale;\n onSuccess?: (data: UploadedImageData, file?: File, uploadId?: string) => void;\n onError?: (error: string, file?: File, uploadId?: string) => void;\n onProgress?: (progress: number, message: string, file?: File, uploadId?: string) => void;\n}\n\nexport function useImageUpload({\n preset, folder, bucket, metadata, altText, outputFormat, quality, maxDimensions,\n customFilename: defaultCustomFilename,\n endpoint: propEndpoint, uploader: propUploader, headers: propHeaders,\n withCredentials: propWithCredentials, locale: propLocale,\n onSuccess, onError, onProgress,\n}: UseImageUploadOptions = {}) {\n const ctx = useImageUploadContext();\n const uploadLocale = normalizeUploadLocale(propLocale || ctx.locale);\n const ui = resolveTranslations(uploadLocale, ctx.translations);\n\n const resolvedEndpoint = propEndpoint || ctx.endpoint || '/api/upload';\n const resolvedUploader = propUploader || ctx.uploader;\n const resolvedHeaders = propHeaders || ctx.headers;\n const resolvedCredentials = propWithCredentials ?? ctx.withCredentials;\n\n const [isUploading, setIsUploading] = useState(false);\n const [progress, setProgress] = useState(0);\n const [uploadMessage, setUploadMessage] = useState('');\n\n const isMountedRef = useRef(true);\n const timeoutsRef = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());\n const trackerRef = useRef<UploadTracker | null>(null);\n if (!trackerRef.current) trackerRef.current = createUploadTracker();\n\n const clearAllTimeouts = useCallback(() => {\n timeoutsRef.current.forEach((t) => clearTimeout(t));\n timeoutsRef.current.clear();\n }, []);\n\n const safeSetTimeout = useCallback((fn: () => void, ms: number) => {\n const handle = setTimeout(() => {\n timeoutsRef.current.delete(handle);\n if (isMountedRef.current) fn();\n }, ms);\n timeoutsRef.current.add(handle);\n return handle;\n }, []);\n\n useEffect(() => {\n isMountedRef.current = true;\n const tracker = trackerRef.current;\n return () => {\n isMountedRef.current = false;\n tracker?.abortAll();\n clearAllTimeouts();\n };\n }, [clearAllTimeouts]);\n\n const abortUpload = useCallback((idOrFile: string | File) => {\n const tracker = trackerRef.current!;\n const aborted = tracker.abortByIdOrFile(idOrFile);\n if (aborted && isMountedRef.current && tracker.entries.size === 0) {\n setIsUploading(false);\n setProgress(0);\n setUploadMessage('');\n }\n return aborted;\n }, []);\n\n const abortAll = useCallback(() => {\n trackerRef.current?.abortAll();\n clearAllTimeouts();\n if (isMountedRef.current) {\n setIsUploading(false);\n setProgress(0);\n setUploadMessage('');\n }\n }, [clearAllTimeouts]);\n\n const uploadImage = useCallback(\n async (\n file: File,\n customAltText?: string,\n overrideCustomFilename?: string,\n callOptions?: UploadCallOptions,\n ): Promise<UploadedImageData | null> => {\n const tracker = trackerRef.current!;\n const entry = tracker.createEntry(\n file,\n ui.preparingUpload,\n callOptions?.uploadId,\n callOptions?.signal,\n );\n const { id: uploadId, controller } = entry;\n\n const originalSize = file.size;\n setIsUploading(true);\n setProgress(0);\n setUploadMessage(ui.preparingUpload);\n onProgress?.(0, ui.preparingUpload, file, uploadId);\n\n const updateEntryProgress = (nextPct: number, maxCap: number) => {\n if (!isMountedRef.current || controller.signal.aborted) return;\n const clamped = Math.max(entry.progress, Math.min(maxCap, nextPct));\n entry.progress = clamped;\n const msg = getStatusMessage(clamped, uploadLocale);\n entry.message = msg;\n setProgress(clamped);\n setUploadMessage(msg);\n onProgress?.(clamped, msg, file, uploadId);\n };\n\n const reportProgress = (nextPct: number) => updateEntryProgress(nextPct, 94);\n\n try {\n entry.interval = setInterval(() => {\n if (!isMountedRef.current || controller.signal.aborted) {\n tracker.removeEntry(uploadId);\n return;\n }\n updateEntryProgress(entry.progress + 6, 92);\n }, 300);\n\n const finalAlt = customAltText !== undefined ? customAltText || undefined : altText || undefined;\n const finalCustomFilename = overrideCustomFilename || defaultCustomFilename || undefined;\n const requestFields = {\n preset, folder, bucket, altText: finalAlt, customFilename: finalCustomFilename,\n outputFormat, quality, maxDimensions, metadata,\n };\n\n const data = resolvedUploader\n ? await resolvedUploader(file, {\n ...requestFields,\n signal: controller.signal,\n onUploadProgress: reportProgress,\n })\n : await uploadViaXhr(resolvedEndpoint, buildUploadFormData(file, requestFields), {\n headers: resolvedHeaders,\n withCredentials: resolvedCredentials,\n signal: controller.signal,\n fallbackErrorLabel: ui.uploadFailed,\n networkErrorLabel: ui.networkError,\n invalidResponseLabel: ui.invalidUploadResponse,\n abortedErrorLabel: ui.uploadAborted,\n onUploadProgress: reportProgress,\n });\n\n tracker.removeEntry(uploadId);\n if (!isMountedRef.current || controller.signal.aborted) return null;\n\n entry.progress = 100;\n setProgress(100);\n const successMsg = createSuccessMessage(originalSize, data.size, false, uploadLocale);\n setUploadMessage(successMsg);\n onProgress?.(100, successMsg, file, uploadId);\n\n safeSetTimeout(() => {\n if (tracker.entries.size === 0) setUploadMessage('');\n }, 5000);\n\n onSuccess?.(data, file, uploadId);\n return data;\n } catch (err) {\n tracker.removeEntry(uploadId);\n if (!isMountedRef.current || controller.signal.aborted || isUploadAbortError(err)) {\n return null;\n }\n const errorMsg = err instanceof Error ? err.message : ui.uploadFailed;\n setUploadMessage('');\n onError?.(errorMsg, file, uploadId);\n return null;\n } finally {\n tracker.removeEntry(uploadId);\n if (isMountedRef.current && tracker.entries.size === 0) {\n setIsUploading(false);\n safeSetTimeout(() => {\n if (tracker.entries.size === 0) setProgress(0);\n }, 1000);\n }\n }\n },\n [\n preset, folder, bucket, metadata, altText, outputFormat, quality, maxDimensions,\n defaultCustomFilename, resolvedEndpoint, resolvedUploader, resolvedHeaders,\n resolvedCredentials, uploadLocale, ui.preparingUpload, ui.uploadFailed,\n ui.networkError, ui.invalidUploadResponse, ui.uploadAborted,\n onSuccess, onError, onProgress, safeSetTimeout,\n ],\n );\n\n const reset = useCallback(() => {\n abortAll();\n }, [abortAll]);\n\n return { uploadImage, abortUpload, abortAll, isUploading, progress, uploadMessage, reset };\n}\n","export interface ActiveUploadEntry {\n id: string;\n file: File;\n progress: number;\n message: string;\n controller: AbortController;\n interval: ReturnType<typeof setInterval> | null;\n}\n\nexport interface UploadTracker {\n entries: Map<string, ActiveUploadEntry>;\n createEntry: (\n file: File,\n initialMessage: string,\n customId?: string,\n externalSignal?: AbortSignal,\n ) => ActiveUploadEntry;\n removeEntry: (id: string) => void;\n abortByIdOrFile: (idOrFile: string | File) => boolean;\n abortAll: () => void;\n}\n\nlet uploadCounter = 0;\n\nexport function createUploadTracker(): UploadTracker {\n const entries = new Map<string, ActiveUploadEntry>();\n\n const clearEntryInterval = (entry: ActiveUploadEntry) => {\n if (entry.interval) {\n clearInterval(entry.interval);\n entry.interval = null;\n }\n };\n\n const createEntry = (\n file: File,\n initialMessage: string,\n customId?: string,\n externalSignal?: AbortSignal,\n ): ActiveUploadEntry => {\n uploadCounter += 1;\n const id = customId || `upload-${Date.now()}-${uploadCounter}`;\n const controller = new AbortController();\n\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort();\n } else {\n externalSignal.addEventListener('abort', () => controller.abort(), {\n once: true,\n });\n }\n }\n\n const existing = entries.get(id);\n if (existing) {\n clearEntryInterval(existing);\n existing.controller.abort();\n entries.delete(id);\n }\n\n const entry: ActiveUploadEntry = {\n id,\n file,\n progress: 0,\n message: initialMessage,\n controller,\n interval: null,\n };\n entries.set(id, entry);\n return entry;\n };\n\n const removeEntry = (id: string) => {\n const entry = entries.get(id);\n if (entry) {\n clearEntryInterval(entry);\n entries.delete(id);\n }\n };\n\n const abortByIdOrFile = (idOrFile: string | File): boolean => {\n let aborted = false;\n for (const [key, entry] of entries.entries()) {\n if (\n (typeof idOrFile === 'string' && key === idOrFile) ||\n entry.file === idOrFile\n ) {\n clearEntryInterval(entry);\n entry.controller.abort();\n entries.delete(key);\n aborted = true;\n }\n }\n return aborted;\n };\n\n const abortAll = () => {\n for (const entry of entries.values()) {\n clearEntryInterval(entry);\n entry.controller.abort();\n }\n entries.clear();\n };\n\n return {\n entries,\n createEntry,\n removeEntry,\n abortByIdOrFile,\n abortAll,\n };\n}\n","import type {\n ExplicitOutputFormat,\n ImageDimensions,\n UploadedImageData,\n} from '../../types';\n\nexport interface XhrUploadOptions {\n headers?: Record<string, string>;\n withCredentials?: boolean;\n signal?: AbortSignal;\n fallbackErrorLabel?: string;\n networkErrorLabel?: string;\n invalidResponseLabel?: string;\n abortedErrorLabel?: string;\n onUploadProgress?: (percent: number) => void;\n}\n\nexport interface UploadFormFields {\n preset?: string;\n folder?: string;\n bucket?: string;\n altText?: string;\n customFilename?: string;\n outputFormat?: ExplicitOutputFormat;\n quality?: number;\n maxDimensions?: ImageDimensions;\n metadata?: Record<string, string>;\n}\n\nconst MAX_ERROR_BODY_LENGTH = 160;\n\nfunction sanitizeRawErrorBody(rawBody: string): string {\n const trimmed = rawBody.trim();\n if (!trimmed) return '';\n\n const looksLikeHtml =\n /<(?:!doctype|html|head|body|title|h[1-6]|p|div|span|center|hr)\\b/i.test(\n trimmed,\n );\n\n let cleaned = trimmed;\n if (looksLikeHtml) {\n const titleMatch = trimmed.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n const h1Match = trimmed.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const extracted = (h1Match?.[1] || titleMatch?.[1] || trimmed)\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n cleaned = extracted;\n }\n\n if (cleaned.length > MAX_ERROR_BODY_LENGTH) {\n return `${cleaned.slice(0, MAX_ERROR_BODY_LENGTH).trimEnd()}…`;\n }\n return cleaned;\n}\n\nexport function createUploadAbortError(message = 'Upload cancelled'): Error {\n const err = new Error(message);\n err.name = 'AbortError';\n return err;\n}\n\nexport function isUploadAbortError(err: unknown): boolean {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'name' in err &&\n (err as { name?: unknown }).name === 'AbortError'\n );\n}\n\nexport function parseErrorPayload(\n status: number,\n rawBody: string,\n fallbackLabel = 'Upload failed',\n): string {\n const statusInfo = `status ${status}`;\n let parsed: Record<string, unknown> | undefined;\n try {\n const json = JSON.parse(rawBody);\n if (json && typeof json === 'object') {\n parsed = json as Record<string, unknown>;\n }\n } catch {\n parsed = undefined;\n }\n\n const candidateKeys = ['error', 'message', 'detail', 'details'] as const;\n if (parsed) {\n for (const key of candidateKeys) {\n const val = parsed[key];\n if (typeof val === 'string' && val.trim()) {\n return `${val.trim()} (${statusInfo})`;\n }\n }\n }\n\n const cleanedBody = rawBody ? sanitizeRawErrorBody(rawBody) : '';\n if (cleanedBody) {\n return `${cleanedBody} (${statusInfo})`;\n }\n\n return `${fallbackLabel} (${statusInfo})`;\n}\n\nexport function buildUploadFormData(\n file: File | Blob,\n fields: UploadFormFields = {},\n): FormData {\n const {\n preset,\n folder,\n bucket,\n altText,\n customFilename,\n outputFormat,\n quality,\n maxDimensions,\n metadata,\n } = fields;\n\n const formData = new FormData();\n formData.append('file', file);\n if (preset) formData.append('preset', preset);\n if (folder) formData.append('folder', folder);\n if (bucket) formData.append('bucket', bucket);\n if (altText) formData.append('altText', altText);\n if (customFilename) formData.append('customFilename', customFilename);\n if (outputFormat) formData.append('outputFormat', outputFormat);\n if (quality !== undefined) formData.append('quality', String(quality));\n if (maxDimensions) {\n formData.append('maxWidth', String(maxDimensions.width));\n formData.append('maxHeight', String(maxDimensions.height));\n }\n if (metadata && Object.keys(metadata).length > 0) {\n formData.append('metadata', JSON.stringify(metadata));\n }\n\n return formData;\n}\n\nexport function uploadViaXhr(\n endpoint: string,\n formData: FormData,\n options: XhrUploadOptions = {},\n): Promise<UploadedImageData> {\n const fallbackLabel = options.fallbackErrorLabel || 'Upload failed';\n const networkLabel =\n options.networkErrorLabel || 'Network error during image upload';\n const invalidJsonLabel =\n options.invalidResponseLabel ||\n 'Invalid JSON response from upload endpoint';\n const abortedLabel = options.abortedErrorLabel || 'Upload cancelled';\n\n if (options.signal?.aborted) {\n return Promise.reject(createUploadAbortError(abortedLabel));\n }\n\n if (typeof XMLHttpRequest === 'undefined') {\n return fetch(endpoint, {\n method: 'POST',\n body: formData,\n headers: options.headers,\n credentials: options.withCredentials ? 'include' : 'same-origin',\n signal: options.signal,\n }).then(async (res) => {\n const text = await res.text();\n if (!res.ok) {\n throw new Error(parseErrorPayload(res.status, text, fallbackLabel));\n }\n try {\n return JSON.parse(text) as UploadedImageData;\n } catch {\n throw new Error(invalidJsonLabel);\n }\n });\n }\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('POST', endpoint, true);\n xhr.withCredentials = Boolean(options.withCredentials);\n\n if (options.headers) {\n for (const [key, value] of Object.entries(options.headers)) {\n xhr.setRequestHeader(key, value);\n }\n }\n\n const onSignalAbort = () => {\n xhr.abort();\n };\n options.signal?.addEventListener('abort', onSignalAbort, { once: true });\n\n const cleanup = () => {\n options.signal?.removeEventListener('abort', onSignalAbort);\n };\n\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable && event.total > 0) {\n const pct = Math.min(\n 85,\n Math.round((event.loaded / event.total) * 85),\n );\n options.onUploadProgress?.(pct);\n }\n };\n\n xhr.onload = () => {\n cleanup();\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const parsed = JSON.parse(xhr.responseText) as UploadedImageData;\n resolve(parsed);\n } catch {\n reject(new Error(invalidJsonLabel));\n }\n } else {\n reject(\n new Error(\n parseErrorPayload(xhr.status, xhr.responseText, fallbackLabel),\n ),\n );\n }\n };\n\n xhr.onerror = () => {\n cleanup();\n reject(new Error(networkLabel));\n };\n\n xhr.onabort = () => {\n cleanup();\n reject(createUploadAbortError(abortedLabel));\n };\n\n xhr.send(formData);\n });\n}\n","import type { CSSProperties } from 'react';\nimport type { UploadVisualState } from '../styling';\n\n/**\n * Inline style applied to `<input type=\"file\">` when `unstyled` mode is active\n * so hidden file inputs remain invisible even when `image-upload-kit/styles.css` is not imported.\n */\nexport const HIDDEN_INPUT_STYLE: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\n/**\n * Joins truthy CSS class names cleanly.\n */\nexport function cx(\n ...classes: Array<string | false | null | undefined>\n): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Resolves a component slot's final `className`:\n * - When `unstyled` is `false` (default): includes `defaultClass` followed by provider & prop overrides.\n * - When `unstyled` is `true`: strips `defaultClass` (`iuk-*`) and applies only user-supplied classes.\n */\nexport function resolveSlotClass(\n defaultClass: string | undefined,\n unstyled: boolean | undefined,\n ...customClasses: Array<string | false | null | undefined>\n): string | undefined {\n const merged = unstyled\n ? cx(...customClasses)\n : cx(defaultClass, ...customClasses);\n return merged || undefined;\n}\n\n/**\n * Merges multiple optional inline `CSSProperties` objects for a slot.\n */\nexport function resolveSlotStyle(\n ...styles: Array<CSSProperties | undefined>\n): CSSProperties | undefined {\n let result: CSSProperties | undefined;\n for (const style of styles) {\n if (style) {\n result = result ? { ...result, ...style } : { ...style };\n }\n }\n return result;\n}\n\n/**\n * Computes a deterministic semantic state string for `data-state=\"...\"` styling\n * (e.g. Tailwind `data-[state=dragging]:border-indigo-500` or CSS `[data-state=\"uploading\"]`).\n */\nexport function resolveUploadDataState(flags: {\n disabled?: boolean;\n hasError?: boolean;\n isUploading?: boolean;\n isDragging?: boolean;\n hasContent?: boolean;\n}): UploadVisualState {\n if (flags.disabled) return 'disabled';\n if (flags.isDragging) return 'dragging';\n if (flags.isUploading) return 'uploading';\n if (flags.hasError) return 'error';\n if (flags.hasContent) return 'ready';\n return 'idle';\n}\n","import type { HTMLAttributes, InputHTMLAttributes } from 'react';\nimport type { UploadPresetName } from '../../types';\nimport { useClipboardPaste } from '../hooks/useClipboardPaste';\nimport { useDragAndDrop } from '../hooks/useDragAndDrop';\nimport { useSingleImageState } from '../hooks/useSingleImageState';\nimport { HIDDEN_INPUT_STYLE, resolveUploadDataState } from '../utils/slotStyles';\nimport type {\n HeadlessDropzoneProps,\n HeadlessFileInputProps,\n SingleUploadController,\n UseSingleUploadOptions,\n} from './types';\n\nconst DEFAULT_ACCEPT = 'image/jpeg,image/png,image/webp,image/avif,image/svg+xml';\n\nexport function useSingleUpload<TPreset extends string = UploadPresetName>(\n options: UseSingleUploadOptions<TPreset>,\n): SingleUploadController {\n const {\n accept = DEFAULT_ACCEPT,\n disabled = false,\n error: externalError,\n capture,\n enablePaste = true,\n pasteTarget = 'dropzone',\n required = false,\n onBlur,\n } = options;\n\n const {\n locale, ui, maxSizeMB, fileInputRef, preview, altText, blurColor,\n detectedBlurColor, blurDataUrlState, validationError, sizeInfo,\n uploadedData, qualityLevel, setQualityLevel, isUploading, progress,\n uploadMessage, currentImageData, canDownload, applyBlurColor,\n handleResetColor, handleFileSelect, handleCancelUpload, handleRemoveTile,\n handleAltTextSave, handleSaveImage, handleDownload,\n } = useSingleImageState(options);\n\n const { isDragging, dragHandlers } = useDragAndDrop({\n disabled,\n onDrop: (files) => { void handleFileSelect(files); },\n });\n\n const { onPaste } = useClipboardPaste({\n enabled: enablePaste,\n pasteTarget,\n disabled,\n multiple: false,\n onPasteFiles: (files) => { void handleFileSelect(files); },\n });\n\n const displayError = externalError || validationError || '';\n const blurDataUrl = blurDataUrlState || currentImageData?.blurDataUrl || '';\n\n const state = resolveUploadDataState({\n disabled,\n hasError: Boolean(displayError),\n isUploading,\n isDragging,\n hasContent: Boolean(preview),\n });\n\n const openFileDialog = () => {\n if (!disabled) fileInputRef.current?.click();\n };\n\n const getDropzoneProps = (\n overrides?: HTMLAttributes<HTMLElement>,\n ): HeadlessDropzoneProps => ({\n ...overrides,\n role: overrides?.role ?? 'button',\n tabIndex: overrides?.tabIndex ?? (disabled ? -1 : 0),\n 'aria-label': overrides?.['aria-label'] ?? ui.uploadImageAria,\n 'aria-disabled': disabled || undefined,\n 'aria-required': required || undefined,\n 'aria-invalid': Boolean(displayError) || undefined,\n 'data-state': state,\n 'data-dragging': isDragging || undefined,\n 'data-uploading': isUploading || undefined,\n 'data-disabled': disabled || undefined,\n 'data-error': Boolean(displayError) || undefined,\n onClick: (event) => {\n if (event.target === fileInputRef.current) return;\n overrides?.onClick?.(event);\n if (!event.defaultPrevented && !disabled) openFileDialog();\n },\n onKeyDown: (event) => {\n overrides?.onKeyDown?.(event);\n if (\n !event.defaultPrevented &&\n !disabled &&\n event.target === event.currentTarget &&\n (event.key === 'Enter' || event.key === ' ')\n ) {\n event.preventDefault();\n openFileDialog();\n }\n },\n onBlur: (event) => {\n overrides?.onBlur?.(event);\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n onBlur?.();\n }\n },\n onPaste: (event) => {\n overrides?.onPaste?.(event);\n if (!event.defaultPrevented) onPaste(event);\n },\n onDragEnter: (event) => { overrides?.onDragEnter?.(event); dragHandlers.onDragEnter(event); },\n onDragLeave: (event) => { overrides?.onDragLeave?.(event); dragHandlers.onDragLeave(event); },\n onDragOver: (event) => { overrides?.onDragOver?.(event); dragHandlers.onDragOver(event); },\n onDrop: (event) => { overrides?.onDrop?.(event); dragHandlers.onDrop(event); },\n });\n\n const getInputProps = (\n overrides?: InputHTMLAttributes<HTMLInputElement>,\n ): HeadlessFileInputProps => ({\n ...overrides,\n ...(capture !== undefined && overrides?.capture === undefined ? { capture } : {}),\n ref: fileInputRef,\n type: 'file',\n accept: overrides?.accept ?? accept,\n disabled: overrides?.disabled ?? disabled,\n 'aria-label': overrides?.['aria-label'] ?? ui.fileInputAria,\n style: overrides?.style ? { ...HIDDEN_INPUT_STYLE, ...overrides.style } : HIDDEN_INPUT_STYLE,\n onClick: (event) => {\n overrides?.onClick?.(event);\n event.stopPropagation();\n },\n onChange: (event) => {\n overrides?.onChange?.(event);\n if (!event.defaultPrevented) {\n const files = event.target.files;\n if (files && files.length > 0) void handleFileSelect(files);\n event.target.value = '';\n }\n },\n });\n\n return {\n state, preview, uploadedData, altText, blurColor, detectedBlurColor,\n blurDataUrl, isDragging, isUploading, progress, uploadMessage,\n error: displayError, sizeInfo, qualityLevel, maxSizeMB, disabled,\n canDownload, locale, ui, openFileDialog,\n selectFiles: handleFileSelect,\n remove: handleRemoveTile,\n cancel: handleCancelUpload,\n cancelUpload: handleCancelUpload,\n save: handleSaveImage,\n download: handleDownload,\n setQualityLevel,\n setAltText: handleAltTextSave,\n setBlurColor: (hex: string) => applyBlurColor(hex),\n resetBlurColor: handleResetColor,\n getDropzoneProps,\n getInputProps,\n };\n}\n","import { type HTMLAttributes, type InputHTMLAttributes, type RefObject, useRef } from 'react';\nimport type { UploadPresetName } from '../../types';\nimport { useClipboardPaste } from '../hooks/useClipboardPaste';\nimport { useDragAndDrop } from '../hooks/useDragAndDrop';\nimport { useMultiImageState } from '../hooks/useMultiImageState';\nimport { HIDDEN_INPUT_STYLE, resolveUploadDataState } from '../utils/slotStyles';\nimport type {\n HeadlessDropzoneProps,\n HeadlessFileInputProps,\n MultiUploadController,\n UseMultiUploadOptions,\n} from './types';\n\nconst DEFAULT_ACCEPT = 'image/jpeg,image/png,image/webp,image/avif,image/svg+xml';\n\nexport function useMultiUpload<TPreset extends string = UploadPresetName>(\n options: UseMultiUploadOptions<TPreset>,\n): MultiUploadController {\n const {\n accept = DEFAULT_ACCEPT,\n disabled = false,\n error: externalError,\n maxImages = 999,\n capture,\n enablePaste = true,\n pasteTarget = 'dropzone',\n required = false,\n onBlur,\n } = options;\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const folderInputRef = useRef<HTMLInputElement>(null);\n\n const {\n locale, ui, maxSizeMB, previews, validationError, infoMessage,\n selectedPreviews, setSelectedPreviews, uploadStats, qualityLevel,\n setQualityLevel, isUploading, hasFailedUploads, uploadingPreviews,\n completedPreviews, handleFileSelect, handleRemove, handleCancelUpload,\n handleRemoveSelected, handleClearAll, toggleSelect, handleRetryUpload,\n handleRetryFailed, handleAltTextSave, handleColorChange, handleSaveImages,\n } = useMultiImageState(options);\n\n const { isDragging, dragHandlers } = useDragAndDrop({\n disabled,\n onDrop: (files) => { void handleFileSelect(files); },\n });\n\n const { onPaste } = useClipboardPaste({\n enabled: enablePaste,\n pasteTarget,\n disabled,\n multiple: true,\n onPasteFiles: (files) => { void handleFileSelect(files); },\n });\n\n const displayError = externalError || validationError || '';\n const remainingCount = Math.max(0, maxImages - previews.length);\n\n const state = resolveUploadDataState({\n disabled,\n hasError: Boolean(displayError),\n isUploading,\n isDragging,\n hasContent: previews.length > 0,\n });\n\n const openFileDialog = () => { if (!disabled) fileInputRef.current?.click(); };\n const openFolderDialog = () => { if (!disabled) folderInputRef.current?.click(); };\n\n const getDropzoneProps = (\n overrides?: HTMLAttributes<HTMLElement>,\n ): HeadlessDropzoneProps => ({\n ...overrides,\n role: overrides?.role ?? 'button',\n tabIndex: overrides?.tabIndex ?? (disabled ? -1 : 0),\n 'aria-label': overrides?.['aria-label'] ?? ui.selectFilesAria,\n 'aria-disabled': disabled || undefined,\n 'aria-required': required || undefined,\n 'aria-invalid': Boolean(displayError) || undefined,\n 'data-state': state,\n 'data-dragging': isDragging || undefined,\n 'data-uploading': isUploading || undefined,\n 'data-disabled': disabled || undefined,\n 'data-error': Boolean(displayError) || undefined,\n onClick: (event) => {\n if (event.target === fileInputRef.current || event.target === folderInputRef.current) return;\n overrides?.onClick?.(event);\n if (!event.defaultPrevented && !disabled) openFileDialog();\n },\n onKeyDown: (event) => {\n overrides?.onKeyDown?.(event);\n if (\n !event.defaultPrevented &&\n !disabled &&\n event.target === event.currentTarget &&\n (event.key === 'Enter' || event.key === ' ')\n ) {\n event.preventDefault();\n openFileDialog();\n }\n },\n onBlur: (event) => {\n overrides?.onBlur?.(event);\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n onBlur?.();\n }\n },\n onPaste: (event) => {\n overrides?.onPaste?.(event);\n if (!event.defaultPrevented) onPaste(event);\n },\n onDragEnter: (event) => { overrides?.onDragEnter?.(event); dragHandlers.onDragEnter(event); },\n onDragLeave: (event) => { overrides?.onDragLeave?.(event); dragHandlers.onDragLeave(event); },\n onDragOver: (event) => { overrides?.onDragOver?.(event); dragHandlers.onDragOver(event); },\n onDrop: (event) => { overrides?.onDrop?.(event); dragHandlers.onDrop(event); },\n });\n\n const buildInputProps = (\n ref: RefObject<HTMLInputElement | null>,\n defaultAriaLabel: string,\n isFolder: boolean,\n overrides?: InputHTMLAttributes<HTMLInputElement>,\n ): HeadlessFileInputProps => ({\n ...overrides,\n ...(isFolder\n ? { webkitdirectory: '', directory: '' }\n : capture !== undefined && overrides?.capture === undefined\n ? { capture }\n : {}),\n ref,\n type: 'file',\n multiple: overrides?.multiple ?? true,\n accept: overrides?.accept ?? accept,\n disabled: overrides?.disabled ?? disabled,\n 'aria-label': overrides?.['aria-label'] ?? defaultAriaLabel,\n style: overrides?.style ? { ...HIDDEN_INPUT_STYLE, ...overrides.style } : HIDDEN_INPUT_STYLE,\n onClick: (event) => {\n overrides?.onClick?.(event);\n event.stopPropagation();\n },\n onChange: (event) => {\n overrides?.onChange?.(event);\n if (!event.defaultPrevented) {\n const files = event.target.files;\n if (files && files.length > 0) void handleFileSelect(files);\n event.target.value = '';\n }\n },\n });\n\n return {\n state,\n items: previews,\n completedItems: completedPreviews,\n uploadingItems: uploadingPreviews,\n selectedIds: selectedPreviews,\n stats: uploadStats,\n isDragging, isUploading, hasFailedUploads,\n error: displayError, infoMessage, qualityLevel,\n maxSizeMB, maxImages, remainingCount, disabled, locale, ui,\n openFileDialog, openFolderDialog,\n selectFiles: handleFileSelect,\n remove: handleRemove,\n removeSelected: handleRemoveSelected,\n clear: handleClearAll,\n cancel: handleCancelUpload,\n cancelUpload: handleCancelUpload,\n retry: handleRetryUpload,\n retryUpload: handleRetryUpload,\n retryAll: handleRetryFailed,\n toggleSelect,\n selectAll: () => setSelectedPreviews(new Set(previews.map((p) => p.id))),\n clearSelection: () => setSelectedPreviews(new Set()),\n retryFailed: handleRetryFailed,\n save: handleSaveImages,\n setQualityLevel,\n setAltText: handleAltTextSave,\n setBlurColor: handleColorChange,\n getDropzoneProps,\n getInputProps: (overrides) => buildInputProps(fileInputRef, ui.selectFilesAria, false, overrides),\n getFolderInputProps: (overrides) => buildInputProps(folderInputRef, ui.selectFolderAria, true, overrides),\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { QualityLevel, StandardPresetName, UploadedImageData, UploadPresetName } from '../../types';\nimport { QUALITY_PRESETS, STANDARD_PRESETS } from '../../types';\nimport { validateMultipleFiles } from '../../utils/validators';\nimport { useImageUploadContext } from '../context';\nimport {\n formatNeedsAltMessage,\n formatSkippedImagesMessage,\n normalizeUploadLocale,\n resolveTranslations,\n} from '../i18n';\nimport type { ImagePreview, MultiImageUploadProps } from '../types';\nimport {\n collectUploadedData,\n formatMinImagesMessage,\n mapUploadedToPreview,\n resolveInitialQuality,\n runParallelUploadPool,\n serializeMultiFormValue,\n syncPreviewsWithValue,\n} from './uploadQueue';\nimport { useFilePreview } from './useFilePreview';\nimport { useImageUpload } from './useImageUpload';\n\nconst EMPTY_IMAGES: UploadedImageData[] = [];\n\nexport interface MultiUploadStats { total: number; completed: number; failed: number; }\n\nexport type UseMultiImageStateOptions<TPreset extends string = UploadPresetName> =\n Omit<MultiImageUploadProps<TPreset>, 'mode'> & { mode?: 'multiple' };\n\nlet tempIdCounter = 0;\n\nexport function useMultiImageState<TPreset extends string = UploadPresetName>(\n options: UseMultiImageStateOptions<TPreset>,\n) {\n const { onChange, preset, isOpen, onClose, minImages, required, formValue } = options;\n const value = options.value ?? EMPTY_IMAGES;\n const maxImages = options.maxImages ?? 999;\n const maxParallelUploads = options.maxParallelUploads ?? 2;\n\n const ctx = useImageUploadContext();\n const locale = normalizeUploadLocale(options.locale || ctx.locale);\n const ui = resolveTranslations(locale, options.translations || ctx.translations);\n const requireAltText = options.requireAltText ?? ctx.requireAltText;\n const standardPreset = preset ? STANDARD_PRESETS[preset as StandardPresetName] : undefined;\n const maxSizeMB = options.maxSizeMB ?? standardPreset?.maxSizeMB ?? 10;\n const requiresManualCommit = Boolean(isOpen);\n\n const [previews, setPreviews] = useState<ImagePreview[]>(() => value.map((img) => mapUploadedToPreview(img)));\n const previewsRef = useRef<ImagePreview[]>(previews);\n previewsRef.current = previews;\n const uncommittedIdsRef = useRef<Set<string>>(new Set());\n const isRetryingRef = useRef(false);\n\n const [validationError, setValidationError] = useState('');\n const [infoMessage, setInfoMessage] = useState('');\n const [selectedPreviews, setSelectedPreviews] = useState<Set<string>>(() => new Set());\n const [uploadStats, setUploadStats] = useState<MultiUploadStats>({ total: 0, completed: 0, failed: 0 });\n const [editingAltTextId, setEditingAltTextId] = useState<string | null>(null);\n const [showMissingAltWarning, setShowMissingAltWarning] = useState(false);\n const [qualityLevel, setQualityLevel] = useState<QualityLevel>(() =>\n resolveInitialQuality(options.quality ?? standardPreset?.quality, standardPreset?.recommendedQualityLevel),\n );\n\n const { createPreview, revokePreview } = useFilePreview({ errorLabel: ui.failedCreatePreview });\n const infoTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => () => { if (infoTimeoutRef.current) clearTimeout(infoTimeoutRef.current); }, []);\n\n useEffect(() => {\n setPreviews((current) => {\n const resolved = syncPreviewsWithValue(current, value, requiresManualCommit, uncommittedIdsRef.current);\n previewsRef.current = resolved;\n return resolved;\n });\n }, [value, requiresManualCommit]);\n\n const emitImmediateChange = (nextPreviews: ImagePreview[]) => {\n if (!requiresManualCommit) onChange(collectUploadedData(nextPreviews, true));\n };\n\n const patchByIdOrFile = (uploadId: string | undefined, file: File | undefined, patch: Partial<ImagePreview>, emit = false) => {\n const next = previewsRef.current.map((p) => {\n if (!(uploadId ? p.id === uploadId : p.file === file)) return p;\n if (patch.url && p.url && p.url !== patch.url) revokePreview(p.url);\n return { ...p, ...patch };\n });\n previewsRef.current = next;\n setPreviews(next);\n if (emit) emitImmediateChange(next);\n };\n\n const patchPreview = (id: string, patch: (p: ImagePreview) => ImagePreview) => {\n const next = previewsRef.current.map((p) => (p.id === id ? patch(p) : p));\n previewsRef.current = next;\n setPreviews(next);\n emitImmediateChange(next);\n };\n\n const { uploadImage, abortUpload, abortAll } = useImageUpload({\n preset, folder: options.folder, bucket: options.bucket, metadata: options.metadata,\n outputFormat: options.outputFormat, quality: QUALITY_PRESETS[qualityLevel].value,\n maxDimensions: options.maxDimensions, customFilename: options.customFilename,\n endpoint: options.endpoint, uploader: options.uploader,\n headers: options.headers, withCredentials: options.withCredentials, locale,\n onSuccess: (data, file, uploadId) => {\n if (uploadId && requiresManualCommit) uncommittedIdsRef.current.add(uploadId);\n patchByIdOrFile(uploadId, file, {\n url: data.url, uploadMessage: undefined, originalSize: data.originalSize,\n optimizedSize: data.size, data, blurColor: data.blurColor,\n error: undefined, isUploading: false, progress: 100,\n }, true);\n setUploadStats((s) => ({ ...s, completed: s.completed + 1 }));\n },\n onError: (error, file, uploadId) => {\n patchByIdOrFile(uploadId, file, { uploadMessage: undefined, error, isUploading: false, progress: 0 });\n setUploadStats((s) => ({ ...s, failed: s.failed + 1 }));\n },\n onProgress: (progress, uploadMessage, file, uploadId) => {\n patchByIdOrFile(uploadId, file, { uploadMessage, progress, isUploading: true });\n },\n });\n\n useEffect(() => {\n if (isOpen === false) { abortAll(); uncommittedIdsRef.current.clear(); }\n }, [isOpen, abortAll]);\n\n const runBatchUploads = async (items: ImagePreview[], files: File[]) => {\n options.onUploadStart?.(files);\n const completedBatch: UploadedImageData[] = [];\n await runParallelUploadPool(items, maxParallelUploads, async (item) => {\n if (!previewsRef.current.some((p) => p.id === item.id) || !item.file) return;\n const uploaded = await uploadImage(item.file, item.altText, undefined, { uploadId: item.id });\n if (uploaded) completedBatch.push(uploaded);\n });\n if (completedBatch.length > 0) options.onUploadComplete?.(completedBatch);\n };\n\n const handleFileSelect = async (files: FileList) => {\n setValidationError('');\n setInfoMessage('');\n const allFiles = Array.from(files);\n const imageFiles = allFiles.filter((file) => file.type.startsWith('image/'));\n if (imageFiles.length === 0) { setValidationError(ui.noValidImageFiles); return; }\n if (allFiles.length > imageFiles.length) {\n setInfoMessage(formatSkippedImagesMessage(ui, imageFiles.length, allFiles.length - imageFiles.length, locale));\n if (infoTimeoutRef.current) clearTimeout(infoTimeoutRef.current);\n infoTimeoutRef.current = setTimeout(() => setInfoMessage(''), 5000);\n }\n\n const activeCount = previewsRef.current.filter((p) => !p.error).length;\n const validationResult = validateMultipleFiles(imageFiles, maxSizeMB, activeCount, maxImages, locale);\n if (validationResult) { setValidationError(validationResult); return; }\n\n setUploadStats({ total: imageFiles.length, completed: 0, failed: 0 });\n const newPreviews: ImagePreview[] = [];\n for (const file of imageFiles) {\n try {\n const url = await createPreview(file);\n tempIdCounter += 1;\n newPreviews.push({\n id: `temp-${Date.now()}-${tempIdCounter}`, url, file, fileName: file.name,\n uploadMessage: ui.queued, originalSize: file.size, optimizedSize: null,\n isUploading: true, progress: 0,\n });\n } catch {\n setValidationError(ui.failedCreatePreviewFor.replace('{file}', file.name));\n return;\n }\n }\n const nextBatch = [...previewsRef.current, ...newPreviews];\n previewsRef.current = nextBatch;\n setPreviews(nextBatch);\n await runBatchUploads(newPreviews, imageFiles);\n };\n\n const removeIds = (ids: Set<string>) => {\n for (const p of previewsRef.current) {\n if (ids.has(p.id)) {\n abortUpload(p.id);\n if (p.file) abortUpload(p.file);\n revokePreview(p.url);\n uncommittedIdsRef.current.delete(p.id);\n }\n }\n const next = previewsRef.current.filter((p) => !ids.has(p.id));\n previewsRef.current = next;\n setPreviews(next);\n emitImmediateChange(next);\n };\n\n const handleRemove = (id: string) => {\n removeIds(new Set([id]));\n setSelectedPreviews((prev) => {\n if (!prev.has(id)) return prev;\n const copy = new Set(prev);\n copy.delete(id);\n return copy;\n });\n };\n\n const handleCancelUpload = (id: string) => {\n if (!previewsRef.current.some((p) => p.id === id)) return;\n handleRemove(id);\n options.onUploadCancel?.(id);\n };\n\n const handleRemoveSelected = () => { removeIds(new Set(selectedPreviews)); setSelectedPreviews(new Set()); };\n const handleClearAll = () => { removeIds(new Set(previewsRef.current.map((p) => p.id))); setSelectedPreviews(new Set()); setValidationError(''); };\n\n const toggleSelect = (id: string) => {\n setSelectedPreviews((prev) => {\n const copy = new Set(prev);\n if (copy.has(id)) copy.delete(id); else copy.add(id);\n return copy;\n });\n };\n\n const handleRetryUpload = async (id: string) => {\n const target = previewsRef.current.find((p) => p.id === id && p.error && p.file);\n if (!target || !target.file) return;\n setUploadStats({ total: 1, completed: 0, failed: 0 });\n const updated = previewsRef.current.map((p) =>\n p.id === id ? { ...p, error: undefined, isUploading: true, progress: 0, uploadMessage: ui.retrying } : p,\n );\n previewsRef.current = updated;\n setPreviews(updated);\n await runBatchUploads([target], [target.file]);\n };\n\n const handleRetryFailed = async () => {\n if (isRetryingRef.current) return;\n const failedItems = previewsRef.current.filter((p) => p.error && p.file);\n if (failedItems.length === 0) return;\n isRetryingRef.current = true;\n try {\n setUploadStats({ total: failedItems.length, completed: 0, failed: 0 });\n const failedIds = new Set(failedItems.map((p) => p.id));\n const updated = previewsRef.current.map((p) =>\n failedIds.has(p.id) ? { ...p, error: undefined, isUploading: true, progress: 0, uploadMessage: ui.retrying } : p,\n );\n previewsRef.current = updated;\n setPreviews(updated);\n await runBatchUploads(failedItems, failedItems.map((p) => p.file!).filter(Boolean));\n } finally {\n isRetryingRef.current = false;\n }\n };\n\n const handleAltTextSave = (previewId: string, altText: string) => {\n patchPreview(previewId, (p) => ({ ...p, altText, data: p.data ? { ...p.data, altText } : p.data }));\n setEditingAltTextId(null);\n setShowMissingAltWarning(false);\n };\n\n const handleColorChange = (previewId: string, blurColor: string) => {\n patchPreview(previewId, (p) => ({ ...p, blurColor, data: p.data ? { ...p.data, blurColor } : p.data }));\n };\n\n const isUploading = previews.some((p) => p.isUploading);\n const hasFailedUploads = previews.some((p) => Boolean(p.error));\n const uploadingPreviews = previews.filter((p) => p.isUploading);\n const completedPreviews = previews.filter((p) => Boolean(p.data) && !p.isUploading);\n const imagesWithoutAlt = completedPreviews.filter((p) => !p.altText || !p.altText.trim());\n const editingTarget = editingAltTextId ? (previews.find((p) => p.id === editingAltTextId) ?? null) : null;\n const minImagesWarning = !isUploading && minImages && previews.length > 0 && completedPreviews.length < minImages\n ? formatMinImagesMessage(ui.minImagesError, minImages) : '';\n const serializedFormValue = serializeMultiFormValue(previews, formValue ?? 'json');\n\n const handleSaveImages = () => {\n if (required && completedPreviews.length === 0) { setValidationError(ui.requiredFieldError); return; }\n if (minImages && completedPreviews.length < minImages) {\n setValidationError(formatMinImagesMessage(ui.minImagesError, minImages));\n return;\n }\n if (requireAltText && imagesWithoutAlt.length > 0) {\n setShowMissingAltWarning(true);\n setValidationError(formatNeedsAltMessage(ui, imagesWithoutAlt.length, locale));\n return;\n }\n setShowMissingAltWarning(false);\n setValidationError('');\n uncommittedIdsRef.current.clear();\n onChange(collectUploadedData(previews, false));\n onClose?.();\n };\n\n return {\n locale, ui, requireAltText, maxSizeMB, previews,\n validationError: validationError || minImagesWarning, infoMessage,\n selectedPreviews, setSelectedPreviews, uploadStats,\n editingAltTextId, setEditingAltTextId, showMissingAltWarning,\n qualityLevel, setQualityLevel, isUploading, hasFailedUploads, uploadingPreviews,\n completedPreviews, imagesWithoutAlt, editingTarget, serializedFormValue,\n handleFileSelect, handleRemove, handleCancelUpload, handleRemoveSelected, handleClearAll,\n toggleSelect, handleRetryUpload, handleRetryFailed, handleAltTextSave,\n handleColorChange, handleSaveImages,\n };\n}\n","import type { QualityLevel, UploadedImageData } from '../../types';\nimport type { ImagePreview } from '../types';\n\nexport function mapUploadedToPreview(\n img: UploadedImageData,\n prev?: ImagePreview,\n): ImagePreview {\n const altText =\n prev?.altText && !img.altText?.trim() ? prev.altText : img.altText;\n return {\n id: prev?.id || img.url,\n url: img.url,\n file: null,\n uploadMessage: null,\n originalSize: img.originalSize ?? prev?.originalSize,\n optimizedSize: img.size ?? prev?.optimizedSize,\n data: { ...img, altText },\n altText,\n blurColor: img.blurColor || prev?.blurColor,\n };\n}\n\nexport function resolveInitialQuality(\n q?: number,\n fallback: QualityLevel = 'high',\n): QualityLevel {\n if (!q) return fallback;\n return q <= 55 ? 'low' : q <= 78 ? 'medium' : q <= 90 ? 'high' : 'maximum';\n}\n\nexport function collectUploadedData(\n list: ImagePreview[],\n readyOnly: boolean,\n): UploadedImageData[] {\n return list\n .filter((p) => p.data && (!readyOnly || !p.isUploading))\n .map((p) => ({\n ...p.data!,\n altText: p.altText ?? p.data!.altText,\n blurColor: p.blurColor ?? p.data!.blurColor,\n }));\n}\n\nexport function syncPreviewsWithValue(\n current: ImagePreview[],\n value: UploadedImageData[],\n requiresManualCommit: boolean,\n uncommittedIds?: ReadonlySet<string>,\n): ImagePreview[] {\n const prevMap = new Map<string, ImagePreview>();\n current.forEach((p) => {\n prevMap.set(p.id, p);\n if (p.url) prevMap.set(p.url, p);\n if (p.data?.url) prevMap.set(p.data.url, p);\n });\n\n const synced = value.map((img) =>\n mapUploadedToPreview(img, prevMap.get(img.url)),\n );\n\n const pending = current.filter((p) => {\n const inValue = value.some(\n (img) =>\n img.url === p.id ||\n (p.url && img.url === p.url) ||\n (p.data?.url && img.url === p.data.url),\n );\n if (inValue) return false;\n if (p.isUploading || Boolean(p.error)) return true;\n if (\n requiresManualCommit &&\n Boolean(p.data) &&\n (uncommittedIds ? uncommittedIds.has(p.id) : p.id.startsWith('temp-'))\n ) {\n return true;\n }\n return false;\n });\n\n const next = [...synced, ...pending];\n const isSame =\n next.length === current.length &&\n next.every((it, idx) => {\n const p = current[idx];\n return (\n p &&\n p.id === it.id &&\n p.url === it.url &&\n p.altText === it.altText &&\n p.blurColor === it.blurColor &&\n p.isUploading === it.isUploading &&\n p.error === it.error\n );\n });\n\n return isSame ? current : next;\n}\n\nexport async function runParallelUploadPool<T>(\n items: readonly T[],\n maxParallel: number,\n workerFn: (item: T) => Promise<unknown>,\n): Promise<void> {\n if (items.length === 0) return;\n let nextIndex = 0;\n const workerCount = Math.max(1, Math.min(maxParallel, items.length));\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (nextIndex < items.length) {\n const item = items[nextIndex];\n nextIndex += 1;\n await workerFn(item);\n }\n }),\n );\n}\n\nexport function serializeMultiFormValue(\n previews: ImagePreview[],\n formValue: 'url' | 'json' = 'json',\n): string {\n const uploaded = collectUploadedData(previews, true);\n if (uploaded.length === 0) return '';\n if (formValue === 'url') {\n return uploaded.map((img) => img.url).join(',');\n }\n return JSON.stringify(uploaded);\n}\n\nexport function formatMinImagesMessage(\n template: string,\n minImages: number,\n): string {\n return template\n .replace('{min}', String(minImages))\n .replace('{suffix}', minImages === 1 ? '' : 's');\n}\n","import type { ReactElement } from 'react';\nimport type { UploadPresetName } from '../../types';\nimport type { HeadlessSingleUploadProps } from './types';\nimport { useSingleUpload } from './useSingleUpload';\n\n/**\n * Headless render-prop primitive for single-image upload workflows.\n * Renders zero DOM elements of its own and delegates 100% of markup and styling to `children(controller)`.\n */\nexport function HeadlessSingleUpload<\n TPreset extends string = UploadPresetName,\n>(props: HeadlessSingleUploadProps<TPreset>): ReactElement {\n const controller = useSingleUpload<TPreset>(props);\n return <>{props.children(controller)}</>;\n}\n\nexport default HeadlessSingleUpload;\n","import type { ReactElement } from 'react';\nimport type { UploadPresetName } from '../../types';\nimport type { HeadlessMultiUploadProps } from './types';\nimport { useMultiUpload } from './useMultiUpload';\n\n/**\n * Headless render-prop primitive for multi-image and folder upload workflows.\n * Renders zero DOM elements of its own and delegates 100% of markup and styling to `children(controller)`.\n */\nexport function HeadlessMultiUpload<\n TPreset extends string = UploadPresetName,\n>(props: HeadlessMultiUploadProps<TPreset>): ReactElement {\n const controller = useMultiUpload<TPreset>(props);\n return <>{props.children(controller)}</>;\n}\n\nexport default HeadlessMultiUpload;\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAqD,aAAa,WAAW,cAAc;AAiB3F,SAAS,qBAAqB,QAAqC;AACjE,MAAI,CAAC,UAAU,OAAO,gBAAgB,eAAe,EAAE,kBAAkB,cAAc;AACrF,WAAO;AAAA,EACT;AACA,QAAM,MAAM,OAAO;AACnB,MAAI,QAAQ,cAAc,OAAO,kBAAmB,QAAO;AAC3D,MAAI,QAAQ,SAAS;AACnB,UAAM,OAAQ,OAA4B,MAAM,YAAY;AAC5D,WAAO,SAAS,UAAU,SAAS,YAAY,SAAS,cAAc,SAAS;AAAA,EACjF;AACA,SAAO;AACT;AAEO,SAAS,2BACd,eACA,UACQ;AACR,MAAI,CAAC,cAAe,QAAO,CAAC;AAE5B,QAAM,YAAoB,CAAC;AAC3B,MAAI,cAAc,SAAS,cAAc,MAAM,SAAS,GAAG;AACzD,aAAS,IAAI,GAAG,IAAI,cAAc,MAAM,QAAQ,KAAK;AACnD,YAAM,OAAO,cAAc,MAAM,CAAC;AAClC,UAAI,QAAQ,KAAK,KAAK,WAAW,QAAQ,GAAG;AAC1C,kBAAU,KAAK,IAAI;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,WAAW,KAAK,cAAc,SAAS,cAAc,MAAM,SAAS,GAAG;AACnF,aAAS,IAAI,GAAG,IAAI,cAAc,MAAM,QAAQ,KAAK;AACnD,YAAM,OAAO,cAAc,MAAM,CAAC;AAClC,UAAI,QAAQ,KAAK,SAAS,UAAU,KAAK,KAAK,WAAW,QAAQ,GAAG;AAClE,cAAM,OAAO,KAAK,UAAU;AAC5B,YAAI,KAAM,WAAU,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AACpC,SAAO,WAAW,YAAY,CAAC,UAAU,CAAC,CAAC;AAC7C;AAEO,SAAS,eAAe,OAAyB;AACtD,MAAI,OAAO,iBAAiB,aAAa;AACvC,QAAI;AACF,YAAM,KAAK,IAAI,aAAa;AAC5B,iBAAW,QAAQ,MAAO,IAAG,MAAM,IAAI,IAAI;AAC3C,UAAI,GAAG,MAAM,WAAW,MAAM,OAAQ,QAAO,GAAG;AAAA,IAClD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,gBAAgB,OAAO,OAAO,CAAC,GAAG,KAAK,GAAG;AAAA,IAC9C,MAAM,CAAC,UAAkB,MAAM,KAAK,KAAK;AAAA,EAC3C,CAAC;AACD,SAAO;AACT;AAEO,SAAS,kBAAkB;AAAA,EAChC,UAAU;AAAA,EACV,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AACF,GAA6B;AAC3B,QAAM,kBAAkB,OAAO,YAAY;AAC3C,kBAAgB,UAAU;AAC1B,QAAM,yBAAyB,OAAuB,oBAAI,QAAQ,CAAC;AAEnE,QAAM,eAAe;AAAA,IACnB,CACE,eACA,QACA,aACA,mBACG;AACH,UAAI,CAAC,WAAW,SAAU;AAC1B,UAAI,uBAAuB,QAAQ,IAAI,WAAW,EAAG;AACrD,UAAI,qBAAqB,MAAM,EAAG;AAElC,YAAM,QAAQ,2BAA2B,eAAe,QAAQ;AAChE,UAAI,MAAM,WAAW,EAAG;AAExB,6BAAuB,QAAQ,IAAI,WAAW;AAC9C,qBAAe;AACf,sBAAgB,QAAQ,eAAe,KAAK,CAAC;AAAA,IAC/C;AAAA,IACA,CAAC,SAAS,UAAU,QAAQ;AAAA,EAC9B;AAEA,QAAM,UAAU;AAAA,IACd,CAAC,UAA4C;AAC3C;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,MAAM,eAAe;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,YAAY,gBAAgB,cAAc,OAAO,aAAa,aAAa;AACzF;AAAA,IACF;AACA,UAAM,sBAAsB,CAAC,UAA0B;AACrD;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,MAAM,MAAM,eAAe;AAAA,MAC7B;AAAA,IACF;AACA,aAAS,iBAAiB,SAAS,mBAAmB;AACtD,WAAO,MAAM,SAAS,oBAAoB,SAAS,mBAAmB;AAAA,EACxE,GAAG,CAAC,SAAS,UAAU,aAAa,YAAY,CAAC;AAEjD,SAAO,EAAE,QAAQ;AACnB;;;ACzIA,SAAyB,eAAAA,cAAa,aAAAC,YAAW,UAAAC,SAAQ,gBAAgB;AAOlE,SAAS,eAAe,EAAE,UAAU,OAAO,GAA0B;AAC1E,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,eAAeA,QAAO,CAAC;AAE7B,EAAAD,WAAU,MAAM;AACd,QAAI,UAAU;AACZ,mBAAa,UAAU;AACvB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,kBAAkBD;AAAA,IACtB,CAAC,MAAiB;AAChB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,UAAI,SAAU;AACd,mBAAa,WAAW;AACxB,oBAAc,IAAI;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,kBAAkBA,aAAY,CAAC,MAAiB;AACpD,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,iBAAa,UAAU,KAAK,IAAI,GAAG,aAAa,UAAU,CAAC;AAC3D,QAAI,aAAa,YAAY,GAAG;AAC9B,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiBA,aAAY,CAAC,MAAiB;AACnD,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,aAAaA;AAAA,IACjB,CAAC,MAAiB;AAChB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,mBAAa,UAAU;AACvB,oBAAc,KAAK;AAEnB,UAAI,SAAU;AAEd,YAAM,QAAQ,EAAE,cAAc;AAC9B,UAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,IACA,CAAC,UAAU,MAAM;AAAA,EACnB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc;AAAA,MACZ,aAAa;AAAA,MACb,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,EACF;AACF;;;ACrEA,SAAS,eAAAG,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACAlE,SAAS,eAA+B,YAAY,eAAe;AA4H/D;AAhGJ,IAAM,kBAA2C;AAAA,EAC/C,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AACZ;AAEA,IAAM,qBACJ,cAAuC,eAAe;AAkCjD,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAC1B,UACH;AAEJ;AAEO,SAAS,wBAAiD;AAC/D,SAAO,WAAW,kBAAkB;AACtC;;;AClIO,IAAM,iBAAqC;AAAA,EAChD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,mBACE;AAAA,EACF,uBACE;AAAA,EACF,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,wBAAwB;AAAA,EACxB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,oBACE;AAAA,EACF,cAAc;AAAA,EACd,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBACE;AAAA,EACF,oBAAoB;AAAA,EACpB,WAAW;AAAA,EACX,eAAe;AAAA,EACf,yBAAyB;AAAA,EACzB,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,cAAc;AAAA,EACd,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,WAAW;AAAA,IACT,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,eACE;AAAA,IACF,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,SAAS;AAAA,IACT,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,eAAe;AAAA,IACf,SAAS;AAAA,IACT,oBAAoB;AAAA,IACpB,cAAc;AAAA,EAChB;AACF;;;AC7GO,IAAM,iBAAqC;AAAA,EAChD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,mBACE;AAAA,EACF,uBACE;AAAA,EACF,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,wBAAwB;AAAA,EACxB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,oBACE;AAAA,EACF,cAAc;AAAA,EACd,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBACE;AAAA,EACF,oBAAoB;AAAA,EACpB,WAAW;AAAA,EACX,eAAe;AAAA,EACf,yBAAyB;AAAA,EACzB,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,cAAc;AAAA,EACd,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,WAAW;AAAA,IACT,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,eACE;AAAA,IACF,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,SAAS;AAAA,IACT,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,eAAe;AAAA,IACf,SAAS;AAAA,IACT,oBACE;AAAA,IACF,cAAc;AAAA,EAChB;AACF;;;ACvGO,IAAM,8BAA8B;AACpC,IAAM,6BAA6B;AAGnC,SAAS,sBAAsB,QAA+B;AACnE,MAAI,QAAQ;AACV,WAAO,OAAO,YAAY,EAAE,WAAW,IAAI,IAAI,OAAO;AAAA,EACxD;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,UACJ,SAAS,gBAAgB,aAAa,MAAM,GAAG,YAAY,KAAK;AAClE,UAAM,SACJ,SAAS,gBAAgB,aAAa,KAAK,GAAG,YAAY,KAAK;AACjE,QAAI,QAAQ,WAAW,IAAI,KAAK,WAAW,OAAO;AAChD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,qBACd,QACA,OACqB;AACrB,MAAI;AACF,WAAO,IAAI,KAAK,YAAY,MAAM,EAAE,OAAO,KAAK;AAAA,EAClD,QAAQ;AACN,WAAO,UAAU,IAAI,QAAQ;AAAA,EAC/B;AACF;AAEO,SAAS,qBACd,IACA,OACA,QACA,WACQ;AACR,MACE,aACA,CAAC,UAAU,mBACV,UAAU,aAAa,UAAU,eAAe,UAAU,eAC3D;AACA,UAAM,QAAQ,qBAAqB,QAAQ,KAAK,MAAM;AACtD,WAAO,GAAG,GAAG,SAAS,IAAI,KAAK,IAAI,QAAQ,GAAG,cAAc,GAAG,YAAY;AAAA,EAC7E;AACA,QAAM,SAAS,qBAAqB,QAAQ,KAAK,MAAM,QAAQ,KAAK;AACpE,SAAO,GAAG,eACP,QAAQ,WAAW,OAAO,KAAK,CAAC,EAChC,QAAQ,YAAY,MAAM;AAC/B;AAEO,SAAS,sBACd,IACA,OACA,QACQ;AACR,QAAM,QAAQ,qBAAqB,QAAQ,KAAK,MAAM;AACtD,SAAO,GAAG,mBACP,QAAQ,WAAW,OAAO,KAAK,CAAC,EAChC,QAAQ,YAAY,QAAQ,KAAK,GAAG,EACpC,QAAQ,gBAAgB,QAAQ,MAAM,EAAE;AAC7C;AAEO,SAAS,2BACd,IACA,YACA,cACA,QACQ;AACR,QAAM,YACJ,qBAAqB,QAAQ,UAAU,MAAM,QAAQ,KAAK;AAC5D,QAAM,aACJ,qBAAqB,QAAQ,YAAY,MAAM,QAAQ,KAAK;AAC9D,SAAO,GAAG,sBACP,QAAQ,gBAAgB,OAAO,UAAU,CAAC,EAC1C,QAAQ,iBAAiB,SAAS,EAClC,QAAQ,kBAAkB,OAAO,YAAY,CAAC,EAC9C,QAAQ,mBAAmB,UAAU;AAC1C;AAEO,IAAM,aAAuD;AAAA,EAClE,IAAI;AAAA,EACJ,IAAI;AACN;AAEO,SAAS,oBACd,QACA,WACoB;AACpB,QAAM,OAAO,WAAW,sBAAsB,MAAM,CAAC;AACrD,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,WAAW;AAAA,MACT,GAAG,KAAK;AAAA,MACR,GAAI,UAAU,aAAa,CAAC;AAAA,IAC9B;AAAA,EACF;AACF;;;ACzFA,IAAM,sBACJ;AAEK,SAAS,mBAAmB,KAAsB;AACvD,SAAO,oBAAoB,KAAK,GAAG;AACrC;AAEO,SAAS,kBACd,KACA,UAA0D,CAAC,GACnD;AACR,MAAI,CAAC,OAAO,CAAC,QAAQ,QAAS,QAAO;AACrC,MAAI,oBAAoB,KAAK,GAAG,KAAK,mBAAmB,GAAG,EAAG,QAAO;AACrE,MAAI,gBAAgB,KAAK,GAAG,EAAG,QAAO;AACtC,QAAM,SAAS,QAAQ,SAAS,KAAK,IAAI;AACzC,SAAO,GAAG,GAAG,GAAG,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,KAAK,MAAM;AAC1D;AAEO,SAAS,gBAAmB,KAAuB;AACxD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,eAAe,QAAQ,GAAG;AAC7C,WAAO,MAAO,KAAK,MAAM,GAAG,IAAU;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,iBAAiB,KAAa,OAAsB;AAClE,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,WAAO,eAAe,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EAC1D,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,yBACd,gBAAgB,OACI;AACpB,QAAM,SAAS,CAAC,OAAe,GAAG,aAAa,aAAa,EAAE;AAC9D,QAAM,UAAU,CAAC,OAAe,GAAG,aAAa,cAAc,EAAE;AAEhE,QAAM,kBAAkB,CAAC,MAAyB,QAAsB;AACtE,QAAI,CAAC,KAAK,KAAK,EAAG;AAClB,QAAI,KAAK,IAAK,kBAAiB,OAAO,KAAK,GAAG,GAAG,GAAG;AACpD,QAAI,KAAK,YAAa,kBAAiB,OAAO,KAAK,WAAW,GAAG,GAAG;AAAA,EACtE;AAEA,QAAM,iBAAiB,CAAC,SAA2C;AACjE,QAAI,KAAK,aAAa;AACpB,YAAM,SAAS,gBAAwB,OAAO,KAAK,WAAW,CAAC;AAC/D,UAAI,OAAQ,QAAO;AAAA,IACrB;AACA,QAAI,KAAK,KAAK;AACZ,YAAM,QAAQ,gBAAwB,OAAO,KAAK,GAAG,CAAC;AACtD,UAAI,MAAO,QAAO;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,CACvB,MACA,SACS;AACT,QAAI,KAAK,IAAK,kBAAiB,QAAQ,KAAK,GAAG,GAAG,IAAI;AACtD,QAAI,KAAK,YAAa,kBAAiB,QAAQ,KAAK,WAAW,GAAG,IAAI;AAAA,EACxE;AAEA,QAAM,kBAAkB,CAAC,SAAmD;AAC1E,QAAI,KAAK,aAAa;AACpB,YAAM,SAAS,gBAAgC,QAAQ,KAAK,WAAW,CAAC;AACxE,UAAI,QAAQ,eAAe,QAAQ,UAAW,QAAO;AAAA,IACvD;AACA,QAAI,KAAK,KAAK;AACZ,YAAM,QAAQ,gBAAgC,QAAQ,KAAK,GAAG,CAAC;AAC/D,UAAI,OAAO,eAAe,OAAO,UAAW,QAAO;AAAA,IACrD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,cAAyD;AAAA,EAC7D,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACnB;AAEA,eAAsB,0BACpB,KACA,QACe;AACf,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,UAAM,WAAW,mBAAmB,GAAG,IACnC,MACA,kBAAkB,KAAK,EAAE,SAAS,KAAK,CAAC;AAC5C,UAAM,MAAM,MAAM,MAAM,UAAU,EAAE,OAAO,WAAW,CAAC;AACvD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,MAAM,+BAA+B,IAAI,MAAM,EAAE;AAAA,IAC7D;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,YAAY,IAAI,gBAAgB,IAAI;AAE1C,UAAM,MACJ,UAAU,YAAY,KAAK,IAAI,KAAK;AAEtC,UAAM,SAAS,SAAS,cAAc,GAAG;AACzC,WAAO,OAAO;AACd,WAAO,WAAW,SAAS,KAAK,IAAI,CAAC,IAAI,GAAG;AAC5C,WAAO,MAAM,UAAU;AACvB,aAAS,MAAM,YAAY,MAAM;AACjC,WAAO,MAAM;AACb,WAAO,OAAO;AACd,eAAW,MAAM;AACf,UAAI;AACF,YAAI,gBAAgB,SAAS;AAAA,MAC/B,QAAQ;AAAA,MAER;AAAA,IACF,GAAG,GAAI;AAAA,EACT,QAAQ;AACN,WAAO,KAAK,KAAK,UAAU,qBAAqB;AAAA,EAClD;AACF;;;ACjJO,SAAS,2BACd,SACA,QACc;AACd,QAAM,IAAI,WAAW,QAAQ;AAC7B,MAAI,CAAC,EAAG,QAAO,QAAQ,2BAA2B;AAClD,MAAI,KAAK,GAAI,QAAO;AACpB,MAAI,KAAK,GAAI,QAAO;AACpB,SAAO,KAAK,KAAK,SAAS;AAC5B;AAEO,SAAS,wBACd,aACA,UACA,oBACA,IAKQ;AACR,MAAI,aAAa;AACf,QAAI,YAAY,GAAI,QAAO,GAAG,mBAAmB,QAAQ,cAAc,IAAI;AAC3E,QAAI,YAAY,GAAI,QAAO,GAAG,mBAAmB,QAAQ,cAAc,IAAI;AAC3E,QAAI,YAAY,GAAI,QAAO,GAAG,mBAAmB,QAAQ,cAAc,IAAI;AAC3E,WAAO,GAAG;AAAA,EACZ;AACA,SAAO,qBAAqB,GAAG,qBAAqB;AACtD;AAEO,SAAS,mBAAmB,GAAiC;AAClE,SAAO,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI;AAC/D;AAEO,SAAS,wBACd,QASA,YAAwC,CAAC,GACf;AAC1B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,OAAiC,eACnC,EAAE,GAAG,aAAa,IAClB,SACE,EAAE,GAAG,OAAO,IACZ,OAAO,UAAU,YAAY,MAAM,KAAK,IACtC;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC,IACA;AACR,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,MAAM,UAAU,OAAO,KAAK,QAAQ,WAAW;AAAA,IAC/C,SAAS,UAAU,WAAW,WAAW,KAAK,WAAW;AAAA,IACzD,WACE,UAAU,aAAa,aAAa,KAAK,aAAa;AAAA,IACxD,aACE,UAAU,eAAe,KAAK,eAAe,oBAAoB;AAAA,EACrE;AACF;AAEO,SAAS,yBAAyB,QAQ9B;AACT,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,MAAI,CAAC,WAAY,eAAe,CAAC,iBAAmB,QAAO;AAC3D,QAAM,iBACJ,WAAW,CAAC,QAAQ,WAAW,OAAO,IAAI,UAAU;AACtD,QAAM,cAAc,kBAAkB,OAAO,kBAAkB;AAC/D,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,cAAc,QAAQ;AACxB,UAAM,UAAU,mBACZ;AAAA,MACE,GAAG;AAAA,MACH,SAAS,WAAW,iBAAiB,WAAW;AAAA,MAChD,WAAW,aAAa,iBAAiB,aAAa;AAAA,IACxD,IACA;AAAA,MACE,KAAK;AAAA,MACL;AAAA,MACA,WAAW,aAAa;AAAA,MACxB,aAAa;AAAA,IACf;AACJ,WAAO,KAAK,UAAU,OAAO;AAAA,EAC/B;AACA,SAAO;AACT;;;ACpIA,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,eAAc;AAMxC,SAAS,eAAe,UAAiC,CAAC,GAAG;AAClE,QAAM,gBAAgBA,QAAoB,oBAAI,IAAI,CAAC;AACnD,QAAM,aAAa,QAAQ,cAAc;AAEzC,QAAM,gBAAgBF,aAAY,CAAC,QAAwB;AACzD,QAAI,CAAC,OAAO,CAAC,IAAI,WAAW,OAAO,EAAG;AACtC,kBAAc,QAAQ,OAAO,GAAG;AAChC,QAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,oBAAoB,YAAY;AAC3E,UAAI;AACF,YAAI,gBAAgB,GAAG;AAAA,MACzB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoBA,aAAY,MAAM;AAC1C,QAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,oBAAoB,YAAY;AAC3E,iBAAW,OAAO,cAAc,SAAS;AACvC,YAAI;AACF,cAAI,gBAAgB,GAAG;AAAA,QACzB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,kBAAc,QAAQ,MAAM;AAAA,EAC9B,GAAG,CAAC,CAAC;AAEL,EAAAC,WAAU,MAAM;AACd,WAAO,MAAM;AACX,wBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAEtB,QAAM,gBAAgBD;AAAA,IACpB,CAAC,SAAgC;AAC/B,UACE,OAAO,QAAQ,eACf,OAAO,IAAI,oBAAoB,YAC/B;AACA,YAAI;AACF,gBAAM,YAAY,IAAI,gBAAgB,IAAI;AAC1C,wBAAc,QAAQ,IAAI,SAAS;AACnC,iBAAO,QAAQ,QAAQ,SAAS;AAAA,QAClC,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,SAAS,IAAI,WAAW;AAC9B,eAAO,YAAY,MAAM;AACvB,cAAI,OAAO,OAAO,WAAW,UAAU;AACrC,oBAAQ,OAAO,MAAM;AAAA,UACvB,OAAO;AACL,mBAAO,IAAI,MAAM,UAAU,CAAC;AAAA,UAC9B;AAAA,QACF;AACA,eAAO,UAAU,MAAM;AACrB,iBAAO,IAAI,MAAM,UAAU,CAAC;AAAA,QAC9B;AACA,eAAO,cAAc,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,SAAO,EAAE,eAAe,eAAe,kBAAkB;AAC3D;;;AC3EA,SAAS,eAAAG,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACsBzD,IAAI,gBAAgB;AAEb,SAAS,sBAAqC;AACnD,QAAM,UAAU,oBAAI,IAA+B;AAEnD,QAAM,qBAAqB,CAAC,UAA6B;AACvD,QAAI,MAAM,UAAU;AAClB,oBAAc,MAAM,QAAQ;AAC5B,YAAM,WAAW;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,cAAc,CAClB,MACA,gBACA,UACA,mBACsB;AACtB,qBAAiB;AACjB,UAAM,KAAK,YAAY,UAAU,KAAK,IAAI,CAAC,IAAI,aAAa;AAC5D,UAAM,aAAa,IAAI,gBAAgB;AAEvC,QAAI,gBAAgB;AAClB,UAAI,eAAe,SAAS;AAC1B,mBAAW,MAAM;AAAA,MACnB,OAAO;AACL,uBAAe,iBAAiB,SAAS,MAAM,WAAW,MAAM,GAAG;AAAA,UACjE,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ,IAAI,EAAE;AAC/B,QAAI,UAAU;AACZ,yBAAmB,QAAQ;AAC3B,eAAS,WAAW,MAAM;AAC1B,cAAQ,OAAO,EAAE;AAAA,IACnB;AAEA,UAAM,QAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA,UAAU;AAAA,IACZ;AACA,YAAQ,IAAI,IAAI,KAAK;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,CAAC,OAAe;AAClC,UAAM,QAAQ,QAAQ,IAAI,EAAE;AAC5B,QAAI,OAAO;AACT,yBAAmB,KAAK;AACxB,cAAQ,OAAO,EAAE;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,aAAqC;AAC5D,QAAI,UAAU;AACd,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG;AAC5C,UACG,OAAO,aAAa,YAAY,QAAQ,YACzC,MAAM,SAAS,UACf;AACA,2BAAmB,KAAK;AACxB,cAAM,WAAW,MAAM;AACvB,gBAAQ,OAAO,GAAG;AAClB,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM;AACrB,eAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,yBAAmB,KAAK;AACxB,YAAM,WAAW,MAAM;AAAA,IACzB;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACnFA,IAAM,wBAAwB;AAE9B,SAAS,qBAAqB,SAAyB;AACrD,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,gBACJ,oEAAoE;AAAA,IAClE;AAAA,EACF;AAEF,MAAI,UAAU;AACd,MAAI,eAAe;AACjB,UAAM,aAAa,QAAQ,MAAM,kCAAkC;AACnE,UAAM,UAAU,QAAQ,MAAM,4BAA4B;AAC1D,UAAM,aAAa,UAAU,CAAC,KAAK,aAAa,CAAC,KAAK,SACnD,QAAQ,6BAA6B,GAAG,EACxC,QAAQ,+BAA+B,GAAG,EAC1C,QAAQ,YAAY,GAAG,EACvB,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACR,cAAU;AAAA,EACZ;AAEA,MAAI,QAAQ,SAAS,uBAAuB;AAC1C,WAAO,GAAG,QAAQ,MAAM,GAAG,qBAAqB,EAAE,QAAQ,CAAC;AAAA,EAC7D;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,UAAU,oBAA2B;AAC1E,QAAM,MAAM,IAAI,MAAM,OAAO;AAC7B,MAAI,OAAO;AACX,SAAO;AACT;AAEO,SAAS,mBAAmB,KAAuB;AACxD,SACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACT,IAA2B,SAAS;AAEzC;AAEO,SAAS,kBACd,QACA,SACA,gBAAgB,iBACR;AACR,QAAM,aAAa,UAAU,MAAM;AACnC,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,QAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,eAAS;AAAA,IACX;AAAA,EACF,QAAQ;AACN,aAAS;AAAA,EACX;AAEA,QAAM,gBAAgB,CAAC,SAAS,WAAW,UAAU,SAAS;AAC9D,MAAI,QAAQ;AACV,eAAW,OAAO,eAAe;AAC/B,YAAM,MAAM,OAAO,GAAG;AACtB,UAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,GAAG;AACzC,eAAO,GAAG,IAAI,KAAK,CAAC,KAAK,UAAU;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,UAAU,qBAAqB,OAAO,IAAI;AAC9D,MAAI,aAAa;AACf,WAAO,GAAG,WAAW,KAAK,UAAU;AAAA,EACtC;AAEA,SAAO,GAAG,aAAa,KAAK,UAAU;AACxC;AAEO,SAAS,oBACd,MACA,SAA2B,CAAC,GAClB;AACV,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,WAAW,IAAI,SAAS;AAC9B,WAAS,OAAO,QAAQ,IAAI;AAC5B,MAAI,OAAQ,UAAS,OAAO,UAAU,MAAM;AAC5C,MAAI,OAAQ,UAAS,OAAO,UAAU,MAAM;AAC5C,MAAI,OAAQ,UAAS,OAAO,UAAU,MAAM;AAC5C,MAAI,QAAS,UAAS,OAAO,WAAW,OAAO;AAC/C,MAAI,eAAgB,UAAS,OAAO,kBAAkB,cAAc;AACpE,MAAI,aAAc,UAAS,OAAO,gBAAgB,YAAY;AAC9D,MAAI,YAAY,OAAW,UAAS,OAAO,WAAW,OAAO,OAAO,CAAC;AACrE,MAAI,eAAe;AACjB,aAAS,OAAO,YAAY,OAAO,cAAc,KAAK,CAAC;AACvD,aAAS,OAAO,aAAa,OAAO,cAAc,MAAM,CAAC;AAAA,EAC3D;AACA,MAAI,YAAY,OAAO,KAAK,QAAQ,EAAE,SAAS,GAAG;AAChD,aAAS,OAAO,YAAY,KAAK,UAAU,QAAQ,CAAC;AAAA,EACtD;AAEA,SAAO;AACT;AAEO,SAAS,aACd,UACA,UACA,UAA4B,CAAC,GACD;AAC5B,QAAM,gBAAgB,QAAQ,sBAAsB;AACpD,QAAM,eACJ,QAAQ,qBAAqB;AAC/B,QAAM,mBACJ,QAAQ,wBACR;AACF,QAAM,eAAe,QAAQ,qBAAqB;AAElD,MAAI,QAAQ,QAAQ,SAAS;AAC3B,WAAO,QAAQ,OAAO,uBAAuB,YAAY,CAAC;AAAA,EAC5D;AAEA,MAAI,OAAO,mBAAmB,aAAa;AACzC,WAAO,MAAM,UAAU;AAAA,MACrB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,QAAQ;AAAA,MACjB,aAAa,QAAQ,kBAAkB,YAAY;AAAA,MACnD,QAAQ,QAAQ;AAAA,IAClB,CAAC,EAAE,KAAK,OAAO,QAAQ;AACrB,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,QAAQ,MAAM,aAAa,CAAC;AAAA,MACpE;AACA,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,QAAQ;AACN,cAAM,IAAI,MAAM,gBAAgB;AAAA,MAClC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,eAAe;AAC/B,QAAI,KAAK,QAAQ,UAAU,IAAI;AAC/B,QAAI,kBAAkB,QAAQ,QAAQ,eAAe;AAErD,QAAI,QAAQ,SAAS;AACnB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC1D,YAAI,iBAAiB,KAAK,KAAK;AAAA,MACjC;AAAA,IACF;AAEA,UAAM,gBAAgB,MAAM;AAC1B,UAAI,MAAM;AAAA,IACZ;AACA,YAAQ,QAAQ,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AAEvE,UAAM,UAAU,MAAM;AACpB,cAAQ,QAAQ,oBAAoB,SAAS,aAAa;AAAA,IAC5D;AAEA,QAAI,OAAO,aAAa,CAAC,UAAU;AACjC,UAAI,MAAM,oBAAoB,MAAM,QAAQ,GAAG;AAC7C,cAAM,MAAM,KAAK;AAAA,UACf;AAAA,UACA,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,EAAE;AAAA,QAC9C;AACA,gBAAQ,mBAAmB,GAAG;AAAA,MAChC;AAAA,IACF;AAEA,QAAI,SAAS,MAAM;AACjB,cAAQ;AACR,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI,YAAY;AAC1C,kBAAQ,MAAM;AAAA,QAChB,QAAQ;AACN,iBAAO,IAAI,MAAM,gBAAgB,CAAC;AAAA,QACpC;AAAA,MACF,OAAO;AACL;AAAA,UACE,IAAI;AAAA,YACF,kBAAkB,IAAI,QAAQ,IAAI,cAAc,aAAa;AAAA,UAC/D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,MAAM;AAClB,cAAQ;AACR,aAAO,IAAI,MAAM,YAAY,CAAC;AAAA,IAChC;AAEA,QAAI,UAAU,MAAM;AAClB,cAAQ;AACR,aAAO,uBAAuB,YAAY,CAAC;AAAA,IAC7C;AAEA,QAAI,KAAK,QAAQ;AAAA,EACnB,CAAC;AACH;;;AF9MO,SAAS,eAAe;AAAA,EAC7B;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAAc;AAAA,EAAS;AAAA,EAClE,gBAAgB;AAAA,EAChB,UAAU;AAAA,EAAc,UAAU;AAAA,EAAc,SAAS;AAAA,EACzD,iBAAiB;AAAA,EAAqB,QAAQ;AAAA,EAC9C;AAAA,EAAW;AAAA,EAAS;AACtB,IAA2B,CAAC,GAAG;AAC7B,QAAM,MAAM,sBAAsB;AAClC,QAAM,eAAe,sBAAsB,cAAc,IAAI,MAAM;AACnE,QAAM,KAAK,oBAAoB,cAAc,IAAI,YAAY;AAE7D,QAAM,mBAAmB,gBAAgB,IAAI,YAAY;AACzD,QAAM,mBAAmB,gBAAgB,IAAI;AAC7C,QAAM,kBAAkB,eAAe,IAAI;AAC3C,QAAM,sBAAsB,uBAAuB,IAAI;AAEvD,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AACpD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,CAAC;AAC1C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,EAAE;AAErD,QAAM,eAAeC,QAAO,IAAI;AAChC,QAAM,cAAcA,QAA2C,oBAAI,IAAI,CAAC;AACxE,QAAM,aAAaA,QAA6B,IAAI;AACpD,MAAI,CAAC,WAAW,QAAS,YAAW,UAAU,oBAAoB;AAElE,QAAM,mBAAmBC,aAAY,MAAM;AACzC,gBAAY,QAAQ,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAC;AAClD,gBAAY,QAAQ,MAAM;AAAA,EAC5B,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiBA,aAAY,CAAC,IAAgB,OAAe;AACjE,UAAM,SAAS,WAAW,MAAM;AAC9B,kBAAY,QAAQ,OAAO,MAAM;AACjC,UAAI,aAAa,QAAS,IAAG;AAAA,IAC/B,GAAG,EAAE;AACL,gBAAY,QAAQ,IAAI,MAAM;AAC9B,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,EAAAC,WAAU,MAAM;AACd,iBAAa,UAAU;AACvB,UAAM,UAAU,WAAW;AAC3B,WAAO,MAAM;AACX,mBAAa,UAAU;AACvB,eAAS,SAAS;AAClB,uBAAiB;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,gBAAgB,CAAC;AAErB,QAAM,cAAcD,aAAY,CAAC,aAA4B;AAC3D,UAAM,UAAU,WAAW;AAC3B,UAAM,UAAU,QAAQ,gBAAgB,QAAQ;AAChD,QAAI,WAAW,aAAa,WAAW,QAAQ,QAAQ,SAAS,GAAG;AACjE,qBAAe,KAAK;AACpB,kBAAY,CAAC;AACb,uBAAiB,EAAE;AAAA,IACrB;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,WAAWA,aAAY,MAAM;AACjC,eAAW,SAAS,SAAS;AAC7B,qBAAiB;AACjB,QAAI,aAAa,SAAS;AACxB,qBAAe,KAAK;AACpB,kBAAY,CAAC;AACb,uBAAiB,EAAE;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,gBAAgB,CAAC;AAErB,QAAM,cAAcA;AAAA,IAClB,OACE,MACA,eACA,wBACA,gBACsC;AACtC,YAAM,UAAU,WAAW;AAC3B,YAAM,QAAQ,QAAQ;AAAA,QACpB;AAAA,QACA,GAAG;AAAA,QACH,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AACA,YAAM,EAAE,IAAI,UAAU,WAAW,IAAI;AAErC,YAAM,eAAe,KAAK;AAC1B,qBAAe,IAAI;AACnB,kBAAY,CAAC;AACb,uBAAiB,GAAG,eAAe;AACnC,mBAAa,GAAG,GAAG,iBAAiB,MAAM,QAAQ;AAElD,YAAM,sBAAsB,CAAC,SAAiB,WAAmB;AAC/D,YAAI,CAAC,aAAa,WAAW,WAAW,OAAO,QAAS;AACxD,cAAM,UAAU,KAAK,IAAI,MAAM,UAAU,KAAK,IAAI,QAAQ,OAAO,CAAC;AAClE,cAAM,WAAW;AACjB,cAAM,MAAM,iBAAiB,SAAS,YAAY;AAClD,cAAM,UAAU;AAChB,oBAAY,OAAO;AACnB,yBAAiB,GAAG;AACpB,qBAAa,SAAS,KAAK,MAAM,QAAQ;AAAA,MAC3C;AAEA,YAAM,iBAAiB,CAAC,YAAoB,oBAAoB,SAAS,EAAE;AAE3E,UAAI;AACF,cAAM,WAAW,YAAY,MAAM;AACjC,cAAI,CAAC,aAAa,WAAW,WAAW,OAAO,SAAS;AACtD,oBAAQ,YAAY,QAAQ;AAC5B;AAAA,UACF;AACA,8BAAoB,MAAM,WAAW,GAAG,EAAE;AAAA,QAC5C,GAAG,GAAG;AAEN,cAAM,WAAW,kBAAkB,SAAY,iBAAiB,SAAY,WAAW;AACvF,cAAM,sBAAsB,0BAA0B,yBAAyB;AAC/E,cAAM,gBAAgB;AAAA,UACpB;AAAA,UAAQ;AAAA,UAAQ;AAAA,UAAQ,SAAS;AAAA,UAAU,gBAAgB;AAAA,UAC3D;AAAA,UAAc;AAAA,UAAS;AAAA,UAAe;AAAA,QACxC;AAEA,cAAM,OAAO,mBACT,MAAM,iBAAiB,MAAM;AAAA,UAC3B,GAAG;AAAA,UACH,QAAQ,WAAW;AAAA,UACnB,kBAAkB;AAAA,QACpB,CAAC,IACD,MAAM,aAAa,kBAAkB,oBAAoB,MAAM,aAAa,GAAG;AAAA,UAC7E,SAAS;AAAA,UACT,iBAAiB;AAAA,UACjB,QAAQ,WAAW;AAAA,UACnB,oBAAoB,GAAG;AAAA,UACvB,mBAAmB,GAAG;AAAA,UACtB,sBAAsB,GAAG;AAAA,UACzB,mBAAmB,GAAG;AAAA,UACtB,kBAAkB;AAAA,QACpB,CAAC;AAEL,gBAAQ,YAAY,QAAQ;AAC5B,YAAI,CAAC,aAAa,WAAW,WAAW,OAAO,QAAS,QAAO;AAE/D,cAAM,WAAW;AACjB,oBAAY,GAAG;AACf,cAAM,aAAa,qBAAqB,cAAc,KAAK,MAAM,OAAO,YAAY;AACpF,yBAAiB,UAAU;AAC3B,qBAAa,KAAK,YAAY,MAAM,QAAQ;AAE5C,uBAAe,MAAM;AACnB,cAAI,QAAQ,QAAQ,SAAS,EAAG,kBAAiB,EAAE;AAAA,QACrD,GAAG,GAAI;AAEP,oBAAY,MAAM,MAAM,QAAQ;AAChC,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,gBAAQ,YAAY,QAAQ;AAC5B,YAAI,CAAC,aAAa,WAAW,WAAW,OAAO,WAAW,mBAAmB,GAAG,GAAG;AACjF,iBAAO;AAAA,QACT;AACA,cAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,GAAG;AACzD,yBAAiB,EAAE;AACnB,kBAAU,UAAU,MAAM,QAAQ;AAClC,eAAO;AAAA,MACT,UAAE;AACA,gBAAQ,YAAY,QAAQ;AAC5B,YAAI,aAAa,WAAW,QAAQ,QAAQ,SAAS,GAAG;AACtD,yBAAe,KAAK;AACpB,yBAAe,MAAM;AACnB,gBAAI,QAAQ,QAAQ,SAAS,EAAG,aAAY,CAAC;AAAA,UAC/C,GAAG,GAAI;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAU;AAAA,MAAS;AAAA,MAAc;AAAA,MAAS;AAAA,MAClE;AAAA,MAAuB;AAAA,MAAkB;AAAA,MAAkB;AAAA,MAC3D;AAAA,MAAqB;AAAA,MAAc,GAAG;AAAA,MAAiB,GAAG;AAAA,MAC1D,GAAG;AAAA,MAAc,GAAG;AAAA,MAAuB,GAAG;AAAA,MAC9C;AAAA,MAAW;AAAA,MAAS;AAAA,MAAY;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,QAAQA,aAAY,MAAM;AAC9B,aAAS;AAAA,EACX,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAO,EAAE,aAAa,aAAa,UAAU,aAAa,UAAU,eAAe,MAAM;AAC3F;;;ARxMO,SAAS,oBACd,OACA;AACA,QAAM;AAAA,IACJ;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAU;AAAA,IAC7D;AAAA,IAAS;AAAA,IAAiB,QAAQ;AAAA,IAAY;AAAA,IAC9C,gBAAgB;AAAA,IAAoB,WAAW;AAAA,IAC/C;AAAA,IAAe,gBAAgB;AAAA,IAAoB;AAAA,IAAU;AAAA,IAAQ;AAAA,IACrE;AAAA,IAAc,SAAS;AAAA,IAAoB;AAAA,IAAgB;AAAA,EAC7D,IAAI;AAEJ,QAAM,MAAM,sBAAsB;AAClC,QAAM,SAAS,sBAAsB,cAAc,IAAI,MAAM;AAC7D,QAAM,KAAK,oBAAoB,QAAQ,gBAAgB,IAAI,YAAY;AACvE,QAAM,iBAAiB,sBAAsB,IAAI;AACjD,QAAM,iBAAiB,SAAS,iBAAiB,MAA4B,IAAI;AACjF,QAAM,YAAY,iBAAiB,gBAAgB,aAAa;AAChE,QAAM,uBAAuB,QAAQ,MAAM;AAC3C,QAAM,QAAQE,SAAQ,MAAM,yBAAyB,IAAI,oBAAoB,KAAK,GAAG,CAAC,IAAI,gBAAgB,CAAC;AAE3G,QAAM,SAAS,OAAO,UAAU,YAAY,QAAQ,QAAQ;AAC5D,QAAM,cAAc,OAAO,UAAU,WAAW,QAAQ,QAAQ,OAAO;AACvE,QAAM,UAAU,QAAQ,WAAW,sBAAsB;AACzD,QAAM,YAAY,QAAQ,aAAa;AAEvC,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAwB,WAAW;AACjE,QAAM,aAAaC,QAAsB,WAAW;AACpD,aAAW,UAAU;AAErB,QAAM,CAAC,SAAS,UAAU,IAAID,UAAiB,OAAO;AACtD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAiB,SAAS;AAC5D,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,UAAiB,SAAS;AAC5E,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAiB,QAAQ,eAAe,EAAE;AAC1F,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,KAAK;AAC9D,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAiB,EAAE;AACjE,QAAM,CAAC,uBAAuB,wBAAwB,IAAIA,UAAS,KAAK;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAyD,IAAI;AAC7F,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAmC,UAAU,SAAS,SAAS,SAAS,IAAI;AACpH,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,KAAK;AAC5D,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAsB,IAAI;AAClE,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAuB,MAAM,2BAA2B,oBAAoB,cAAc,CAAC;AACnI,QAAM,eAAeC,QAAyB,IAAI;AAClD,QAAM,aAAaA,QAAe,OAAO;AACzC,QAAM,oBAAoBA,QAAsB,IAAI;AACpD,QAAM,eAAeA,QAAO,CAAC;AAE7B,QAAM,0BAA0B,MAAM;AACpC,uBAAmB,EAAE;AACrB,wBAAoB,KAAK;AACzB,iBAAa,kBAAkB;AAC/B,yBAAqB,kBAAkB;AACvC,uBAAmB,KAAK;AAAA,EAC1B;AAEA,QAAM,EAAE,eAAe,cAAc,IAAI,eAAe,EAAE,YAAY,GAAG,oBAAoB,CAAC;AAE9F,QAAM,EAAE,aAAa,UAAU,aAAa,UAAU,eAAe,MAAM,IAAI,eAAe;AAAA,IAC5F;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAc;AAAA,IAAe;AAAA,IAC/D,SAAS,gBAAgB,YAAY,EAAE;AAAA,IACvC;AAAA,IAAU;AAAA,IAAU;AAAA,IAAS;AAAA,IAAiB;AAAA,IAC9C,WAAW,CAAC,MAAM,OAAO,aAAa;AACpC,UAAI,YAAY,kBAAkB,WAAW,aAAa,kBAAkB,QAAS;AACrF,UAAI,WAAW,WAAW,WAAW,YAAY,KAAK,IAAK,eAAc,WAAW,OAAO;AAC3F,iBAAW,KAAK,GAAG;AACnB,kBAAY,EAAE,UAAU,KAAK,gBAAgB,GAAG,WAAW,KAAK,KAAK,CAAC;AACtE,YAAM,YAAY,KAAK,aAAa;AACpC,mBAAa,SAAS;AACtB,2BAAqB,SAAS;AAC9B,0BAAoB,KAAK;AACzB,0BAAoB,KAAK,eAAe,EAAE;AAC1C,YAAM,UAAU,EAAE,KAAK,KAAK,KAAK,aAAa,KAAK,YAAY;AAC/D,YAAM,iBAAiB,SAAS,EAAE,aAAa,KAAK,eAAe,IAAI,WAAW,UAAU,CAAC;AAC7F,YAAM,aAAa,WAAW,WAAW,KAAK,WAAW;AACzD,iBAAW,UAAU;AACrB,iBAAW,UAAU;AACrB,YAAM,UAA6B,EAAE,GAAG,MAAM,SAAS,YAAY,WAAW,UAAU;AACxF,sBAAgB,OAAO;AACvB,YAAM,gBAAgB,SAAS,UAAU;AACzC,UAAI,CAAC,qBAAsB,UAAS,OAAO;AAAA,IAC7C;AAAA,IACA,SAAS,CAAC,UAAU,OAAO,aAAa;AACtC,UAAI,YAAY,kBAAkB,WAAW,aAAa,kBAAkB,QAAS;AACrF,oBAAc,WAAW,OAAO;AAChC,yBAAmB,QAAQ;AAC3B,iBAAW,IAAI;AACf,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,CAAC;AAED,EAAAC,WAAU,MAAM;AACd,QAAI,WAAW,MAAO,UAAS;AAAA,EACjC,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAErB,EAAAA,WAAU,MAAM;AACd,UAAM,cAAc,OAAO,UAAU,YAAY,QAAQ,QAAQ;AACjE,UAAM,cAAc,OAAO,UAAU,WAAW,QAAQ,aAAa,OAAO;AAC5E,UAAM,SAAS,aAAa,WAAW,sBAAsB;AAC7D,UAAM,YAAY,MAAM,eAAe,EAAE,KAAK,cAAc,YAAY,MAAM,aAAa,aAAa,aAAa,eAAe,KAAK,CAAC;AAC1I,UAAM,UACJ,OAAO,KAAK,MAAM,MAAM,gBAAgB,YAAY,WAAW,QAAQ,KAAK,MAAM,MAAM,WAAW,KAAK,KACpG,WAAW,QAAQ,KAAK,KAAK,aAAa,SAC1C;AACN,UAAM,YAAY,aAAa,aAAa;AAE5C,QAAI,mBAAmB,WAAW,MAAM,mBAAmB,OAAO,EAAG,YAAW,WAAW;AAC3F,QAAI,YAAY,YAAY,gBAAgB,WAAW,QAAQ,KAAK,MAAM,MAAM,CAAC,QAAQ,KAAK,IAAI;AAChG,iBAAW,OAAO;AAClB,iBAAW,UAAU;AAAA,IACvB;AAEA,UAAM,iBAAiB,cAAc;AACrC,QAAI,cAAc,cAAc,gBAAgB,WAAY,CAAC,qBAAqB,CAAC,kBAAkB,cAAc,sBAAuB;AACxI,mBAAa,SAAS;AACtB,2BAAqB,SAAS;AAC9B,0BAAoB,KAAK;AAAA,IAC3B;AACA,UAAM,cAAc,aAAa,eAAe;AAChD,QAAI,gBAAgB,WAAY,eAAe,gBAAgB,kBAAmB;AAChF,0BAAoB,WAAW;AAAA,IACjC;AAEA,QAAI,aAAa;AACf,YAAM,SAAS,MAAM,gBAAgB;AAAA,QACnC,KAAK;AAAA,QACL,aAAa,aAAa,eAAe,cAAc;AAAA,MACzD,CAAC;AACD,UAAI,QAAQ,eAAe,CAAC,YAAa,qBAAoB,OAAO,WAAW;AAC/E,UAAI,QAAQ,aAAa,kBAAkB,cAAc,oBAAoB;AAC3E,qBAAa,OAAO,SAAS;AAC7B,6BAAqB,OAAO,SAAS;AAAA,MACvC;AAAA,IACF;AAEA,QAAI,CAAC,OAAO;AACV,sBAAgB,IAAI;AACpB,0BAAoB,EAAE;AACtB,kBAAY,IAAI;AAChB,sBAAgB,IAAI;AACpB,iBAAW,UAAU,sBAAsB;AAC3C,UAAI,aAAa,QAAS,cAAa,QAAQ,QAAQ;AAAA,IACzD,WAAW,aAAa,KAAK;AAC3B,sBAAgB,CAAC,SAAU,MAAM,QAAQ,YAAY,MAAM,OAAO,WAAY;AAAA,IAChF;AAAA,EAEF,GAAG,CAAC,OAAO,kBAAkB,CAAC;AAE9B,QAAM,eAAe,CAAC,cAA0C;AAC9D,QAAI,CAAC,sBAAsB;AACzB,YAAM,UAAU;AAAA,QACd,EAAE,cAAc,QAAQ,OAAO,SAAS,SAAS,WAAW,iBAAiB;AAAA,QAC7E;AAAA,MACF;AACA,UAAI,QAAS,UAAS,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,OAAe,UAAsC,CAAC,MAAM;AAClF,UAAM,aAAa,oBAAoB,KAAK;AAC5C,QAAI,CAAC,WAAY;AACjB,iBAAa,UAAU;AACvB,wBAAoB,QAAQ,iBAAiB,KAAK;AAClD,oBAAgB,CAAC,SAAU,OAAO,EAAE,GAAG,MAAM,WAAW,WAAW,IAAI,IAAK;AAC5E,QAAI,QAAS,OAAM,iBAAiB,EAAE,KAAK,SAAS,aAAa,cAAc,YAAY,GAAG,EAAE,aAAa,kBAAkB,WAAW,WAAW,CAAC;AACtJ,iBAAa,EAAE,WAAW,WAAW,CAAC;AAAA,EACxC;AAEA,QAAM,mBAAmB,MAAM;AAC7B,QAAI,kBAAmB,gBAAe,mBAAmB,EAAE,cAAc,MAAM,CAAC;AAAA,EAClF;AAEA,QAAM,mBAAmB,OAAO,UAAoB;AAClD,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,CAAC,KAAM;AACX,aAAS;AACT,oBAAgB,IAAI;AACpB,4BAAwB;AACxB,UAAM,MAAM,kBAAkB,MAAM,WAAW,MAAM;AACrD,QAAI,KAAK;AACP,yBAAmB,GAAG;AACtB;AAAA,IACF;AACA,iBAAa,WAAW;AACxB,UAAM,WAAW,UAAU,aAAa,OAAO;AAC/C,sBAAkB,UAAU;AAC5B,QAAI;AACF,YAAM,cAAc,MAAM,cAAc,IAAI;AAC5C,UAAI,kBAAkB,YAAY,UAAU;AAC1C,sBAAc,WAAW;AACzB;AAAA,MACF;AACA,oBAAc,WAAW,OAAO;AAChC,iBAAW,WAAW;AACtB,iBAAW,EAAE;AACb,iBAAW,UAAU;AAAA,IACvB,QAAQ;AACN,yBAAmB,GAAG,mBAAmB;AACzC;AAAA,IACF;AACA,UAAM,YAAY,MAAM,IAAI,gBAAgB,EAAE,SAAS,CAAC;AAAA,EAC1D;AAEA,QAAM,qBAAqB,MAAM;AAC/B,sBAAkB,UAAU;AAC5B,aAAS;AACT,kBAAc,WAAW,OAAO;AAChC,eAAW,IAAI;AACf,oBAAgB,IAAI;AACpB,UAAM;AACN,QAAI,aAAa,QAAS,cAAa,QAAQ,QAAQ;AAAA,EACzD;AAEA,QAAM,mBAAmB,MAAM;AAC7B,uBAAmB;AACnB,eAAW,EAAE;AACb,eAAW,UAAU;AACrB,wBAAoB,EAAE;AACtB,gBAAY,IAAI;AAChB,oBAAgB,IAAI;AACpB,4BAAwB;AACxB,QAAI,CAAC,qBAAsB,UAAS,IAAI;AAAA,EAC1C;AAEA,QAAM,oBAAoB,CAAC,eAAuB;AAChD,eAAW,UAAU;AACrB,eAAW,UAAU;AACrB,QAAI,QAAS,OAAM,gBAAgB,EAAE,KAAK,SAAS,aAAa,cAAc,YAAY,GAAG,UAAU;AACvG,6BAAyB,KAAK;AAC9B,sBAAkB,KAAK;AACvB,iBAAa,EAAE,SAAS,WAAW,CAAC;AAAA,EACtC;AAEA,QAAM,kBAAkB,MAAM;AAC5B,QAAI,YAAY,CAAC,SAAS;AACxB,yBAAmB,GAAG,kBAAkB;AACxC;AAAA,IACF;AACA,QAAI,kBAAkB,WAAW,CAAC,SAAS,KAAK,GAAG;AACjD,+BAAyB,IAAI;AAC7B,yBAAmB,GAAG,kBAAkB;AACxC;AAAA,IACF;AACA,6BAAyB,KAAK;AAC9B,uBAAmB,EAAE;AACrB,UAAM,UAAU,wBAAwB,EAAE,cAAc,QAAQ,OAAO,SAAS,SAAS,WAAW,iBAAiB,CAAC;AACtH,QAAI,SAAS;AACX,iBAAW,UAAU,QAAQ,WAAW,WAAW;AACnD,YAAM,gBAAgB,EAAE,KAAK,QAAQ,KAAK,aAAa,QAAQ,YAAY,GAAG,QAAQ,WAAW,EAAE;AACnG,eAAS,OAAO;AAAA,IAClB;AACA,cAAU;AAAA,EACZ;AAEA,QAAM,mBAAmB,UAAW,gBAAgB,SAAU;AAC9D,QAAM,cAAc,UACf,cAAc,QAAQ,OAAO,UAAU,WAAW,QAAQ,QAAQ,QAAQ,OAC3E;AACJ,QAAM,iBAAiBC,aAAY,YAAY;AAC7C,QAAI,YAAa,OAAM,0BAA0B,aAAa,cAAc,MAAM;AAAA,EACpF,GAAG,CAAC,aAAa,cAAc,MAAM,CAAC;AAEtC,QAAM,mBAAmB,wBAAwB,aAAa,UAAU,QAAQ,YAAY,CAAC,WAAW,GAAG,EAAE;AAC7G,QAAM,sBAAsB,yBAAyB;AAAA,IACnD;AAAA,IAAW;AAAA,IAAkB;AAAA,IAAS;AAAA,IAAS;AAAA,IAAW;AAAA,IAAkB;AAAA,EAC9E,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IAAQ;AAAA,IAAI;AAAA,IAAgB;AAAA,IAAW;AAAA,IACvC;AAAA,IAAS;AAAA,IAAS;AAAA,IAAW;AAAA,IAAmB;AAAA,IAChD;AAAA,IAAiB;AAAA,IAAuB;AAAA,IAAgB;AAAA,IACxD;AAAA,IAAU;AAAA,IAAc;AAAA,IAAiB;AAAA,IACzC;AAAA,IAAc;AAAA,IAAc;AAAA,IAAiB;AAAA,IAAa;AAAA,IAAU;AAAA,IAAe;AAAA,IACnF;AAAA,IAAkB,aAAa,QAAQ,WAAW;AAAA,IAAG;AAAA,IACrD;AAAA,IAAgB;AAAA,IAAkB;AAAA,IAAkB;AAAA,IAAoB;AAAA,IACxE;AAAA,IAAmB;AAAA,IAAiB;AAAA,EACtC;AACF;;;AWlSO,IAAM,qBAAoC;AAAA,EAC/C,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,QAAQ;AACV;AAKO,SAAS,MACX,SACK;AACR,SAAO,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;AAOO,SAAS,iBACd,cACA,aACG,eACiB;AACpB,QAAM,SAAS,WACX,GAAG,GAAG,aAAa,IACnB,GAAG,cAAc,GAAG,aAAa;AACrC,SAAO,UAAU;AACnB;AAKO,SAAS,oBACX,QACwB;AAC3B,MAAI;AACJ,aAAW,SAAS,QAAQ;AAC1B,QAAI,OAAO;AACT,eAAS,SAAS,EAAE,GAAG,QAAQ,GAAG,MAAM,IAAI,EAAE,GAAG,MAAM;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,uBAAuB,OAMjB;AACpB,MAAI,MAAM,SAAU,QAAO;AAC3B,MAAI,MAAM,WAAY,QAAO;AAC7B,MAAI,MAAM,YAAa,QAAO;AAC9B,MAAI,MAAM,SAAU,QAAO;AAC3B,MAAI,MAAM,WAAY,QAAO;AAC7B,SAAO;AACT;;;AC/DA,IAAM,iBAAiB;AAEhB,SAAS,gBACd,SACwB;AACxB,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,WAAW;AAAA,IACX,OAAO;AAAA,IACP;AAAA,IACA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,WAAW;AAAA,IACX;AAAA,EACF,IAAI;AAEJ,QAAM;AAAA,IACJ;AAAA,IAAQ;AAAA,IAAI;AAAA,IAAW;AAAA,IAAc;AAAA,IAAS;AAAA,IAAS;AAAA,IACvD;AAAA,IAAmB;AAAA,IAAkB;AAAA,IAAiB;AAAA,IACtD;AAAA,IAAc;AAAA,IAAc;AAAA,IAAiB;AAAA,IAAa;AAAA,IAC1D;AAAA,IAAe;AAAA,IAAkB;AAAA,IAAa;AAAA,IAC9C;AAAA,IAAkB;AAAA,IAAkB;AAAA,IAAoB;AAAA,IACxD;AAAA,IAAmB;AAAA,IAAiB;AAAA,EACtC,IAAI,oBAAoB,OAAO;AAE/B,QAAM,EAAE,YAAY,aAAa,IAAI,eAAe;AAAA,IAClD;AAAA,IACA,QAAQ,CAAC,UAAU;AAAE,WAAK,iBAAiB,KAAK;AAAA,IAAG;AAAA,EACrD,CAAC;AAED,QAAM,EAAE,QAAQ,IAAI,kBAAkB;AAAA,IACpC,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,cAAc,CAAC,UAAU;AAAE,WAAK,iBAAiB,KAAK;AAAA,IAAG;AAAA,EAC3D,CAAC;AAED,QAAM,eAAe,iBAAiB,mBAAmB;AACzD,QAAM,cAAc,oBAAoB,kBAAkB,eAAe;AAEzE,QAAM,QAAQ,uBAAuB;AAAA,IACnC;AAAA,IACA,UAAU,QAAQ,YAAY;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,YAAY,QAAQ,OAAO;AAAA,EAC7B,CAAC;AAED,QAAM,iBAAiB,MAAM;AAC3B,QAAI,CAAC,SAAU,cAAa,SAAS,MAAM;AAAA,EAC7C;AAEA,QAAM,mBAAmB,CACvB,eAC2B;AAAA,IAC3B,GAAG;AAAA,IACH,MAAM,WAAW,QAAQ;AAAA,IACzB,UAAU,WAAW,aAAa,WAAW,KAAK;AAAA,IAClD,cAAc,YAAY,YAAY,KAAK,GAAG;AAAA,IAC9C,iBAAiB,YAAY;AAAA,IAC7B,iBAAiB,YAAY;AAAA,IAC7B,gBAAgB,QAAQ,YAAY,KAAK;AAAA,IACzC,cAAc;AAAA,IACd,iBAAiB,cAAc;AAAA,IAC/B,kBAAkB,eAAe;AAAA,IACjC,iBAAiB,YAAY;AAAA,IAC7B,cAAc,QAAQ,YAAY,KAAK;AAAA,IACvC,SAAS,CAAC,UAAU;AAClB,UAAI,MAAM,WAAW,aAAa,QAAS;AAC3C,iBAAW,UAAU,KAAK;AAC1B,UAAI,CAAC,MAAM,oBAAoB,CAAC,SAAU,gBAAe;AAAA,IAC3D;AAAA,IACA,WAAW,CAAC,UAAU;AACpB,iBAAW,YAAY,KAAK;AAC5B,UACE,CAAC,MAAM,oBACP,CAAC,YACD,MAAM,WAAW,MAAM,kBACtB,MAAM,QAAQ,WAAW,MAAM,QAAQ,MACxC;AACA,cAAM,eAAe;AACrB,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,QAAQ,CAAC,UAAU;AACjB,iBAAW,SAAS,KAAK;AACzB,UAAI,CAAC,MAAM,cAAc,SAAS,MAAM,aAA4B,GAAG;AACrE,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,SAAS,CAAC,UAAU;AAClB,iBAAW,UAAU,KAAK;AAC1B,UAAI,CAAC,MAAM,iBAAkB,SAAQ,KAAK;AAAA,IAC5C;AAAA,IACA,aAAa,CAAC,UAAU;AAAE,iBAAW,cAAc,KAAK;AAAG,mBAAa,YAAY,KAAK;AAAA,IAAG;AAAA,IAC5F,aAAa,CAAC,UAAU;AAAE,iBAAW,cAAc,KAAK;AAAG,mBAAa,YAAY,KAAK;AAAA,IAAG;AAAA,IAC5F,YAAY,CAAC,UAAU;AAAE,iBAAW,aAAa,KAAK;AAAG,mBAAa,WAAW,KAAK;AAAA,IAAG;AAAA,IACzF,QAAQ,CAAC,UAAU;AAAE,iBAAW,SAAS,KAAK;AAAG,mBAAa,OAAO,KAAK;AAAA,IAAG;AAAA,EAC/E;AAEA,QAAM,gBAAgB,CACpB,eAC4B;AAAA,IAC5B,GAAG;AAAA,IACH,GAAI,YAAY,UAAa,WAAW,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/E,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,WAAW,UAAU;AAAA,IAC7B,UAAU,WAAW,YAAY;AAAA,IACjC,cAAc,YAAY,YAAY,KAAK,GAAG;AAAA,IAC9C,OAAO,WAAW,QAAQ,EAAE,GAAG,oBAAoB,GAAG,UAAU,MAAM,IAAI;AAAA,IAC1E,SAAS,CAAC,UAAU;AAClB,iBAAW,UAAU,KAAK;AAC1B,YAAM,gBAAgB;AAAA,IACxB;AAAA,IACA,UAAU,CAAC,UAAU;AACnB,iBAAW,WAAW,KAAK;AAC3B,UAAI,CAAC,MAAM,kBAAkB;AAC3B,cAAM,QAAQ,MAAM,OAAO;AAC3B,YAAI,SAAS,MAAM,SAAS,EAAG,MAAK,iBAAiB,KAAK;AAC1D,cAAM,OAAO,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IAAO;AAAA,IAAS;AAAA,IAAc;AAAA,IAAS;AAAA,IAAW;AAAA,IAClD;AAAA,IAAa;AAAA,IAAY;AAAA,IAAa;AAAA,IAAU;AAAA,IAChD,OAAO;AAAA,IAAc;AAAA,IAAU;AAAA,IAAc;AAAA,IAAW;AAAA,IACxD;AAAA,IAAa;AAAA,IAAQ;AAAA,IAAI;AAAA,IACzB,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA,YAAY;AAAA,IACZ,cAAc,CAAC,QAAgB,eAAe,GAAG;AAAA,IACjD,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;;;AC7JA,SAAwE,UAAAC,eAAc;;;ACAtF,SAAS,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACGrC,SAAS,qBACd,KACA,MACc;AACd,QAAM,UACJ,MAAM,WAAW,CAAC,IAAI,SAAS,KAAK,IAAI,KAAK,UAAU,IAAI;AAC7D,SAAO;AAAA,IACL,IAAI,MAAM,MAAM,IAAI;AAAA,IACpB,KAAK,IAAI;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,cAAc,IAAI,gBAAgB,MAAM;AAAA,IACxC,eAAe,IAAI,QAAQ,MAAM;AAAA,IACjC,MAAM,EAAE,GAAG,KAAK,QAAQ;AAAA,IACxB;AAAA,IACA,WAAW,IAAI,aAAa,MAAM;AAAA,EACpC;AACF;AAEO,SAAS,sBACd,GACA,WAAyB,QACX;AACd,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,KAAK,KAAK,QAAQ,KAAK,KAAK,WAAW,KAAK,KAAK,SAAS;AACnE;AAEO,SAAS,oBACd,MACA,WACqB;AACrB,SAAO,KACJ,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,aAAa,CAAC,EAAE,YAAY,EACtD,IAAI,CAAC,OAAO;AAAA,IACX,GAAG,EAAE;AAAA,IACL,SAAS,EAAE,WAAW,EAAE,KAAM;AAAA,IAC9B,WAAW,EAAE,aAAa,EAAE,KAAM;AAAA,EACpC,EAAE;AACN;AAEO,SAAS,sBACd,SACA,OACA,sBACA,gBACgB;AAChB,QAAM,UAAU,oBAAI,IAA0B;AAC9C,UAAQ,QAAQ,CAAC,MAAM;AACrB,YAAQ,IAAI,EAAE,IAAI,CAAC;AACnB,QAAI,EAAE,IAAK,SAAQ,IAAI,EAAE,KAAK,CAAC;AAC/B,QAAI,EAAE,MAAM,IAAK,SAAQ,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EAC5C,CAAC;AAED,QAAM,SAAS,MAAM;AAAA,IAAI,CAAC,QACxB,qBAAqB,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC;AAAA,EAChD;AAEA,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM;AACpC,UAAM,UAAU,MAAM;AAAA,MACpB,CAAC,QACC,IAAI,QAAQ,EAAE,MACb,EAAE,OAAO,IAAI,QAAQ,EAAE,OACvB,EAAE,MAAM,OAAO,IAAI,QAAQ,EAAE,KAAK;AAAA,IACvC;AACA,QAAI,QAAS,QAAO;AACpB,QAAI,EAAE,eAAe,QAAQ,EAAE,KAAK,EAAG,QAAO;AAC9C,QACE,wBACA,QAAQ,EAAE,IAAI,MACb,iBAAiB,eAAe,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,WAAW,OAAO,IACpE;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AAED,QAAM,OAAO,CAAC,GAAG,QAAQ,GAAG,OAAO;AACnC,QAAM,SACJ,KAAK,WAAW,QAAQ,UACxB,KAAK,MAAM,CAAC,IAAI,QAAQ;AACtB,UAAM,IAAI,QAAQ,GAAG;AACrB,WACE,KACA,EAAE,OAAO,GAAG,MACZ,EAAE,QAAQ,GAAG,OACb,EAAE,YAAY,GAAG,WACjB,EAAE,cAAc,GAAG,aACnB,EAAE,gBAAgB,GAAG,eACrB,EAAE,UAAU,GAAG;AAAA,EAEnB,CAAC;AAEH,SAAO,SAAS,UAAU;AAC5B;AAEA,eAAsB,sBACpB,OACA,aACA,UACe;AACf,MAAI,MAAM,WAAW,EAAG;AACxB,MAAI,YAAY;AAChB,QAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,aAAa,MAAM,MAAM,CAAC;AACnE,QAAM,QAAQ;AAAA,IACZ,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,YAAY;AAC9C,aAAO,YAAY,MAAM,QAAQ;AAC/B,cAAM,OAAO,MAAM,SAAS;AAC5B,qBAAa;AACb,cAAM,SAAS,IAAI;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,SAAS,wBACd,UACA,YAA4B,QACpB;AACR,QAAM,WAAW,oBAAoB,UAAU,IAAI;AACnD,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,MAAI,cAAc,OAAO;AACvB,WAAO,SAAS,IAAI,CAAC,QAAQ,IAAI,GAAG,EAAE,KAAK,GAAG;AAAA,EAChD;AACA,SAAO,KAAK,UAAU,QAAQ;AAChC;AAEO,SAAS,uBACd,UACA,WACQ;AACR,SAAO,SACJ,QAAQ,SAAS,OAAO,SAAS,CAAC,EAClC,QAAQ,YAAY,cAAc,IAAI,KAAK,GAAG;AACnD;;;ADhHA,IAAM,eAAoC,CAAC;AAO3C,IAAI,gBAAgB;AAEb,SAAS,mBACd,SACA;AACA,QAAM,EAAE,UAAU,QAAQ,QAAQ,SAAS,WAAW,UAAU,UAAU,IAAI;AAC9E,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,qBAAqB,QAAQ,sBAAsB;AAEzD,QAAM,MAAM,sBAAsB;AAClC,QAAM,SAAS,sBAAsB,QAAQ,UAAU,IAAI,MAAM;AACjE,QAAM,KAAK,oBAAoB,QAAQ,QAAQ,gBAAgB,IAAI,YAAY;AAC/E,QAAM,iBAAiB,QAAQ,kBAAkB,IAAI;AACrD,QAAM,iBAAiB,SAAS,iBAAiB,MAA4B,IAAI;AACjF,QAAM,YAAY,QAAQ,aAAa,gBAAgB,aAAa;AACpE,QAAM,uBAAuB,QAAQ,MAAM;AAE3C,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAyB,MAAM,MAAM,IAAI,CAAC,QAAQ,qBAAqB,GAAG,CAAC,CAAC;AAC5G,QAAM,cAAcC,QAAuB,QAAQ;AACnD,cAAY,UAAU;AACtB,QAAM,oBAAoBA,QAAoB,oBAAI,IAAI,CAAC;AACvD,QAAM,gBAAgBA,QAAO,KAAK;AAElC,QAAM,CAAC,iBAAiB,kBAAkB,IAAID,UAAS,EAAE;AACzD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,EAAE;AACjD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAsB,MAAM,oBAAI,IAAI,CAAC;AACrF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA2B,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;AACtG,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAwB,IAAI;AAC5E,QAAM,CAAC,uBAAuB,wBAAwB,IAAIA,UAAS,KAAK;AACxE,QAAM,CAAC,cAAc,eAAe,IAAIA;AAAA,IAAuB,MAC7D,sBAAsB,QAAQ,WAAW,gBAAgB,SAAS,gBAAgB,uBAAuB;AAAA,EAC3G;AAEA,QAAM,EAAE,eAAe,cAAc,IAAI,eAAe,EAAE,YAAY,GAAG,oBAAoB,CAAC;AAC9F,QAAM,iBAAiBC,QAA6C,IAAI;AAExE,EAAAC,WAAU,MAAM,MAAM;AAAE,QAAI,eAAe,QAAS,cAAa,eAAe,OAAO;AAAA,EAAG,GAAG,CAAC,CAAC;AAE/F,EAAAA,WAAU,MAAM;AACd,gBAAY,CAAC,YAAY;AACvB,YAAM,WAAW,sBAAsB,SAAS,OAAO,sBAAsB,kBAAkB,OAAO;AACtG,kBAAY,UAAU;AACtB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,oBAAoB,CAAC;AAEhC,QAAM,sBAAsB,CAAC,iBAAiC;AAC5D,QAAI,CAAC,qBAAsB,UAAS,oBAAoB,cAAc,IAAI,CAAC;AAAA,EAC7E;AAEA,QAAM,kBAAkB,CAAC,UAA8B,MAAwB,OAA8B,OAAO,UAAU;AAC5H,UAAM,OAAO,YAAY,QAAQ,IAAI,CAAC,MAAM;AAC1C,UAAI,EAAE,WAAW,EAAE,OAAO,WAAW,EAAE,SAAS,MAAO,QAAO;AAC9D,UAAI,MAAM,OAAO,EAAE,OAAO,EAAE,QAAQ,MAAM,IAAK,eAAc,EAAE,GAAG;AAClE,aAAO,EAAE,GAAG,GAAG,GAAG,MAAM;AAAA,IAC1B,CAAC;AACD,gBAAY,UAAU;AACtB,gBAAY,IAAI;AAChB,QAAI,KAAM,qBAAoB,IAAI;AAAA,EACpC;AAEA,QAAM,eAAe,CAAC,IAAY,UAA6C;AAC7E,UAAM,OAAO,YAAY,QAAQ,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,MAAM,CAAC,IAAI,CAAE;AACxE,gBAAY,UAAU;AACtB,gBAAY,IAAI;AAChB,wBAAoB,IAAI;AAAA,EAC1B;AAEA,QAAM,EAAE,aAAa,aAAa,SAAS,IAAI,eAAe;AAAA,IAC5D;AAAA,IAAQ,QAAQ,QAAQ;AAAA,IAAQ,QAAQ,QAAQ;AAAA,IAAQ,UAAU,QAAQ;AAAA,IAC1E,cAAc,QAAQ;AAAA,IAAc,SAAS,gBAAgB,YAAY,EAAE;AAAA,IAC3E,eAAe,QAAQ;AAAA,IAAe,gBAAgB,QAAQ;AAAA,IAC9D,UAAU,QAAQ;AAAA,IAAU,UAAU,QAAQ;AAAA,IAC9C,SAAS,QAAQ;AAAA,IAAS,iBAAiB,QAAQ;AAAA,IAAiB;AAAA,IACpE,WAAW,CAAC,MAAM,MAAM,aAAa;AACnC,UAAI,YAAY,qBAAsB,mBAAkB,QAAQ,IAAI,QAAQ;AAC5E,sBAAgB,UAAU,MAAM;AAAA,QAC9B,KAAK,KAAK;AAAA,QAAK,eAAe;AAAA,QAAW,cAAc,KAAK;AAAA,QAC5D,eAAe,KAAK;AAAA,QAAM;AAAA,QAAM,WAAW,KAAK;AAAA,QAChD,OAAO;AAAA,QAAW,aAAa;AAAA,QAAO,UAAU;AAAA,MAClD,GAAG,IAAI;AACP,qBAAe,CAAC,OAAO,EAAE,GAAG,GAAG,WAAW,EAAE,YAAY,EAAE,EAAE;AAAA,IAC9D;AAAA,IACA,SAAS,CAAC,OAAO,MAAM,aAAa;AAClC,sBAAgB,UAAU,MAAM,EAAE,eAAe,QAAW,OAAO,aAAa,OAAO,UAAU,EAAE,CAAC;AACpG,qBAAe,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,SAAS,EAAE,EAAE;AAAA,IACxD;AAAA,IACA,YAAY,CAAC,UAAU,eAAe,MAAM,aAAa;AACvD,sBAAgB,UAAU,MAAM,EAAE,eAAe,UAAU,aAAa,KAAK,CAAC;AAAA,IAChF;AAAA,EACF,CAAC;AAED,EAAAA,WAAU,MAAM;AACd,QAAI,WAAW,OAAO;AAAE,eAAS;AAAG,wBAAkB,QAAQ,MAAM;AAAA,IAAG;AAAA,EACzE,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAErB,QAAM,kBAAkB,OAAO,OAAuB,UAAkB;AACtE,YAAQ,gBAAgB,KAAK;AAC7B,UAAM,iBAAsC,CAAC;AAC7C,UAAM,sBAAsB,OAAO,oBAAoB,OAAO,SAAS;AACrE,UAAI,CAAC,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,KAAK,CAAC,KAAK,KAAM;AACtE,YAAM,WAAW,MAAM,YAAY,KAAK,MAAM,KAAK,SAAS,QAAW,EAAE,UAAU,KAAK,GAAG,CAAC;AAC5F,UAAI,SAAU,gBAAe,KAAK,QAAQ;AAAA,IAC5C,CAAC;AACD,QAAI,eAAe,SAAS,EAAG,SAAQ,mBAAmB,cAAc;AAAA,EAC1E;AAEA,QAAM,mBAAmB,OAAO,UAAoB;AAClD,uBAAmB,EAAE;AACrB,mBAAe,EAAE;AACjB,UAAM,WAAW,MAAM,KAAK,KAAK;AACjC,UAAM,aAAa,SAAS,OAAO,CAAC,SAAS,KAAK,KAAK,WAAW,QAAQ,CAAC;AAC3E,QAAI,WAAW,WAAW,GAAG;AAAE,yBAAmB,GAAG,iBAAiB;AAAG;AAAA,IAAQ;AACjF,QAAI,SAAS,SAAS,WAAW,QAAQ;AACvC,qBAAe,2BAA2B,IAAI,WAAW,QAAQ,SAAS,SAAS,WAAW,QAAQ,MAAM,CAAC;AAC7G,UAAI,eAAe,QAAS,cAAa,eAAe,OAAO;AAC/D,qBAAe,UAAU,WAAW,MAAM,eAAe,EAAE,GAAG,GAAI;AAAA,IACpE;AAEA,UAAM,cAAc,YAAY,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE;AAChE,UAAM,mBAAmB,sBAAsB,YAAY,WAAW,aAAa,WAAW,MAAM;AACpG,QAAI,kBAAkB;AAAE,yBAAmB,gBAAgB;AAAG;AAAA,IAAQ;AAEtE,mBAAe,EAAE,OAAO,WAAW,QAAQ,WAAW,GAAG,QAAQ,EAAE,CAAC;AACpE,UAAM,cAA8B,CAAC;AACrC,eAAW,QAAQ,YAAY;AAC7B,UAAI;AACF,cAAM,MAAM,MAAM,cAAc,IAAI;AACpC,yBAAiB;AACjB,oBAAY,KAAK;AAAA,UACf,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,aAAa;AAAA,UAAI;AAAA,UAAK;AAAA,UAAM,UAAU,KAAK;AAAA,UACrE,eAAe,GAAG;AAAA,UAAQ,cAAc,KAAK;AAAA,UAAM,eAAe;AAAA,UAClE,aAAa;AAAA,UAAM,UAAU;AAAA,QAC/B,CAAC;AAAA,MACH,QAAQ;AACN,2BAAmB,GAAG,uBAAuB,QAAQ,UAAU,KAAK,IAAI,CAAC;AACzE;AAAA,MACF;AAAA,IACF;AACA,UAAM,YAAY,CAAC,GAAG,YAAY,SAAS,GAAG,WAAW;AACzD,gBAAY,UAAU;AACtB,gBAAY,SAAS;AACrB,UAAM,gBAAgB,aAAa,UAAU;AAAA,EAC/C;AAEA,QAAM,YAAY,CAAC,QAAqB;AACtC,eAAW,KAAK,YAAY,SAAS;AACnC,UAAI,IAAI,IAAI,EAAE,EAAE,GAAG;AACjB,oBAAY,EAAE,EAAE;AAChB,YAAI,EAAE,KAAM,aAAY,EAAE,IAAI;AAC9B,sBAAc,EAAE,GAAG;AACnB,0BAAkB,QAAQ,OAAO,EAAE,EAAE;AAAA,MACvC;AAAA,IACF;AACA,UAAM,OAAO,YAAY,QAAQ,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;AAC7D,gBAAY,UAAU;AACtB,gBAAY,IAAI;AAChB,wBAAoB,IAAI;AAAA,EAC1B;AAEA,QAAM,eAAe,CAAC,OAAe;AACnC,cAAU,oBAAI,IAAI,CAAC,EAAE,CAAC,CAAC;AACvB,wBAAoB,CAAC,SAAS;AAC5B,UAAI,CAAC,KAAK,IAAI,EAAE,EAAG,QAAO;AAC1B,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,WAAK,OAAO,EAAE;AACd,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,qBAAqB,CAAC,OAAe;AACzC,QAAI,CAAC,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,EAAG;AACnD,iBAAa,EAAE;AACf,YAAQ,iBAAiB,EAAE;AAAA,EAC7B;AAEA,QAAM,uBAAuB,MAAM;AAAE,cAAU,IAAI,IAAI,gBAAgB,CAAC;AAAG,wBAAoB,oBAAI,IAAI,CAAC;AAAA,EAAG;AAC3G,QAAM,iBAAiB,MAAM;AAAE,cAAU,IAAI,IAAI,YAAY,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAAG,wBAAoB,oBAAI,IAAI,CAAC;AAAG,uBAAmB,EAAE;AAAA,EAAG;AAEjJ,QAAM,eAAe,CAAC,OAAe;AACnC,wBAAoB,CAAC,SAAS;AAC5B,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAAQ,MAAK,IAAI,EAAE;AACnD,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,oBAAoB,OAAO,OAAe;AAC9C,UAAM,SAAS,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,SAAS,EAAE,IAAI;AAC/E,QAAI,CAAC,UAAU,CAAC,OAAO,KAAM;AAC7B,mBAAe,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;AACpD,UAAM,UAAU,YAAY,QAAQ;AAAA,MAAI,CAAC,MACvC,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,OAAO,QAAW,aAAa,MAAM,UAAU,GAAG,eAAe,GAAG,SAAS,IAAI;AAAA,IACzG;AACA,gBAAY,UAAU;AACtB,gBAAY,OAAO;AACnB,UAAM,gBAAgB,CAAC,MAAM,GAAG,CAAC,OAAO,IAAI,CAAC;AAAA,EAC/C;AAEA,QAAM,oBAAoB,YAAY;AACpC,QAAI,cAAc,QAAS;AAC3B,UAAM,cAAc,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI;AACvE,QAAI,YAAY,WAAW,EAAG;AAC9B,kBAAc,UAAU;AACxB,QAAI;AACF,qBAAe,EAAE,OAAO,YAAY,QAAQ,WAAW,GAAG,QAAQ,EAAE,CAAC;AACrE,YAAM,YAAY,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACtD,YAAM,UAAU,YAAY,QAAQ;AAAA,QAAI,CAAC,MACvC,UAAU,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,QAAW,aAAa,MAAM,UAAU,GAAG,eAAe,GAAG,SAAS,IAAI;AAAA,MACjH;AACA,kBAAY,UAAU;AACtB,kBAAY,OAAO;AACnB,YAAM,gBAAgB,aAAa,YAAY,IAAI,CAAC,MAAM,EAAE,IAAK,EAAE,OAAO,OAAO,CAAC;AAAA,IACpF,UAAE;AACA,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,oBAAoB,CAAC,WAAmB,YAAoB;AAChE,iBAAa,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,IAAI,EAAE,KAAK,EAAE;AAClG,wBAAoB,IAAI;AACxB,6BAAyB,KAAK;AAAA,EAChC;AAEA,QAAM,oBAAoB,CAAC,WAAmB,cAAsB;AAClE,iBAAa,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,WAAW,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,IAAI,EAAE,KAAK,EAAE;AAAA,EACxG;AAEA,QAAM,cAAc,SAAS,KAAK,CAAC,MAAM,EAAE,WAAW;AACtD,QAAM,mBAAmB,SAAS,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC;AAC9D,QAAM,oBAAoB,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW;AAC9D,QAAM,oBAAoB,SAAS,OAAO,CAAC,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE,WAAW;AAClF,QAAM,mBAAmB,kBAAkB,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,KAAK,CAAC;AACxF,QAAM,gBAAgB,mBAAoB,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,gBAAgB,KAAK,OAAQ;AACrG,QAAM,mBAAmB,CAAC,eAAe,aAAa,SAAS,SAAS,KAAK,kBAAkB,SAAS,YACpG,uBAAuB,GAAG,gBAAgB,SAAS,IAAI;AAC3D,QAAM,sBAAsB,wBAAwB,UAAU,aAAa,MAAM;AAEjF,QAAM,mBAAmB,MAAM;AAC7B,QAAI,YAAY,kBAAkB,WAAW,GAAG;AAAE,yBAAmB,GAAG,kBAAkB;AAAG;AAAA,IAAQ;AACrG,QAAI,aAAa,kBAAkB,SAAS,WAAW;AACrD,yBAAmB,uBAAuB,GAAG,gBAAgB,SAAS,CAAC;AACvE;AAAA,IACF;AACA,QAAI,kBAAkB,iBAAiB,SAAS,GAAG;AACjD,+BAAyB,IAAI;AAC7B,yBAAmB,sBAAsB,IAAI,iBAAiB,QAAQ,MAAM,CAAC;AAC7E;AAAA,IACF;AACA,6BAAyB,KAAK;AAC9B,uBAAmB,EAAE;AACrB,sBAAkB,QAAQ,MAAM;AAChC,aAAS,oBAAoB,UAAU,KAAK,CAAC;AAC7C,cAAU;AAAA,EACZ;AAEA,SAAO;AAAA,IACL;AAAA,IAAQ;AAAA,IAAI;AAAA,IAAgB;AAAA,IAAW;AAAA,IACvC,iBAAiB,mBAAmB;AAAA,IAAkB;AAAA,IACtD;AAAA,IAAkB;AAAA,IAAqB;AAAA,IACvC;AAAA,IAAkB;AAAA,IAAqB;AAAA,IACvC;AAAA,IAAc;AAAA,IAAiB;AAAA,IAAa;AAAA,IAAkB;AAAA,IAC9D;AAAA,IAAmB;AAAA,IAAkB;AAAA,IAAe;AAAA,IACpD;AAAA,IAAkB;AAAA,IAAc;AAAA,IAAoB;AAAA,IAAsB;AAAA,IAC1E;AAAA,IAAc;AAAA,IAAmB;AAAA,IAAmB;AAAA,IACpD;AAAA,IAAmB;AAAA,EACrB;AACF;;;AD9RA,IAAMC,kBAAiB;AAEhB,SAAS,eACd,SACuB;AACvB,QAAM;AAAA,IACJ,SAASA;AAAA,IACT,WAAW;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,IACZ;AAAA,IACA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,WAAW;AAAA,IACX;AAAA,EACF,IAAI;AAEJ,QAAM,eAAeC,QAAyB,IAAI;AAClD,QAAM,iBAAiBA,QAAyB,IAAI;AAEpD,QAAM;AAAA,IACJ;AAAA,IAAQ;AAAA,IAAI;AAAA,IAAW;AAAA,IAAU;AAAA,IAAiB;AAAA,IAClD;AAAA,IAAkB;AAAA,IAAqB;AAAA,IAAa;AAAA,IACpD;AAAA,IAAiB;AAAA,IAAa;AAAA,IAAkB;AAAA,IAChD;AAAA,IAAmB;AAAA,IAAkB;AAAA,IAAc;AAAA,IACnD;AAAA,IAAsB;AAAA,IAAgB;AAAA,IAAc;AAAA,IACpD;AAAA,IAAmB;AAAA,IAAmB;AAAA,IAAmB;AAAA,EAC3D,IAAI,mBAAmB,OAAO;AAE9B,QAAM,EAAE,YAAY,aAAa,IAAI,eAAe;AAAA,IAClD;AAAA,IACA,QAAQ,CAAC,UAAU;AAAE,WAAK,iBAAiB,KAAK;AAAA,IAAG;AAAA,EACrD,CAAC;AAED,QAAM,EAAE,QAAQ,IAAI,kBAAkB;AAAA,IACpC,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,cAAc,CAAC,UAAU;AAAE,WAAK,iBAAiB,KAAK;AAAA,IAAG;AAAA,EAC3D,CAAC;AAED,QAAM,eAAe,iBAAiB,mBAAmB;AACzD,QAAM,iBAAiB,KAAK,IAAI,GAAG,YAAY,SAAS,MAAM;AAE9D,QAAM,QAAQ,uBAAuB;AAAA,IACnC;AAAA,IACA,UAAU,QAAQ,YAAY;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,YAAY,SAAS,SAAS;AAAA,EAChC,CAAC;AAED,QAAM,iBAAiB,MAAM;AAAE,QAAI,CAAC,SAAU,cAAa,SAAS,MAAM;AAAA,EAAG;AAC7E,QAAM,mBAAmB,MAAM;AAAE,QAAI,CAAC,SAAU,gBAAe,SAAS,MAAM;AAAA,EAAG;AAEjF,QAAM,mBAAmB,CACvB,eAC2B;AAAA,IAC3B,GAAG;AAAA,IACH,MAAM,WAAW,QAAQ;AAAA,IACzB,UAAU,WAAW,aAAa,WAAW,KAAK;AAAA,IAClD,cAAc,YAAY,YAAY,KAAK,GAAG;AAAA,IAC9C,iBAAiB,YAAY;AAAA,IAC7B,iBAAiB,YAAY;AAAA,IAC7B,gBAAgB,QAAQ,YAAY,KAAK;AAAA,IACzC,cAAc;AAAA,IACd,iBAAiB,cAAc;AAAA,IAC/B,kBAAkB,eAAe;AAAA,IACjC,iBAAiB,YAAY;AAAA,IAC7B,cAAc,QAAQ,YAAY,KAAK;AAAA,IACvC,SAAS,CAAC,UAAU;AAClB,UAAI,MAAM,WAAW,aAAa,WAAW,MAAM,WAAW,eAAe,QAAS;AACtF,iBAAW,UAAU,KAAK;AAC1B,UAAI,CAAC,MAAM,oBAAoB,CAAC,SAAU,gBAAe;AAAA,IAC3D;AAAA,IACA,WAAW,CAAC,UAAU;AACpB,iBAAW,YAAY,KAAK;AAC5B,UACE,CAAC,MAAM,oBACP,CAAC,YACD,MAAM,WAAW,MAAM,kBACtB,MAAM,QAAQ,WAAW,MAAM,QAAQ,MACxC;AACA,cAAM,eAAe;AACrB,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,QAAQ,CAAC,UAAU;AACjB,iBAAW,SAAS,KAAK;AACzB,UAAI,CAAC,MAAM,cAAc,SAAS,MAAM,aAA4B,GAAG;AACrE,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,SAAS,CAAC,UAAU;AAClB,iBAAW,UAAU,KAAK;AAC1B,UAAI,CAAC,MAAM,iBAAkB,SAAQ,KAAK;AAAA,IAC5C;AAAA,IACA,aAAa,CAAC,UAAU;AAAE,iBAAW,cAAc,KAAK;AAAG,mBAAa,YAAY,KAAK;AAAA,IAAG;AAAA,IAC5F,aAAa,CAAC,UAAU;AAAE,iBAAW,cAAc,KAAK;AAAG,mBAAa,YAAY,KAAK;AAAA,IAAG;AAAA,IAC5F,YAAY,CAAC,UAAU;AAAE,iBAAW,aAAa,KAAK;AAAG,mBAAa,WAAW,KAAK;AAAA,IAAG;AAAA,IACzF,QAAQ,CAAC,UAAU;AAAE,iBAAW,SAAS,KAAK;AAAG,mBAAa,OAAO,KAAK;AAAA,IAAG;AAAA,EAC/E;AAEA,QAAM,kBAAkB,CACtB,KACA,kBACA,UACA,eAC4B;AAAA,IAC5B,GAAG;AAAA,IACH,GAAI,WACA,EAAE,iBAAiB,IAAI,WAAW,GAAG,IACrC,YAAY,UAAa,WAAW,YAAY,SAC9C,EAAE,QAAQ,IACV,CAAC;AAAA,IACP;AAAA,IACA,MAAM;AAAA,IACN,UAAU,WAAW,YAAY;AAAA,IACjC,QAAQ,WAAW,UAAU;AAAA,IAC7B,UAAU,WAAW,YAAY;AAAA,IACjC,cAAc,YAAY,YAAY,KAAK;AAAA,IAC3C,OAAO,WAAW,QAAQ,EAAE,GAAG,oBAAoB,GAAG,UAAU,MAAM,IAAI;AAAA,IAC1E,SAAS,CAAC,UAAU;AAClB,iBAAW,UAAU,KAAK;AAC1B,YAAM,gBAAgB;AAAA,IACxB;AAAA,IACA,UAAU,CAAC,UAAU;AACnB,iBAAW,WAAW,KAAK;AAC3B,UAAI,CAAC,MAAM,kBAAkB;AAC3B,cAAM,QAAQ,MAAM,OAAO;AAC3B,YAAI,SAAS,MAAM,SAAS,EAAG,MAAK,iBAAiB,KAAK;AAC1D,cAAM,OAAO,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO;AAAA,IACP,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,OAAO;AAAA,IACP;AAAA,IAAY;AAAA,IAAa;AAAA,IACzB,OAAO;AAAA,IAAc;AAAA,IAAa;AAAA,IAClC;AAAA,IAAW;AAAA,IAAW;AAAA,IAAgB;AAAA,IAAU;AAAA,IAAQ;AAAA,IACxD;AAAA,IAAgB;AAAA,IAChB,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,IACV;AAAA,IACA,WAAW,MAAM,oBAAoB,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAAA,IACvE,gBAAgB,MAAM,oBAAoB,oBAAI,IAAI,CAAC;AAAA,IACnD,aAAa;AAAA,IACb,MAAM;AAAA,IACN;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,IACd;AAAA,IACA,eAAe,CAAC,cAAc,gBAAgB,cAAc,GAAG,iBAAiB,OAAO,SAAS;AAAA,IAChG,qBAAqB,CAAC,cAAc,gBAAgB,gBAAgB,GAAG,kBAAkB,MAAM,SAAS;AAAA,EAC1G;AACF;;;AGzKS,0BAAAC,YAAA;AAJF,SAAS,qBAEd,OAAyD;AACzD,QAAM,aAAa,gBAAyB,KAAK;AACjD,SAAO,gBAAAA,KAAA,YAAG,gBAAM,SAAS,UAAU,GAAE;AACvC;;;ACDS,qBAAAC,WAAA,OAAAC,YAAA;AAJF,SAAS,oBAEd,OAAwD;AACxD,QAAM,aAAa,eAAwB,KAAK;AAChD,SAAO,gBAAAA,KAAAD,WAAA,EAAG,gBAAM,SAAS,UAAU,GAAE;AACvC;","names":["useCallback","useEffect","useRef","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useMemo","useState","useRef","useEffect","useCallback","useRef","useEffect","useRef","useState","useState","useRef","useEffect","DEFAULT_ACCEPT","useRef","jsx","Fragment","jsx"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/types.ts","../src/storage/memoryAdapter.ts"],"sourcesContent":["import type { ImageUploadRecord } from '../types';\n\nexport interface StorageUploadParams {\n bucket: string;\n path: string;\n data: Buffer | Uint8Array;\n contentType: string;\n upsert?: boolean;\n cacheControl?: string;\n contentHash?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface StorageUploadResult {\n bucket: string;\n path: string;\n url: string;\n}\n\nexport interface StorageFileEntry {\n name: string;\n id?: string;\n createdAt?: string;\n size?: number;\n}\n\nexport interface StorageListOptions {\n cursor?: string;\n limit?: number;\n}\n\nexport type StoragePaginatedListResult = StorageFileEntry[] & {\n items: StorageFileEntry[];\n nextCursor?: string;\n};\n\nexport type StorageUploadOptions = StorageUploadParams;\nexport type StorageFileMetadata = StorageFileEntry;\nexport type StorageListResult = StoragePaginatedListResult;\n\nexport interface StorageAdapter {\n /**\n * Upload optimized image bytes to your storage backend and return its public URL.\n */\n upload(params: StorageUploadParams): Promise<StorageUploadResult>;\n\n /**\n * Delete an image from your storage backend by bucket and relative path.\n */\n delete(bucket: string, path: string): Promise<void>;\n\n /**\n * Optional: Check whether an object exists at the given bucket and relative path.\n */\n exists?(bucket: string, path: string): Promise<boolean>;\n\n /**\n * Optional: Resolve a public URL for a bucket and path.\n */\n getPublicUrl?(bucket: string, path: string): string | Promise<string>;\n\n /**\n * Optional: List files inside a bucket/folder (supports optional cursor/limit pagination).\n */\n list?(\n bucket: string,\n folder?: string,\n options?: StorageListOptions,\n ): Promise<StoragePaginatedListResult>;\n\n /**\n * Optional: Delete an asset directly from its public URL.\n */\n deleteByUrl?(url: string): Promise<boolean>;\n\n /**\n * Optional: Parse a public storage URL into its bucket and relative path.\n */\n parseUrl?(url: string): { bucket?: string; path: string } | null;\n}\n\nexport type UploadRecordHook = (\n record: ImageUploadRecord,\n) => void | Promise<void>;\n\n/**\n * Attaches non-enumerable `.items` and `.nextCursor` properties to a `StorageFileEntry[]`\n * array so both array callers (`entries.map(...)`) and paginated destructuring callers\n * (`const { items, nextCursor } = await adapter.list(...)`) work seamlessly.\n */\nexport function toPaginatedListResult(\n entries: StorageFileEntry[],\n nextCursor?: string,\n): StoragePaginatedListResult {\n const result = entries as StoragePaginatedListResult;\n Object.defineProperties(result, {\n items: {\n value: entries,\n enumerable: false,\n configurable: true,\n },\n nextCursor: {\n value: nextCursor,\n enumerable: false,\n configurable: true,\n },\n });\n return result;\n}\n\nconst PRINTABLE_ASCII_REGEX = /^[\\x20-\\x7E]*$/;\nconst MAX_METADATA_BYTES = 2048;\n\n/**\n * Validates that storage metadata keys and values contain only printable ASCII\n * characters (preventing HTTP header/CRLF injection) and do not exceed 2 KB total.\n */\nexport function validateStorageMetadata(\n metadata?: Record<string, string>,\n): void {\n if (!metadata) return;\n let totalBytes = 0;\n\n for (const [key, value] of Object.entries(metadata)) {\n if (\n !key ||\n typeof value !== 'string' ||\n !PRINTABLE_ASCII_REGEX.test(key) ||\n !PRINTABLE_ASCII_REGEX.test(value)\n ) {\n const err = new Error(\n 'Invalid storage metadata: keys and values must contain printable ASCII characters only',\n ) as Error & { status?: number };\n err.status = 400;\n throw err;\n }\n totalBytes += key.length + value.length;\n if (totalBytes > MAX_METADATA_BYTES) {\n const err = new Error(\n `Storage metadata exceeds maximum size of ${MAX_METADATA_BYTES} bytes`,\n ) as Error & { status?: number };\n err.status = 400;\n throw err;\n }\n }\n}\n\n/**\n * Type-safe helper for authoring a custom StorageAdapter (S3, R2, Vercel Blob, Firebase, etc.).\n */\nexport function createStorageAdapter(adapter: StorageAdapter): StorageAdapter {\n return adapter;\n}\n","import {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n} from './types';\n\nexport interface StoredMemoryObject {\n bucket: string;\n path: string;\n data: Uint8Array;\n contentType: string;\n createdAt: string;\n metadata?: Record<string, string>;\n}\n\nexport interface MemoryStorageAdapter extends StorageAdapter {\n exists(bucket: string, path: string): Promise<boolean>;\n getPublicUrl(bucket: string, path: string): string;\n list(\n bucket: string,\n folder?: string,\n options?: StorageListOptions,\n ): Promise<StoragePaginatedListResult>;\n deleteByUrl(url: string): Promise<boolean>;\n getObject(bucket: string, path: string): StoredMemoryObject | undefined;\n clear(): void;\n readonly size: number;\n}\n\nexport interface MemoryStorageOptions {\n baseUrl?: string;\n}\n\nexport function createMemoryStorageAdapter(\n options: MemoryStorageOptions = {},\n): MemoryStorageAdapter {\n const baseUrl = (options.baseUrl || 'https://memory.local/uploads').replace(\n /\\/+$/,\n '',\n );\n const store = new Map<string, StoredMemoryObject>();\n\n const toKey = (bucket: string, path: string) =>\n `${bucket}::${path.replace(/^\\/+/, '')}`;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const cleanPath = path.replace(/^\\/+/, '');\n return `${baseUrl}/${bucket}/${cleanPath}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const cleanPath = params.path.replace(/^\\/+/, '');\n const key = toKey(params.bucket, cleanPath);\n\n if (params.upsert === false && store.has(key)) {\n const existsErr = new Error(\n `Object already exists at ${params.bucket}/${cleanPath}`,\n ) as Error & { code?: string };\n existsErr.code = 'EEXIST';\n throw existsErr;\n }\n\n store.set(key, {\n bucket: params.bucket,\n path: cleanPath,\n data: new Uint8Array(params.data),\n contentType: params.contentType,\n createdAt: new Date().toISOString(),\n metadata: params.metadata,\n });\n\n return {\n bucket: params.bucket,\n path: cleanPath,\n url: getPublicUrl(params.bucket, cleanPath),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const key = toKey(bucket, path);\n store.delete(key);\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n return store.has(toKey(bucket, path));\n },\n\n getPublicUrl,\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanFolder = folder ? folder.replace(/^\\/+|\\/+$/g, '') : '';\n const entries: StorageFileEntry[] = [];\n\n for (const obj of store.values()) {\n if (obj.bucket !== bucket) continue;\n if (cleanFolder && !obj.path.startsWith(`${cleanFolder}/`)) continue;\n const name = cleanFolder\n ? obj.path.slice(cleanFolder.length + 1)\n : obj.path;\n entries.push({\n name,\n id: `${obj.bucket}/${obj.path}`,\n createdAt: obj.createdAt,\n size: obj.data.byteLength,\n });\n }\n\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n const limit = listOptions?.limit ?? entries.length;\n const sliced =\n listOptions?.limit !== undefined || offset > 0\n ? entries.slice(offset, offset + limit)\n : entries;\n const nextCursor =\n listOptions?.limit !== undefined && offset + sliced.length < entries.length\n ? String(offset + sliced.length)\n : undefined;\n\n return toPaginatedListResult(sliced, nextCursor);\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = this.parseUrl?.(url);\n if (!parsed || !parsed.bucket) return false;\n const key = toKey(parsed.bucket, parsed.path);\n return store.delete(key);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n const prefix = `${baseUrl}/`;\n if (!url || !url.startsWith(prefix)) return null;\n const cleanUrl = url.split(/[?#]/)[0];\n const rest = cleanUrl.slice(prefix.length);\n const slashIdx = rest.indexOf('/');\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;\n const bucket = rest.slice(0, slashIdx);\n const filePath = rest.slice(slashIdx + 1);\n return { bucket, path: filePath };\n },\n\n getObject(bucket: string, path: string): StoredMemoryObject | undefined {\n return store.get(toKey(bucket, path));\n },\n\n clear(): void {\n store.clear();\n },\n\n get size(): number {\n return store.size;\n },\n };\n}\n"],"mappings":";AA0FO,SAAS,sBACd,SACA,YAC4B;AAC5B,QAAM,SAAS;AACf,SAAO,iBAAiB,QAAQ;AAAA,IAC9B,OAAO;AAAA,MACL,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,IAAM,wBAAwB;AAC9B,IAAM,qBAAqB;AAMpB,SAAS,wBACd,UACM;AACN,MAAI,CAAC,SAAU;AACf,MAAI,aAAa;AAEjB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,QACE,CAAC,OACD,OAAO,UAAU,YACjB,CAAC,sBAAsB,KAAK,GAAG,KAC/B,CAAC,sBAAsB,KAAK,KAAK,GACjC;AACA,YAAM,MAAM,IAAI;AAAA,QACd;AAAA,MACF;AACA,UAAI,SAAS;AACb,YAAM;AAAA,IACR;AACA,kBAAc,IAAI,SAAS,MAAM;AACjC,QAAI,aAAa,oBAAoB;AACnC,YAAM,MAAM,IAAI;AAAA,QACd,4CAA4C,kBAAkB;AAAA,MAChE;AACA,UAAI,SAAS;AACb,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAKO,SAAS,qBAAqB,SAAyC;AAC5E,SAAO;AACT;;;ACnHO,SAAS,2BACd,UAAgC,CAAC,GACX;AACtB,QAAM,WAAW,QAAQ,WAAW,gCAAgC;AAAA,IAClE;AAAA,IACA;AAAA,EACF;AACA,QAAM,QAAQ,oBAAI,IAAgC;AAElD,QAAM,QAAQ,CAAC,QAAgB,SAC7B,GAAG,MAAM,KAAK,KAAK,QAAQ,QAAQ,EAAE,CAAC;AAExC,QAAM,eAAe,CAAC,QAAgB,SAAyB;AAC7D,UAAM,YAAY,KAAK,QAAQ,QAAQ,EAAE;AACzC,WAAO,GAAG,OAAO,IAAI,MAAM,IAAI,SAAS;AAAA,EAC1C;AAEA,SAAO;AAAA,IACL,MAAM,OAAO,QAA2D;AACtE,YAAM,YAAY,OAAO,KAAK,QAAQ,QAAQ,EAAE;AAChD,YAAM,MAAM,MAAM,OAAO,QAAQ,SAAS;AAE1C,UAAI,OAAO,WAAW,SAAS,MAAM,IAAI,GAAG,GAAG;AAC7C,cAAM,YAAY,IAAI;AAAA,UACpB,4BAA4B,OAAO,MAAM,IAAI,SAAS;AAAA,QACxD;AACA,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAEA,YAAM,IAAI,KAAK;AAAA,QACb,QAAQ,OAAO;AAAA,QACf,MAAM;AAAA,QACN,MAAM,IAAI,WAAW,OAAO,IAAI;AAAA,QAChC,aAAa,OAAO;AAAA,QACpB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,UAAU,OAAO;AAAA,MACnB,CAAC;AAED,aAAO;AAAA,QACL,QAAQ,OAAO;AAAA,QACf,MAAM;AAAA,QACN,KAAK,aAAa,OAAO,QAAQ,SAAS;AAAA,MAC5C;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgB,MAA6B;AACxD,YAAM,MAAM,MAAM,QAAQ,IAAI;AAC9B,YAAM,OAAO,GAAG;AAAA,IAClB;AAAA,IAEA,MAAM,OAAO,QAAgB,MAAgC;AAC3D,aAAO,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,IACtC;AAAA,IAEA;AAAA,IAEA,MAAM,KACJ,QACA,QACA,aACqC;AACrC,YAAM,cAAc,SAAS,OAAO,QAAQ,cAAc,EAAE,IAAI;AAChE,YAAM,UAA8B,CAAC;AAErC,iBAAW,OAAO,MAAM,OAAO,GAAG;AAChC,YAAI,IAAI,WAAW,OAAQ;AAC3B,YAAI,eAAe,CAAC,IAAI,KAAK,WAAW,GAAG,WAAW,GAAG,EAAG;AAC5D,cAAM,OAAO,cACT,IAAI,KAAK,MAAM,YAAY,SAAS,CAAC,IACrC,IAAI;AACR,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA,IAAI,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI;AAAA,UAC7B,WAAW,IAAI;AAAA,UACf,MAAM,IAAI,KAAK;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,YAAM,SAAS,aAAa,SAAS,OAAO,YAAY,MAAM,KAAK,IAAI;AACvE,YAAM,QAAQ,aAAa,SAAS,QAAQ;AAC5C,YAAM,SACJ,aAAa,UAAU,UAAa,SAAS,IACzC,QAAQ,MAAM,QAAQ,SAAS,KAAK,IACpC;AACN,YAAM,aACJ,aAAa,UAAU,UAAa,SAAS,OAAO,SAAS,QAAQ,SACjE,OAAO,SAAS,OAAO,MAAM,IAC7B;AAEN,aAAO,sBAAsB,QAAQ,UAAU;AAAA,IACjD;AAAA,IAEA,MAAM,YAAY,KAA+B;AAC/C,YAAM,SAAS,KAAK,WAAW,GAAG;AAClC,UAAI,CAAC,UAAU,CAAC,OAAO,OAAQ,QAAO;AACtC,YAAM,MAAM,MAAM,OAAO,QAAQ,OAAO,IAAI;AAC5C,aAAO,MAAM,OAAO,GAAG;AAAA,IACzB;AAAA,IAEA,SAAS,KAAuD;AAC9D,YAAM,SAAS,GAAG,OAAO;AACzB,UAAI,CAAC,OAAO,CAAC,IAAI,WAAW,MAAM,EAAG,QAAO;AAC5C,YAAM,WAAW,IAAI,MAAM,MAAM,EAAE,CAAC;AACpC,YAAM,OAAO,SAAS,MAAM,OAAO,MAAM;AACzC,YAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,UAAI,YAAY,KAAK,aAAa,KAAK,SAAS,EAAG,QAAO;AAC1D,YAAM,SAAS,KAAK,MAAM,GAAG,QAAQ;AACrC,YAAM,WAAW,KAAK,MAAM,WAAW,CAAC;AACxC,aAAO,EAAE,QAAQ,MAAM,SAAS;AAAA,IAClC;AAAA,IAEA,UAAU,QAAgB,MAA8C;AACtE,aAAO,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,IACtC;AAAA,IAEA,QAAc;AACZ,YAAM,MAAM;AAAA,IACd;AAAA,IAEA,IAAI,OAAe;AACjB,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/quality.ts","../src/utils/formatters.ts"],"sourcesContent":["export type UploadLocale = 'en' | 'ar';\n\nexport type ImageOutputFormat = 'avif' | 'webp' | 'jpeg' | 'png' | 'svg';\n\nexport type ExplicitOutputFormat = 'avif' | 'webp' | 'jpeg' | 'png';\n\nexport type QualityLevel = 'low' | 'medium' | 'high' | 'maximum';\n\nexport interface QualityPreset {\n label: string;\n labelAr: string;\n value: number;\n description: string;\n descriptionAr: string;\n /** Rough fraction of original bytes after modern AVIF/WebP compression */\n compressionRatio: number;\n}\n\nexport const QUALITY_PRESETS: Record<QualityLevel, QualityPreset> = {\n low: {\n label: 'Low',\n labelAr: 'منخفضة',\n value: 50,\n description: 'Smallest file — best for tiny thumbnails and icons',\n descriptionAr: 'أصغر حجم — مناسب للمصغرات والأيقونات الصغيرة',\n compressionRatio: 0.12,\n },\n medium: {\n label: 'Medium',\n labelAr: 'متوسطة',\n value: 72,\n description: 'Balanced size and quality — suitable for most web images',\n descriptionAr: 'توازن بين الحجم والجودة — مناسب لمعظم صور الويب',\n compressionRatio: 0.22,\n },\n high: {\n label: 'High',\n labelAr: 'عالية',\n value: 85,\n description: 'Recommended — sharp visuals with excellent compression',\n descriptionAr: 'موصى به — جودة عالية وضغط ممتاز',\n compressionRatio: 0.3,\n },\n maximum: {\n label: 'Maximum',\n labelAr: 'أقصى جودة',\n value: 95,\n description: 'Closest to original — large file, use for hero / print',\n descriptionAr: 'أقرب للأصل — حجم كبير، مناسب للصور الرئيسية والطباعة',\n compressionRatio: 0.55,\n },\n};\n\nexport interface ImageDimensions {\n width: number;\n height: number;\n}\n","import type { QualityLevel, UploadLocale } from '../types';\nimport { QUALITY_PRESETS } from '../types';\n\nexport function formatFileSize(bytes: number): string {\n if (bytes <= 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.min(\n sizes.length - 1,\n Math.floor(Math.log(bytes) / Math.log(k)),\n );\n return `${Math.round((bytes / Math.pow(k, i)) * 100) / 100} ${sizes[i]}`;\n}\n\nexport function calculateReduction(\n original: number,\n optimized: number,\n): number {\n if (original <= 0) return 0;\n return Math.round(((original - optimized) / original) * 100);\n}\n\nexport function estimateOutputSize(\n originalBytes: number,\n level: QualityLevel,\n): number {\n return Math.round(originalBytes * QUALITY_PRESETS[level].compressionRatio);\n}\n\nconst creativeMessages = [\n 'Sprinkling digital magic ✨',\n 'Painting pixels with perfection 🎨',\n 'Launching to the cloud 🚀',\n 'Creating something beautiful ⭐',\n 'Transforming your vision ⚡',\n 'Weaving visual excellence 💫',\n 'Crafting your masterpiece 🎭',\n 'Working our magic 🔮',\n 'Making it pixel-perfect ✨',\n 'Keep calm, creating magic 🎪',\n 'Hitting the sweet spot 🎯',\n 'Artist mode activated 🎨',\n 'Shooting for the stars 🌠',\n 'Compressing with love 💝',\n 'Enhancing quality 💎',\n 'Polishing to perfection ✨',\n];\n\nconst creativeMessagesAr = [\n 'نضيف لمسة رقمية ساحرة ✨',\n 'ننسق البكسلات بإتقان 🎨',\n 'الرفع جارٍ إلى السحابة 🚀',\n 'نصنع شيئاً جميلاً ⭐',\n 'نحوّل رؤيتك إلى واقع ⚡',\n 'نبني تجربة بصرية مميزة 💫',\n 'نصقل عملك باحتراف 🎭',\n 'السحر جارٍ الآن 🔮',\n 'نضبط كل بكسل بعناية ✨',\n 'اهدأ، نحن نهتم بالتفاصيل 🎪',\n 'نقترب من النتيجة المثالية 🎯',\n 'وضع الإبداع مفعل 🎨',\n 'نستهدف أفضل نتيجة 🌠',\n 'ضغط ذكي مع الحفاظ على الجودة 💝',\n 'تحسين الجودة مستمر 💎',\n 'اللمسات الأخيرة قيد التنفيذ ✨',\n];\n\nexport function getStatusMessage(\n progress: number,\n locale: UploadLocale = 'en',\n): string {\n if (progress >= 95) {\n return locale === 'ar' ? '🎉 اقتربنا جداً...' : '🎉 Almost there...';\n }\n if (progress >= 85) {\n return locale === 'ar'\n ? '✨ نضع اللمسات الأخيرة...'\n : '✨ Polishing the final details...';\n }\n\n const list = locale === 'ar' ? creativeMessagesAr : creativeMessages;\n const clampedProgress = Math.max(0, Math.min(99, progress));\n const messageIndex = Math.min(\n list.length - 1,\n Math.floor((clampedProgress / 100) * list.length),\n );\n\n return list[messageIndex];\n}\n\nexport function createSuccessMessage(\n originalSize: number,\n optimizedSize: number,\n compact: boolean = false,\n locale: UploadLocale = 'en',\n): string {\n const reduction = calculateReduction(originalSize, optimizedSize);\n const original = formatFileSize(originalSize);\n const optimized = formatFileSize(optimizedSize);\n\n if (compact) {\n if (reduction > 50) {\n return locale === 'ar'\n ? `🎉 أصغر بنسبة ${reduction}%! (${original} → ${optimized})`\n : `🎉 ${reduction}% smaller! (${original} → ${optimized})`;\n }\n if (reduction > 0) {\n return locale === 'ar'\n ? `✓ أصغر بنسبة ${reduction}% (${original} → ${optimized})`\n : `✓ ${reduction}% smaller (${original} → ${optimized})`;\n }\n return `✓ ${optimized}`;\n }\n\n if (reduction > 70) {\n return locale === 'ar'\n ? `🎉 رائع! تم تقليل الحجم بنسبة ${reduction}% (${original} → ${optimized})`\n : `🎉 Amazing! Reduced by ${reduction}% (${original} → ${optimized})`;\n }\n if (reduction > 50) {\n return locale === 'ar'\n ? `✨ ممتاز! تم تقليل الحجم بنسبة ${reduction}% (${original} → ${optimized})`\n : `✨ Great! Reduced by ${reduction}% (${original} → ${optimized})`;\n }\n if (reduction > 20) {\n return locale === 'ar'\n ? `✓ اكتمل الرفع! تم تقليل الحجم بنسبة ${reduction}% (${original} → ${optimized})`\n : `✓ Upload complete! Reduced by ${reduction}% (${original} → ${optimized})`;\n }\n return locale === 'ar'\n ? `✓ اكتمل الرفع! (${original} → ${optimized})`\n : `✓ Upload complete! (${original} → ${optimized})`;\n}\n\nconst FILE_TYPE_ERROR_REGEX =\n /\\b(?:file\\s+type|mime\\s+type|invalid\\s+type|unsupported|format|mime)\\b/i;\n\nexport function getFriendlyErrorMessage(\n error: string,\n locale: UploadLocale = 'en',\n): string {\n const lower = error.toLowerCase();\n if (\n lower.includes('size') ||\n lower.includes('large') ||\n lower.includes('exceeds')\n ) {\n return locale === 'ar'\n ? 'الملف كبير جداً. يرجى اختيار صورة أصغر.'\n : 'File is too large. Please choose a smaller image.';\n }\n if (\n lower.includes('network') ||\n lower.includes('fetch') ||\n lower.includes('connection')\n ) {\n return locale === 'ar'\n ? 'حدث خطأ في الشبكة. يرجى التحقق من الاتصال والمحاولة مرة أخرى.'\n : 'Network error. Please check your connection and try again.';\n }\n if (lower.includes('timeout') || lower.includes('timed out')) {\n return locale === 'ar'\n ? 'انتهت مهلة الرفع. يرجى المحاولة مرة أخرى.'\n : 'Upload timed out. Please try again.';\n }\n if (FILE_TYPE_ERROR_REGEX.test(error)) {\n return locale === 'ar'\n ? 'نوع الملف غير صالح. يرجى رفع صورة صالحة (JPEG, PNG, WebP, AVIF).'\n : 'Invalid file type. Please upload a valid image (JPEG, PNG, WebP, AVIF).';\n }\n if (error) return error;\n return locale === 'ar'\n ? 'فشل الرفع. يرجى المحاولة مرة أخرى.'\n : 'Upload failed. Please try again.';\n}\n"],"mappings":"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","names":[]}
|