{"version":3,"sources":["../src/core/file-preview.ts"],"names":["_FilePreviewManager","__publicField","FORMATS","file","options","cacheKey","preview","maxWidth","DIMENSIONS","maxHeight","quality","QUALITY","format","resolve","reject","canvas","ctx","img","width","height","dataUrl","thumbnailUrl","error","video","VIDEO","size","thumbnailCanvas","x","y","originalWidth","originalHeight","aspectRatio","mimeType","FilePreviewManager"],"mappings":"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","file":"chunk-VLVSIWQL.cjs","sourcesContent":["import { DIMENSIONS, QUALITY, FORMATS, VIDEO } from \"./config\";\r\n\r\nexport interface PreviewOptions {\r\n  maxWidth?: number;\r\n  maxHeight?: number;\r\n  quality?: number;\r\n  format?: \"webp\" | \"jpeg\" | \"png\";\r\n  generateThumbnail?: boolean;\r\n  thumbnailSize?: number;\r\n}\r\n\r\nexport interface FilePreview {\r\n  id: string;\r\n  url: string;\r\n  thumbnailUrl?: string;\r\n  type: \"image\" | \"video\" | \"document\" | \"other\";\r\n  dimensions?: { width: number; height: number };\r\n  duration?: number; // for videos\r\n  size: number;\r\n  mimeType: string;\r\n  metadata?: Record<string, any>;\r\n}\r\n\r\nexport class FilePreviewManager {\r\n  private static instance: FilePreviewManager;\r\n  private previewCache: Map<string, FilePreview> = new Map();\r\n  private supportedImageTypes = [...FORMATS.SUPPORTED_IMAGE_FORMATS];\r\n  private supportedVideoTypes = [...FORMATS.SUPPORTED_VIDEO_FORMATS];\r\n\r\n  static getInstance(): FilePreviewManager {\r\n    if (!FilePreviewManager.instance) {\r\n      FilePreviewManager.instance = new FilePreviewManager();\r\n    }\r\n    return FilePreviewManager.instance;\r\n  }\r\n\r\n  async generatePreview(\r\n    file: File,\r\n    options: PreviewOptions = {}\r\n  ): Promise<FilePreview> {\r\n    const cacheKey = `${file.name}-${file.size}-${file.lastModified}`;\r\n\r\n    if (this.previewCache.has(cacheKey)) {\r\n      return this.previewCache.get(cacheKey)!;\r\n    }\r\n\r\n    const preview = await this.createPreview(file, options);\r\n    this.previewCache.set(cacheKey, preview);\r\n\r\n    return preview;\r\n  }\r\n\r\n  private async createPreview(\r\n    file: File,\r\n    options: PreviewOptions\r\n  ): Promise<FilePreview> {\r\n    const {\r\n      maxWidth = DIMENSIONS.DEFAULT_MAX_WIDTH,\r\n      maxHeight = DIMENSIONS.DEFAULT_MAX_HEIGHT,\r\n      quality = QUALITY.DEFAULT_IMAGE_QUALITY,\r\n      format = FORMATS.DEFAULT_FORMAT,\r\n    } = options;\r\n\r\n    if (this.supportedImageTypes.includes(file.type as any)) {\r\n      return this.createImagePreview(file, {\r\n        maxWidth,\r\n        maxHeight,\r\n        quality,\r\n        format,\r\n      });\r\n    } else if (this.supportedVideoTypes.includes(file.type as any)) {\r\n      return this.createVideoPreview(file, {\r\n        maxWidth,\r\n        maxHeight,\r\n        thumbnailSize: options.thumbnailSize || DIMENSIONS.THUMBNAIL_SIZE,\r\n      });\r\n    } else {\r\n      return this.createGenericPreview(file);\r\n    }\r\n  }\r\n\r\n  private async createImagePreview(\r\n    file: File,\r\n    options: PreviewOptions\r\n  ): Promise<FilePreview> {\r\n    return new Promise((resolve, reject) => {\r\n      const canvas = document.createElement(\"canvas\");\r\n      const ctx = canvas.getContext(\"2d\");\r\n      const img = new Image();\r\n\r\n      img.onload = () => {\r\n        try {\r\n          const { width, height } = this.calculateDimensions(\r\n            img.width,\r\n            img.height,\r\n            options.maxWidth!,\r\n            options.maxHeight!\r\n          );\r\n\r\n          canvas.width = width;\r\n          canvas.height = height;\r\n\r\n          // Enable image smoothing for better quality\r\n          ctx!.imageSmoothingEnabled = true;\r\n          ctx!.imageSmoothingQuality = \"high\";\r\n\r\n          // Draw image with proper scaling\r\n          ctx!.drawImage(img, 0, 0, width, height);\r\n\r\n          // Convert to desired format with quality\r\n          const dataUrl = canvas.toDataURL(\r\n            `image/${options.format}`,\r\n            options.quality\r\n          );\r\n\r\n          const preview: FilePreview = {\r\n            id: this.generatePreviewId(file),\r\n            url: dataUrl,\r\n            type: \"image\",\r\n            dimensions: { width, height: height },\r\n            size: file.size,\r\n            mimeType: file.type,\r\n            metadata: {\r\n              originalWidth: img.width,\r\n              originalHeight: img.height,\r\n              aspectRatio: img.width / img.height,\r\n              format: options.format,\r\n              quality: options.quality,\r\n            },\r\n          };\r\n\r\n          // Generate thumbnail if requested\r\n          if (options.generateThumbnail) {\r\n            this.generateThumbnail(\r\n              canvas,\r\n              options.thumbnailSize || DIMENSIONS.THUMBNAIL_SIZE\r\n            ).then((thumbnailUrl) => {\r\n              preview.thumbnailUrl = thumbnailUrl;\r\n            });\r\n          }\r\n\r\n          resolve(preview);\r\n        } catch (error) {\r\n          reject(new Error(`Failed to create image preview: ${error}`));\r\n        }\r\n      };\r\n\r\n      img.onerror = () => reject(new Error(\"Failed to load image\"));\r\n      img.src = URL.createObjectURL(file);\r\n    });\r\n  }\r\n\r\n  private async createVideoPreview(\r\n    file: File,\r\n    options: PreviewOptions\r\n  ): Promise<FilePreview> {\r\n    return new Promise((resolve, reject) => {\r\n      const video = document.createElement(\"video\");\r\n      const canvas = document.createElement(\"canvas\");\r\n      const ctx = canvas.getContext(\"2d\");\r\n\r\n      video.onloadedmetadata = () => {\r\n        try {\r\n          // Seek to 25% of video for thumbnail\r\n          video.currentTime = video.duration * VIDEO.THUMBNAIL_SEEK_PERCENTAGE;\r\n        } catch (error) {\r\n          video.currentTime = VIDEO.FALLBACK_SEEK_TIME;\r\n        }\r\n      };\r\n\r\n      video.onseeked = () => {\r\n        try {\r\n          const { width, height } = this.calculateDimensions(\r\n            video.videoWidth,\r\n            video.videoHeight,\r\n            options.maxWidth!,\r\n            options.maxHeight!\r\n          );\r\n\r\n          canvas.width = width;\r\n          canvas.height = height;\r\n\r\n          // Enable high-quality rendering\r\n          ctx!.imageSmoothingEnabled = true;\r\n          ctx!.imageSmoothingQuality = \"high\";\r\n\r\n          // Draw video frame\r\n          ctx!.drawImage(video, 0, 0, width, height);\r\n\r\n          const thumbnailUrl = canvas.toDataURL(\r\n            `image/${FORMATS.VIDEO_THUMBNAIL_FORMAT}`,\r\n            QUALITY.VIDEO_THUMBNAIL_QUALITY\r\n          );\r\n\r\n          const preview: FilePreview = {\r\n            id: this.generatePreviewId(file),\r\n            url: URL.createObjectURL(file),\r\n            thumbnailUrl,\r\n            type: \"video\",\r\n            dimensions: { width, height },\r\n            duration: video.duration,\r\n            size: file.size,\r\n            mimeType: file.type,\r\n            metadata: {\r\n              originalWidth: video.videoWidth,\r\n              originalHeight: video.videoHeight,\r\n              aspectRatio: video.videoWidth / video.videoHeight,\r\n              frameRate:\r\n                video.videoWidth > 0 ? VIDEO.ESTIMATED_FRAME_RATE : undefined,\r\n              codec: this.detectVideoCodec(file.type),\r\n            },\r\n          };\r\n\r\n          resolve(preview);\r\n        } catch (error) {\r\n          reject(new Error(`Failed to create video preview: ${error}`));\r\n        }\r\n      };\r\n\r\n      video.onerror = () => reject(new Error(\"Failed to load video\"));\r\n      video.src = URL.createObjectURL(file);\r\n      video.load();\r\n    });\r\n  }\r\n\r\n  private createGenericPreview(file: File): FilePreview {\r\n    return {\r\n      id: this.generatePreviewId(file),\r\n      url: URL.createObjectURL(file),\r\n      type: \"other\",\r\n      size: file.size,\r\n      mimeType: file.type,\r\n      metadata: {\r\n        extension: file.name.split(\".\").pop()?.toLowerCase(),\r\n        lastModified: file.lastModified,\r\n      },\r\n    };\r\n  }\r\n\r\n  private async generateThumbnail(\r\n    canvas: HTMLCanvasElement,\r\n    size: number\r\n  ): Promise<string> {\r\n    const thumbnailCanvas = document.createElement(\"canvas\");\r\n    const ctx = thumbnailCanvas.getContext(\"2d\")!;\r\n\r\n    thumbnailCanvas.width = size;\r\n    thumbnailCanvas.height = size;\r\n\r\n    // Enable high-quality scaling\r\n    ctx.imageSmoothingEnabled = true;\r\n    ctx.imageSmoothingQuality = \"high\";\r\n\r\n    // Calculate thumbnail dimensions maintaining aspect ratio\r\n    const { width, height } = this.calculateThumbnailDimensions(\r\n      canvas.width,\r\n      canvas.height,\r\n      size\r\n    );\r\n\r\n    // Center the thumbnail\r\n    const x = (size - width) / 2;\r\n    const y = (size - height) / 2;\r\n\r\n    ctx.drawImage(canvas, x, y, width, height);\r\n\r\n    return thumbnailCanvas.toDataURL(\r\n      `image/${FORMATS.VIDEO_THUMBNAIL_FORMAT}`,\r\n      QUALITY.THUMBNAIL_QUALITY\r\n    );\r\n  }\r\n\r\n  private calculateDimensions(\r\n    originalWidth: number,\r\n    originalHeight: number,\r\n    maxWidth: number,\r\n    maxHeight: number\r\n  ) {\r\n    let { width, height } = { width: originalWidth, height: originalHeight };\r\n\r\n    if (width > maxWidth) {\r\n      height = (height * maxWidth) / width;\r\n      width = maxWidth;\r\n    }\r\n\r\n    if (height > maxHeight) {\r\n      width = (width * maxHeight) / height;\r\n      height = maxHeight;\r\n    }\r\n\r\n    return { width: Math.round(width), height: Math.round(height) };\r\n  }\r\n\r\n  private calculateThumbnailDimensions(\r\n    originalWidth: number,\r\n    originalHeight: number,\r\n    size: number\r\n  ) {\r\n    const aspectRatio = originalWidth / originalHeight;\r\n\r\n    if (aspectRatio > 1) {\r\n      return { width: size, height: size / aspectRatio };\r\n    } else {\r\n      return { width: size * aspectRatio, height: size };\r\n    }\r\n  }\r\n\r\n  private generatePreviewId(file: File): string {\r\n    return `preview-${file.name}-${file.size}-${file.lastModified}`;\r\n  }\r\n\r\n  private detectVideoCodec(mimeType: string): string {\r\n    return (\r\n      VIDEO.CODEC_DETECTION_MAP[\r\n        mimeType as keyof typeof VIDEO.CODEC_DETECTION_MAP\r\n      ] || \"Unknown\"\r\n    );\r\n  }\r\n\r\n  clearCache(): void {\r\n    this.previewCache.clear();\r\n  }\r\n\r\n  getCachedPreview(file: File): FilePreview | undefined {\r\n    const cacheKey = `${file.name}-${file.size}-${file.lastModified}`;\r\n    return this.previewCache.get(cacheKey);\r\n  }\r\n}\r\n"]}