import { ImageResult, PlatformService, PlatformCapabilities, } from "../PlatformService"; import { logger } from "../../utils/logger"; /** * Platform service implementation for web browser platform. * Implements the PlatformService interface with web-specific functionality. * * @remarks * This service provides web-based implementations for: * - Image capture using the browser's file input * - Image selection from local filesystem * - Image processing and conversion * * Note: File system operations are not available in the web platform * due to browser security restrictions. These methods throw appropriate errors. */ export class WebPlatformService implements PlatformService { /** * Gets the capabilities of the web platform * @returns Platform capabilities object */ getCapabilities(): PlatformCapabilities { return { hasFileSystem: false, hasCamera: true, // Through file input with capture isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent), hasFileDownload: true, needsFileHandlingInstructions: false, }; } /** * Not supported in web platform. * @param _path - Unused path parameter * @throws Error indicating file system access is not available */ async readFile(_path: string): Promise { throw new Error("File system access not available in web platform"); } /** * Not supported in web platform. * @param _path - Unused path parameter * @param _content - Unused content parameter * @throws Error indicating file system access is not available */ async writeFile(_path: string, _content: string): Promise { throw new Error("File system access not available in web platform"); } /** * Not supported in web platform. * @param _path - Unused path parameter * @throws Error indicating file system access is not available */ async deleteFile(_path: string): Promise { throw new Error("File system access not available in web platform"); } /** * Not supported in web platform. * @param _directory - Unused directory parameter * @throws Error indicating file system access is not available */ async listFiles(_directory: string): Promise { throw new Error("File system access not available in web platform"); } /** * Opens a file input dialog configured for camera capture. * Creates a temporary file input element to access the device camera. * * @returns Promise resolving to the captured image data * @throws Error if image capture fails or no image is selected * * @remarks * Uses the 'capture' attribute to prefer the device camera. * Falls back to file selection if camera is not available. * Processes the captured image to ensure consistent format. */ async takePicture(): Promise { return new Promise((resolve, reject) => { const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; input.capture = "environment"; input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { try { const blob = await this.processImageFile(file); resolve({ blob, fileName: file.name || "photo.jpg", }); } catch (error) { logger.error("Error processing camera image:", error); reject(new Error("Failed to process camera image")); } } else { reject(new Error("No image captured")); } }; input.click(); }); } /** * Opens a file input dialog for selecting an image file. * Creates a temporary file input element to access local files. * * @returns Promise resolving to the selected image data * @throws Error if image processing fails or no image is selected * * @remarks * Allows selection of any image file type. * Processes the selected image to ensure consistent format. */ async pickImage(): Promise { return new Promise((resolve, reject) => { const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { try { const blob = await this.processImageFile(file); resolve({ blob, fileName: file.name || "photo.jpg", }); } catch (error) { logger.error("Error processing picked image:", error); reject(new Error("Failed to process picked image")); } } else { reject(new Error("No image selected")); } }; input.click(); }); } /** * Processes an image file to ensure consistent format. * Converts the file to a data URL and then to a Blob. * * @param file - The image File object to process * @returns Promise resolving to processed image Blob * @throws Error if file reading or conversion fails * * @remarks * This method ensures consistent image format across different * input sources by converting through data URL to Blob. */ private async processImageFile(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (event) => { const dataUrl = event.target?.result as string; // Convert to blob to ensure consistent format fetch(dataUrl) .then((res) => res.blob()) .then((blob) => resolve(blob)) .catch((error) => { logger.error("Error converting data URL to blob:", error); reject(error); }); }; reader.onerror = (error) => { logger.error("Error reading file:", error); reject(error); }; reader.readAsDataURL(file); }); } /** * Checks if running on Capacitor platform. * @returns false, as this is not Capacitor */ isCapacitor(): boolean { return false; } /** * Checks if running on Electron platform. * @returns false, as this is not Electron */ isElectron(): boolean { return false; } /** * Checks if running on PyWebView platform. * @returns false, as this is not PyWebView */ isPyWebView(): boolean { return false; } /** * Checks if running on web platform. * @returns true, as this is the web implementation */ isWeb(): boolean { return true; } /** * Handles deep link URLs in the web platform. * Deep links are handled through URL parameters in the web environment. * * @param _url - The deep link URL to handle (unused in web implementation) * @returns Promise that resolves immediately as web handles URLs naturally */ async handleDeepLink(_url: string): Promise { // Web platform can handle deep links through URL parameters return Promise.resolve(); } }