Spaces:
Sleeping
Sleeping
| // content.js β Injected into every page | |
| // Manages the screen selection overlay and sidebar panel | |
| let overlayActive = false; | |
| let sidebarFrame = null; | |
| // ββ Listen for messages from background / popup βββββββββββββββββββββββββββββββ | |
| chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { | |
| if (msg.type === "START_SELECTION") { | |
| if (!overlayActive) startSelection(); | |
| sendResponse({ ok: true }); | |
| return false; | |
| } | |
| if (msg.type === "SHOW_SIDEBAR") { | |
| showSidebar({}); | |
| return false; | |
| } | |
| if (msg.type === "SHOW_RESULT") { | |
| showSidebar(msg.data); | |
| return false; | |
| } | |
| }); | |
| // ββ Selection overlay βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function startSelection() { | |
| overlayActive = true; | |
| // Dim the page | |
| const overlay = document.createElement("div"); | |
| overlay.id = "glmocr-overlay"; | |
| // Crosshair hint | |
| const hint = document.createElement("div"); | |
| hint.id = "glmocr-hint"; | |
| hint.textContent = "Drag to select text region β Press Esc to cancel"; | |
| overlay.appendChild(hint); | |
| // Selection box | |
| const selBox = document.createElement("div"); | |
| selBox.id = "glmocr-selbox"; | |
| overlay.appendChild(selBox); | |
| document.body.appendChild(overlay); | |
| let startX = 0, startY = 0, isDragging = false; | |
| function onMouseDown(e) { | |
| if (e.button !== 0) return; | |
| isDragging = true; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| selBox.style.cssText = `left:${startX}px; top:${startY}px; width:0; height:0; display:block`; | |
| hint.style.opacity = "0"; | |
| e.preventDefault(); | |
| } | |
| function onMouseMove(e) { | |
| if (!isDragging) return; | |
| const x = Math.min(e.clientX, startX); | |
| const y = Math.min(e.clientY, startY); | |
| const w = Math.abs(e.clientX - startX); | |
| const h = Math.abs(e.clientY - startY); | |
| selBox.style.cssText = `left:${x}px; top:${y}px; width:${w}px; height:${h}px; display:block`; | |
| } | |
| function onMouseUp(e) { | |
| if (!isDragging) return; | |
| isDragging = false; | |
| const x = Math.min(e.clientX, startX); | |
| const y = Math.min(e.clientY, startY); | |
| const w = Math.abs(e.clientX - startX); | |
| const h = Math.abs(e.clientY - startY); | |
| cleanup(); | |
| if (w < 10 || h < 10) { | |
| showToast("Selection too small β try again."); | |
| return; | |
| } | |
| const dpr = window.devicePixelRatio || 1; | |
| const rect = { | |
| x: x + window.scrollX, | |
| y: y + window.scrollY, | |
| width: w, | |
| height: h, | |
| dpr, | |
| }; | |
| runOcr(rect); | |
| } | |
| function onKeyDown(e) { | |
| if (e.key === "Escape") { | |
| cleanup(); | |
| showToast("Cancelled."); | |
| } | |
| } | |
| function cleanup() { | |
| overlayActive = false; | |
| overlay.removeEventListener("mousedown", onMouseDown); | |
| overlay.removeEventListener("mousemove", onMouseMove); | |
| overlay.removeEventListener("mouseup", onMouseUp); | |
| document.removeEventListener("keydown", onKeyDown); | |
| overlay.remove(); | |
| } | |
| overlay.addEventListener("mousedown", onMouseDown); | |
| overlay.addEventListener("mousemove", onMouseMove); | |
| overlay.addEventListener("mouseup", onMouseUp); | |
| document.addEventListener("keydown", onKeyDown); | |
| } | |
| // ββ Send region to background for capture + OCR βββββββββββββββββββββββββββββββ | |
| function runOcr(rect) { | |
| // Show a loading sidebar immediately | |
| showSidebar({ loading: true }); | |
| chrome.runtime.sendMessage({ type: "CAPTURE_REGION", rect }, (response) => { | |
| if (chrome.runtime.lastError) { | |
| showSidebar({ error: chrome.runtime.lastError.message }); | |
| return; | |
| } | |
| if (response.success) { | |
| showSidebar(response); | |
| } else { | |
| showSidebar({ error: response.error }); | |
| } | |
| }); | |
| } | |
| // ββ Sidebar panel βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function showSidebar(data) { | |
| // Remove existing sidebar if any | |
| if (sidebarFrame) sidebarFrame.remove(); | |
| const frame = document.createElement("iframe"); | |
| frame.id = "glmocr-sidebar"; | |
| frame.src = chrome.runtime.getURL("sidebar.html"); | |
| document.body.appendChild(frame); | |
| sidebarFrame = frame; | |
| // Wait for iframe to load, then send data | |
| frame.onload = () => { | |
| frame.contentWindow.postMessage({ type: "SIDEBAR_DATA", data }, "*"); | |
| }; | |
| // Close button via message from sidebar | |
| window.addEventListener("message", (e) => { | |
| if (e.data?.type === "CLOSE_SIDEBAR") { | |
| frame.remove(); | |
| sidebarFrame = null; | |
| } | |
| if (e.data?.type === "START_NEW_SELECTION") { | |
| frame.remove(); | |
| sidebarFrame = null; | |
| startSelection(); | |
| } | |
| }); | |
| } | |
| // ββ Toast notification ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function showToast(msg) { | |
| const existing = document.getElementById("glmocr-toast"); | |
| if (existing) existing.remove(); | |
| const toast = document.createElement("div"); | |
| toast.id = "glmocr-toast"; | |
| toast.textContent = msg; | |
| document.body.appendChild(toast); | |
| setTimeout(() => toast?.remove(), 3000); | |
| } | |