@firecms/media_manager 3.1.0-canary.1df3b2c
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/LICENSE +6 -0
- package/dist/MediaManagerProvider.d.ts +14 -0
- package/dist/components/MediaAssetCard.d.ts +13 -0
- package/dist/components/MediaAssetDetails.d.ts +11 -0
- package/dist/components/MediaLibraryCard.d.ts +8 -0
- package/dist/components/MediaLibraryView.d.ts +9 -0
- package/dist/components/MediaUploadDialog.d.ts +13 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.es.js +1280 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +1276 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/config.d.ts +87 -0
- package/dist/types/controller.d.ts +66 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/media_asset.d.ts +70 -0
- package/dist/useMediaManagerController.d.ts +18 -0
- package/dist/useMediaManagerPlugin.d.ts +23 -0
- package/package.json +58 -0
- package/src/MediaManagerProvider.tsx +34 -0
- package/src/components/MediaAssetCard.tsx +134 -0
- package/src/components/MediaAssetDetails.tsx +353 -0
- package/src/components/MediaLibraryCard.tsx +55 -0
- package/src/components/MediaLibraryView.tsx +194 -0
- package/src/components/MediaUploadDialog.tsx +264 -0
- package/src/components/index.ts +5 -0
- package/src/index.ts +10 -0
- package/src/types/config.ts +100 -0
- package/src/types/controller.ts +79 -0
- package/src/types/index.ts +3 -0
- package/src/types/media_asset.ts +84 -0
- package/src/useMediaManagerController.tsx +387 -0
- package/src/useMediaManagerPlugin.tsx +97 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/MediaManagerProvider.tsx","../src/useMediaManagerController.tsx","../src/components/MediaAssetCard.tsx","../src/components/MediaAssetDetails.tsx","../src/components/MediaUploadDialog.tsx","../src/components/MediaLibraryView.tsx","../src/useMediaManagerPlugin.tsx","../src/components/MediaLibraryCard.tsx"],"sourcesContent":["import React, { createContext, PropsWithChildren, useContext } from \"react\";\nimport { MediaManagerController } from \"./types\";\n\nconst MediaManagerContext = createContext<MediaManagerController | undefined>(undefined);\n\n/**\n * Hook to access the MediaManagerController from context.\n * Must be used within a MediaManagerProvider.\n */\nexport function useMediaManager(): MediaManagerController {\n const context = useContext(MediaManagerContext);\n if (!context) {\n throw new Error(\"useMediaManager must be used within a MediaManagerProvider\");\n }\n return context;\n}\n\nexport interface MediaManagerProviderProps {\n controller: MediaManagerController;\n}\n\n/**\n * Provider component that makes the MediaManagerController available to all children.\n */\nexport function MediaManagerProvider({\n controller,\n children\n}: PropsWithChildren<MediaManagerProviderProps>) {\n return (\n <MediaManagerContext.Provider value={controller}>\n {children}\n </MediaManagerContext.Provider>\n );\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { DataSourceDelegate, StorageSource } from \"@firecms/core\";\nimport { MediaAsset, MediaManagerController, ThumbnailSize } from \"./types\";\nimport Compressor from \"compressorjs\";\n\nexport interface UseMediaManagerControllerProps {\n storageSource: StorageSource;\n dataSourceDelegate: DataSourceDelegate;\n storagePath: string;\n collectionPath: string;\n bucket?: string;\n /** Thumbnail sizes to generate on upload */\n thumbnailSizes?: ThumbnailSize[];\n /** Path prefix for thumbnails. Default: \"thumbs\" */\n thumbnailPath?: string;\n}\n\nconst DEFAULT_THUMBNAIL_PATH = \"thumbs\";\n\n/**\n * Hook that creates a MediaManagerController for managing media assets.\n * Handles all CRUD operations for files and their metadata.\n */\nexport function useMediaManagerController({\n storageSource,\n dataSourceDelegate,\n storagePath,\n collectionPath,\n bucket,\n thumbnailSizes,\n thumbnailPath = DEFAULT_THUMBNAIL_PATH\n}: UseMediaManagerControllerProps): MediaManagerController {\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | undefined>();\n const [assets, setAssets] = useState<MediaAsset[]>([]);\n const [selectedAsset, setSelectedAsset] = useState<MediaAsset | undefined>();\n const [searchQuery, setSearchQuery] = useState(\"\");\n\n // Helper to fetch download URL for an asset\n const fetchDownloadURL = useCallback(async (asset: Omit<MediaAsset, \"downloadURL\">): Promise<MediaAsset> => {\n try {\n const downloadConfig = await storageSource.getDownloadURL(asset.storagePath, asset.bucket);\n return {\n ...asset,\n downloadURL: downloadConfig.url ?? undefined\n };\n } catch (err) {\n console.warn(`Failed to get download URL for ${asset.storagePath}:`, err);\n return { ...asset, downloadURL: undefined };\n }\n }, [storageSource]);\n\n // Fetch assets from the database\n const refreshAssets = useCallback(async () => {\n setLoading(true);\n setError(undefined);\n try {\n console.log(\"Fetching media assets from:\", collectionPath);\n const entities = await dataSourceDelegate.fetchCollection<Record<string, any>>({\n path: collectionPath,\n orderBy: \"createdAt\",\n order: \"desc\"\n });\n\n console.log(\"Fetched entities:\", entities.length);\n\n if (entities.length === 0) {\n setAssets([]);\n return;\n }\n\n // Convert entities to assets and fetch download URLs\n const loadedAssets: MediaAsset[] = await Promise.all(\n entities.map(async (entity) => {\n const values = entity.values;\n const baseAsset: Omit<MediaAsset, \"downloadURL\"> = {\n id: entity.id,\n fileName: values.fileName,\n storagePath: values.storagePath,\n mimeType: values.mimeType,\n size: values.size,\n dimensions: values.dimensions,\n title: values.title,\n altText: values.altText,\n caption: values.caption,\n tags: values.tags,\n bucket: values.bucket,\n createdAt: values.createdAt instanceof Date\n ? values.createdAt\n : (values.createdAt?.toDate ? values.createdAt.toDate() : new Date(values.createdAt)),\n updatedAt: values.updatedAt instanceof Date\n ? values.updatedAt\n : (values.updatedAt?.toDate ? values.updatedAt.toDate() : new Date(values.updatedAt))\n };\n\n // Fetch download URL for images\n if (baseAsset.mimeType?.startsWith(\"image/\") || baseAsset.mimeType?.startsWith(\"video/\")) {\n return await fetchDownloadURL(baseAsset);\n }\n return { ...baseAsset, downloadURL: undefined };\n })\n );\n\n console.log(\"Loaded assets with URLs:\", loadedAssets.length);\n setAssets(loadedAssets);\n } catch (err) {\n console.error(\"Error fetching media assets:\", err);\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setLoading(false);\n }\n }, [dataSourceDelegate, collectionPath, fetchDownloadURL]);\n\n // Initial load\n useEffect(() => {\n refreshAssets();\n }, [refreshAssets]);\n\n // Upload a new file\n const uploadFile = useCallback(async (\n file: File,\n metadata?: Partial<Pick<MediaAsset, \"title\" | \"altText\" | \"caption\" | \"tags\">>\n ): Promise<MediaAsset> => {\n console.log(\"Uploading file:\", file.name, \"to path:\", storagePath);\n\n // Upload to storage\n const uploadResult = await storageSource.uploadFile({\n file,\n fileName: file.name,\n path: storagePath,\n bucket\n });\n\n console.log(\"Upload result:\", uploadResult);\n\n // Get download URL immediately after upload\n let downloadURL: string | undefined;\n try {\n const downloadConfig = await storageSource.getDownloadURL(uploadResult.path, uploadResult.bucket);\n downloadURL = downloadConfig.url ?? undefined;\n console.log(\"Got download URL:\", downloadURL);\n } catch (err) {\n console.warn(\"Failed to get download URL after upload:\", err);\n }\n\n // Get dimensions for images\n let dimensions: { width: number; height: number } | undefined;\n if (file.type.startsWith(\"image/\")) {\n try {\n dimensions = await getImageDimensions(file);\n } catch (err) {\n console.warn(\"Failed to get image dimensions:\", err);\n }\n }\n\n // Generate thumbnails if configured and file is a raster image (skip SVGs)\n const thumbnails: Record<string, string> = {};\n const isRasterImage = file.type.startsWith(\"image/\") && file.type !== \"image/svg+xml\";\n if (thumbnailSizes && thumbnailSizes.length > 0 && isRasterImage) {\n console.log(\"Generating thumbnails for sizes:\", thumbnailSizes.map(s => s.name));\n\n for (const size of thumbnailSizes) {\n try {\n const thumbnailBlob = await generateThumbnail(file, size.width, size.height, size.quality ?? 0.8);\n const thumbFileName = `${Date.now()}_${size.name}_${file.name}`;\n const thumbPath = `${storagePath}/${thumbnailPath}/${size.name}`;\n\n // Upload thumbnail\n const thumbUploadResult = await storageSource.uploadFile({\n file: new File([thumbnailBlob], thumbFileName, { type: \"image/jpeg\" }),\n fileName: thumbFileName,\n path: thumbPath,\n bucket\n });\n\n // Get download URL for thumbnail\n const thumbDownloadConfig = await storageSource.getDownloadURL(thumbUploadResult.path, thumbUploadResult.bucket);\n if (thumbDownloadConfig.url) {\n thumbnails[size.name] = thumbDownloadConfig.url;\n console.log(`Generated ${size.name} thumbnail:`, thumbDownloadConfig.url);\n }\n } catch (err) {\n console.warn(`Failed to generate ${size.name} thumbnail:`, err);\n }\n }\n }\n\n const now = new Date();\n\n // Build asset data, only including defined values (Firestore doesn't allow undefined)\n const assetData: Record<string, any> = {\n fileName: file.name,\n storagePath: uploadResult.path,\n mimeType: file.type,\n size: file.size,\n createdAt: now,\n updatedAt: now\n };\n\n // Only add optional fields if they are defined\n if (dimensions) assetData.dimensions = dimensions;\n if (metadata?.title) assetData.title = metadata.title;\n if (metadata?.altText) assetData.altText = metadata.altText;\n if (metadata?.caption) assetData.caption = metadata.caption;\n if (metadata?.tags && metadata.tags.length > 0) assetData.tags = metadata.tags;\n if (uploadResult.bucket) assetData.bucket = uploadResult.bucket;\n // Store downloadURL in database for quick access later\n if (downloadURL) assetData.downloadURL = downloadURL;\n // Store thumbnails if any were generated\n if (Object.keys(thumbnails).length > 0) assetData.thumbnails = thumbnails;\n\n console.log(\"Saving asset data to database:\", assetData);\n\n // Save metadata to database\n const entity = await dataSourceDelegate.saveEntity<Record<string, any>>({\n path: collectionPath,\n values: assetData,\n status: \"new\"\n });\n\n console.log(\"Saved entity:\", entity.id);\n\n const newAsset: MediaAsset = {\n id: entity.id,\n fileName: file.name,\n storagePath: uploadResult.path,\n downloadURL,\n mimeType: file.type,\n size: file.size,\n createdAt: now,\n updatedAt: now,\n dimensions,\n title: metadata?.title,\n altText: metadata?.altText,\n caption: metadata?.caption,\n tags: metadata?.tags,\n bucket: uploadResult.bucket,\n thumbnails: Object.keys(thumbnails).length > 0 ? thumbnails : undefined\n };\n\n setAssets(prev => [newAsset, ...prev]);\n return newAsset;\n }, [storageSource, dataSourceDelegate, storagePath, collectionPath, bucket, thumbnailSizes, thumbnailPath]);\n\n // Delete an asset\n const deleteAsset = useCallback(async (assetId: string): Promise<void> => {\n const asset = assets.find(a => a.id === assetId);\n if (!asset) {\n throw new Error(`Asset with id ${assetId} not found`);\n }\n\n console.log(\"Deleting asset:\", assetId, asset.storagePath);\n\n // Delete from storage\n try {\n await storageSource.deleteFile(asset.storagePath, asset.bucket);\n } catch (err) {\n console.warn(\"Failed to delete from storage (may not exist):\", err);\n }\n\n // Delete from database\n await dataSourceDelegate.deleteEntity({\n entity: {\n id: assetId,\n path: collectionPath,\n values: asset\n }\n });\n\n setAssets(prev => prev.filter(a => a.id !== assetId));\n if (selectedAsset?.id === assetId) {\n setSelectedAsset(undefined);\n }\n }, [assets, storageSource, dataSourceDelegate, collectionPath, selectedAsset]);\n\n // Update asset metadata\n const updateAsset = useCallback(async (\n assetId: string,\n data: Partial<MediaAsset>\n ): Promise<void> => {\n const asset = assets.find(a => a.id === assetId);\n if (!asset) {\n throw new Error(`Asset with id ${assetId} not found`);\n }\n\n // Filter out undefined values\n const cleanData: Record<string, any> = {};\n Object.entries(data).forEach(([key, value]) => {\n if (value !== undefined) {\n cleanData[key] = value;\n }\n });\n cleanData.updatedAt = new Date();\n\n console.log(\"Updating asset:\", assetId, cleanData);\n\n await dataSourceDelegate.saveEntity({\n path: collectionPath,\n entityId: assetId,\n values: cleanData,\n previousValues: asset,\n status: \"existing\"\n });\n\n setAssets(prev => prev.map(a =>\n a.id === assetId ? { ...a, ...cleanData } : a\n ));\n\n if (selectedAsset?.id === assetId) {\n setSelectedAsset(prev => prev ? { ...prev, ...cleanData } : prev);\n }\n }, [assets, dataSourceDelegate, collectionPath, selectedAsset]);\n\n // Search assets (client-side filtering for now)\n const searchAssets = useCallback((query: string) => {\n setSearchQuery(query);\n }, []);\n\n const selectAsset = useCallback((asset: MediaAsset | undefined) => {\n setSelectedAsset(asset);\n }, []);\n\n // Filter assets based on search query\n const filteredAssets = searchQuery\n ? assets.filter(asset =>\n asset.fileName.toLowerCase().includes(searchQuery.toLowerCase()) ||\n asset.title?.toLowerCase().includes(searchQuery.toLowerCase()) ||\n asset.tags?.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase()))\n )\n : assets;\n\n return {\n loading,\n error,\n assets: filteredAssets,\n totalCount: assets.length,\n selectedAsset,\n selectAsset,\n uploadFile,\n deleteAsset,\n updateAsset,\n refreshAssets,\n searchAssets,\n storagePath,\n collectionPath\n };\n}\n\n/**\n * Helper to get image dimensions from a File\n */\nfunction getImageDimensions(file: File): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.width, height: img.height });\n URL.revokeObjectURL(img.src);\n };\n img.onerror = reject;\n img.src = URL.createObjectURL(file);\n });\n}\n\n/**\n * Generate a thumbnail from an image file using compressorjs.\n * Uses the same library as the core FireCMS image resize implementation.\n * Maintains aspect ratio while fitting within maxWidth x maxHeight.\n */\nasync function generateThumbnail(\n file: File,\n maxWidth: number,\n maxHeight: number,\n quality: number\n): Promise<Blob> {\n return new Promise<Blob>((resolve, reject) => {\n new Compressor(file, {\n quality,\n maxWidth,\n maxHeight,\n mimeType: \"image/jpeg\",\n success: (result) => {\n resolve(result);\n },\n error: reject,\n });\n });\n}\n","import React from \"react\";\nimport {\n Card,\n Typography,\n cls,\n ImageIcon,\n DescriptionIcon,\n VideoLibraryIcon,\n AudiotrackIcon,\n CheckIcon,\n defaultBorderMixin\n} from \"@firecms/ui\";\nimport { MediaAsset } from \"../types\";\n\nexport interface MediaAssetCardProps {\n asset: MediaAsset;\n viewMode: \"grid\" | \"list\";\n onClick: () => void;\n selected?: boolean;\n /** Preferred thumbnail size to display (e.g., \"small\", \"medium\") */\n thumbnailSize?: string;\n}\n\n/**\n * Card component for displaying a media asset in the grid or list.\n */\nexport function MediaAssetCard({\n asset,\n viewMode,\n onClick,\n selected,\n thumbnailSize = \"small\"\n}: MediaAssetCardProps) {\n const isImage = asset.mimeType.startsWith(\"image/\");\n const isVideo = asset.mimeType.startsWith(\"video/\");\n const isAudio = asset.mimeType.startsWith(\"audio/\");\n\n const FileIcon = isImage ? ImageIcon\n : isVideo ? VideoLibraryIcon\n : isAudio ? AudiotrackIcon\n : DescriptionIcon;\n\n const formatSize = (bytes: number): string => {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n };\n\n // Use thumbnail if available, fallback to downloadURL\n const imageUrl = asset.thumbnails?.[thumbnailSize] ?? asset.downloadURL;\n\n const thumbnail = isImage && imageUrl ? (\n <img\n src={imageUrl}\n alt={asset.altText || asset.fileName}\n className=\"w-full h-full object-cover transition-transform duration-200 group-hover:scale-105\"\n loading=\"lazy\"\n />\n ) : (\n <div className=\"w-full h-full flex items-center justify-center bg-surface-100 dark:bg-surface-800\">\n <FileIcon size=\"large\" className=\"text-surface-400 dark:text-surface-500\" />\n </div>\n );\n\n if (viewMode === \"list\") {\n return (\n <div\n className={cls(\n \"p-3 cursor-pointer flex items-center gap-3 rounded-lg\",\n \"hover:bg-surface-100 dark:hover:bg-surface-800\",\n \"transition-colors duration-150\",\n `border ${defaultBorderMixin}`,\n selected && \"ring-2 ring-primary bg-primary/5\"\n )}\n onClick={onClick}\n >\n <div className=\"w-12 h-12 rounded-md overflow-hidden flex-shrink-0 bg-surface-100 dark:bg-surface-800\">\n {thumbnail}\n </div>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"font-medium truncate text-surface-900 dark:text-white\">\n {asset.title || asset.fileName}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {formatSize(asset.size)} • {asset.mimeType.split(\"/\")[1]?.toUpperCase()}\n </Typography>\n </div>\n {selected && (\n <div className=\"w-6 h-6 rounded-full bg-primary flex items-center justify-center flex-shrink-0\">\n <CheckIcon size=\"smallest\" className=\"text-white\" />\n </div>\n )}\n </div>\n );\n }\n\n return (\n <Card\n className={cls(\n \"cursor-pointer overflow-hidden group relative\",\n \"transition-all duration-200\",\n \"hover:shadow-lg hover:-translate-y-0.5\",\n selected && \"ring-2 ring-primary\"\n )}\n onClick={onClick}\n >\n <div className=\"aspect-square relative overflow-hidden bg-surface-100 dark:bg-surface-800\">\n {thumbnail}\n\n {/* Hover overlay */}\n <div className={cls(\n \"absolute inset-0 bg-black/0 group-hover:bg-black/20\",\n \"transition-colors duration-200\"\n )} />\n\n {/* Selection indicator */}\n {selected && (\n <div className=\"absolute top-2 right-2 w-6 h-6 rounded-full bg-primary flex items-center justify-center shadow-md\">\n <CheckIcon size=\"smallest\" className=\"text-white\" />\n </div>\n )}\n </div>\n\n <div className=\"p-3\">\n <Typography variant=\"body2\" className=\"font-medium truncate text-surface-900 dark:text-white\">\n {asset.title || asset.fileName}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate block mt-0.5\">\n {formatSize(asset.size)} • {asset.mimeType.split(\"/\")[1]?.toUpperCase()}\n </Typography>\n </div>\n </Card>\n );\n}\n","import React, { useCallback, useState } from \"react\";\nimport { useCreateFormex } from \"@firecms/formex\";\nimport {\n Button,\n Typography,\n cls,\n CloseIcon,\n DeleteIcon,\n DownloadIcon,\n IconButton,\n TextField,\n Chip,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent\n} from \"@firecms/ui\";\nimport { useSnackbarController, useStorageSource } from \"@firecms/core\";\nimport { MediaAsset } from \"../types\";\n\nexport interface MediaAssetDetailsProps {\n asset: MediaAsset;\n onClose: () => void;\n onUpdate: (assetId: string, data: Partial<MediaAsset>) => Promise<void>;\n onDelete: (assetId: string) => Promise<void>;\n}\n\n/**\n * Side panel component for viewing and editing media asset details.\n */\nexport function MediaAssetDetails({\n asset,\n onClose,\n onUpdate,\n onDelete\n}: MediaAssetDetailsProps) {\n const snackbarController = useSnackbarController();\n const storageSource = useStorageSource();\n const [saving, setSaving] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [tagInput, setTagInput] = useState(\"\");\n\n const { values, setFieldValue, dirty } = useCreateFormex<Partial<MediaAsset>>({\n initialValues: {\n title: asset.title ?? \"\",\n altText: asset.altText ?? \"\",\n caption: asset.caption ?? \"\",\n tags: asset.tags ?? []\n }\n });\n\n const handleSave = useCallback(async () => {\n setSaving(true);\n try {\n await onUpdate(asset.id, values);\n snackbarController.open({\n type: \"success\",\n message: \"Asset updated successfully\"\n });\n } catch (error) {\n snackbarController.open({\n type: \"error\",\n message: `Error updating asset: ${error instanceof Error ? error.message : String(error)}`\n });\n } finally {\n setSaving(false);\n }\n }, [asset.id, values, onUpdate, snackbarController]);\n\n const handleDelete = useCallback(async () => {\n setDeleting(true);\n try {\n await onDelete(asset.id);\n snackbarController.open({\n type: \"success\",\n message: \"Asset deleted successfully\"\n });\n onClose();\n } catch (error) {\n snackbarController.open({\n type: \"error\",\n message: `Error deleting asset: ${error instanceof Error ? error.message : String(error)}`\n });\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n }\n }, [asset.id, onDelete, snackbarController, onClose]);\n\n const handleDownload = useCallback(async () => {\n try {\n const downloadConfig = await storageSource.getDownloadURL(asset.storagePath, asset.bucket);\n if (downloadConfig.url) {\n window.open(downloadConfig.url, \"_blank\");\n }\n } catch (error) {\n snackbarController.open({\n type: \"error\",\n message: \"Error getting download URL\"\n });\n }\n }, [asset, storageSource, snackbarController]);\n\n const handleAddTag = useCallback(() => {\n const tag = tagInput.trim();\n if (tag && !values.tags?.includes(tag)) {\n setFieldValue(\"tags\", [...(values.tags ?? []), tag]);\n setTagInput(\"\");\n }\n }, [tagInput, values.tags, setFieldValue]);\n\n const handleRemoveTag = useCallback((tagToRemove: string) => {\n setFieldValue(\"tags\", values.tags?.filter((t: string) => t !== tagToRemove) ?? []);\n }, [values.tags, setFieldValue]);\n\n const formatSize = (bytes: number): string => {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n };\n\n const formatDate = (date: Date): string => {\n return new Intl.DateTimeFormat(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\"\n }).format(date);\n };\n\n const isImage = asset.mimeType.startsWith(\"image/\");\n const isVideo = asset.mimeType.startsWith(\"video/\");\n\n return (\n <>\n <div className={cls(\n \"fixed inset-y-0 right-0 w-full sm:w-96 lg:w-[480px]\",\n \"bg-surface-50 dark:bg-surface-900\",\n \"border-l border-surface-accent-200 dark:border-surface-accent-700\",\n \"shadow-xl z-50\",\n \"flex flex-col\",\n \"animate-slide-in-right\"\n )}>\n {/* Header */}\n <div className=\"flex items-center justify-between p-4 border-b border-surface-accent-200 dark:border-surface-accent-700\">\n <Typography variant=\"subtitle1\" className=\"font-medium truncate flex-1 mr-2\">\n {asset.title || asset.fileName}\n </Typography>\n <div className=\"flex items-center gap-1\">\n <IconButton onClick={handleDownload}>\n <DownloadIcon size=\"small\" />\n </IconButton>\n <IconButton\n onClick={() => setDeleteDialogOpen(true)}\n className=\"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20\"\n >\n <DeleteIcon size=\"small\" />\n </IconButton>\n <IconButton onClick={onClose}>\n <CloseIcon size=\"small\" />\n </IconButton>\n </div>\n </div>\n\n {/* Preview */}\n <div className=\"p-4 bg-surface-accent-100 dark:bg-surface-accent-800 flex items-center justify-center min-h-48 max-h-64\">\n {isImage && asset.downloadURL ? (\n <img\n src={asset.downloadURL}\n alt={asset.altText || asset.fileName}\n className=\"max-w-full max-h-full object-contain\"\n />\n ) : isVideo && asset.downloadURL ? (\n <video\n src={asset.downloadURL}\n className=\"max-w-full max-h-full\"\n controls\n />\n ) : (\n <div className=\"text-surface-accent-400\">\n Preview not available\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-auto p-4 space-y-4\">\n {/* Metadata */}\n <div className=\"grid grid-cols-2 gap-3\">\n {asset.dimensions && (\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Dimensions\n </Typography>\n <Typography variant=\"body2\">\n {asset.dimensions.width} × {asset.dimensions.height} px\n </Typography>\n </div>\n )}\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Size\n </Typography>\n <Typography variant=\"body2\">\n {formatSize(asset.size)}\n </Typography>\n </div>\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Type\n </Typography>\n <Typography variant=\"body2\">\n {asset.mimeType}\n </Typography>\n </div>\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Created\n </Typography>\n <Typography variant=\"body2\">\n {formatDate(asset.createdAt)}\n </Typography>\n </div>\n </div>\n\n <hr className=\"border-surface-accent-200 dark:border-surface-accent-700\" />\n\n {/* Editable Fields */}\n <TextField\n label=\"File Name\"\n value={asset.fileName}\n disabled\n size=\"small\"\n />\n\n <TextField\n label=\"Title\"\n value={values.title ?? \"\"}\n onChange={(e) => setFieldValue(\"title\", e.target.value)}\n size=\"small\"\n />\n\n <div>\n <TextField\n label=\"Alt Text\"\n value={values.altText ?? \"\"}\n onChange={(e) => setFieldValue(\"altText\", e.target.value)}\n size=\"small\"\n />\n <Typography variant=\"caption\" className=\"text-surface-accent-500 mt-1\">\n Recommended for SEO\n </Typography>\n </div>\n\n <TextField\n label=\"Caption\"\n value={values.caption ?? \"\"}\n onChange={(e) => setFieldValue(\"caption\", e.target.value)}\n size=\"small\"\n multiline\n />\n\n {/* Tags */}\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 mb-1 block\">\n Tags\n </Typography>\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {values.tags?.map((tag: string) => (\n <Chip\n key={tag}\n size=\"small\"\n colorScheme=\"blueLighter\"\n onClick={() => handleRemoveTag(tag)}\n >\n {tag} ×\n </Chip>\n ))}\n </div>\n <div className=\"flex gap-2\">\n <TextField\n placeholder=\"Add a tag...\"\n value={tagInput}\n onChange={(e) => setTagInput(e.target.value)}\n size=\"small\"\n className=\"flex-1\"\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleAddTag();\n }\n }}\n />\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={handleAddTag}\n disabled={!tagInput.trim()}\n >\n Add\n </Button>\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex-shrink-0 p-4 border-t border-surface-accent-200 dark:border-surface-accent-700\">\n <Button\n variant=\"filled\"\n onClick={handleSave}\n disabled={!dirty || saving}\n className=\"w-full\"\n >\n {saving ? <CircularProgress size=\"small\" /> : \"Save Changes\"}\n </Button>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <Dialog\n open={deleteDialogOpen}\n onOpenChange={setDeleteDialogOpen}\n >\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-medium mb-2\">\n Delete Asset?\n </Typography>\n <Typography className=\"text-surface-accent-600 dark:text-surface-accent-400\">\n Are you sure you want to delete \"{asset.title || asset.fileName}\"?\n This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => setDeleteDialogOpen(false)}\n disabled={deleting}\n >\n Cancel\n </Button>\n <Button\n variant=\"filled\"\n color=\"error\"\n onClick={handleDelete}\n disabled={deleting}\n >\n {deleting ? <CircularProgress size=\"small\" /> : \"Delete\"}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n","import React, { useCallback, useState } from \"react\";\nimport {\n Button,\n Typography,\n cls,\n Dialog,\n DialogActions,\n DialogContent,\n CloudUploadIcon,\n CircularProgress\n} from \"@firecms/ui\";\n\nexport interface MediaUploadDialogProps {\n open: boolean;\n onClose: () => void;\n onUpload: (files: File[]) => Promise<void>;\n maxFileSize?: number;\n acceptedMimeTypes?: string[];\n}\n\n/**\n * Dialog component for uploading files to the media library.\n * Supports drag-and-drop and file browser selection.\n */\nexport function MediaUploadDialog({\n open,\n onClose,\n onUpload,\n maxFileSize = 52428800, // 50MB default\n acceptedMimeTypes\n}: MediaUploadDialogProps) {\n const [isDragging, setIsDragging] = useState(false);\n const [uploading, setUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [selectedFiles, setSelectedFiles] = useState<File[]>([]);\n\n const validateFiles = useCallback((files: File[]): { valid: File[]; errors: string[] } => {\n const valid: File[] = [];\n const errors: string[] = [];\n\n for (const file of files) {\n if (maxFileSize && file.size > maxFileSize) {\n errors.push(`${file.name}: File too large (max ${formatSize(maxFileSize)})`);\n continue;\n }\n if (acceptedMimeTypes && !acceptedMimeTypes.some(type => {\n if (type.endsWith(\"/*\")) {\n return file.type.startsWith(type.slice(0, -1));\n }\n return file.type === type;\n })) {\n errors.push(`${file.name}: File type not allowed`);\n continue;\n }\n valid.push(file);\n }\n\n return { valid, errors };\n }, [maxFileSize, acceptedMimeTypes]);\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(true);\n }, []);\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n }, []);\n\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n\n const files = Array.from(e.dataTransfer.files);\n const { valid, errors } = validateFiles(files);\n\n if (errors.length > 0) {\n setError(errors.join(\"\\n\"));\n } else {\n setError(null);\n }\n\n setSelectedFiles(prev => [...prev, ...valid]);\n }, [validateFiles]);\n\n const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(e.target.files ?? []);\n const { valid, errors } = validateFiles(files);\n\n if (errors.length > 0) {\n setError(errors.join(\"\\n\"));\n } else {\n setError(null);\n }\n\n setSelectedFiles(prev => [...prev, ...valid]);\n }, [validateFiles]);\n\n const handleRemoveFile = useCallback((index: number) => {\n setSelectedFiles(prev => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (selectedFiles.length === 0) return;\n\n setUploading(true);\n setError(null);\n\n try {\n await onUpload(selectedFiles);\n setSelectedFiles([]);\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n }\n }, [selectedFiles, onUpload, onClose]);\n\n const handleClose = useCallback(() => {\n if (!uploading) {\n setSelectedFiles([]);\n setError(null);\n onClose();\n }\n }, [uploading, onClose]);\n\n const formatSize = (bytes: number): string => {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open) => !open && handleClose()}\n maxWidth=\"md\"\n >\n <DialogContent className=\"p-0\">\n <div className=\"p-4 border-b border-surface-accent-200 dark:border-surface-accent-700\">\n <Typography variant=\"h6\">\n Upload Files\n </Typography>\n </div>\n\n <div className=\"p-4\">\n {/* Drop Zone */}\n <div\n className={cls(\n \"border-2 border-dashed rounded-lg p-8\",\n \"flex flex-col items-center justify-center gap-4\",\n \"transition-colors duration-150\",\n isDragging\n ? \"border-primary bg-primary/5\"\n : \"border-surface-accent-300 dark:border-surface-accent-600\",\n \"hover:border-primary hover:bg-primary/5\",\n \"cursor-pointer\"\n )}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n onClick={() => document.getElementById(\"file-upload-input\")?.click()}\n >\n <CloudUploadIcon\n size=\"large\"\n className={cls(\n isDragging ? \"text-primary\" : \"text-surface-accent-400\"\n )}\n />\n <div className=\"text-center\">\n <Typography variant=\"body1\" className=\"font-medium\">\n Drop files here or click to browse\n </Typography>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Maximum file size: {formatSize(maxFileSize)}\n </Typography>\n </div>\n <input\n id=\"file-upload-input\"\n type=\"file\"\n multiple\n accept={acceptedMimeTypes?.join(\",\")}\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n </div>\n\n {/* Error Display */}\n {error && (\n <Typography variant=\"caption\" className=\"text-red-500 mt-2 block whitespace-pre-line\">\n {error}\n </Typography>\n )}\n\n {/* Selected Files */}\n {selectedFiles.length > 0 && (\n <div className=\"mt-4 space-y-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n Selected files ({selectedFiles.length})\n </Typography>\n <div className=\"max-h-40 overflow-auto space-y-1\">\n {selectedFiles.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className={cls(\n \"flex items-center justify-between p-2 rounded\",\n \"bg-surface-accent-50 dark:bg-surface-accent-800\"\n )}\n >\n <div className=\"flex-1 min-w-0 mr-2\">\n <Typography variant=\"body2\" className=\"truncate\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" className=\"text-surface-accent-500\">\n {formatSize(file.size)}\n </Typography>\n </div>\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n handleRemoveFile(index);\n }}\n disabled={uploading}\n >\n Remove\n </Button>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n </DialogContent>\n\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={handleClose}\n disabled={uploading}\n >\n Cancel\n </Button>\n <Button\n variant=\"filled\"\n onClick={handleUpload}\n disabled={selectedFiles.length === 0 || uploading}\n >\n {uploading ? (\n <>\n <CircularProgress size=\"smallest\" />\n Uploading...\n </>\n ) : (\n `Upload ${selectedFiles.length > 0 ? `(${selectedFiles.length})` : \"\"}`\n )}\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n","import React, { useCallback, useRef, useState } from \"react\";\nimport {\n Button,\n Container,\n SearchBar,\n Typography,\n cls,\n AddIcon,\n CircularProgress,\n RefreshIcon,\n IconButton,\n Tooltip,\n AppsIcon,\n Icon\n} from \"@firecms/ui\";\nimport { useMediaManager } from \"../MediaManagerProvider\";\nimport { MediaAssetCard } from \"./MediaAssetCard\";\nimport { MediaAssetDetails } from \"./MediaAssetDetails\";\nimport { MediaUploadDialog } from \"./MediaUploadDialog\";\n\nexport interface MediaLibraryViewProps {\n maxFileSize?: number;\n acceptedMimeTypes?: string[];\n}\n\n/**\n * Main view component for the Media Library.\n * Displays a grid of assets with search, upload, and management capabilities.\n */\nexport function MediaLibraryView({\n maxFileSize,\n acceptedMimeTypes\n }: MediaLibraryViewProps) {\n const controller = useMediaManager();\n const [uploadDialogOpen, setUploadDialogOpen] = useState(false);\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const handleSearch = useCallback((query?: string) => {\n controller.searchAssets(query ?? \"\");\n }, [controller]);\n\n const handleUploadClick = useCallback(() => {\n setUploadDialogOpen(true);\n }, []);\n\n const handleFileSelect = useCallback(async (files: File[]) => {\n for (const file of files) {\n await controller.uploadFile(file);\n }\n setUploadDialogOpen(false);\n }, [controller]);\n\n const handleRefresh = useCallback(() => {\n controller.refreshAssets();\n }, [controller]);\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/* Header */}\n <div\n className=\"flex-shrink-0 border-b border-surface-accent-200 dark:border-surface-accent-700 bg-surface-50 dark:bg-surface-900\">\n <Container maxWidth=\"6xl\" className=\"py-4\">\n <div className=\"flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Typography variant=\"h5\" className=\"font-semibold\">\n Media Library\n </Typography>\n {controller.totalCount !== undefined && (\n <Typography\n variant=\"caption\"\n className=\"bg-surface-accent-100 dark:bg-surface-accent-800 px-2 py-0.5 rounded-full\"\n >\n {controller.totalCount} assets\n </Typography>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 w-full sm:w-auto\">\n <SearchBar\n onTextSearch={handleSearch}\n placeholder=\"Search assets...\"\n className=\"flex-1 sm:w-64\"\n />\n\n <div\n className=\"flex items-center gap-1 border-l border-surface-accent-200 dark:border-surface-accent-700 pl-2 ml-2\">\n <Tooltip title=\"Grid view\">\n <IconButton\n onClick={() => setViewMode(\"grid\")}\n className={cls(\n viewMode === \"grid\" && \"bg-surface-accent-100 dark:bg-surface-accent-800\"\n )}\n >\n <AppsIcon size=\"small\"/>\n </IconButton>\n </Tooltip>\n <Tooltip title=\"List view\">\n <IconButton\n onClick={() => setViewMode(\"list\")}\n className={cls(\n viewMode === \"list\" && \"bg-surface-accent-100 dark:bg-surface-accent-800\"\n )}\n >\n <Icon iconKey=\"list\" size=\"small\"/>\n </IconButton>\n </Tooltip>\n </div>\n\n <Tooltip title=\"Refresh\">\n <IconButton onClick={handleRefresh} disabled={controller.loading}>\n <RefreshIcon size=\"small\"/>\n </IconButton>\n </Tooltip>\n\n <Button\n variant=\"filled\"\n onClick={handleUploadClick}\n >\n <AddIcon size=\"small\"/>\n Upload\n </Button>\n </div>\n </div>\n </Container>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-auto\">\n <Container maxWidth=\"6xl\" className=\"py-6\">\n {controller.loading && controller.assets.length === 0 ? (\n <div className=\"flex items-center justify-center h-64\">\n <CircularProgress/>\n </div>\n ) : controller.error ? (\n <div className=\"flex flex-col items-center justify-center h-64 gap-4\">\n <Typography className=\"text-red-500\">\n Error loading assets: {controller.error.message}\n </Typography>\n <Button onClick={handleRefresh}>\n Try Again\n </Button>\n </div>\n ) : controller.assets.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-64 gap-4\">\n <Typography className=\"text-surface-accent-500\">\n No media assets yet\n </Typography>\n <Button onClick={handleUploadClick}>\n <AddIcon size=\"small\"/>\n Upload your first file\n </Button>\n </div>\n ) : (\n <div className={cls(\n viewMode === \"grid\"\n ? \"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4\"\n : \"flex flex-col gap-2\"\n )}>\n {controller.assets.map(asset => (\n <MediaAssetCard\n key={asset.id}\n asset={asset}\n viewMode={viewMode}\n onClick={() => controller.selectAsset(asset)}\n selected={controller.selectedAsset?.id === asset.id}\n />\n ))}\n </div>\n )}\n </Container>\n </div>\n\n {/* Details Panel */}\n {controller.selectedAsset && (\n <MediaAssetDetails\n asset={controller.selectedAsset}\n onClose={() => controller.selectAsset(undefined)}\n onUpdate={controller.updateAsset}\n onDelete={controller.deleteAsset}\n />\n )}\n\n {/* Upload Dialog */}\n <MediaUploadDialog\n open={uploadDialogOpen}\n onClose={() => setUploadDialogOpen(false)}\n onUpload={handleFileSelect}\n maxFileSize={maxFileSize}\n acceptedMimeTypes={acceptedMimeTypes}\n />\n </div>\n );\n}\n","import React, { useMemo, createContext, useContext, PropsWithChildren } from \"react\";\nimport { FireCMSPlugin, CMSView } from \"@firecms/core\";\nimport { MediaManagerConfig } from \"./types\";\nimport { MediaManagerProvider } from \"./MediaManagerProvider\";\nimport { useMediaManagerController } from \"./useMediaManagerController\";\nimport { MediaLibraryCard } from \"./components/MediaLibraryCard\";\nimport { MediaLibraryView } from \"./components/MediaLibraryView\";\n\nconst DEFAULT_STORAGE_PATH = \"media\";\nconst DEFAULT_COLLECTION_PATH = \"media_assets\";\n\nexport interface MediaManagerPluginProps extends MediaManagerConfig { }\n\n// Context to store the config\nconst MediaManagerConfigContext = createContext<MediaManagerConfig | null>(null);\n\nfunction useMediaManagerConfig(): MediaManagerConfig {\n const config = useContext(MediaManagerConfigContext);\n if (!config) {\n throw new Error(\"useMediaManagerConfig must be used within MediaManagerConfigProvider\");\n }\n return config;\n}\n\n/**\n * Internal wrapper that reads config from context\n */\nfunction MediaLibraryViewInternal() {\n const config = useMediaManagerConfig();\n const controller = useMediaManagerController({\n storageSource: config.storageSource,\n dataSourceDelegate: config.dataSourceDelegate,\n storagePath: config.storagePath ?? DEFAULT_STORAGE_PATH,\n collectionPath: config.collectionPath ?? DEFAULT_COLLECTION_PATH,\n bucket: config.bucket,\n thumbnailSizes: config.thumbnailSizes,\n thumbnailPath: config.thumbnailPath\n });\n\n return (\n <MediaManagerProvider controller={controller}>\n <MediaLibraryView\n maxFileSize={config.maxFileSize}\n acceptedMimeTypes={config.acceptedMimeTypes}\n />\n </MediaManagerProvider>\n );\n}\n\n/**\n * Build the media view - this is a static object that doesn't change\n */\nfunction buildMediaView(): CMSView {\n return {\n path: \"media\",\n name: \"Media Library\",\n description: \"Manage your media files and assets\",\n group: \"Media\",\n icon: \"perm_media\",\n view: <MediaLibraryViewInternal />\n };\n}\n\n// Single static instance of the view\nconst MEDIA_VIEW = buildMediaView();\n\n/**\n * Hook to create the Media Manager plugin for FireCMS.\n *\n * The plugin automatically registers the Media Library view in the navigation.\n *\n * @example\n * ```tsx\n * const { plugin: mediaManagerPlugin } = useMediaManagerPlugin({\n * storageSource,\n * dataSourceDelegate: firestoreDelegate,\n * storagePath: \"media\",\n * collectionPath: \"media_assets\"\n * });\n *\n * // Add plugin to your plugins array - view is auto-registered\n * const plugins = [mediaManagerPlugin, ...otherPlugins];\n * ```\n */\nexport function useMediaManagerPlugin(props: MediaManagerPluginProps): FireCMSPlugin {\n return useMemo(() => ({\n key: \"media_manager\",\n views: [MEDIA_VIEW],\n provider: {\n Component: ({ children }: PropsWithChildren) => (\n <MediaManagerConfigContext.Provider value={props}>\n {children}\n </MediaManagerConfigContext.Provider>\n )\n }\n } satisfies FireCMSPlugin), []);\n}\n","import React from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n Card,\n Typography,\n cls,\n ImageIcon\n} from \"@firecms/ui\";\n\nexport interface MediaLibraryCardProps {\n group?: string;\n context?: unknown;\n}\n\n/**\n * Card component displayed on the home page that links to the Media Library.\n */\nexport function MediaLibraryCard({ group }: MediaLibraryCardProps) {\n // Only render in the \"Media\" group\n if (group !== \"Media\") return null;\n\n return (\n <Link to=\"/media\" className=\"no-underline\">\n <Card\n className={cls(\n \"p-4 cursor-pointer\",\n \"hover:bg-surface-accent-100 dark:hover:bg-surface-accent-800\",\n \"transition-colors duration-200\",\n \"flex flex-col gap-2\"\n )}\n >\n <div className=\"flex items-center gap-3\">\n <div className={cls(\n \"w-10 h-10 rounded-lg\",\n \"bg-primary/10 dark:bg-primary/20\",\n \"flex items-center justify-center\"\n )}>\n <ImageIcon className=\"text-primary\" size=\"medium\" />\n </div>\n <div className=\"flex-1\">\n <Typography variant=\"subtitle2\" className=\"font-medium\">\n Media Library\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-surface-accent-600 dark:text-surface-accent-400\"\n >\n Manage images and files\n </Typography>\n </div>\n </div>\n </Card>\n </Link>\n );\n}\n"],"names":["MediaManagerContext","createContext","undefined","useMediaManager","context","useContext","Error","MediaManagerProvider","t0","$","_c","controller","children","t1","DEFAULT_THUMBNAIL_PATH","useMediaManagerController","storageSource","dataSourceDelegate","storagePath","collectionPath","bucket","thumbnailSizes","thumbnailPath","loading","setLoading","useState","error","setError","assets","setAssets","selectedAsset","setSelectedAsset","searchQuery","setSearchQuery","fetchDownloadURL","useCallback","asset","downloadConfig","getDownloadURL","downloadURL","url","err","console","warn","refreshAssets","log","entities","fetchCollection","path","orderBy","order","length","loadedAssets","Promise","all","map","entity","values","baseAsset","id","fileName","mimeType","size","dimensions","title","altText","caption","tags","createdAt","Date","toDate","updatedAt","startsWith","String","useEffect","uploadFile","file","metadata","name","uploadResult","type","getImageDimensions","thumbnails","isRasterImage","s","thumbnailBlob","generateThumbnail","width","height","quality","thumbFileName","now","thumbPath","thumbUploadResult","File","thumbDownloadConfig","assetData","Object","keys","saveEntity","status","newAsset","prev","deleteAsset","assetId","find","a","deleteFile","deleteEntity","filter","updateAsset","data","cleanData","entries","forEach","key","value","entityId","previousValues","searchAssets","query","selectAsset","filteredAssets","toLowerCase","includes","some","tag","totalCount","resolve","reject","img","Image","onload","URL","revokeObjectURL","src","onerror","createObjectURL","maxWidth","maxHeight","Compressor","success","result","MediaAssetCard","viewMode","onClick","selected","thumbnailSize","t2","isImage","isVideo","isAudio","FileIcon","ImageIcon","VideoLibraryIcon","AudiotrackIcon","DescriptionIcon","formatSize","_temp","imageUrl","t3","jsx","thumbnail","t4","t5","cls","defaultBorderMixin","t6","t7","t8","Typography","t9","t10","split","toUpperCase","t11","jsxs","t12","t13","CheckIcon","t14","Symbol","for","t15","Card","bytes","toFixed","MediaAssetDetails","onClose","onUpdate","onDelete","snackbarController","useSnackbarController","useStorageSource","saving","setSaving","deleting","setDeleting","deleteDialogOpen","setDeleteDialogOpen","tagInput","setTagInput","setFieldValue","dirty","useCreateFormex","initialValues","handleSave","open","message","handleDelete","handleDownload","window","handleAddTag","trim","handleRemoveTag","tagToRemove","t","formatDate","date","Intl","DateTimeFormat","year","month","day","format","Fragment","IconButton","DownloadIcon","DeleteIcon","CloseIcon","TextField","e","target","Chip","preventDefault","Button","CircularProgress","Dialog","DialogContent","DialogActions","MediaUploadDialog","onUpload","maxFileSize","acceptedMimeTypes","isDragging","setIsDragging","uploading","setUploading","selectedFiles","setSelectedFiles","validateFiles","files","valid","errors","push","endsWith","slice","handleDragOver","handleDragLeave","handleDrop","Array","from","dataTransfer","join","handleFileSelect","handleRemoveFile","index","_","i","handleUpload","handleClose","document","getElementById","click","CloudUploadIcon","stopPropagation","MediaLibraryView","uploadDialogOpen","setUploadDialogOpen","setViewMode","useRef","handleSearch","handleUploadClick","handleRefresh","SearchBar","AppsIcon","Tooltip","t16","t17","Icon","t18","t19","t20","RefreshIcon","t21","t22","AddIcon","t23","t24","Container","t25","t26","t27","t28","t29","DEFAULT_STORAGE_PATH","DEFAULT_COLLECTION_PATH","MediaManagerConfigContext","useMediaManagerConfig","config","MediaLibraryViewInternal","buildMediaView","description","group","icon","view","MEDIA_VIEW","useMediaManagerPlugin","props","useMemo","views","provider","Component","MediaLibraryCard","Link"],"mappings":";;;;AAGA,QAAMA,sBAAsBC,MAAAA,cAAkDC,MAAS;AAMhF,WAAAC,kBAAA;AACH,UAAAC,UAAgBC,MAAAA,WAAAL,mBAA8B;AAAE,QAAA,CAC3CI,SAAO;AAAA,YAAA,IAAAE,MACQ,4DAA4D;AAAA,IAAA;AAAA,WAEzEF;AAAAA,EAAO;AAUX,WAAAG,qBAAAC,IAAA;AAAA,UAAAC,IAAAC,qBAAAA,EAAA,CAAA;AAA8B,UAAA;AAAA,MAAAC;AAAAA,MAAAC;AAAAA,IAAAA,IAAAJ;AAGU,QAAAK;AAAA,QAAAJ,EAAA,CAAA,MAAAG,YAAAH,SAAAE,YAAA;AAEvCE,0CAAA,oBAAA,UAAA,EAAqCF,OAAAA,YAChCC,UACL;AAA+BH,aAAAG;AAAAH,aAAAE;AAAAF,aAAAI;AAAAA,IAAA,OAAA;AAAAA,WAAAJ,EAAA,CAAA;AAAA,IAAA;AAAA,WAF/BI;AAAAA,EAE+B;ACdvC,QAAMC,yBAAyB;AAMxB,WAASC,0BAA0B;AAAA,IACtCC;AAAAA,IACAC;AAAAA,IACAC;AAAAA,IACAC;AAAAA,IACAC;AAAAA,IACAC;AAAAA,IACAC,gBAAgBR;AAAAA,EACY,GAA2B;AACvD,UAAM,CAACS,SAASC,UAAU,IAAIC,MAAAA,SAAS,IAAI;AAC3C,UAAM,CAACC,OAAOC,QAAQ,IAAIF,eAAAA;AAC1B,UAAM,CAACG,QAAQC,SAAS,IAAIJ,MAAAA,SAAuB,CAAA,CAAE;AACrD,UAAM,CAACK,eAAeC,gBAAgB,IAAIN,eAAAA;AAC1C,UAAM,CAACO,aAAaC,cAAc,IAAIR,MAAAA,SAAS,EAAE;AAGjD,UAAMS,mBAAmBC,kBAAY,OAAOC,UAAgE;AACxG,UAAI;AACA,cAAMC,iBAAiB,MAAMrB,cAAcsB,eAAeF,MAAMlB,aAAakB,MAAMhB,MAAM;AACzF,eAAO;AAAA,UACH,GAAGgB;AAAAA,UACHG,aAAaF,eAAeG,OAAOtC;AAAAA,QAAAA;AAAAA,MAE3C,SAASuC,KAAK;AACVC,gBAAQC,KAAK,kCAAkCP,MAAMlB,WAAW,KAAKuB,GAAG;AACxE,eAAO;AAAA,UAAE,GAAGL;AAAAA,UAAOG,aAAarC;AAAAA,QAAAA;AAAAA,MACpC;AAAA,IACJ,GAAG,CAACc,aAAa,CAAC;AAGlB,UAAM4B,gBAAgBT,MAAAA,YAAY,YAAY;AAC1CX,iBAAW,IAAI;AACfG,eAASzB,MAAS;AAClB,UAAI;AACAwC,gBAAQG,IAAI,+BAA+B1B,cAAc;AACzD,cAAM2B,WAAW,MAAM7B,mBAAmB8B,gBAAqC;AAAA,UAC3EC,MAAM7B;AAAAA,UACN8B,SAAS;AAAA,UACTC,OAAO;AAAA,QAAA,CACV;AAEDR,gBAAQG,IAAI,qBAAqBC,SAASK,MAAM;AAEhD,YAAIL,SAASK,WAAW,GAAG;AACvBtB,oBAAU,CAAA,CAAE;AACZ;AAAA,QACJ;AAGA,cAAMuB,eAA6B,MAAMC,QAAQC,IAC7CR,SAASS,IAAI,OAAOC,WAAW;AAC3B,gBAAMC,SAASD,OAAOC;AACtB,gBAAMC,YAA6C;AAAA,YAC/CC,IAAIH,OAAOG;AAAAA,YACXC,UAAUH,OAAOG;AAAAA,YACjB1C,aAAauC,OAAOvC;AAAAA,YACpB2C,UAAUJ,OAAOI;AAAAA,YACjBC,MAAML,OAAOK;AAAAA,YACbC,YAAYN,OAAOM;AAAAA,YACnBC,OAAOP,OAAOO;AAAAA,YACdC,SAASR,OAAOQ;AAAAA,YAChBC,SAAST,OAAOS;AAAAA,YAChBC,MAAMV,OAAOU;AAAAA,YACb/C,QAAQqC,OAAOrC;AAAAA,YACfgD,WAAWX,OAAOW,qBAAqBC,OACjCZ,OAAOW,YACNX,OAAOW,WAAWE,SAASb,OAAOW,UAAUE,OAAAA,IAAW,IAAID,KAAKZ,OAAOW,SAAS;AAAA,YACvFG,WAAWd,OAAOc,qBAAqBF,OACjCZ,OAAOc,YACNd,OAAOc,WAAWD,SAASb,OAAOc,UAAUD,OAAAA,IAAW,IAAID,KAAKZ,OAAOc,SAAS;AAAA,UAAA;AAI3F,cAAIb,UAAUG,UAAUW,WAAW,QAAQ,KAAKd,UAAUG,UAAUW,WAAW,QAAQ,GAAG;AACtF,mBAAO,MAAMtC,iBAAiBwB,SAAS;AAAA,UAC3C;AACA,iBAAO;AAAA,YAAE,GAAGA;AAAAA,YAAWnB,aAAarC;AAAAA,UAAAA;AAAAA,QACxC,CAAC,CACL;AAEAwC,gBAAQG,IAAI,4BAA4BO,aAAaD,MAAM;AAC3DtB,kBAAUuB,YAAY;AAAA,MAC1B,SAASX,OAAK;AACVC,gBAAQhB,MAAM,gCAAgCe,KAAG;AACjDd,iBAASc,iBAAenC,QAAQmC,QAAM,IAAInC,MAAMmE,OAAOhC,KAAG,CAAC,CAAC;AAAA,MAChE,UAAA;AACIjB,mBAAW,KAAK;AAAA,MACpB;AAAA,IACJ,GAAG,CAACP,oBAAoBE,gBAAgBe,gBAAgB,CAAC;AAGzDwC,UAAAA,UAAU,MAAM;AACZ9B,oBAAAA;AAAAA,IACJ,GAAG,CAACA,aAAa,CAAC;AAGlB,UAAM+B,aAAaxC,MAAAA,YAAY,OAC3ByC,MACAC,aACsB;AACtBnC,cAAQG,IAAI,mBAAmB+B,KAAKE,MAAM,YAAY5D,WAAW;AAGjE,YAAM6D,eAAe,MAAM/D,cAAc2D,WAAW;AAAA,QAChDC;AAAAA,QACAhB,UAAUgB,KAAKE;AAAAA,QACf9B,MAAM9B;AAAAA,QACNE;AAAAA,MAAAA,CACH;AAEDsB,cAAQG,IAAI,kBAAkBkC,YAAY;AAG1C,UAAIxC;AACJ,UAAI;AACA,cAAMF,mBAAiB,MAAMrB,cAAcsB,eAAeyC,aAAa/B,MAAM+B,aAAa3D,MAAM;AAChGmB,sBAAcF,iBAAeG,OAAOtC;AACpCwC,gBAAQG,IAAI,qBAAqBN,WAAW;AAAA,MAChD,SAASE,OAAK;AACVC,gBAAQC,KAAK,4CAA4CF,KAAG;AAAA,MAChE;AAGA,UAAIsB;AACJ,UAAIa,KAAKI,KAAKR,WAAW,QAAQ,GAAG;AAChC,YAAI;AACAT,uBAAa,MAAMkB,mBAAmBL,IAAI;AAAA,QAC9C,SAASnC,OAAK;AACVC,kBAAQC,KAAK,mCAAmCF,KAAG;AAAA,QACvD;AAAA,MACJ;AAGA,YAAMyC,aAAqC,CAAA;AAC3C,YAAMC,gBAAgBP,KAAKI,KAAKR,WAAW,QAAQ,KAAKI,KAAKI,SAAS;AACtE,UAAI3D,kBAAkBA,eAAe8B,SAAS,KAAKgC,eAAe;AAC9DzC,gBAAQG,IAAI,oCAAoCxB,eAAekC,IAAI6B,CAAAA,MAAKA,EAAEN,IAAI,CAAC;AAE/E,mBAAWhB,QAAQzC,gBAAgB;AAC/B,cAAI;AACA,kBAAMgE,gBAAgB,MAAMC,kBAAkBV,MAAMd,KAAKyB,OAAOzB,KAAK0B,QAAQ1B,KAAK2B,WAAW,GAAG;AAChG,kBAAMC,gBAAgB,GAAGrB,KAAKsB,IAAAA,CAAK,IAAI7B,KAAKgB,IAAI,IAAIF,KAAKE,IAAI;AAC7D,kBAAMc,YAAY,GAAG1E,WAAW,IAAII,aAAa,IAAIwC,KAAKgB,IAAI;AAG9D,kBAAMe,oBAAoB,MAAM7E,cAAc2D,WAAW;AAAA,cACrDC,MAAM,IAAIkB,KAAK,CAACT,aAAa,GAAGK,eAAe;AAAA,gBAAEV,MAAM;AAAA,cAAA,CAAc;AAAA,cACrEpB,UAAU8B;AAAAA,cACV1C,MAAM4C;AAAAA,cACNxE;AAAAA,YAAAA,CACH;AAGD,kBAAM2E,sBAAsB,MAAM/E,cAAcsB,eAAeuD,kBAAkB7C,MAAM6C,kBAAkBzE,MAAM;AAC/G,gBAAI2E,oBAAoBvD,KAAK;AACzB0C,yBAAWpB,KAAKgB,IAAI,IAAIiB,oBAAoBvD;AAC5CE,sBAAQG,IAAI,aAAaiB,KAAKgB,IAAI,eAAeiB,oBAAoBvD,GAAG;AAAA,YAC5E;AAAA,UACJ,SAASC,OAAK;AACVC,oBAAQC,KAAK,sBAAsBmB,KAAKgB,IAAI,eAAerC,KAAG;AAAA,UAClE;AAAA,QACJ;AAAA,MACJ;AAEA,YAAMkD,0BAAUtB,KAAAA;AAGhB,YAAM2B,YAAiC;AAAA,QACnCpC,UAAUgB,KAAKE;AAAAA,QACf5D,aAAa6D,aAAa/B;AAAAA,QAC1Ba,UAAUe,KAAKI;AAAAA,QACflB,MAAMc,KAAKd;AAAAA,QACXM,WAAWuB;AAAAA,QACXpB,WAAWoB;AAAAA,MAAAA;AAIf,UAAI5B,sBAAsBA,aAAaA;AACvC,UAAIc,UAAUb,MAAOgC,WAAUhC,QAAQa,SAASb;AAChD,UAAIa,UAAUZ,QAAS+B,WAAU/B,UAAUY,SAASZ;AACpD,UAAIY,UAAUX,QAAS8B,WAAU9B,UAAUW,SAASX;AACpD,UAAIW,UAAUV,QAAQU,SAASV,KAAKhB,SAAS,EAAG6C,WAAU7B,OAAOU,SAASV;AAC1E,UAAIY,aAAa3D,OAAQ4E,WAAU5E,SAAS2D,aAAa3D;AAEzD,UAAImB,uBAAuBA,cAAcA;AAEzC,UAAI0D,OAAOC,KAAKhB,UAAU,EAAE/B,SAAS,aAAa+B,aAAaA;AAE/DxC,cAAQG,IAAI,kCAAkCmD,SAAS;AAGvD,YAAMxC,WAAS,MAAMvC,mBAAmBkF,WAAgC;AAAA,QACpEnD,MAAM7B;AAAAA,QACNsC,QAAQuC;AAAAA,QACRI,QAAQ;AAAA,MAAA,CACX;AAED1D,cAAQG,IAAI,iBAAiBW,SAAOG,EAAE;AAEtC,YAAM0C,WAAuB;AAAA,QACzB1C,IAAIH,SAAOG;AAAAA,QACXC,UAAUgB,KAAKE;AAAAA,QACf5D,aAAa6D,aAAa/B;AAAAA,QAC1BT;AAAAA,QACAsB,UAAUe,KAAKI;AAAAA,QACflB,MAAMc,KAAKd;AAAAA,QACXM,WAAWuB;AAAAA,QACXpB,WAAWoB;AAAAA,QACX5B;AAAAA,QACAC,OAAOa,UAAUb;AAAAA,QACjBC,SAASY,UAAUZ;AAAAA,QACnBC,SAASW,UAAUX;AAAAA,QACnBC,MAAMU,UAAUV;AAAAA,QAChB/C,QAAQ2D,aAAa3D;AAAAA,QACrB8D,YAAYe,OAAOC,KAAKhB,UAAU,EAAE/B,SAAS,IAAI+B,aAAahF;AAAAA,MAAAA;AAGlE2B,gBAAUyE,CAAAA,SAAQ,CAACD,UAAU,GAAGC,IAAI,CAAC;AACrC,aAAOD;AAAAA,IACX,GAAG,CAACrF,eAAeC,oBAAoBC,aAAaC,gBAAgBC,QAAQC,gBAAgBC,aAAa,CAAC;AAG1G,UAAMiF,cAAcpE,kBAAY,OAAOqE,YAAmC;AACtE,YAAMpE,UAAQR,OAAO6E,KAAKC,CAAAA,MAAKA,EAAE/C,OAAO6C,OAAO;AAC/C,UAAI,CAACpE,SAAO;AACR,cAAM,IAAI9B,MAAM,iBAAiBkG,OAAO,YAAY;AAAA,MACxD;AAEA9D,cAAQG,IAAI,mBAAmB2D,SAASpE,QAAMlB,WAAW;AAGzD,UAAI;AACA,cAAMF,cAAc2F,WAAWvE,QAAMlB,aAAakB,QAAMhB,MAAM;AAAA,MAClE,SAASqB,OAAK;AACVC,gBAAQC,KAAK,kDAAkDF,KAAG;AAAA,MACtE;AAGA,YAAMxB,mBAAmB2F,aAAa;AAAA,QAClCpD,QAAQ;AAAA,UACJG,IAAI6C;AAAAA,UACJxD,MAAM7B;AAAAA,UACNsC,QAAQrB;AAAAA,QAAAA;AAAAA,MACZ,CACH;AAEDP,gBAAUyE,YAAQA,OAAKO,OAAOH,SAAKA,IAAE/C,OAAO6C,OAAO,CAAC;AACpD,UAAI1E,eAAe6B,OAAO6C,SAAS;AAC/BzE,yBAAiB7B,MAAS;AAAA,MAC9B;AAAA,IACJ,GAAG,CAAC0B,QAAQZ,eAAeC,oBAAoBE,gBAAgBW,aAAa,CAAC;AAG7E,UAAMgF,cAAc3E,MAAAA,YAAY,OAC5BqE,WACAO,SACgB;AAChB,YAAM3E,UAAQR,OAAO6E,KAAKC,CAAAA,QAAKA,IAAE/C,OAAO6C,SAAO;AAC/C,UAAI,CAACpE,SAAO;AACR,cAAM,IAAI9B,MAAM,iBAAiBkG,SAAO,YAAY;AAAA,MACxD;AAGA,YAAMQ,YAAiC,CAAA;AACvCf,aAAOgB,QAAQF,IAAI,EAAEG,QAAQ,CAAC,CAACC,KAAKC,KAAK,MAAM;AAC3C,YAAIA,UAAUlH,QAAW;AACrB8G,oBAAUG,GAAG,IAAIC;AAAAA,QACrB;AAAA,MACJ,CAAC;AACDJ,gBAAUzC,gCAAgBF,KAAAA;AAE1B3B,cAAQG,IAAI,mBAAmB2D,WAASQ,SAAS;AAEjD,YAAM/F,mBAAmBkF,WAAW;AAAA,QAChCnD,MAAM7B;AAAAA,QACNkG,UAAUb;AAAAA,QACV/C,QAAQuD;AAAAA,QACRM,gBAAgBlF;AAAAA,QAChBgE,QAAQ;AAAA,MAAA,CACX;AAEDvE,gBAAUyE,YAAQA,OAAK/C,IAAImD,CAAAA,QACvBA,IAAE/C,OAAO6C,YAAU;AAAA,QAAE,GAAGE;AAAAA,QAAG,GAAGM;AAAAA,MAAAA,IAAcN,GAChD,CAAC;AAED,UAAI5E,eAAe6B,OAAO6C,WAAS;AAC/BzE,yBAAiBuE,YAAQA,SAAO;AAAA,UAAE,GAAGA;AAAAA,UAAM,GAAGU;AAAAA,QAAAA,IAAcV,MAAI;AAAA,MACpE;AAAA,IACJ,GAAG,CAAC1E,QAAQX,oBAAoBE,gBAAgBW,aAAa,CAAC;AAG9D,UAAMyF,eAAepF,kBAAY,CAACqF,UAAkB;AAChDvF,qBAAeuF,KAAK;AAAA,IACxB,GAAG,CAAA,CAAE;AAEL,UAAMC,cAActF,kBAAY,CAACC,YAAkC;AAC/DL,uBAAiBK,OAAK;AAAA,IAC1B,GAAG,CAAA,CAAE;AAGL,UAAMsF,iBAAiB1F,cACjBJ,OAAOiF,OAAOzE,CAAAA,YACZA,QAAMwB,SAAS+D,YAAAA,EAAcC,SAAS5F,YAAY2F,YAAAA,CAAa,KAC/DvF,QAAM4B,OAAO2D,YAAAA,EAAcC,SAAS5F,YAAY2F,aAAa,KAC7DvF,QAAM+B,MAAM0D,KAAKC,CAAAA,QAAOA,IAAIH,YAAAA,EAAcC,SAAS5F,YAAY2F,YAAAA,CAAa,CAAC,CACjF,IACE/F;AAEN,WAAO;AAAA,MACHL;AAAAA,MACAG;AAAAA,MACAE,QAAQ8F;AAAAA,MACRK,YAAYnG,OAAOuB;AAAAA,MACnBrB;AAAAA,MACA2F;AAAAA,MACA9C;AAAAA,MACA4B;AAAAA,MACAO;AAAAA,MACAlE;AAAAA,MACA2E;AAAAA,MACArG;AAAAA,MACAC;AAAAA,IAAAA;AAAAA,EAER;AAKA,WAAS8D,mBAAmBL,MAAwD;AAChF,WAAO,IAAIvB,QAAQ,CAAC2E,SAASC,WAAW;AACpC,YAAMC,MAAM,IAAIC,MAAAA;AAChBD,UAAIE,SAAS,MAAM;AACfJ,gBAAQ;AAAA,UAAEzC,OAAO2C,IAAI3C;AAAAA,UAAOC,QAAQ0C,IAAI1C;AAAAA,QAAAA,CAAQ;AAChD6C,YAAIC,gBAAgBJ,IAAIK,GAAG;AAAA,MAC/B;AACAL,UAAIM,UAAUP;AACdC,UAAIK,MAAMF,IAAII,gBAAgB7D,IAAI;AAAA,IACtC,CAAC;AAAA,EACL;AAOA,iBAAeU,kBACXV,MACA8D,UACAC,WACAlD,SACa;AACb,WAAO,IAAIpC,QAAc,CAAC2E,SAASC,WAAW;AAC1C,UAAIW,WAAWhE,MAAM;AAAA,QACjBa;AAAAA,QACAiD;AAAAA,QACAC;AAAAA,QACA9E,UAAU;AAAA,QACVgF,SAAUC,CAAAA,WAAW;AACjBd,kBAAQc,MAAM;AAAA,QAClB;AAAA,QACApH,OAAOuG;AAAAA,MAAAA,CACV;AAAA,IACL,CAAC;AAAA,EACL;ACxWO,WAAAc,eAAAvI,IAAA;AAAA,UAAAC,IAAAC,qBAAAA,EAAA,EAAA;AAAwB,UAAA;AAAA,MAAA0B;AAAAA,MAAA4G;AAAAA,MAAAC;AAAAA,MAAAC;AAAAA,MAAAC,eAAAtI;AAAAA,IAAAA,IAAAL;AAK3B,UAAA2I,gBAAAtI,OAAuBX,SAAP,UAAhBW;AAAuB,QAAAuI;AAAA,QAAA3I,EAAA,CAAA,MAAA2B,MAAAyB,UAAA;AAEPuF,WAAAhH,MAAKyB,SAAAW,WAAqB,QAAQ;AAAC/D,QAAA,CAAA,IAAA2B,MAAAyB;AAAApD,aAAA2I;AAAAA,IAAA,OAAA;AAAAA,WAAA3I,EAAA,CAAA;AAAA,IAAA;AAAnD,UAAA4I,UAAgBD;AAChB,UAAAE,UAAgBlH,MAAKyB,SAAAW,WAAqB,QAAQ;AAClD,UAAA+E,UAAgBnH,MAAKyB,SAAAW,WAAqB,QAAQ;AAElD,UAAAgF,WAAiBH,UAAOI,GAAAA,YAClBH,UAAOI,sBACHH,UAAOI,GAAAA,iBAAAC,GAAAA;AAGjB,UAAAC,aAAAC;AAOA,UAAAC,WAAiB3H,MAAK8C,aAAciE,aAAa,KAAK/G,MAAKG;AAAa,QAAAyH;AAAA,QAAAvJ,SAAA+I,YAAA/I,EAAA,CAAA,MAAA2B,MAAA6B,WAAAxD,SAAA2B,MAAAwB,YAAAnD,SAAAsJ,YAAAtJ,EAAA,CAAA,MAAA4I,SAAA;AAEtDW,WAAAX,WAAWU,WACzBE,2BAAAA,aACSF,KAAAA,UACA,KAAA3H,MAAK6B,WAAY7B,MAAKwB,UACjB,WAAA,sFACF,SAAA,OAAA,CAAM,IAGlBqG,2BAAAA,aAAe,WAAA,qFACX,UAAAA,2BAAAA,IAAC,UAAA,EAAc,MAAA,SAAkB,WAAA,yCAAA,CAAwC,GAC7E;AACHxJ,aAAA+I;AAAA/I,QAAA,CAAA,IAAA2B,MAAA6B;AAAAxD,QAAA,CAAA,IAAA2B,MAAAwB;AAAAnD,aAAAsJ;AAAAtJ,aAAA4I;AAAA5I,aAAAuJ;AAAAA,IAAA,OAAA;AAAAA,WAAAvJ,EAAA,CAAA;AAAA,IAAA;AAXD,UAAAyJ,YAAkBF;AAWhB,QAEEhB,aAAa,QAAM;AAQP,YAAAmB,MAAAjB,YAAY;AAAkC,UAAAkB;AAAA,UAAA3J,SAAA0J,KAAA;AALvCC,cAAAC,GAAAA,IACP,yDACA,kDACA,kCACA,UAAAC,GAAAA,kBAAA,IACAH,GACJ;AAAC1J,eAAA0J;AAAA1J,eAAA2J;AAAAA,MAAA,OAAA;AAAAA,cAAA3J,EAAA,CAAA;AAAA,MAAA;AAAA,UAAA8J;AAAA,UAAA9J,UAAAyJ,WAAA;AAGDK,cAAAN,2BAAAA,IAAA,OAAA,EAAe,WAAA,yFACVC,UAAAA,WACL;AAAMzJ,gBAAAyJ;AAAAzJ,gBAAA8J;AAAAA,MAAA,OAAA;AAAAA,cAAA9J,EAAA,EAAA;AAAA,MAAA;AAGG,YAAA+J,MAAApI,MAAK4B,SAAU5B,MAAKwB;AAAS,UAAA6G;AAAA,UAAAhK,UAAA+J,KAAA;AADlCC,6CAACC,GAAAA,YAAA,EAAmB,SAAA,SAAkB,WAAA,yDACjCF,UAAAA,KACL;AAAa/J,gBAAA+J;AAAA/J,gBAAAgK;AAAAA,MAAA,OAAA;AAAAA,cAAAhK,EAAA,EAAA;AAAA,MAAA;AAER,YAAAkK,MAAAd,WAAWzH,MAAK0B,IAAK;AAAC,UAAA8G;AAAA,UAAAnK,EAAA,EAAA,MAAA2B,MAAAyB,UAAA;AAAK+G,eAAAxI,MAAKyB,SAAAgH,MAAgB,GAAG,EAAC,CAAA,GAAAC,YAAAA;AAAkBrK,UAAA,EAAA,IAAA2B,MAAAyB;AAAApD,gBAAAmK;AAAAA,MAAA,OAAA;AAAAA,eAAAnK,EAAA,EAAA;AAAA,MAAA;AAAA,UAAAsK;AAAA,UAAAtK,EAAA,EAAA,MAAAmK,QAAAnK,UAAAkK,KAAA;AAD3EI,eAAAC,2BAAAA,KAACN,GAAAA,YAAA,EAAmB,SAAA,WAAgB,OAAA,aAC/BC,UAAAA;AAAAA,UAAAA;AAAAA,UAAuB;AAAA,UAAIC;AAAAA,QAAAA,GAChC;AAAanK,gBAAAmK;AAAAnK,gBAAAkK;AAAAlK,gBAAAsK;AAAAA,MAAA,OAAA;AAAAA,eAAAtK,EAAA,EAAA;AAAA,MAAA;AAAA,UAAAwK;AAAA,UAAAxK,EAAA,EAAA,MAAAsK,QAAAtK,UAAAgK,KAAA;AANjBQ,eAAAD,2BAAAA,KAAA,OAAA,EAAe,WAAA,kBACXP,UAAAA;AAAAA,UAAAA;AAAAA,UAGAM;AAAAA,QAAAA,GAGJ;AAAMtK,gBAAAsK;AAAAtK,gBAAAgK;AAAAhK,gBAAAwK;AAAAA,MAAA,OAAA;AAAAA,eAAAxK,EAAA,EAAA;AAAA,MAAA;AAAA,UAAAyK;AAAA,UAAAzK,UAAAyI,UAAA;AACLgC,eAAAhC,YACGe,2BAAAA,IAAA,OAAA,EAAe,WAAA,kFACX,UAAAA,2BAAAA,IAACkB,GAAAA,WAAA,EAAe,MAAA,YAAqB,WAAA,aAAA,CAAY,GACrD;AACH1K,gBAAAyI;AAAAzI,gBAAAyK;AAAAA,MAAA,OAAA;AAAAA,eAAAzK,EAAA,EAAA;AAAA,MAAA;AAAA,UAAA2K;AAAA,UAAA3K,EAAA,EAAA,MAAAwI,WAAAxI,EAAA,EAAA,MAAAwK,QAAAxK,EAAA,EAAA,MAAAyK,QAAAzK,EAAA,EAAA,MAAA2J,OAAA3J,UAAA8J,KAAA;AAzBLa,eAAAJ,2BAAAA,KAAA,OAAA,EACe,WAAAZ,KAOFnB,SAETsB,UAAAA;AAAAA,UAAAA;AAAAA,UAGAU;AAAAA,UAQCC;AAAAA,QAAAA,GAKL;AAAMzK,gBAAAwI;AAAAxI,gBAAAwK;AAAAxK,gBAAAyK;AAAAzK,gBAAA2J;AAAA3J,gBAAA8J;AAAA9J,gBAAA2K;AAAAA,MAAA,OAAA;AAAAA,eAAA3K,EAAA,EAAA;AAAA,MAAA;AAAA,aA1BN2K;AAAAA,IA0BM;AAUF,UAAAjB,KAAAjB,YAAY;AAAqB,QAAAkB;AAAA,QAAA3J,UAAA0J,IAAA;AAJ1BC,WAAAC,GAAAA,IACP,iDACA,+BACA,0CACAF,EACJ;AAAC1J,cAAA0J;AAAA1J,cAAA2J;AAAAA,IAAA,OAAA;AAAAA,WAAA3J,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA8J;AAAA,QAAA9J,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAOGf,0CAAA,OAAA,EAAgB,WAAAF,GAAAA,IACZ,uDACA,gCACJ,GAAC;AAAI5J,cAAA8J;AAAAA,IAAA,OAAA;AAAAA,WAAA9J,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA+J;AAAA,QAAA/J,UAAAyI,UAAA;AAGJsB,WAAAtB,YACGe,2BAAAA,IAAA,OAAA,EAAe,WAAA,qGACX,UAAAA,2BAAAA,IAACkB,GAAAA,WAAA,EAAe,MAAA,YAAqB,WAAA,aAAA,CAAY,GACrD;AACH1K,cAAAyI;AAAAzI,cAAA+J;AAAAA,IAAA,OAAA;AAAAA,WAAA/J,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAgK;AAAA,QAAAhK,EAAA,EAAA,MAAA+J,MAAA/J,UAAAyJ,WAAA;AAdLO,WAAAO,2BAAAA,KAAA,OAAA,EAAe,WAAA,6EACVd,UAAAA;AAAAA,QAAAA;AAAAA,QAGDK;AAAAA,QAMCC;AAAAA,MAAAA,GAKL;AAAM/J,cAAA+J;AAAA/J,cAAAyJ;AAAAzJ,cAAAgK;AAAAA,IAAA,OAAA;AAAAA,WAAAhK,EAAA,EAAA;AAAA,IAAA;AAIG,UAAAkK,KAAAvI,MAAK4B,SAAU5B,MAAKwB;AAAS,QAAAgH;AAAA,QAAAnK,UAAAkK,IAAA;AADlCC,2CAACF,GAAAA,YAAA,EAAmB,SAAA,SAAkB,WAAA,yDACjCC,UAAAA,IACL;AAAalK,cAAAkK;AAAAlK,cAAAmK;AAAAA,IAAA,OAAA;AAAAA,YAAAnK,EAAA,EAAA;AAAA,IAAA;AAER,UAAAsK,MAAAlB,WAAWzH,MAAK0B,IAAK;AAAC,QAAAmH;AAAA,QAAAxK,EAAA,EAAA,MAAA2B,MAAAyB,UAAA;AAAKoH,YAAA7I,MAAKyB,SAAAgH,MAAgB,GAAG,EAAC,CAAA,GAAAC,YAAAA;AAAkBrK,QAAA,EAAA,IAAA2B,MAAAyB;AAAApD,cAAAwK;AAAAA,IAAA,OAAA;AAAAA,YAAAxK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAyK;AAAA,QAAAzK,EAAA,EAAA,MAAAsK,OAAAtK,UAAAwK,KAAA;AAD3EC,4CAACR,eAAA,EAAmB,SAAA,WAAgB,OAAA,aAAsB,WAAA,yBACrDK,UAAAA;AAAAA,QAAAA;AAAAA,QAAuB;AAAA,QAAIE;AAAAA,MAAAA,GAChC;AAAaxK,cAAAsK;AAAAtK,cAAAwK;AAAAxK,cAAAyK;AAAAA,IAAA,OAAA;AAAAA,YAAAzK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA2K;AAAA,QAAA3K,EAAA,EAAA,MAAAmK,OAAAnK,UAAAyK,KAAA;AANjBE,YAAAJ,2BAAAA,KAAA,OAAA,EAAe,WAAA,OACXJ,UAAAA;AAAAA,QAAAA;AAAAA,QAGAM;AAAAA,MAAAA,GAGJ;AAAMzK,cAAAmK;AAAAnK,cAAAyK;AAAAzK,cAAA2K;AAAAA,IAAA,OAAA;AAAAA,YAAA3K,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA8K;AAAA,QAAA9K,EAAA,EAAA,MAAAwI,WAAAxI,EAAA,EAAA,MAAA2K,OAAA3K,EAAA,EAAA,MAAA2J,MAAA3J,UAAAgK,IAAA;AAjCVc,YAAAP,2BAAAA,KAACQ,SAAA,EACc,WAAApB,IAMFnB,SAETwB,UAAAA;AAAAA,QAAAA;AAAAA,QAiBAW;AAAAA,MAAAA,GAQJ;AAAO3K,cAAAwI;AAAAxI,cAAA2K;AAAA3K,cAAA2J;AAAA3J,cAAAgK;AAAAhK,cAAA8K;AAAAA,IAAA,OAAA;AAAAA,YAAA9K,EAAA,EAAA;AAAA,IAAA;AAAA,WAlCP8K;AAAAA,EAkCO;AAzGR,WAAAzB,MAAA2B,OAAA;AAAA,QAiBKA,QAAK,MAAO;AAAA,aAAS,GAAGA,KAAK;AAAA,IAAI;AAAA,QACjCA,QAAK,SAAc;AAAA,aAAS,IAAIA,QAAK,MAAOC,QAAA,CAAW,CAAC;AAAA,IAAK;AAAA,WAC1D,IAAID,QAAK,SAAgBC,QAAA,CAAW,CAAC;AAAA,EAAK;ACflD,WAASC,kBAAkB;AAAA,IAC9BvJ;AAAAA,IACAwJ;AAAAA,IACAC;AAAAA,IACAC;AAAAA,EACoB,GAAG;AACvB,UAAMC,qBAAqBC,KAAAA,sBAAAA;AAC3B,UAAMhL,gBAAgBiL,KAAAA,iBAAAA;AACtB,UAAM,CAACC,QAAQC,SAAS,IAAI1K,MAAAA,SAAS,KAAK;AAC1C,UAAM,CAAC2K,UAAUC,WAAW,IAAI5K,MAAAA,SAAS,KAAK;AAC9C,UAAM,CAAC6K,kBAAkBC,mBAAmB,IAAI9K,MAAAA,SAAS,KAAK;AAC9D,UAAM,CAAC+K,UAAUC,WAAW,IAAIhL,MAAAA,SAAS,EAAE;AAE3C,UAAM;AAAA,MAAEgC;AAAAA,MAAQiJ;AAAAA,MAAeC;AAAAA,IAAAA,IAAUC,uBAAqC;AAAA,MAC1EC,eAAe;AAAA,QACX7I,OAAO5B,MAAM4B,SAAS;AAAA,QACtBC,SAAS7B,MAAM6B,WAAW;AAAA,QAC1BC,SAAS9B,MAAM8B,WAAW;AAAA,QAC1BC,MAAM/B,MAAM+B,QAAQ,CAAA;AAAA,MAAA;AAAA,IACxB,CACH;AAED,UAAM2I,aAAa3K,MAAAA,YAAY,YAAY;AACvCgK,gBAAU,IAAI;AACd,UAAI;AACA,cAAMN,SAASzJ,MAAMuB,IAAIF,MAAM;AAC/BsI,2BAAmBgB,KAAK;AAAA,UACpB/H,MAAM;AAAA,UACNgI,SAAS;AAAA,QAAA,CACZ;AAAA,MACL,SAAStL,OAAO;AACZqK,2BAAmBgB,KAAK;AAAA,UACpB/H,MAAM;AAAA,UACNgI,SAAS,yBAAyBtL,iBAAiBpB,QAAQoB,MAAMsL,UAAUvI,OAAO/C,KAAK,CAAC;AAAA,QAAA,CAC3F;AAAA,MACL,UAAA;AACIyK,kBAAU,KAAK;AAAA,MACnB;AAAA,IACJ,GAAG,CAAC/J,MAAMuB,IAAIF,QAAQoI,UAAUE,kBAAkB,CAAC;AAEnD,UAAMkB,eAAe9K,MAAAA,YAAY,YAAY;AACzCkK,kBAAY,IAAI;AAChB,UAAI;AACA,cAAMP,SAAS1J,MAAMuB,EAAE;AACvBoI,2BAAmBgB,KAAK;AAAA,UACpB/H,MAAM;AAAA,UACNgI,SAAS;AAAA,QAAA,CACZ;AACDpB,gBAAAA;AAAAA,MACJ,SAASlK,SAAO;AACZqK,2BAAmBgB,KAAK;AAAA,UACpB/H,MAAM;AAAA,UACNgI,SAAS,yBAAyBtL,mBAAiBpB,QAAQoB,QAAMsL,UAAUvI,OAAO/C,OAAK,CAAC;AAAA,QAAA,CAC3F;AAAA,MACL,UAAA;AACI2K,oBAAY,KAAK;AACjBE,4BAAoB,KAAK;AAAA,MAC7B;AAAA,IACJ,GAAG,CAACnK,MAAMuB,IAAImI,UAAUC,oBAAoBH,OAAO,CAAC;AAEpD,UAAMsB,iBAAiB/K,MAAAA,YAAY,YAAY;AAC3C,UAAI;AACA,cAAME,iBAAiB,MAAMrB,cAAcsB,eAAeF,MAAMlB,aAAakB,MAAMhB,MAAM;AACzF,YAAIiB,eAAeG,KAAK;AACpB2K,iBAAOJ,KAAK1K,eAAeG,KAAK,QAAQ;AAAA,QAC5C;AAAA,MACJ,SAASd,SAAO;AACZqK,2BAAmBgB,KAAK;AAAA,UACpB/H,MAAM;AAAA,UACNgI,SAAS;AAAA,QAAA,CACZ;AAAA,MACL;AAAA,IACJ,GAAG,CAAC5K,OAAOpB,eAAe+K,kBAAkB,CAAC;AAE7C,UAAMqB,eAAejL,MAAAA,YAAY,MAAM;AACnC,YAAM2F,MAAM0E,SAASa,KAAAA;AACrB,UAAIvF,OAAO,CAACrE,OAAOU,MAAMyD,SAASE,GAAG,GAAG;AACpC4E,sBAAc,QAAQ,CAAC,GAAIjJ,OAAOU,QAAQ,CAAA,GAAK2D,GAAG,CAAC;AACnD2E,oBAAY,EAAE;AAAA,MAClB;AAAA,IACJ,GAAG,CAACD,UAAU/I,OAAOU,MAAMuI,aAAa,CAAC;AAEzC,UAAMY,kBAAkBnL,kBAAY,CAACoL,gBAAwB;AACzDb,oBAAc,QAAQjJ,OAAOU,MAAM0C,OAAO,CAAC2G,MAAcA,MAAMD,WAAW,KAAK,EAAE;AAAA,IACrF,GAAG,CAAC9J,OAAOU,MAAMuI,aAAa,CAAC;AAE/B,UAAM7C,aAAaA,CAAC4B,UAA0B;AAC1C,UAAIA,QAAQ,KAAM,QAAO,GAAGA,KAAK;AACjC,UAAIA,QAAQ,OAAO,KAAM,QAAO,IAAIA,QAAQ,MAAMC,QAAQ,CAAC,CAAC;AAC5D,aAAO,IAAID,SAAS,OAAO,OAAOC,QAAQ,CAAC,CAAC;AAAA,IAChD;AAEA,UAAM+B,aAAaA,CAACC,SAAuB;AACvC,aAAO,IAAIC,KAAKC,eAAe1N,QAAW;AAAA,QACtC2N,MAAM;AAAA,QACNC,OAAO;AAAA,QACPC,KAAK;AAAA,MAAA,CACR,EAAEC,OAAON,IAAI;AAAA,IAClB;AAEA,UAAMrE,UAAUjH,MAAMyB,SAASW,WAAW,QAAQ;AAClD,UAAM8E,UAAUlH,MAAMyB,SAASW,WAAW,QAAQ;AAElD,WACIwG,2BAAAA,KAAAiD,qBAAA,EACI,UAAA;AAAA,MAAAjD,2BAAAA,KAAC,OAAA,EAAI,WAAWX,GAAAA,IACZ,uDACA,qCACA,qEACA,kBACA,iBACA,wBACJ,GAEI,UAAA;AAAA,QAAAW,2BAAAA,KAAC,OAAA,EAAI,WAAU,2GACX,UAAA;AAAA,UAAAf,2BAAAA,IAACS,GAAAA,YAAA,EAAW,SAAQ,aAAY,WAAU,oCACrCtI,UAAAA,MAAM4B,SAAS5B,MAAMwB,SAAAA,CAC1B;AAAA,UACAoH,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACX,UAAA;AAAA,YAAAf,2BAAAA,IAACiE,GAAAA,cAAW,SAAShB,gBACjB,yCAACiB,GAAAA,cAAA,EAAa,MAAK,SAAO,EAAA,CAC9B;AAAA,YACAlE,2BAAAA,IAACiE,GAAAA,YAAA,EACG,SAAS,MAAM3B,oBAAoB,IAAI,GACvC,WAAU,yDAEV,UAAAtC,2BAAAA,IAACmE,GAAAA,YAAA,EAAW,MAAK,SAAO,GAC5B;AAAA,YACAnE,2BAAAA,IAACiE,GAAAA,cAAW,SAAStC,SACjB,yCAACyC,cAAA,EAAU,MAAK,SAAO,EAAA,CAC3B;AAAA,UAAA,EAAA,CACJ;AAAA,QAAA,GACJ;AAAA,uCAGC,OAAA,EAAI,WAAU,2GACVhF,UAAAA,WAAWjH,MAAMG,cACd0H,+BAAC,OAAA,EACG,KAAK7H,MAAMG,aACX,KAAKH,MAAM6B,WAAW7B,MAAMwB,UAC5B,WAAU,uCAAA,CAAsC,IAEpD0F,WAAWlH,MAAMG,cACjB0H,2BAAAA,IAAC,SAAA,EACG,KAAK7H,MAAMG,aACX,WAAU,yBACV,UAAQ,MAAA,IAGZ0H,2BAAAA,IAAC,SAAI,WAAU,2BAAyB,mCAExC,GAER;AAAA,QAGAe,2BAAAA,KAAC,OAAA,EAAI,WAAU,sCAEX,UAAA;AAAA,UAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,0BACV5I,UAAAA;AAAAA,YAAAA,MAAM2B,8CACF,OAAA,EACG,UAAA;AAAA,cAAAkG,+BAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA,cAEjE;AAAA,cACAM,2BAAAA,KAACN,GAAAA,YAAA,EAAW,SAAQ,SACftI,UAAAA;AAAAA,gBAAAA,MAAM2B,WAAWwB;AAAAA,gBAAM;AAAA,gBAAInD,MAAM2B,WAAWyB;AAAAA,gBAAO;AAAA,cAAA,EAAA,CACxD;AAAA,YAAA,GACJ;AAAA,4CAEH,OAAA,EACG,UAAA;AAAA,cAAAyE,+BAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA,QAEjE;AAAA,6CACCA,GAAAA,YAAA,EAAW,SAAQ,SACfb,UAAAA,WAAWzH,MAAM0B,IAAI,EAAA,CAC1B;AAAA,YAAA,GACJ;AAAA,4CACC,OAAA,EACG,UAAA;AAAA,cAAAmG,+BAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA,QAEjE;AAAA,cACAT,2BAAAA,IAACS,GAAAA,YAAA,EAAW,SAAQ,SACftI,gBAAMyB,SAAAA,CACX;AAAA,YAAA,GACJ;AAAA,4CACC,OAAA,EACG,UAAA;AAAA,cAAAoG,+BAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA,WAEjE;AAAA,6CACCA,GAAAA,YAAA,EAAW,SAAQ,SACf+C,UAAAA,WAAWrL,MAAMgC,SAAS,EAAA,CAC/B;AAAA,YAAA,EAAA,CACJ;AAAA,UAAA,GACJ;AAAA,UAEA6F,2BAAAA,IAAC,MAAA,EAAG,WAAU,2DAAA,CAA0D;AAAA,UAGxEA,2BAAAA,IAACqE,GAAAA,WAAA,EACG,OAAM,aACN,OAAOlM,MAAMwB,UACb,UAAQ,MACR,MAAK,QAAA,CAAO;AAAA,yCAGf0K,GAAAA,WAAA,EACG,OAAM,SACN,OAAO7K,OAAOO,SAAS,IACvB,UAAWuK,CAAAA,MAAM7B,cAAc,SAAS6B,EAAEC,OAAOpH,KAAK,GACtD,MAAK,SAAO;AAAA,0CAGf,OAAA,EACG,UAAA;AAAA,YAAA6C,+BAACqE,GAAAA,aACG,OAAM,YACN,OAAO7K,OAAOQ,WAAW,IACzB,UAAWsK,CAAAA,QAAM7B,cAAc,WAAW6B,IAAEC,OAAOpH,KAAK,GACxD,MAAK,SAAO;AAAA,2CAEfsD,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,gCAA8B,UAAA,sBAAA,CAEtE;AAAA,UAAA,GACJ;AAAA,yCAEC4D,GAAAA,WAAA,EACG,OAAM,WACN,OAAO7K,OAAOS,WAAW,IACzB,UAAWqK,SAAM7B,cAAc,WAAW6B,IAAEC,OAAOpH,KAAK,GACxD,MAAK,SACL,WAAS,MAAA;AAAA,0CAIZ,OAAA,EACG,UAAA;AAAA,YAAA6C,+BAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,sCAAoC,UAAA,QAE5E;AAAA,2CACC,OAAA,EAAI,WAAU,6BACVjH,UAAAA,OAAOU,MAAMZ,IAAI,CAACuE,0CACd2G,GAAAA,MAAA,EAEG,MAAK,SACL,aAAY,eACZ,SAAS,MAAMnB,gBAAgBxF,KAAG,GAEjCA,UAAAA;AAAAA,cAAAA;AAAAA,cAAI;AAAA,YAAA,KALAA,KAMT,CACH,GACL;AAAA,YACAkD,2BAAAA,KAAC,OAAA,EAAI,WAAU,cACX,UAAA;AAAA,cAAAf,+BAACqE,GAAAA,aACG,aAAY,gBACZ,OAAO9B,UACP,UAAW+B,CAAAA,QAAM9B,YAAY8B,IAAEC,OAAOpH,KAAK,GAC3C,MAAK,SACL,WAAU,UACV,WAAYmH,CAAAA,QAAM;AACd,oBAAIA,IAAEpH,QAAQ,SAAS;AACnBoH,sBAAEG,eAAAA;AACFtB,+BAAAA;AAAAA,gBACJ;AAAA,cACJ,GAAE;AAAA,cAENnD,2BAAAA,IAAC0E,GAAAA,QAAA,EACG,SAAQ,QACR,MAAK,SACL,SAASvB,cACT,UAAU,CAACZ,SAASa,KAAAA,GAAO,UAAA,MAAA,CAG/B;AAAA,YAAA,EAAA,CACJ;AAAA,UAAA,EAAA,CACJ;AAAA,QAAA,GACJ;AAAA,QAGApD,2BAAAA,IAAC,SAAI,WAAU,uFACX,yCAAC0E,GAAAA,QAAA,EACG,SAAQ,UACR,SAAS7B,YACT,UAAU,CAACH,SAAST,QACpB,WAAU,UAETA,UAAAA,SAASjC,2BAAAA,IAAC2E,GAAAA,oBAAiB,MAAK,QAAA,CAAO,IAAM,eAAA,CAClD,EAAA,CACJ;AAAA,MAAA,GACJ;AAAA,MAGA5D,2BAAAA,KAAC6D,GAAAA,QAAA,EACG,MAAMvC,kBACN,cAAcC,qBAEd,UAAA;AAAA,QAAAvB,gCAAC8D,GAAAA,eAAA,EACG,UAAA;AAAA,UAAA7E,+BAACS,GAAAA,YAAA,EAAW,SAAQ,aAAY,WAAU,oBAAkB,UAAA,iBAE5D;AAAA,UACAM,2BAAAA,KAACN,GAAAA,YAAA,EAAW,WAAU,wDAAsD,UAAA;AAAA,YAAA;AAAA,YACtCtI,MAAM4B,SAAS5B,MAAMwB;AAAAA,YAAS;AAAA,UAAA,EAAA,CAEpE;AAAA,QAAA,GACJ;AAAA,wCACCmL,GAAAA,eAAA,EACG,UAAA;AAAA,UAAA9E,2BAAAA,IAAC0E,GAAAA,QAAA,EACG,SAAQ,QACR,SAAS,MAAMpC,oBAAoB,KAAK,GACxC,UAAUH,UAAS,UAAA,SAAA,CAGvB;AAAA,yCACCuC,GAAAA,QAAA,EACG,SAAQ,UACR,OAAM,SACN,SAAS1B,cACT,UAAUb,UAETA,qBAAWnC,+BAAC2E,GAAAA,kBAAA,EAAiB,MAAK,SAAO,IAAM,SAAA,CACpD;AAAA,QAAA,EAAA,CACJ;AAAA,MAAA,EAAA,CACJ;AAAA,IAAA,GACJ;AAAA,EAER;ACxUO,WAASI,kBAAkB;AAAA,IAC9BjC;AAAAA,IACAnB;AAAAA,IACAqD;AAAAA,IACAC,cAAc;AAAA;AAAA,IACdC;AAAAA,EACoB,GAAG;AACvB,UAAM,CAACC,YAAYC,aAAa,IAAI5N,MAAAA,SAAS,KAAK;AAClD,UAAM,CAAC6N,WAAWC,YAAY,IAAI9N,MAAAA,SAAS,KAAK;AAChD,UAAM,CAACC,OAAOC,QAAQ,IAAIF,MAAAA,SAAwB,IAAI;AACtD,UAAM,CAAC+N,eAAeC,gBAAgB,IAAIhO,MAAAA,SAAiB,CAAA,CAAE;AAE7D,UAAMiO,gBAAgBvN,kBAAY,CAACwN,UAAuD;AACtF,YAAMC,QAAgB,CAAA;AACtB,YAAMC,SAAmB,CAAA;AAEzB,iBAAWjL,QAAQ+K,OAAO;AACtB,YAAIT,eAAetK,KAAKd,OAAOoL,aAAa;AACxCW,iBAAOC,KAAK,GAAGlL,KAAKE,IAAI,yBAAyB+E,WAAWqF,WAAW,CAAC,GAAG;AAC3E;AAAA,QACJ;AACA,YAAIC,qBAAqB,CAACA,kBAAkBtH,KAAK7C,CAAAA,SAAQ;AACrD,cAAIA,KAAK+K,SAAS,IAAI,GAAG;AACrB,mBAAOnL,KAAKI,KAAKR,WAAWQ,KAAKgL,MAAM,GAAG,EAAE,CAAC;AAAA,UACjD;AACA,iBAAOpL,KAAKI,SAASA;AAAAA,QACzB,CAAC,GAAG;AACA6K,iBAAOC,KAAK,GAAGlL,KAAKE,IAAI,yBAAyB;AACjD;AAAA,QACJ;AACA8K,cAAME,KAAKlL,IAAI;AAAA,MACnB;AAEA,aAAO;AAAA,QAAEgL;AAAAA,QAAOC;AAAAA,MAAAA;AAAAA,IACpB,GAAG,CAACX,aAAaC,iBAAiB,CAAC;AAEnC,UAAMc,iBAAiB9N,kBAAY,CAACoM,MAAuB;AACvDA,QAAEG,eAAAA;AACFW,oBAAc,IAAI;AAAA,IACtB,GAAG,CAAA,CAAE;AAEL,UAAMa,kBAAkB/N,kBAAY,CAACoM,QAAuB;AACxDA,UAAEG,eAAAA;AACFW,oBAAc,KAAK;AAAA,IACvB,GAAG,CAAA,CAAE;AAEL,UAAMc,aAAahO,kBAAY,CAACoM,QAAuB;AACnDA,UAAEG,eAAAA;AACFW,oBAAc,KAAK;AAEnB,YAAMM,UAAQS,MAAMC,KAAK9B,IAAE+B,aAAaX,KAAK;AAC7C,YAAM;AAAA,QAAEC,OAAAA;AAAAA,QAAOC,QAAAA;AAAAA,MAAAA,IAAWH,cAAcC,OAAK;AAE7C,UAAIE,SAAO1M,SAAS,GAAG;AACnBxB,iBAASkO,SAAOU,KAAK,IAAI,CAAC;AAAA,MAC9B,OAAO;AACH5O,iBAAS,IAAI;AAAA,MACjB;AAEA8N,uBAAiBnJ,UAAQ,CAAC,GAAGA,MAAM,GAAGsJ,OAAK,CAAC;AAAA,IAChD,GAAG,CAACF,aAAa,CAAC;AAElB,UAAMc,mBAAmBrO,kBAAY,CAACoM,QAA2C;AAC7E,YAAMoB,UAAQS,MAAMC,KAAK9B,IAAEC,OAAOmB,SAAS,EAAE;AAC7C,YAAM;AAAA,QAAEC,OAAAA;AAAAA,QAAOC,QAAAA;AAAAA,MAAAA,IAAWH,cAAcC,OAAK;AAE7C,UAAIE,SAAO1M,SAAS,GAAG;AACnBxB,iBAASkO,SAAOU,KAAK,IAAI,CAAC;AAAA,MAC9B,OAAO;AACH5O,iBAAS,IAAI;AAAA,MACjB;AAEA8N,uBAAiBnJ,YAAQ,CAAC,GAAGA,QAAM,GAAGsJ,OAAK,CAAC;AAAA,IAChD,GAAG,CAACF,aAAa,CAAC;AAElB,UAAMe,mBAAmBtO,kBAAY,CAACuO,UAAkB;AACpDjB,uBAAiBnJ,CAAAA,WAAQA,OAAKO,OAAO,CAAC8J,GAAGC,MAAMA,MAAMF,KAAK,CAAC;AAAA,IAC/D,GAAG,CAAA,CAAE;AAEL,UAAMG,eAAe1O,MAAAA,YAAY,YAAY;AACzC,UAAIqN,cAAcrM,WAAW,EAAG;AAEhCoM,mBAAa,IAAI;AACjB5N,eAAS,IAAI;AAEb,UAAI;AACA,cAAMsN,SAASO,aAAa;AAC5BC,yBAAiB,CAAA,CAAE;AACnB7D,gBAAAA;AAAAA,MACJ,SAASnJ,KAAK;AACVd,iBAASc,eAAenC,QAAQmC,IAAIuK,UAAU,eAAe;AAAA,MACjE,UAAA;AACIuC,qBAAa,KAAK;AAAA,MACtB;AAAA,IACJ,GAAG,CAACC,eAAeP,UAAUrD,OAAO,CAAC;AAErC,UAAMkF,cAAc3O,MAAAA,YAAY,MAAM;AAClC,UAAI,CAACmN,WAAW;AACZG,yBAAiB,CAAA,CAAE;AACnB9N,iBAAS,IAAI;AACbiK,gBAAAA;AAAAA,MACJ;AAAA,IACJ,GAAG,CAAC0D,WAAW1D,OAAO,CAAC;AAEvB,UAAM/B,aAAaA,CAAC4B,UAA0B;AAC1C,UAAIA,QAAQ,KAAM,QAAO,GAAGA,KAAK;AACjC,UAAIA,QAAQ,OAAO,KAAM,QAAO,IAAIA,QAAQ,MAAMC,QAAQ,CAAC,CAAC;AAC5D,aAAO,IAAID,SAAS,OAAO,OAAOC,QAAQ,CAAC,CAAC;AAAA,IAChD;AAEA,WACIV,2BAAAA,KAAC6D,GAAAA,QAAA,EACG,MACA,cAAe9B,CAAAA,WAAS,CAACA,UAAQ+D,YAAAA,GACjC,UAAS,MAET,UAAA;AAAA,MAAA9F,2BAAAA,KAAC8D,GAAAA,eAAA,EAAc,WAAU,OACrB,UAAA;AAAA,QAAA7E,2BAAAA,IAAC,OAAA,EAAI,WAAU,yEACX,UAAAA,2BAAAA,IAACS,iBAAW,SAAQ,MAAI,0BAExB,EAAA,CACJ;AAAA,QAEAM,2BAAAA,KAAC,OAAA,EAAI,WAAU,OAEX,UAAA;AAAA,UAAAA,2BAAAA,KAAC,OAAA,EACG,WAAWX,GAAAA,IACP,yCACA,mDACA,kCACA+E,aACM,gCACA,4DACN,2CACA,gBACJ,GACA,YAAYa,gBACZ,aAAaC,iBACb,QAAQC,YACR,SAAS,MAAMY,SAASC,eAAe,mBAAmB,GAAGC,MAAAA,GAE7D,UAAA;AAAA,YAAAhH,2BAAAA,IAACiH,GAAAA,iBAAA,EACG,MAAK,SACL,WAAW7G,GAAAA,IACP+E,aAAa,iBAAiB,yBAClC,GAAE;AAAA,YAENpE,2BAAAA,KAAC,OAAA,EAAI,WAAU,eACX,UAAA;AAAA,cAAAf,+BAACS,GAAAA,YAAA,EAAW,SAAQ,SAAQ,WAAU,eAAa,UAAA,sCAEnD;AAAA,cACAM,2BAAAA,KAACN,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA;AAAA,gBAAA;AAAA,gBACzCb,WAAWqF,WAAW;AAAA,cAAA,EAAA,CAC9C;AAAA,YAAA,GACJ;AAAA,2CACC,SAAA,EACG,IAAG,qBACH,MAAK,QACL,UAAQ,MACR,QAAQC,mBAAmBoB,KAAK,GAAG,GACnC,UAAUC,kBACV,WAAU,SAAA,CAAQ;AAAA,UAAA,GAE1B;AAAA,UAGC9O,SACGuI,2BAAAA,IAACS,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,+CACnChJ,UAAAA,OACL;AAAA,UAIH8N,cAAcrM,SAAS,KACpB6H,2BAAAA,KAAC,OAAA,EAAI,WAAU,kBACX,UAAA;AAAA,YAAAA,2BAAAA,KAACN,GAAAA,YAAA,EAAW,SAAQ,WAAU,WAAU,2BAAyB,UAAA;AAAA,cAAA;AAAA,cAC5C8E,cAAcrM;AAAAA,cAAO;AAAA,YAAA,GAC1C;AAAA,YACA8G,2BAAAA,IAAC,OAAA,EAAI,WAAU,oCACVuF,wBAAcjM,IAAI,CAACqB,QAAM8L,4CACrB,OAAA,EAEG,WAAWrG,GAAAA,IACP,iDACA,iDACJ,GAEA,UAAA;AAAA,cAAAW,2BAAAA,KAAC,OAAA,EAAI,WAAU,uBACX,UAAA;AAAA,gBAAAf,+BAACS,GAAAA,cAAW,SAAQ,SAAQ,WAAU,YACjC9F,iBAAKE,MACV;AAAA,gBACAmF,2BAAAA,IAACS,GAAAA,cAAW,SAAQ,WAAU,WAAU,2BACnCb,UAAAA,WAAWjF,OAAKd,IAAI,EAAA,CACzB;AAAA,cAAA,GACJ;AAAA,6CACC6K,GAAAA,QAAA,EACG,SAAQ,QACR,MAAK,SACL,SAAUJ,CAAAA,QAAM;AACZA,oBAAE4C,gBAAAA;AACFV,iCAAiBC,OAAK;AAAA,cAC1B,GACA,UAAUpB,WAAU,UAAA,SAAA,CAGxB;AAAA,YAAA,EAAA,GAxBK,GAAG1K,OAAKE,IAAI,IAAI4L,OAAK,EAyB9B,CACH,EAAA,CACL;AAAA,UAAA,EAAA,CACJ;AAAA,QAAA,EAAA,CAER;AAAA,MAAA,GACJ;AAAA,sCAEC3B,GAAAA,eAAA,EACG,UAAA;AAAA,QAAA9E,2BAAAA,IAAC0E,GAAAA,UACG,SAAQ,QACR,SAASmC,aACT,UAAUxB,WAAU,UAAA,SAAA,CAGxB;AAAA,QACArF,2BAAAA,IAAC0E,GAAAA,QAAA,EACG,SAAQ,UACR,SAASkC,cACT,UAAUrB,cAAcrM,WAAW,KAAKmM,WAEvCA,UAAAA,YACGtE,gCAAAiD,WAAAA,UAAA,EACI,UAAA;AAAA,UAAAhE,2BAAAA,IAAC2E,GAAAA,kBAAA,EAAiB,MAAK,WAAA,CAAU;AAAA,UAAA;AAAA,QAAA,EAAA,CAErC,IAEA,UAAUY,cAAcrM,SAAS,IAAI,IAAIqM,cAAcrM,MAAM,MAAM,EAAE,GAAA,CAE7E;AAAA,MAAA,EAAA,CACJ;AAAA,IAAA,GACJ;AAAA,EAER;AC1OO,WAAAiO,iBAAA5Q,IAAA;AAAA,UAAAC,IAAAC,qBAAAA,EAAA,EAAA;AAA0B,UAAA;AAAA,MAAAwO;AAAAA,MAAAC;AAAAA,IAAAA,IAAA3O;AAI7B,UAAAG,aAAmBR,gBAAAA;AACnB,UAAA,CAAAkR,kBAAAC,mBAAA,IAAgD7P,MAAAA,cAAc;AAC9D,UAAA,CAAAuH,UAAAuI,WAAA,IAAgC9P,MAAAA,SAA0B,MAAM;AAC3C+P,UAAAA,WAA6B;AAAC,QAAA3Q;AAAA,QAAAJ,SAAAE,YAAA;AAElBE,WAAA2G,CAAAA,UAAA;AAC7B7G,mBAAU4G,aAAcC,SAAS,EAAE;AAAA,MAAC;AACvC/G,aAAAE;AAAAF,aAAAI;AAAAA,IAAA,OAAA;AAAAA,WAAAJ,EAAA,CAAA;AAAA,IAAA;AAFD,UAAAgR,eAAqB5Q;AAEJ,QAAAuI;AAAA,QAAA3I,EAAA,CAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAEqBlC,WAAAA,MAAA;AAClCkI,gCAAwB;AAAA,MAAC;AAC5B7Q,aAAA2I;AAAAA,IAAA,OAAA;AAAAA,WAAA3I,EAAA,CAAA;AAAA,IAAA;AAFD,UAAAiR,oBAA0BtI;AAEnB,QAAAY;AAAA,QAAAvJ,SAAAE,YAAA;AAE8BqJ,kBAAA2F,UAAA;AAAA,mBAC5B/K,QAAc+K,OAAK;AAAA,gBACdhP,WAAUgE,WAAYC,IAAI;AAAA,QAAC;AAErC0M,iCAAyB;AAAA,MAAC;AAC7B7Q,aAAAE;AAAAF,aAAAuJ;AAAAA,IAAA,OAAA;AAAAA,WAAAvJ,EAAA,CAAA;AAAA,IAAA;AALD,UAAA+P,mBAAyBxG;AAKR,QAAAG;AAAA,QAAA1J,SAAAE,YAAA;AAEiBwJ,WAAAA,MAAA;AAC9BxJ,mBAAUiC,cAAAA;AAAAA,MAAgB;AAC7BnC,aAAAE;AAAAF,aAAA0J;AAAAA,IAAA,OAAA;AAAAA,WAAA1J,EAAA,CAAA;AAAA,IAAA;AAFD,UAAAkR,gBAAsBxH;AAEL,QAAAC;AAAA,QAAA3J,EAAA,CAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAUOlB,0CAACM,GAAAA,YAAA,EAAmB,SAAA,MAAe,WAAA,iBAAgB,UAAA,iBAEnD;AAAajK,aAAA2J;AAAAA,IAAA,OAAA;AAAAA,WAAA3J,EAAA,CAAA;AAAA,IAAA;AAAA,QAAA8J;AAAA,QAAA9J,EAAA,CAAA,MAAAE,WAAAoH,YAAA;AACZwC,WAAA5J,WAAUoH,eAAA7H,UACP8K,2BAAAA,KAACN,GAAAA,cACW,SAAA,WACE,WAAA,6EAET/J,UAAAA;AAAAA,QAAAA,WAAUoH;AAAAA,QAAY;AAAA,MAAA,GAC3B;AACHtH,QAAA,CAAA,IAAAE,WAAAoH;AAAAtH,aAAA8J;AAAAA,IAAA,OAAA;AAAAA,WAAA9J,EAAA,CAAA;AAAA,IAAA;AAAA,QAAA+J;AAAA,QAAA/J,UAAA8J,IAAA;AAXLC,WAAAQ,2BAAAA,KAAA,OAAA,EAAe,WAAA,2BACXZ,UAAAA;AAAAA,QAAAA;AAAAA,QAGCG;AAAAA,MAAAA,GAQL;AAAM9J,cAAA8J;AAAA9J,cAAA+J;AAAAA,IAAA,OAAA;AAAAA,WAAA/J,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAgK;AAAA,QAAAhK,UAAAgR,cAAA;AAGFhH,0CAACmH,GAAAA,WAAA,EACiBH,cAAAA,cACF,aAAA,oBACF,WAAA,kBAAgB;AAC5BhR,cAAAgR;AAAAhR,cAAAgK;AAAAA,IAAA,OAAA;AAAAA,WAAAhK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAkK;AAAA,QAAAlK,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAMmBX,WAAAA,MAAM4G,YAAY,MAAM;AAAC9Q,cAAAkK;AAAAA,IAAA,OAAA;AAAAA,WAAAlK,EAAA,EAAA;AAAA,IAAA;AAE9B,UAAAmK,MAAA5B,aAAa,UAAU;AAAkD,QAAA+B;AAAA,QAAAtK,UAAAmK,KAAA;AADlEG,YAAAV,GAAAA,IACPO,GACJ;AAACnK,cAAAmK;AAAAnK,cAAAsK;AAAAA,IAAA,OAAA;AAAAA,YAAAtK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAwK;AAAA,QAAAxK,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAEDL,2CAAC4G,GAAAA,UAAA,EAAc,MAAA,QAAA,CAAO;AAAEpR,cAAAwK;AAAAA,IAAA,OAAA;AAAAA,YAAAxK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAyK;AAAA,QAAAzK,UAAAsK,KAAA;AAPhCG,YAAAjB,2BAAAA,IAAC6H,YAAA,EAAc,OAAA,aACX,UAAA7H,+BAACiE,GAAAA,YAAA,EACY,SAAAvD,IACE,WAAAI,KAIXE,UAAAA,IAAAA,CACJ,GACJ;AAAUxK,cAAAsK;AAAAtK,cAAAyK;AAAAA,IAAA,OAAA;AAAAA,YAAAzK,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA2K;AAAA,QAAA3K,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAGOF,YAAAA,MAAMmG,YAAY,MAAM;AAAC9Q,cAAA2K;AAAAA,IAAA,OAAA;AAAAA,YAAA3K,EAAA,EAAA;AAAA,IAAA;AAE9B,UAAA8K,MAAAvC,aAAa,UAAU;AAAkD,QAAA+I;AAAA,QAAAtR,UAAA8K,KAAA;AADlEwG,YAAA1H,GAAAA,IACPkB,GACJ;AAAC9K,cAAA8K;AAAA9K,cAAAsR;AAAAA,IAAA,OAAA;AAAAA,YAAAtR,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAuR;AAAA,QAAAvR,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAED0G,YAAA/H,2BAAAA,IAACgI,SAAA,EAAa,SAAA,QAAY,MAAA,SAAO;AAAExR,cAAAuR;AAAAA,IAAA,OAAA;AAAAA,YAAAvR,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAyR;AAAA,QAAAzR,UAAAsR,KAAA;AAP3CG,YAAAjI,2BAAAA,IAAC6H,YAAA,EAAc,OAAA,aACX,UAAA7H,+BAACiE,GAAAA,YAAA,EACY,SAAA9C,KACE,WAAA2G,KAIXC,UAAAA,IAAAA,CACJ,GACJ;AAAUvR,cAAAsR;AAAAtR,cAAAyR;AAAAA,IAAA,OAAA;AAAAA,YAAAzR,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA0R;AAAA,QAAA1R,EAAA,EAAA,MAAAyK,OAAAzK,UAAAyR,KAAA;AArBdC,YAAAnH,2BAAAA,KAAA,OAAA,EACc,WAAA,uGACVE,UAAAA;AAAAA,QAAAA;AAAAA,QAUAgH;AAAAA,MAAAA,GAUJ;AAAMzR,cAAAyK;AAAAzK,cAAAyR;AAAAzR,cAAA0R;AAAAA,IAAA,OAAA;AAAAA,YAAA1R,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA2R;AAAA,QAAA3R,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAIE8G,2CAACC,GAAAA,aAAA,EAAiB,MAAA,QAAA,CAAO;AAAE5R,cAAA2R;AAAAA,IAAA,OAAA;AAAAA,YAAA3R,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA6R;AAAA,QAAA7R,UAAAE,WAAAY,WAAAd,UAAAkR,eAAA;AAFnCW,YAAArI,2BAAAA,IAAC6H,YAAA,EAAc,OAAA,WACX,UAAA7H,2BAAAA,IAACiE,GAAAA,YAAA,EAAoByD,wBAAyB,UAAAhR,WAAUY,SACpD6Q,UAAAA,IAAAA,CACJ,GACJ;AAAU3R,QAAA,EAAA,IAAAE,WAAAY;AAAAd,cAAAkR;AAAAlR,cAAA6R;AAAAA,IAAA,OAAA;AAAAA,YAAA7R,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA8R;AAAA,QAAA9R,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAEViH,YAAAvH,2BAAAA,KAAC2D,GAAAA,QAAA,EACW,SAAA,UACC+C,SAAAA,mBAET,UAAA;AAAA,QAAAzH,2BAAAA,IAACuI,GAAAA,SAAA,EAAa,MAAA,QAAA,CAAO;AAAA,QAAE;AAAA,MAAA,GAE3B;AAAS/R,cAAA8R;AAAAA,IAAA,OAAA;AAAAA,YAAA9R,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAgS;AAAA,QAAAhS,EAAA,EAAA,MAAA0R,OAAA1R,UAAA6R,OAAA7R,EAAA,EAAA,MAAAgK,IAAA;AA3CbgI,qDAAe,WAAA,4CACXhI,UAAAA;AAAAA,QAAAA;AAAAA,QAMA0H;AAAAA,QAwBAG;AAAAA,QAMAC;AAAAA,MAAAA,GAOJ;AAAM9R,cAAA0R;AAAA1R,cAAA6R;AAAA7R,cAAAgK;AAAAhK,cAAAgS;AAAAA,IAAA,OAAA;AAAAA,YAAAhS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAiS;AAAA,QAAAjS,EAAA,EAAA,MAAAgS,OAAAhS,UAAA+J,IAAA;AA9DlBkI,oDACc,WAAA,qHACV,UAAAzI,+BAAC0I,GAAAA,WAAA,EAAmB,UAAA,OAAgB,WAAA,QAChC,UAAA3H,2BAAAA,KAAA,OAAA,EAAe,WAAA,+EACXR,UAAAA;AAAAA,QAAAA;AAAAA,QAcAiI;AAAAA,MAAAA,EAAAA,CA6CJ,GACJ,GACJ;AAAMhS,cAAAgS;AAAAhS,cAAA+J;AAAA/J,cAAAiS;AAAAA,IAAA,OAAA;AAAAA,YAAAjS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAmS;AAAA,QAAAnS,EAAA,EAAA,MAAAE,cAAAF,UAAAkR,iBAAAlR,EAAA,EAAA,MAAAuI,UAAA;AAGN4J,YAAA3I,2BAAAA,IAAA,OAAA,EAAe,WAAA,wBACX,UAAAA,2BAAAA,IAAC0I,GAAAA,WAAA,EAAmB,UAAA,OAAgB,WAAA,QAC/BhS,UAAAA,WAAUY,WAAYZ,WAAUiB,OAAAuB,eAC7B8G,2BAAAA,IAAA,OAAA,EAAe,WAAA,yCACX,UAAAA,2BAAAA,IAAC2E,qBAAA,CAAA,IACL,IACAjO,WAAUe,QACVsJ,2BAAAA,KAAA,OAAA,EAAe,WAAA,wDACX,UAAA;AAAA,QAAAA,2BAAAA,KAACN,GAAAA,YAAA,EAAqB,WAAA,gBAAe,UAAA;AAAA,UAAA;AAAA,UACV/J,WAAUe,MAAAsL;AAAAA,QAAAA,GACrC;AAAA,QACA/C,2BAAAA,IAAC0E,GAAAA,QAAA,EAAgBgD,SAAAA,eAAe,UAAA,YAAA,CAEhC;AAAA,MAAA,EAAA,CACJ,IACAhR,WAAUiB,OAAAuB,WAAA,IACV6H,2BAAAA,KAAA,OAAA,EAAe,WAAA,wDACX,UAAA;AAAA,QAAAf,2BAAAA,IAACS,GAAAA,YAAA,EAAqB,WAAA,2BAA0B,UAAA,uBAEhD;AAAA,QACAM,2BAAAA,KAAC2D,GAAAA,QAAA,EAAgB+C,SAAAA,mBACb,UAAA;AAAA,UAAAzH,2BAAAA,IAACuI,GAAAA,SAAA,EAAa,MAAA,QAAA,CAAO;AAAA,UAAE;AAAA,QAAA,EAAA,CAE3B;AAAA,MAAA,EAAA,CACJ,IAEAvI,2BAAAA,aAAgB,WAAAI,GAAAA,IACZrB,aAAa,SACP,wEACA,qBACV,GACKrI,UAAAA,WAAUiB,OAAA2B,IAAAnB,CAAAA,yCACN,gBAAA,EAEUA,OACG4G,UACD,SAAA,MAAMrI,WAAU8G,YAAarF,KAAK,GACjC,UAAAzB,WAAUmB,eAAA6B,OAAuBvB,MAAKuB,MAJ3CvB,MAAKuB,EAIyC,CAE1D,EAAA,CACL,GAER,GACJ;AAAMlD,cAAAE;AAAAF,cAAAkR;AAAAlR,cAAAuI;AAAAvI,cAAAmS;AAAAA,IAAA,OAAA;AAAAA,YAAAnS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAoS;AAAA,QAAApS,UAAAE,YAAA;AAGLkS,YAAAlS,WAAUmB,iBACPmI,2BAAAA,IAAC,qBACU,OAAAtJ,WAAUmB,eACR,SAAA,MAAMnB,WAAU8G,YAAAvH,MAAsB,GACrC,UAAAS,WAAUmG,aACV,UAAAnG,WAAU4F,aAAY;AAEvC9F,cAAAE;AAAAF,cAAAoS;AAAAA,IAAA,OAAA;AAAAA,YAAApS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAqS;AAAA,QAAArS,EAAA,EAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAKYwH,YAAAA,MAAMxB,yBAAyB;AAAC7Q,cAAAqS;AAAAA,IAAA,OAAA;AAAAA,YAAArS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAsS;AAAA,QAAAtS,EAAA,EAAA,MAAA0O,qBAAA1O,EAAA,EAAA,MAAA+P,oBAAA/P,EAAA,EAAA,MAAAyO,eAAAzO,UAAA4Q,kBAAA;AAF7C0B,YAAA9I,2BAAAA,IAAC,qBACSoH,MAAAA,kBACG,SAAAyB,KACCtC,UAAAA,kBACGtB,aACMC,kBAAAA,CAAiB;AACtC1O,cAAA0O;AAAA1O,cAAA+P;AAAA/P,cAAAyO;AAAAzO,cAAA4Q;AAAA5Q,cAAAsS;AAAAA,IAAA,OAAA;AAAAA,YAAAtS,EAAA,EAAA;AAAA,IAAA;AAAA,QAAAuS;AAAA,QAAAvS,EAAA,EAAA,MAAAiS,OAAAjS,EAAA,EAAA,MAAAmS,OAAAnS,EAAA,EAAA,MAAAoS,OAAApS,UAAAsS,KAAA;AApINC,qDAAe,WAAA,wCAEXN,UAAAA;AAAAA,QAAAA;AAAAA,QAoEAE;AAAAA,QA8CCC;AAAAA,QAUDE;AAAAA,MAAAA,GAOJ;AAAMtS,cAAAiS;AAAAjS,cAAAmS;AAAAnS,cAAAoS;AAAApS,cAAAsS;AAAAtS,cAAAuS;AAAAA,IAAA,OAAA;AAAAA,YAAAvS,EAAA,EAAA;AAAA,IAAA;AAAA,WArINuS;AAAAA,EAqIM;ACvLd,QAAMC,uBAAuB;AAC7B,QAAMC,0BAA0B;AAKhC,QAAMC,4BAA4BlT,MAAAA,cAAyC,IAAI;AAE/E,WAAAmT,wBAAA;AACI,UAAAC,SAAehT,MAAAA,WAAA8S,yBAAoC;AAAE,QAAA,CAChDE,QAAM;AAAA,YAAA,IAAA/S,MACS,sEAAsE;AAAA,IAAA;AAAA,WAEnF+S;AAAAA,EAAM;AAMjB,WAAAC,2BAAA;AAAA,UAAA7S,IAAAC,qBAAAA,EAAA,EAAA;AACI,UAAA2S,SAAeD,sBAAAA;AAIE,UAAA5S,KAAA6S,OAAMnS,eAAA+R;AACH,UAAApS,KAAAwS,OAAMlS,kBAAA+R;AAA0C,QAAA9J;AAAA,QAAA3I,EAAA,CAAA,MAAA4S,OAAAjS,UAAAX,EAAA,CAAA,MAAA4S,OAAApS,sBAAAR,EAAA,CAAA,MAAA4S,OAAArS,iBAAAP,EAAA,CAAA,MAAA4S,OAAA/R,iBAAAb,SAAA4S,OAAAhS,kBAAAZ,EAAA,CAAA,MAAAD,MAAAC,SAAAI,IAAA;AAJvBuI,WAAA;AAAA,QAAApI,eAC1BqS,OAAMrS;AAAAA,QAAAC,oBACDoS,OAAMpS;AAAAA,QAAAC,aACbV;AAAAA,QAA0CW,gBACvCN;AAAAA,QAAgDO,QACxDiS,OAAMjS;AAAAA,QAAAC,gBACEgS,OAAMhS;AAAAA,QAAAC,eACP+R,OAAM/R;AAAAA,MAAAA;AACxBb,QAAA,CAAA,IAAA4S,OAAAjS;AAAAX,QAAA,CAAA,IAAA4S,OAAApS;AAAAR,QAAA,CAAA,IAAA4S,OAAArS;AAAAP,QAAA,CAAA,IAAA4S,OAAA/R;AAAAb,QAAA,CAAA,IAAA4S,OAAAhS;AAAAZ,aAAAD;AAAAC,aAAAI;AAAAJ,aAAA2I;AAAAA,IAAA,OAAA;AAAAA,WAAA3I,EAAA,CAAA;AAAA,IAAA;AARD,UAAAE,aAAmBI,0BAA0BqI,EAQ5C;AAAE,QAAAY;AAAA,QAAAvJ,EAAA,CAAA,MAAA4S,OAAAlE,qBAAA1O,EAAA,CAAA,MAAA4S,OAAAnE,aAAA;AAIKlF,0CAAC,kBAAA,EACgB,aAAAqJ,OAAMnE,aACA,mBAAAmE,OAAMlE,mBAAkB;AAC7C1O,QAAA,CAAA,IAAA4S,OAAAlE;AAAA1O,QAAA,CAAA,IAAA4S,OAAAnE;AAAAzO,cAAAuJ;AAAAA,IAAA,OAAA;AAAAA,WAAAvJ,EAAA,EAAA;AAAA,IAAA;AAAA,QAAA0J;AAAA,QAAA1J,EAAA,EAAA,MAAAE,cAAAF,UAAAuJ,IAAA;AAJNG,WAAAF,2BAAAA,IAAC,sBAAA,EAAiCtJ,YAC9BqJ,UAAAA,IAIJ;AAAuBvJ,cAAAE;AAAAF,cAAAuJ;AAAAvJ,cAAA0J;AAAAA,IAAA,OAAA;AAAAA,WAAA1J,EAAA,EAAA;AAAA,IAAA;AAAA,WALvB0J;AAAAA,EAKuB;AAO/B,WAASoJ,iBAA0B;AAC/B,WAAO;AAAA,MACHvQ,MAAM;AAAA,MACN8B,MAAM;AAAA,MACN0O,aAAa;AAAA,MACbC,OAAO;AAAA,MACPC,MAAM;AAAA,MACNC,qCAAO,0BAAA,CAAA,CAAwB;AAAA,IAAA;AAAA,EAEvC;AAGA,QAAMC,aAAaL,eAAAA;AAoBZ,WAASM,sBAAsBC,OAA+C;AACjF,WAAOC,MAAAA,QAAQ,OAAO;AAAA,MAClB5M,KAAK;AAAA,MACL6M,OAAO,CAACJ,UAAU;AAAA,MAClBK,UAAU;AAAA,QACNC,WAAWA,CAAC;AAAA,UAAEtT;AAAAA,QAAAA,MACVqJ,2BAAAA,IAAC,0BAA0B,UAA1B,EAAmC,OAAO6J,OACtClT,SAAAA,CACL;AAAA,MAAA;AAAA,IAER,IACwB,CAAA,CAAE;AAAA,EAClC;AC/EO,WAAAuT,iBAAA3T,IAAA;AAAA,UAAAC,IAAAC,qBAAAA,EAAA,CAAA;AAA0B,UAAA;AAAA,MAAA+S;AAAAA,IAAAA,IAAAjT;AAAgC,QAEzDiT,UAAU,SAAO;AAAA,aAAA;AAAA,IAAA;AAAA,QAAA5S;AAAA,QAAAJ,EAAA,CAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAKEzK,WAAAwJ,GAAAA,IACP,sBACA,gEACA,kCACA,qBACJ;AAAC5J,aAAAI;AAAAA,IAAA,OAAA;AAAAA,WAAAJ,EAAA,CAAA;AAAA,IAAA;AAAA,QAAA2I;AAAA,QAAA3I,EAAA,CAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAGGlC,WAAAa,2BAAAA,IAAA,OAAA,EAAgB,WAAAI,OACZ,wBACA,oCACA,kCACJ,GACI,UAAAJ,2BAAAA,IAACR,GAAAA,WAAA,EAAoB,WAAA,gBAAoB,MAAA,UAAQ,GACrD;AAAMhJ,aAAA2I;AAAAA,IAAA,OAAA;AAAAA,WAAA3I,EAAA,CAAA;AAAA,IAAA;AAAA,QAAAuJ;AAAA,QAAAvJ,EAAA,CAAA,MAAA4K,uBAAAC,IAAA,2BAAA,GAAA;AAhBlBtB,0CAACoK,eAAAA,MAAA,EAAQ,IAAA,UAAmB,WAAA,gBACxB,UAAAnK,2BAAAA,IAACuB,GAAAA,MAAA,EACc,WAAA3K,IAOX,UAAAmK,2BAAAA,cAAe,WAAA,2BACX5B,UAAAA;AAAAA,QAAAA;AAAAA,QAOA4B,2BAAAA,KAAA,OAAA,EAAe,WAAA,UACX,UAAA;AAAA,UAAAf,+BAACS,GAAAA,YAAA,EAAmB,SAAA,aAAsB,WAAA,eAAc,UAAA,iBAExD;AAAA,yCACCA,GAAAA,YAAA,EACW,SAAA,WACE,WAAA,wDACb,UAAA,0BAAA,CAED;AAAA,QAAA,EAAA,CACJ;AAAA,MAAA,EAAA,CACJ,GACJ,GACJ;AAAOjK,aAAAuJ;AAAAA,IAAA,OAAA;AAAAA,WAAAvJ,EAAA,CAAA;AAAA,IAAA;AAAA,WA9BPuJ;AAAAA,EA8BO;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DataSourceDelegate, StorageSource } from "@firecms/core";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for a thumbnail size.
|
|
4
|
+
* Thumbnails are generated client-side before upload.
|
|
5
|
+
*/
|
|
6
|
+
export interface ThumbnailSize {
|
|
7
|
+
/**
|
|
8
|
+
* Unique name for this size (e.g., "small", "medium", "large").
|
|
9
|
+
* Used as a key in the thumbnails record.
|
|
10
|
+
*/
|
|
11
|
+
name: string;
|
|
12
|
+
/**
|
|
13
|
+
* Maximum width in pixels.
|
|
14
|
+
* The image will be scaled to fit within this width while maintaining aspect ratio.
|
|
15
|
+
*/
|
|
16
|
+
width: number;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum height in pixels.
|
|
19
|
+
* The image will be scaled to fit within this height while maintaining aspect ratio.
|
|
20
|
+
*/
|
|
21
|
+
height: number;
|
|
22
|
+
/**
|
|
23
|
+
* JPEG quality for the thumbnail (0-1).
|
|
24
|
+
* @default 0.8
|
|
25
|
+
*/
|
|
26
|
+
quality?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configuration options for the Media Manager plugin.
|
|
30
|
+
*/
|
|
31
|
+
export interface MediaManagerConfig {
|
|
32
|
+
/**
|
|
33
|
+
* Storage source for file operations (upload, download, delete).
|
|
34
|
+
* Typically useFirebaseStorageSource() or similar.
|
|
35
|
+
*/
|
|
36
|
+
storageSource: StorageSource;
|
|
37
|
+
/**
|
|
38
|
+
* Data source delegate for metadata persistence.
|
|
39
|
+
* Typically useFirestoreDelegate() or similar.
|
|
40
|
+
*/
|
|
41
|
+
dataSourceDelegate: DataSourceDelegate;
|
|
42
|
+
/**
|
|
43
|
+
* Path in storage where media files will be uploaded.
|
|
44
|
+
* @default "media"
|
|
45
|
+
*/
|
|
46
|
+
storagePath?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Collection path in the database for storing asset metadata.
|
|
49
|
+
* @default "media_assets"
|
|
50
|
+
*/
|
|
51
|
+
collectionPath?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Optional bucket name for storage operations.
|
|
54
|
+
* If not specified, uses the default bucket.
|
|
55
|
+
*/
|
|
56
|
+
bucket?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Maximum file size allowed for uploads (in bytes).
|
|
59
|
+
* @default 52428800 (50MB)
|
|
60
|
+
*/
|
|
61
|
+
maxFileSize?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Allowed MIME types for uploads.
|
|
64
|
+
* If not specified, all file types are allowed.
|
|
65
|
+
*/
|
|
66
|
+
acceptedMimeTypes?: string[];
|
|
67
|
+
/**
|
|
68
|
+
* Thumbnail sizes to generate on upload.
|
|
69
|
+
* Thumbnails are generated client-side using canvas before upload.
|
|
70
|
+
* If not specified, no thumbnails are generated.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* thumbnailSizes: [
|
|
75
|
+
* { name: "small", width: 150, height: 150 },
|
|
76
|
+
* { name: "medium", width: 400, height: 400 }
|
|
77
|
+
* ]
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
thumbnailSizes?: ThumbnailSize[];
|
|
81
|
+
/**
|
|
82
|
+
* Subfolder path for storing thumbnails.
|
|
83
|
+
* Thumbnails are stored under `{storagePath}/{thumbnailPath}/{sizeName}/`.
|
|
84
|
+
* @default "thumbs"
|
|
85
|
+
*/
|
|
86
|
+
thumbnailPath?: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { MediaAsset } from "./media_asset";
|
|
2
|
+
/**
|
|
3
|
+
* Controller interface for managing media assets.
|
|
4
|
+
* Provides methods for CRUD operations and state management.
|
|
5
|
+
*/
|
|
6
|
+
export interface MediaManagerController {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the controller is currently loading data
|
|
9
|
+
*/
|
|
10
|
+
loading: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Error if any operation failed
|
|
13
|
+
*/
|
|
14
|
+
error?: Error;
|
|
15
|
+
/**
|
|
16
|
+
* List of loaded media assets
|
|
17
|
+
*/
|
|
18
|
+
assets: MediaAsset[];
|
|
19
|
+
/**
|
|
20
|
+
* Total count of assets (may differ from assets.length during pagination)
|
|
21
|
+
*/
|
|
22
|
+
totalCount?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Currently selected asset (for details view)
|
|
25
|
+
*/
|
|
26
|
+
selectedAsset?: MediaAsset;
|
|
27
|
+
/**
|
|
28
|
+
* Select an asset for viewing/editing
|
|
29
|
+
*/
|
|
30
|
+
selectAsset: (asset: MediaAsset | undefined) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Upload a new file to the media library
|
|
33
|
+
* @param file The file to upload
|
|
34
|
+
* @param metadata Optional metadata to attach
|
|
35
|
+
* @returns The created MediaAsset
|
|
36
|
+
*/
|
|
37
|
+
uploadFile: (file: File, metadata?: Partial<Pick<MediaAsset, "title" | "altText" | "caption" | "tags">>) => Promise<MediaAsset>;
|
|
38
|
+
/**
|
|
39
|
+
* Delete an asset from storage and database
|
|
40
|
+
* @param assetId The ID of the asset to delete
|
|
41
|
+
*/
|
|
42
|
+
deleteAsset: (assetId: string) => Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Update asset metadata
|
|
45
|
+
* @param assetId The ID of the asset to update
|
|
46
|
+
* @param data The fields to update
|
|
47
|
+
*/
|
|
48
|
+
updateAsset: (assetId: string, data: Partial<MediaAsset>) => Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Refresh the assets list from the database
|
|
51
|
+
*/
|
|
52
|
+
refreshAssets: () => Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Search assets by text
|
|
55
|
+
* @param query Search query string
|
|
56
|
+
*/
|
|
57
|
+
searchAssets: (query: string) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Storage path configuration
|
|
60
|
+
*/
|
|
61
|
+
storagePath: string;
|
|
62
|
+
/**
|
|
63
|
+
* Collection path configuration
|
|
64
|
+
*/
|
|
65
|
+
collectionPath: string;
|
|
66
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a media asset stored in the media library.
|
|
3
|
+
* This interface defines the metadata stored in the database for each file.
|
|
4
|
+
*/
|
|
5
|
+
export interface MediaAsset {
|
|
6
|
+
/**
|
|
7
|
+
* Unique identifier for the asset (document ID in the database)
|
|
8
|
+
*/
|
|
9
|
+
id: string;
|
|
10
|
+
/**
|
|
11
|
+
* Original file name
|
|
12
|
+
*/
|
|
13
|
+
fileName: string;
|
|
14
|
+
/**
|
|
15
|
+
* Path in storage where the file is located
|
|
16
|
+
*/
|
|
17
|
+
storagePath: string;
|
|
18
|
+
/**
|
|
19
|
+
* Cached download URL for quick access
|
|
20
|
+
*/
|
|
21
|
+
downloadURL?: string;
|
|
22
|
+
/**
|
|
23
|
+
* MIME type of the file (e.g., "image/jpeg", "application/pdf")
|
|
24
|
+
*/
|
|
25
|
+
mimeType: string;
|
|
26
|
+
/**
|
|
27
|
+
* File size in bytes
|
|
28
|
+
*/
|
|
29
|
+
size: number;
|
|
30
|
+
/**
|
|
31
|
+
* Dimensions for image/video files
|
|
32
|
+
*/
|
|
33
|
+
dimensions?: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* User-defined title for the asset
|
|
39
|
+
*/
|
|
40
|
+
title?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Alternative text for accessibility (SEO recommended)
|
|
43
|
+
*/
|
|
44
|
+
altText?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional caption/description
|
|
47
|
+
*/
|
|
48
|
+
caption?: string;
|
|
49
|
+
/**
|
|
50
|
+
* User-defined tags for categorization
|
|
51
|
+
*/
|
|
52
|
+
tags?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Timestamp when the asset was created
|
|
55
|
+
*/
|
|
56
|
+
createdAt: Date;
|
|
57
|
+
/**
|
|
58
|
+
* Timestamp when the asset was last updated
|
|
59
|
+
*/
|
|
60
|
+
updatedAt: Date;
|
|
61
|
+
/**
|
|
62
|
+
* Storage bucket name (for multi-bucket setups)
|
|
63
|
+
*/
|
|
64
|
+
bucket?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Thumbnail URLs keyed by size name (e.g., "small", "medium").
|
|
67
|
+
* These are generated during upload based on thumbnailSizes config.
|
|
68
|
+
*/
|
|
69
|
+
thumbnails?: Record<string, string>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DataSourceDelegate, StorageSource } from "@firecms/core";
|
|
2
|
+
import { MediaManagerController, ThumbnailSize } from "./types";
|
|
3
|
+
export interface UseMediaManagerControllerProps {
|
|
4
|
+
storageSource: StorageSource;
|
|
5
|
+
dataSourceDelegate: DataSourceDelegate;
|
|
6
|
+
storagePath: string;
|
|
7
|
+
collectionPath: string;
|
|
8
|
+
bucket?: string;
|
|
9
|
+
/** Thumbnail sizes to generate on upload */
|
|
10
|
+
thumbnailSizes?: ThumbnailSize[];
|
|
11
|
+
/** Path prefix for thumbnails. Default: "thumbs" */
|
|
12
|
+
thumbnailPath?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hook that creates a MediaManagerController for managing media assets.
|
|
16
|
+
* Handles all CRUD operations for files and their metadata.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useMediaManagerController({ storageSource, dataSourceDelegate, storagePath, collectionPath, bucket, thumbnailSizes, thumbnailPath }: UseMediaManagerControllerProps): MediaManagerController;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FireCMSPlugin } from "@firecms/core";
|
|
2
|
+
import { MediaManagerConfig } from "./types";
|
|
3
|
+
export interface MediaManagerPluginProps extends MediaManagerConfig {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Hook to create the Media Manager plugin for FireCMS.
|
|
7
|
+
*
|
|
8
|
+
* The plugin automatically registers the Media Library view in the navigation.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { plugin: mediaManagerPlugin } = useMediaManagerPlugin({
|
|
13
|
+
* storageSource,
|
|
14
|
+
* dataSourceDelegate: firestoreDelegate,
|
|
15
|
+
* storagePath: "media",
|
|
16
|
+
* collectionPath: "media_assets"
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Add plugin to your plugins array - view is auto-registered
|
|
20
|
+
* const plugins = [mediaManagerPlugin, ...otherPlugins];
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function useMediaManagerPlugin(props: MediaManagerPluginProps): FireCMSPlugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firecms/media_manager",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "3.1.0-canary.1df3b2c",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"firebase",
|
|
10
|
+
"cms",
|
|
11
|
+
"media",
|
|
12
|
+
"media manager",
|
|
13
|
+
"asset manager",
|
|
14
|
+
"firecms",
|
|
15
|
+
"storage"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.es.js",
|
|
21
|
+
"require": "./dist/index.umd.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.umd.js",
|
|
26
|
+
"module": "./dist/index.es.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"source": "src/index.ts",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@firecms/core": "^3.1.0-canary.1df3b2c",
|
|
31
|
+
"@firecms/formex": "^3.1.0-canary.1df3b2c",
|
|
32
|
+
"@firecms/ui": "^3.1.0-canary.1df3b2c"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=18.0.0",
|
|
36
|
+
"react-dom": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.19.17",
|
|
40
|
+
"@types/react": "^18.3.24",
|
|
41
|
+
"@types/react-dom": "^18.3.7",
|
|
42
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
43
|
+
"babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
|
|
44
|
+
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vite": "^7.2.4"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"dev": "vite",
|
|
50
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
51
|
+
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"src"
|
|
56
|
+
],
|
|
57
|
+
"gitHead": "5074584b15be0d0507a4dadc148f03d82e9fe495"
|
|
58
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { createContext, PropsWithChildren, useContext } from "react";
|
|
2
|
+
import { MediaManagerController } from "./types";
|
|
3
|
+
|
|
4
|
+
const MediaManagerContext = createContext<MediaManagerController | undefined>(undefined);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to access the MediaManagerController from context.
|
|
8
|
+
* Must be used within a MediaManagerProvider.
|
|
9
|
+
*/
|
|
10
|
+
export function useMediaManager(): MediaManagerController {
|
|
11
|
+
const context = useContext(MediaManagerContext);
|
|
12
|
+
if (!context) {
|
|
13
|
+
throw new Error("useMediaManager must be used within a MediaManagerProvider");
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MediaManagerProviderProps {
|
|
19
|
+
controller: MediaManagerController;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Provider component that makes the MediaManagerController available to all children.
|
|
24
|
+
*/
|
|
25
|
+
export function MediaManagerProvider({
|
|
26
|
+
controller,
|
|
27
|
+
children
|
|
28
|
+
}: PropsWithChildren<MediaManagerProviderProps>) {
|
|
29
|
+
return (
|
|
30
|
+
<MediaManagerContext.Provider value={controller}>
|
|
31
|
+
{children}
|
|
32
|
+
</MediaManagerContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
}
|