also looking at this
fix(shortcuts): make global hotkeys actually fire
#54
11 files
+2435
-417
| @@ -1119,6 +1119,92 @@ export interface CraftBridgeAPI { | ||
| 1119 | 1119 | * Screen-sharing and screen-recording detection (macOS) |
| 1120 | 1120 | */ |
| 1121 | 1121 | 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. */ | |
| 1134 | export 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. */ | |
| 1150 | export 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 | */ | |
| 1171 | export 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 | |
| 1122 | 1208 | } |
| 1123 | 1209 | |
| 1124 | 1210 | /** |