116 lines
4.3 KiB
JavaScript
116 lines
4.3 KiB
JavaScript
|
|
// Region-select overlay. Injected into the target tab by main.js via
|
||
|
|
// wc.executeJavaScript(); the last expression is what executeJavaScript
|
||
|
|
// resolves with, so the whole file is an IIFE that returns a Promise for
|
||
|
|
// {x, y, w, h} — or null when the user cancels.
|
||
|
|
//
|
||
|
|
// Coordinates are CSS pixels relative to the viewport (not the document),
|
||
|
|
// which is exactly what WebContents.capturePage(rect) wants.
|
||
|
|
//
|
||
|
|
// Runs in the page's world, so it shares globals with the page. That's
|
||
|
|
// acceptable here: the overlay lives for a few seconds while the user
|
||
|
|
// drags, then removes itself and its listeners.
|
||
|
|
(() => {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
// If a previous overlay is still up (double-click on the panel button),
|
||
|
|
// tear it down before installing a fresh one.
|
||
|
|
const prev = document.getElementById("__theseus_screenshot_overlay__");
|
||
|
|
if (prev) prev.remove();
|
||
|
|
|
||
|
|
const overlay = document.createElement("div");
|
||
|
|
overlay.id = "__theseus_screenshot_overlay__";
|
||
|
|
overlay.style.cssText = [
|
||
|
|
"position:fixed", "inset:0", "z-index:2147483647",
|
||
|
|
"cursor:crosshair",
|
||
|
|
"background:rgba(11,14,20,0.35)",
|
||
|
|
"user-select:none", "-webkit-user-select:none",
|
||
|
|
].join(";");
|
||
|
|
|
||
|
|
const box = document.createElement("div");
|
||
|
|
box.style.cssText = [
|
||
|
|
"position:absolute", "left:0", "top:0", "width:0", "height:0",
|
||
|
|
"border:1px solid #d6ff3d",
|
||
|
|
"box-shadow:0 0 0 9999px rgba(11,14,20,0.35)",
|
||
|
|
"background:transparent",
|
||
|
|
"pointer-events:none",
|
||
|
|
].join(";");
|
||
|
|
box.hidden = true;
|
||
|
|
overlay.appendChild(box);
|
||
|
|
|
||
|
|
const hint = document.createElement("div");
|
||
|
|
hint.textContent = "Drag to select a region — Esc to cancel";
|
||
|
|
hint.style.cssText = [
|
||
|
|
"position:absolute", "top:12px", "left:50%", "transform:translateX(-50%)",
|
||
|
|
"padding:6px 12px", "border-radius:999px",
|
||
|
|
"background:rgba(11,14,20,0.92)", "color:#e7eaf1",
|
||
|
|
"font:12px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif",
|
||
|
|
"border:1px solid rgba(214,255,61,0.30)", "pointer-events:none",
|
||
|
|
].join(";");
|
||
|
|
overlay.appendChild(hint);
|
||
|
|
|
||
|
|
document.documentElement.appendChild(overlay);
|
||
|
|
|
||
|
|
let startX = 0, startY = 0, dragging = false;
|
||
|
|
let curX = 0, curY = 0;
|
||
|
|
|
||
|
|
const dpr = window.devicePixelRatio || 1;
|
||
|
|
|
||
|
|
function updateBox(x, y, w, h) {
|
||
|
|
box.style.left = x + "px";
|
||
|
|
box.style.top = y + "px";
|
||
|
|
box.style.width = w + "px";
|
||
|
|
box.style.height = h + "px";
|
||
|
|
box.hidden = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
function finish(result) {
|
||
|
|
cleanup();
|
||
|
|
resolve(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
function cleanup() {
|
||
|
|
overlay.removeEventListener("mousedown", onDown, true);
|
||
|
|
window.removeEventListener("mousemove", onMove, true);
|
||
|
|
window.removeEventListener("mouseup", onUp, true);
|
||
|
|
window.removeEventListener("keydown", onKey, true);
|
||
|
|
overlay.removeEventListener("contextmenu", onCtx, true);
|
||
|
|
try { overlay.remove(); } catch {}
|
||
|
|
}
|
||
|
|
|
||
|
|
function onDown(e) {
|
||
|
|
if (e.button !== 0) return;
|
||
|
|
e.preventDefault(); e.stopPropagation();
|
||
|
|
dragging = true;
|
||
|
|
startX = e.clientX; startY = e.clientY;
|
||
|
|
curX = startX; curY = startY;
|
||
|
|
updateBox(startX, startY, 0, 0);
|
||
|
|
}
|
||
|
|
function onMove(e) {
|
||
|
|
if (!dragging) return;
|
||
|
|
curX = e.clientX; curY = e.clientY;
|
||
|
|
const x = Math.min(startX, curX), y = Math.min(startY, curY);
|
||
|
|
const w = Math.abs(curX - startX), h = Math.abs(curY - startY);
|
||
|
|
updateBox(x, y, w, h);
|
||
|
|
}
|
||
|
|
function onUp(e) {
|
||
|
|
if (!dragging) return;
|
||
|
|
dragging = false;
|
||
|
|
const x = Math.min(startX, curX), y = Math.min(startY, curY);
|
||
|
|
const w = Math.abs(curX - startX), h = Math.abs(curY - startY);
|
||
|
|
// A click-with-no-drag is treated as cancel — capturing a 0px region
|
||
|
|
// isn't useful, and it lets the user bail without hunting for Esc.
|
||
|
|
if (w < 4 || h < 4) { finish(null); return; }
|
||
|
|
finish({ x, y, w, h, dpr });
|
||
|
|
}
|
||
|
|
function onKey(e) {
|
||
|
|
if (e.key === "Escape") { e.preventDefault(); finish(null); }
|
||
|
|
}
|
||
|
|
function onCtx(e) { e.preventDefault(); finish(null); }
|
||
|
|
|
||
|
|
overlay.addEventListener("mousedown", onDown, true);
|
||
|
|
window.addEventListener("mousemove", onMove, true);
|
||
|
|
window.addEventListener("mouseup", onUp, true);
|
||
|
|
window.addEventListener("keydown", onKey, true);
|
||
|
|
overlay.addEventListener("contextmenu", onCtx, true);
|
||
|
|
});
|
||
|
|
})()
|