1// @effect-diagnostics nodeBuiltinImport:off -- This macOS platform boundary spawns the native modifier-key poller with Node.
2
3import * as NodeChildProcess from "node:child_process";
4
5import type { SnapShotModifier } from "@t3tools/contracts";
6
7const MAC_MODIFIER_PAIR_DEVICE_MASKS: Record<SnapShotModifier, readonly [number, number]> = {
8 shift: [0x2, 0x4],
9 control: [0x1, 0x2000],
10 alt: [0x20, 0x40],
11 meta: [0x8, 0x10],
12};
13
14// The CoreGraphics query connects osascript to the window server, which registers it as a
15// foreground app attributed to T3 Code. Go background-only first so it never gets a Dock tile.
16const POLLER_SCRIPT = `
17ObjC.import("AppKit");
18$.NSApplication.sharedApplication.setActivationPolicy($.NSApplicationActivationPolicyProhibited);
19ObjC.import("CoreGraphics");
20ObjC.import("unistd");
21function run(argv) {
22 const left = Number(argv[0]);
23 const right = Number(argv[1]);
24 const both = left | right;
25 let active = false;
26 console.log("ready");
27 while ($.getppid() !== 1) {
28 const pressed = ($.CGEventSourceFlagsState(0) & both) === both;
29 if (pressed && !active) console.log("trigger");
30 active = pressed;
31 delay(0.05);
32 }
33 return "orphaned";
34}`;
35
36export function startMacModifierPairShortcutProcess(
37 modifier: SnapShotModifier,
38 onTrigger: () => void,
39 onFailure: (error: Error) => void,
40): Promise<() => void> {
41 const [left, right] = MAC_MODIFIER_PAIR_DEVICE_MASKS[modifier];
42 const poller = NodeChildProcess.spawn(
43 "/usr/bin/osascript",
44 ["-l", "JavaScript", "-e", POLLER_SCRIPT, String(left), String(right)],
45 { stdio: ["ignore", "ignore", "pipe"] },
46 );
47
48 return new Promise((resolve, reject) => {
49 let settled = false;
50 let stopped = false;
51 let buffered = "";
52 const stop = () => {
53 if (stopped) return;
54 stopped = true;
55 poller.kill();
56 };
57 const fail = (error: Error) => {
58 if (stopped) return;
59 if (settled) {
60 stop();
61 onFailure(error);
62 return;
63 }
64 settled = true;
65 stop();
66 reject(error);
67 };
68
69 poller.stderr.on("data", (chunk: Buffer) => {
70 buffered += chunk.toString();
71 const lines = buffered.split("\n");
72 buffered = lines.pop() ?? "";
73 for (const line of lines) {
74 const message = line.trim();
75 if (message === "ready" && !settled) {
76 settled = true;
77 resolve(stop);
78 continue;
79 }
80 if (message !== "trigger" || !settled || stopped) continue;
81 try {
82 onTrigger();
83 } catch {}
84 }
85 });
86 poller.once("error", (error) => {
87 fail(error);
88 });
89 poller.once("exit", (code) => {
90 fail(new Error(`Snapshot shortcut helper exited with code ${code}`));
91 });
92 });
93}