ReviewOS

also looking at this

craft-native/craft

fix(shortcuts): make global hotkeys actually fire

#54
Merged glennmichael123 wants to merge fix/global-shortcuts-never-fire into main
11 files +2435 -417
packages/typescript/src/types.tsmodified+86-0
Changes to packages/typescript/src/types.ts
@@ -1119,6 +1119,92 @@ export interface CraftBridgeAPI {
11191119 * Screen-sharing and screen-recording detection (macOS)
11201120 */
11211121 screenSharing?: CraftScreenSharingAPI
1122
1123 /**
1124 * System-wide hotkeys (macOS).
1125 *
1126 * Absent in effect on Linux and Windows: the calls exist, and every
1127 * registration is refused, because Craft has no implementation there and a
1128 * shortcut that can never fire is worse than one that was never accepted.
1129 */
1130 shortcuts?: CraftGlobalShortcutsAPI
1131}
1132
1133/** A global hotkey, as `craft.shortcuts.list()` reports it. */
1134export interface GlobalShortcut {
1135 /** The id the app registered it under. */
1136 id: string
1137 /**
1138 * Craft's canonical spelling of the combination — `Cmd+Delete`, whatever
1139 * the app originally wrote — so it can be passed straight back to
1140 * `register()`.
1141 */
1142 accelerator: string
1143 /** The key alone, canonically named. */
1144 key: string
1145 /** False while `disable()` has the key released back to the system. */
1146 enabled: boolean
1147}
1148
1149/** Why a registration was refused. */
1150export interface GlobalShortcutError {
1151 /** The id from the payload that failed, or `''` if it had none. */
1152 id: string
1153 /** Craft's error code, e.g. `NATIVE_CALL_FAILED`, `INVALID_PARAMETER`. */
1154 code: string
1155 message: string
1156}
1157
1158/**
1159 * System-wide hotkeys: they fire whether or not the app is frontmost.
1160 *
1161 * Accelerators are `+`-separated and case-insensitive — `'Cmd+Shift+H'`. The
1162 * last component is the key, everything before it a modifier (`Cmd`/`Command`/
1163 * `Meta`, `Ctrl`/`Control`, `Alt`/`Option`, `Shift`, or `CmdOrCtrl` for the
1164 * platform's own). Keys are named by position on the keyboard, not by the
1165 * character they produce, so a binding survives a layout change.
1166 *
1167 * At least one of Command, Control or Option is required, except on the
1168 * function keys: a bare global hotkey on `H` would mean no application on the
1169 * system ever saw the user type an h again.
1170 */
1171export interface CraftGlobalShortcutsAPI {
1172 /**
1173 * Reserve a combination system-wide. Registering an `id` twice replaces the
1174 * first binding.
1175 *
1176 * The promise resolves once the message is posted, **not** once the key is
1177 * reserved — so a combination that belongs to another app or to the system
1178 * resolves here and reports on {@link CraftGlobalShortcutsAPI.onError}.
1179 */
1180 register: (id: string, accelerator: string) => Promise<void>
1181 /** Give the key back to the system and forget the shortcut. */
1182 unregister: (id: string) => Promise<void>
1183 /** The same, for every shortcut this app holds. */
1184 unregisterAll: () => Promise<void>
1185 /**
1186 * Stop firing *and release the key*, so other apps can use it again. The
1187 * shortcut stays listed. Holding a reservation while dropping the event
1188 * would make the combination dead in every other app for as long as Craft
1189 * ran.
1190 */
1191 disable: (id: string) => Promise<void>
1192 /**
1193 * Take the key back. Can fail if something else claimed it while it was
1194 * released, which reports on {@link CraftGlobalShortcutsAPI.onError}.
1195 */
1196 enable: (id: string) => Promise<void>
1197 /** Whether the id is known — enabled or not. */
1198 isRegistered: (id: string) => Promise<boolean>
1199 list: () => Promise<GlobalShortcut[]>
1200 /** Every press of a registered, enabled shortcut. Returns an unsubscribe. */
1201 on: (handler: (event: { id: string, accelerator: string }) => void) => () => void
1202 /**
1203 * Where every fire-and-forget call reports its failure — `register`,
1204 * `enable`, `disable`, `unregister`. (`isRegistered` and `list` are
1205 * requests and reject their own promise instead.) Returns an unsubscribe.
1206 */
1207 onError: (handler: (error: GlobalShortcutError) => void) => () => void
11221208}
11231209
11241210/**