diff --git a/manifest.json b/manifest.json index 1453dde..f2bf8fc 100644 --- a/manifest.json +++ b/manifest.json @@ -17,5 +17,8 @@ "run_at": "document_idle" } ], - "permissions": ["storage"] + "background": { + "service_worker": "dist/background.js" + }, + "permissions": ["storage", "tabs", "debugger"] } diff --git a/package.json b/package.json index af90dbe..96ebf52 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "description": "Tweaks for WoV Assassins Convention", "scripts": { - "build": "esbuild src/content.ts src/popup.ts --bundle --outdir=dist --platform=browser --target=chrome120", - "watch": "esbuild src/content.ts src/popup.ts --bundle --outdir=dist --platform=browser --target=chrome120 --watch", + "build": "esbuild src/content.ts src/popup.ts src/background.ts --bundle --outdir=dist --platform=browser --target=chrome120", + "watch": "esbuild src/content.ts src/popup.ts src/background.ts --bundle --outdir=dist --platform=browser --target=chrome120 --watch", "dev": "pnpm watch" }, "devDependencies": { diff --git a/src/background.ts b/src/background.ts new file mode 100644 index 0000000..9fdf9e2 --- /dev/null +++ b/src/background.ts @@ -0,0 +1,28 @@ +type SimulateClickMessage = { + type: "simulate-click"; + x: number; + y: number; +}; + +chrome.runtime.onMessage.addListener((message: SimulateClickMessage) => { + if (message.type !== "simulate-click") return; + + const { x, y } = message; + + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + if (!tabs[0]?.id) return; + const target = { tabId: tabs[0].id! }; + + chrome.debugger.attach(target, "1.2", () => { + if (chrome.runtime.lastError) return; + + chrome.debugger.sendCommand(target, "Input.dispatchMouseEvent", { type: "mouseMoved", x, y }, () => { + chrome.debugger.sendCommand(target, "Input.dispatchMouseEvent", { type: "mousePressed", button: "left", x, y, clickCount: 1 }, () => { + chrome.debugger.sendCommand(target, "Input.dispatchMouseEvent", { type: "mouseReleased", button: "left", x, y, clickCount: 1 }, () => { + chrome.debugger.detach(target); + }); + }); + }); + }); + }); +}); diff --git a/src/content.ts b/src/content.ts index 94885bb..3251807 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,14 +1,13 @@ import { State, STORAGE_KEY } from "./types"; -const KEY_BINDINGS: Record = { - // TODO -}; - let enabled = false; -const clickButton = (selector: string): void => { - const el = document.querySelector(selector); - el?.click(); +const findButtonForKey = (key: string): HTMLElement | null => { + if (key.length !== 1 || !/[a-zA-Z]/.test(key)) return null; + const letter = key.toUpperCase(); + const label = Array.from(document.querySelectorAll("div[dir=\"auto\"]")) + .find(el => el.textContent === letter); + return label?.closest("[tabindex=\"0\"]") ?? null; }; const onKeyDown = (event: KeyboardEvent): void => { @@ -21,10 +20,15 @@ const onKeyDown = (event: KeyboardEvent): void => { target.isContentEditable ) return; - const selector = KEY_BINDINGS[event.code]; - if (selector) { + const btn = findButtonForKey(event.key); + if (btn) { event.preventDefault(); - clickButton(selector); + const rect = btn.getBoundingClientRect(); + chrome.runtime.sendMessage({ + type: "simulate-click", + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2, + }); } }; @@ -33,7 +37,7 @@ const init = async (): Promise => { const state = result[STORAGE_KEY] as State | undefined; enabled = state?.enabled ?? true; - document.addEventListener("keydown", onKeyDown); + document.addEventListener("keydown", onKeyDown, { capture: true }); chrome.storage.onChanged.addListener((changes) => { if (STORAGE_KEY in changes) {