// TypeScript interface for window.chrome declare global { interface Window { chrome?: { storage?: { local?: { set: (items: object, callback?: () => void) => void; get: (keys: string[] | string, callback: (items: { [key: string]: string }) => void) => void; remove: (keys: string | string[], callback?: () => void) => void; }; }; runtime?: { lastError?: { message: string }; }; }; } } let isChromeStorageLocalAvailable: boolean | null = null; /** * Checks if chrome.storage.local is available and caches the result. */ export function checkChromeStorageLocalAvailable(): boolean { if (isChromeStorageLocalAvailable !== null) return isChromeStorageLocalAvailable; isChromeStorageLocalAvailable = typeof window !== 'undefined' && typeof window.chrome !== 'undefined' && typeof window.chrome.storage !== 'undefined' && typeof window.chrome.storage.local !== 'undefined'; return isChromeStorageLocalAvailable; } /** * Adds a new wallpaper to chrome.storage.local. * If the URL is fetchable, it will be stored as base64 and the name will be derived from the URL. * If the URL is not fetchable (e.g., CORS), it will be stored as a URL and the provided name will be used. * @param name Wallpaper name (string), used as a fallback. * @param url Wallpaper image URL (string) or base64 data URL. * @returns Promise The name under which the wallpaper was stored. * @throws Error if chrome.storage.local is unavailable or if a name is not provided for a non-fetchable URL. */ export async function addWallpaperToChromeStorageLocal(name: string, url: string): Promise { if (!checkChromeStorageLocalAvailable()) { throw new Error('chrome.storage.local is not available'); } if (url.startsWith('data:')) { // This is a base64 encoded image from a file upload. // The name is the file name. return new Promise((resolve, reject) => { if (window.chrome?.storage?.local) { window.chrome.storage.local.set({ [name]: url }, function () { if (window.chrome?.runtime?.lastError) { reject(new Error(window.chrome.runtime.lastError.message)); } else { resolve(); } }); } else { reject(new Error('chrome.storage.local is not available')); } }).then(() => name); } // This is a URL. Let's try to fetch it. try { const response = await fetch(url); if (!response.ok) throw new Error('Failed to fetch image'); const imageBlob = await response.blob(); const reader = new FileReader(); const base64 = await new Promise((resolve, reject) => { reader.onloadend = () => resolve(reader.result as string); reader.onerror = reject; reader.readAsDataURL(imageBlob); }); // If successful, use the filename from URL as the name. const finalName = url.substring(url.lastIndexOf('/') + 1).replace(/[?#].*$/, '') || name; return new Promise((resolve, reject) => { if (window.chrome?.storage?.local) { window.chrome.storage.local.set({ [finalName]: base64 }, function () { if (window.chrome?.runtime?.lastError) { reject(new Error(window.chrome.runtime.lastError.message)); } else { resolve(); } }); } else { reject(new Error('chrome.storage.local is not available')); } }).then(() => finalName); } catch (error) { // If fetch fails (e.g., CORS), store the URL directly with the user-provided name. console.warn('Could not fetch wallpaper, storing URL instead. Error:', error); if (!name) { throw new Error("A name for the wallpaper is required when the URL can't be accessed."); } return new Promise((resolve, reject) => { if (window.chrome?.storage?.local) { window.chrome.storage.local.set({ [name]: url }, function () { if (window.chrome?.runtime?.lastError) { reject(new Error(window.chrome.runtime.lastError.message)); } else { resolve(); } }); } else { reject(new Error('chrome.storage.local is not available')); } }).then(() => name); } } /** * Gets a specific wallpaper from chrome.storage.local by name. * @param name Wallpaper name (string) * @returns Promise (base64 string or null) * @throws Error if chrome.storage.local is unavailable */ export async function getWallpaperFromChromeStorageLocal(name: string): Promise { if (!checkChromeStorageLocalAvailable()) { throw new Error('chrome.storage.local is not available'); } return new Promise((resolve, reject) => { if (window.chrome?.storage?.local) { window.chrome.storage.local.get([name], function (result: { [key: string]: string }) { if (window.chrome?.runtime?.lastError) { reject(new Error(window.chrome.runtime.lastError.message)); } else { resolve(result[name] || null); } }); } else { reject(new Error('chrome.storage.local is not available')); } }); } /** * Removes a wallpaper from chrome.storage.local by name. * @param name Wallpaper name (string) * @returns Promise * @throws Error if chrome.storage.local is unavailable */ export async function removeWallpaperFromChromeStorageLocal(name: string): Promise { if (!checkChromeStorageLocalAvailable()) { throw new Error('chrome.storage.local is not available'); } return new Promise((resolve, reject) => { if (window.chrome?.storage?.local) { window.chrome.storage.local.remove(name, function () { if (window.chrome?.runtime?.lastError) { reject(new Error(window.chrome.runtime.lastError.message)); } else { resolve(); } }); } else { reject(new Error('chrome.storage.local is not available')); } }); }