99 lines · 3.1 KB · TypeScript
RawDownload
import * as NodeWorkerThreads from "node:worker_threads"; import type { WindowsForegroundFocusTarget } from "./WindowsForegroundFocusThread.ts";import type { Element } from "@crowecawcaw/xa11y"; type FocusRequest = {  readonly type: "prepare" | "focus";  readonly requestId: number;  readonly target: WindowsForegroundFocusTarget;}; function matchesTarget(  element: {    readonly name?: string | null;    readonly bounds: WindowsForegroundFocusTarget["bounds"] | null;  },  target: WindowsForegroundFocusTarget,): boolean {  if ((element.name ?? "").trim() !== target.title.trim()) return false;  if (!element.bounds) return false;  return [target.bounds, target.contentBounds].some((bounds) =>    (["x", "y", "width", "height"] as const).every(      (key) => Math.abs(element.bounds![key] - bounds[key]) <= 2,    ),  );} async function findTarget(  App: (typeof import("@crowecawcaw/xa11y"))["App"],  target: WindowsForegroundFocusTarget,): Promise<Element | undefined> {  const children = await App.byPid(target.processId, { timeout: 0 })    .then((app) => app.children())    .catch(() => []);  return (    children.find((candidate) => matchesTarget(candidate, target)) ??    (await App.list())      .filter((candidate) => candidate.pid === target.processId)      .map((candidate) => candidate.asElement())      .find((candidate) => matchesTarget(candidate, target))  );} const cachedElements = new Map<number, Element>(); async function prepareTarget(  App: (typeof import("@crowecawcaw/xa11y"))["App"],  target: WindowsForegroundFocusTarget,): Promise<boolean> {  const element = await findTarget(App, target);  if (!element) {    cachedElements.delete(target.windowId);    return false;  }  cachedElements.set(target.windowId, element);  return true;} async function focusTarget(  App: (typeof import("@crowecawcaw/xa11y"))["App"],  target: WindowsForegroundFocusTarget,): Promise<boolean> {  let element = cachedElements.get(target.windowId);  if (!element) {    element = await findTarget(App, target);    if (!element) return false;    cachedElements.set(target.windowId, element);  }  try {    await element.focus();    return true;  } catch {    cachedElements.delete(target.windowId);    return false;  }} async function start() {  const { App } = await import("@crowecawcaw/xa11y");  const parentPort = NodeWorkerThreads.parentPort;  if (!parentPort) return;  let work = Promise.resolve();  parentPort.on("message", (message: FocusRequest) => {    if (message.type !== "prepare" && message.type !== "focus") return;    work = work.then(async () => {      const focused = await (        message.type === "prepare"          ? prepareTarget(App, message.target)          : focusTarget(App, message.target)      ).catch(() => false);      // oxlint-disable-next-line unicorn/require-post-message-target-origin -- Node workers do not accept a target origin.      parentPort.postMessage({ type: "result", requestId: message.requestId, focused });    });  });  // oxlint-disable-next-line unicorn/require-post-message-target-origin -- Node workers do not accept a target origin.  parentPort.postMessage("ready");} void start();