1import { useEffect, useState } from "react";
2
3import { isMacPlatform } from "../lib/utils";
4
5// A released hold hint lingers for the original hold duration. Double-press
6// hints disappear as soon as their acceptance window closes.
7const HOLD_HINT_LINGER_MS = 1200;
8
9/**
10 * The desktop main process intercepts the quit accelerator and pushes
11 * press/release states while it waits for a hold or second press.
12 */
13export function QuitHoldOverlay() {
14 const [visibleMode, setVisibleMode] = useState<"hold" | "double-click" | null>(null);
15
16 useEffect(() => {
17 const subscribe = window.desktopBridge?.onQuitShortcut;
18 if (!subscribe) return;
19 let hideTimer: number | undefined;
20 let pressedMode: "hold" | "double-click" = "hold";
21 const unsubscribe = subscribe((hint) => {
22 window.clearTimeout(hideTimer);
23 if (hint.state === "down") {
24 pressedMode = hint.mode;
25 setVisibleMode(hint.mode);
26 return;
27 }
28 if (pressedMode === "double-click") {
29 setVisibleMode(null);
30 return;
31 }
32 hideTimer = window.setTimeout(() => setVisibleMode(null), HOLD_HINT_LINGER_MS);
33 });
34 return () => {
35 window.clearTimeout(hideTimer);
36 unsubscribe();
37 };
38 }, []);
39
40 if (!visibleMode) return null;
41 const shortcut = isMacPlatform(navigator.platform) ? "⌘Q" : "Ctrl+Q";
42 const message =
43 visibleMode === "hold"
44 ? `Hold ${shortcut} or press twice to quit`
45 : `Press ${shortcut} again to quit`;
46 return (
47 <div
48 role="status"
49 className="pointer-events-none fixed inset-x-0 top-[22%] z-100 flex justify-center"
50 >
51 <div className="rounded-full bg-foreground/95 px-8 py-4 text-2xl font-bold text-background shadow-xl">
52 {message}
53 </div>
54 </div>
55 );
56}