Buckets:
| // Register Context Menu on Installation | |
| chrome.runtime.onInstalled.addListener(() => { | |
| chrome.contextMenus.create({ | |
| id: "translate-manga-image", | |
| title: "Traducir imagen de manga", | |
| contexts: ["image"] | |
| }); | |
| console.log("Manga Translator context menu registered."); | |
| }); | |
| // Listener for Context Menu Clicks | |
| chrome.contextMenus.onClicked.addListener((info, tab) => { | |
| if (info.menuItemId === "translate-manga-image") { | |
| console.log("Triggering single image translation for:", info.srcUrl); | |
| // Retrieve configuration preferences | |
| chrome.storage.local.get({ | |
| targetLang: 'es', | |
| translator: 'google', | |
| ollamaModel: 'llama3', | |
| ollamaHost: 'http://localhost:8080', | |
| apiUrl: 'http://127.0.0.1:8001' | |
| }, (settings) => { | |
| // Send translation command to the active tab content script | |
| chrome.tabs.sendMessage(tab.id, { | |
| action: 'translate_single_image', | |
| imageUrl: info.srcUrl, | |
| ...settings | |
| }, (response) => { | |
| if (chrome.runtime.lastError) { | |
| console.error("Error sending message to content script:", chrome.runtime.lastError.message); | |
| } else { | |
| console.log("Single image translation request received by content script:", response); | |
| } | |
| }); | |
| }); | |
| } | |
| }); | |
| // Listener for runtime messages | |
| chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { | |
| if (request.action === 'fetch_image_base64') { | |
| fetchImageBase64(request.url) | |
| .then(base64 => sendResponse({ success: true, base64 })) | |
| .catch(err => sendResponse({ success: false, error: err.message })); | |
| return true; // Keep message channel open for async response | |
| } | |
| else if (request.action === 'fetch_ollama_models') { | |
| const safeApiUrl = request.apiUrl.replace('localhost', '127.0.0.1').replace(/\/$/, ''); | |
| fetch(`${safeApiUrl}/ollama/models`) | |
| .then(resp => { | |
| if (!resp.ok) throw new Error(`HTTP ${resp.status}`); | |
| return resp.json(); | |
| }) | |
| .then(data => sendResponse({ success: true, data })) | |
| .catch(err => sendResponse({ success: false, error: err.message })); | |
| return true; // Keep message channel open for async response | |
| } | |
| else if (request.action === 'translate_image') { | |
| // Force 127.0.0.1 to avoid IPv6 timeout issues with Docker Desktop on Windows | |
| const safeApiUrl = request.apiUrl.replace('localhost', '127.0.0.1').replace(/\/$/, ''); | |
| fetch(`${safeApiUrl}/translate/json`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(request.payload) | |
| }) | |
| .then(resp => { | |
| if (!resp.ok) throw new Error(`API returned HTTP ${resp.status}`); | |
| return resp.json(); | |
| }) | |
| .then(data => sendResponse({ success: true, data })) | |
| .catch(err => sendResponse({ success: false, error: err.message })); | |
| return true; // Keep message channel open for async response | |
| } | |
| }); | |
| // Helper to fetch and convert image to base64 bypassing CORS | |
| async function fetchImageBase64(url) { | |
| const response = await fetch(url); | |
| if (!response.ok) { | |
| throw new Error(`Failed to fetch image: HTTP ${response.status}`); | |
| } | |
| const arrayBuffer = await response.arrayBuffer(); | |
| const bytes = new Uint8Array(arrayBuffer); | |
| let binary = ''; | |
| const len = bytes.byteLength; | |
| for (let i = 0; i < len; i++) { | |
| binary += String.fromCharCode(bytes[i]); | |
| } | |
| return btoa(binary); | |
| } | |
Xet Storage Details
- Size:
- 3.44 kB
- Xet hash:
- 09a8bab40ffb7fca827ee0f2c5f50eeebb1b7f63f0159c37b8bc66120dc8871d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.