// Background service worker for Auto Cover Generator // 1. Native Click Behavior chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }); const openTabs = new Set(); // 2. Click Handler (Native behavior handles opening, we just manage content) // Note: chrome.action.onClicked is ignored when openPanelOnActionClick is true. // 3. Tab State Management // Monitor all tab changes to ensure correct panel is shown chrome.runtime.onInstalled.addListener(() => { initializeAllTabs(); }); chrome.runtime.onStartup.addListener(() => { initializeAllTabs(); }); async function initializeAllTabs() { const tabs = await chrome.tabs.query({}); for (const tab of tabs) { await checkAndSetSidePanel(tab.id); } } // 4. Tab Activation / Isolation Logic chrome.tabs.onActivated.addListener(async (activeInfo) => { const tabId = activeInfo.tabId; await checkAndSetSidePanel(tabId); }); chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { if (changeInfo.status === 'complete' || changeInfo.url) { await checkAndSetSidePanel(tabId); } }); async function checkAndSetSidePanel(tabId) { try { const tab = await chrome.tabs.get(tabId); if (!tab) return; // Tab doesn't exist // If we don't have permission to see the URL, it's definitely not Gemini (since we have host_permissions for Gemini). // So undefined tab.url means "Restricted/Other Site". const isGemini = tab.url && tab.url.startsWith('https://gemini.google.com/'); if (isGemini) { // Enable and set path for Gemini await chrome.sidePanel.setOptions({ tabId: tabId, path: 'sidepanel/panel.html', enabled: true }); } else { // Disable for all other sites - this CLOSES the panel if open // and ensures it stays closed when returning to the tab (until clicked manually) await chrome.sidePanel.setOptions({ tabId: tabId, enabled: false }); } } catch (e) { // Tab might be closed or we heavily failed access console.log('Cannot access tab or set panel', e); } } chrome.runtime.onInstalled.addListener(() => { chrome.sidePanel.setOptions({ enabled: false }); }); chrome.tabs.onRemoved.addListener((tabId) => { openTabs.delete(tabId); }); // 4. Message Routing chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // Forward messages to content script if (message.action === 'generateCover' || message.action === 'removeObjectFromImage') { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { if (tabs[0]) { chrome.tabs.sendMessage(tabs[0].id, message, (response) => { sendResponse(response); }); } }); return true; } if (message.action === 'downloadImage') { const url = message.url; const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `gemini_gen_${timestamp}.png`; chrome.downloads.download({ url: url, filename: filename, conflictAction: 'uniquify' }, (downloadId) => { if (chrome.runtime.lastError) { console.error('Download failed:', chrome.runtime.lastError); } else { console.log('Download started, ID:', downloadId); } }); return; } if (message.action === 'generationError') { chrome.runtime.sendMessage(message).catch(() => { }); } }); console.log('Auto Cover Generator: Background Service Ready');