@witchcraft/spellcraft 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +579 -0
  2. package/dist/core/EmulatedEvent.d.ts +10 -0
  3. package/dist/core/EmulatedEvent.js +19 -0
  4. package/dist/core/Emulator.d.ts +74 -0
  5. package/dist/core/Emulator.js +153 -0
  6. package/dist/core/ShortcutManagerManager.d.ts +103 -0
  7. package/dist/core/ShortcutManagerManager.js +267 -0
  8. package/dist/core/addCommand.d.ts +7 -0
  9. package/dist/core/addCommand.js +7 -0
  10. package/dist/core/addKey.d.ts +7 -0
  11. package/dist/core/addKey.js +7 -0
  12. package/dist/core/addShortcut.d.ts +7 -0
  13. package/dist/core/addShortcut.js +7 -0
  14. package/dist/core/attach.d.ts +10 -0
  15. package/dist/core/attach.js +13 -0
  16. package/dist/core/createCommand.d.ts +4 -0
  17. package/dist/core/createCommand.js +11 -0
  18. package/dist/core/createCommands.d.ts +11 -0
  19. package/dist/core/createCommands.js +20 -0
  20. package/dist/core/createCondition.d.ts +7 -0
  21. package/dist/core/createCondition.js +11 -0
  22. package/dist/core/createContext.d.ts +2 -0
  23. package/dist/core/createContext.js +8 -0
  24. package/dist/core/createKey.d.ts +10 -0
  25. package/dist/core/createKey.js +35 -0
  26. package/dist/core/createKeys.d.ts +5 -0
  27. package/dist/core/createKeys.js +36 -0
  28. package/dist/core/createManager.d.ts +81 -0
  29. package/dist/core/createManager.js +83 -0
  30. package/dist/core/createManagerEventListeners.d.ts +4 -0
  31. package/dist/core/createManagerEventListeners.js +130 -0
  32. package/dist/core/createManagerOptions.d.ts +8 -0
  33. package/dist/core/createManagerOptions.js +17 -0
  34. package/dist/core/createShortcut.d.ts +8 -0
  35. package/dist/core/createShortcut.js +24 -0
  36. package/dist/core/createShortcuts.d.ts +13 -0
  37. package/dist/core/createShortcuts.js +20 -0
  38. package/dist/core/detach.d.ts +10 -0
  39. package/dist/core/detach.js +6 -0
  40. package/dist/core/index.d.ts +32 -0
  41. package/dist/core/index.js +32 -0
  42. package/dist/core/removeCommand.d.ts +7 -0
  43. package/dist/core/removeCommand.js +7 -0
  44. package/dist/core/removeKey.d.ts +7 -0
  45. package/dist/core/removeKey.js +7 -0
  46. package/dist/core/removeShortcut.d.ts +7 -0
  47. package/dist/core/removeShortcut.js +7 -0
  48. package/dist/core/setCommandProp.d.ts +17 -0
  49. package/dist/core/setCommandProp.js +96 -0
  50. package/dist/core/setCommandsProp.d.ts +15 -0
  51. package/dist/core/setCommandsProp.js +94 -0
  52. package/dist/core/setKeyProp.d.ts +16 -0
  53. package/dist/core/setKeyProp.js +47 -0
  54. package/dist/core/setKeysProp.d.ts +10 -0
  55. package/dist/core/setKeysProp.js +166 -0
  56. package/dist/core/setManagerProp.d.ts +34 -0
  57. package/dist/core/setManagerProp.js +54 -0
  58. package/dist/core/setShortcutProp.d.ts +9 -0
  59. package/dist/core/setShortcutProp.js +50 -0
  60. package/dist/core/setShortcutsProp.d.ts +12 -0
  61. package/dist/core/setShortcutsProp.js +93 -0
  62. package/dist/defaults/KeysSorter.d.ts +35 -0
  63. package/dist/defaults/KeysSorter.js +20 -0
  64. package/dist/defaults/Stringifier.d.ts +66 -0
  65. package/dist/defaults/Stringifier.js +119 -0
  66. package/dist/defaults/defaultConditionEquals.d.ts +19 -0
  67. package/dist/defaults/defaultConditionEquals.js +5 -0
  68. package/dist/defaults/defaultManagerCallback.d.ts +7 -0
  69. package/dist/defaults/defaultManagerCallback.js +5 -0
  70. package/dist/helpers/KnownError.d.ts +8 -0
  71. package/dist/helpers/KnownError.js +3 -0
  72. package/dist/helpers/calculateAndSetPositionAndWidth.d.ts +13 -0
  73. package/dist/helpers/calculateAndSetPositionAndWidth.js +18 -0
  74. package/dist/helpers/calculateLayoutSize.d.ts +9 -0
  75. package/dist/helpers/calculateLayoutSize.js +13 -0
  76. package/dist/helpers/doesShortcutConflict.d.ts +18 -0
  77. package/dist/helpers/doesShortcutConflict.js +42 -0
  78. package/dist/helpers/equalsCommand.d.ts +7 -0
  79. package/dist/helpers/equalsCommand.js +4 -0
  80. package/dist/helpers/equalsContext.d.ts +7 -0
  81. package/dist/helpers/equalsContext.js +19 -0
  82. package/dist/helpers/equalsShortcut.d.ts +9 -0
  83. package/dist/helpers/equalsShortcut.js +7 -0
  84. package/dist/helpers/forceClear.d.ts +12 -0
  85. package/dist/helpers/forceClear.js +16 -0
  86. package/dist/helpers/forceUpdateNativeKeysState.d.ts +5 -0
  87. package/dist/helpers/forceUpdateNativeKeysState.js +4 -0
  88. package/dist/helpers/generateKeyShortcutMap.d.ts +23 -0
  89. package/dist/helpers/generateKeyShortcutMap.js +64 -0
  90. package/dist/helpers/getKeyFromEventCode.d.ts +15 -0
  91. package/dist/helpers/getKeyFromEventCode.js +36 -0
  92. package/dist/helpers/getKeyFromIdOrVariant.d.ts +4 -0
  93. package/dist/helpers/getKeyFromIdOrVariant.js +26 -0
  94. package/dist/helpers/getKeyboardLayoutMap.d.ts +5 -0
  95. package/dist/helpers/getKeyboardLayoutMap.js +7 -0
  96. package/dist/helpers/getLabel.d.ts +9 -0
  97. package/dist/helpers/getLabel.js +6 -0
  98. package/dist/helpers/getTriggerableShortcut.d.ts +6 -0
  99. package/dist/helpers/getTriggerableShortcut.js +25 -0
  100. package/dist/helpers/index.d.ts +28 -0
  101. package/dist/helpers/index.js +28 -0
  102. package/dist/helpers/isValidManager.d.ts +3 -0
  103. package/dist/helpers/isValidManager.js +20 -0
  104. package/dist/helpers/isValidShortcut.d.ts +3 -0
  105. package/dist/helpers/isValidShortcut.js +10 -0
  106. package/dist/helpers/labelWithEvent.d.ts +13 -0
  107. package/dist/helpers/labelWithEvent.js +20 -0
  108. package/dist/helpers/labelWithKeyboardMap.d.ts +15 -0
  109. package/dist/helpers/labelWithKeyboardMap.js +17 -0
  110. package/dist/helpers/managerToStorableClone.d.ts +12 -0
  111. package/dist/helpers/managerToStorableClone.js +13 -0
  112. package/dist/helpers/onKeyboardLayoutChange.d.ts +10 -0
  113. package/dist/helpers/onKeyboardLayoutChange.js +7 -0
  114. package/dist/helpers/safeSetManagerChain.d.ts +20 -0
  115. package/dist/helpers/safeSetManagerChain.js +37 -0
  116. package/dist/helpers/shortcutCanExecuteIn.d.ts +4 -0
  117. package/dist/helpers/shortcutCanExecuteIn.js +9 -0
  118. package/dist/helpers/shortcutIsTriggerableBy.d.ts +2 -0
  119. package/dist/helpers/shortcutIsTriggerableBy.js +5 -0
  120. package/dist/helpers/shortcutSwapChords.d.ts +75 -0
  121. package/dist/helpers/shortcutSwapChords.js +118 -0
  122. package/dist/helpers/virtualPress.d.ts +13 -0
  123. package/dist/helpers/virtualPress.js +15 -0
  124. package/dist/helpers/virtualRelease.d.ts +5 -0
  125. package/dist/helpers/virtualRelease.js +15 -0
  126. package/dist/helpers/virtualToggle.d.ts +7 -0
  127. package/dist/helpers/virtualToggle.js +16 -0
  128. package/dist/internal/addToChain.d.ts +3 -0
  129. package/dist/internal/addToChain.js +27 -0
  130. package/dist/internal/areValidKeys.d.ts +7 -0
  131. package/dist/internal/areValidKeys.js +28 -0
  132. package/dist/internal/areValidVariants.d.ts +4 -0
  133. package/dist/internal/areValidVariants.js +45 -0
  134. package/dist/internal/checkTrigger.d.ts +2 -0
  135. package/dist/internal/checkTrigger.js +47 -0
  136. package/dist/internal/checkUntrigger.d.ts +2 -0
  137. package/dist/internal/checkUntrigger.js +18 -0
  138. package/dist/internal/cloneLastChord.d.ts +4 -0
  139. package/dist/internal/cloneLastChord.js +5 -0
  140. package/dist/internal/containsPossibleToggleChords.d.ts +13 -0
  141. package/dist/internal/containsPossibleToggleChords.js +65 -0
  142. package/dist/internal/errorTextAdd.d.ts +1 -0
  143. package/dist/internal/errorTextAdd.js +11 -0
  144. package/dist/internal/errorTextInUse.d.ts +1 -0
  145. package/dist/internal/errorTextInUse.js +8 -0
  146. package/dist/internal/errorTextRemove.d.ts +1 -0
  147. package/dist/internal/errorTextRemove.js +8 -0
  148. package/dist/internal/getModifierState.d.ts +2 -0
  149. package/dist/internal/getModifierState.js +7 -0
  150. package/dist/internal/getPressedKeys.d.ts +7 -0
  151. package/dist/internal/getPressedKeys.js +18 -0
  152. package/dist/internal/getPressedModifierKeys.d.ts +2 -0
  153. package/dist/internal/getPressedModifierKeys.js +10 -0
  154. package/dist/internal/getPressedNonModifierKeys.d.ts +7 -0
  155. package/dist/internal/getPressedNonModifierKeys.js +11 -0
  156. package/dist/internal/inChain.d.ts +5 -0
  157. package/dist/internal/inChain.js +14 -0
  158. package/dist/internal/isValidChain.d.ts +10 -0
  159. package/dist/internal/isValidChain.js +22 -0
  160. package/dist/internal/isValidChord.d.ts +11 -0
  161. package/dist/internal/isValidChord.js +59 -0
  162. package/dist/internal/isValidCommand.d.ts +12 -0
  163. package/dist/internal/isValidCommand.js +20 -0
  164. package/dist/internal/keyOrder.d.ts +6 -0
  165. package/dist/internal/keyOrder.js +14 -0
  166. package/dist/internal/removeFromChain.d.ts +3 -0
  167. package/dist/internal/removeFromChain.js +32 -0
  168. package/dist/internal/safeSetEmulatedToggleState.d.ts +4 -0
  169. package/dist/internal/safeSetEmulatedToggleState.js +13 -0
  170. package/dist/internal/setKeysState.d.ts +8 -0
  171. package/dist/internal/setKeysState.js +42 -0
  172. package/dist/internal/updateNativeKeysState.d.ts +14 -0
  173. package/dist/internal/updateNativeKeysState.js +58 -0
  174. package/dist/layouts/createLayout.d.ts +25 -0
  175. package/dist/layouts/createLayout.js +221 -0
  176. package/dist/module.d.mts +13 -0
  177. package/dist/module.json +9 -0
  178. package/dist/module.mjs +40 -0
  179. package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.d.ts +9 -0
  180. package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.js +19 -0
  181. package/dist/runtime/composables/usePointerCoords.d.ts +32 -0
  182. package/dist/runtime/composables/usePointerCoords.js +17 -0
  183. package/dist/runtime/composables/useShortcutManagerContextCount.d.ts +14 -0
  184. package/dist/runtime/composables/useShortcutManagerContextCount.js +61 -0
  185. package/dist/runtime/composables/useShortcutManagerKeysLayout.d.ts +18 -0
  186. package/dist/runtime/composables/useShortcutManagerKeysLayout.js +22 -0
  187. package/dist/runtime/composables/useShortcutManagerVirtualPress.d.ts +6 -0
  188. package/dist/runtime/composables/useShortcutManagerVirtualPress.js +24 -0
  189. package/dist/runtime/types.d.ts +10 -0
  190. package/dist/runtime/types.js +1 -0
  191. package/dist/runtime/utils/shortcutToId.d.ts +5 -0
  192. package/dist/runtime/utils/shortcutToId.js +6 -0
  193. package/dist/types/commands.d.ts +113 -0
  194. package/dist/types/commands.js +0 -0
  195. package/dist/types/condition.d.ts +29 -0
  196. package/dist/types/condition.js +0 -0
  197. package/dist/types/context.d.ts +18 -0
  198. package/dist/types/context.js +0 -0
  199. package/dist/types/enums.d.ts +186 -0
  200. package/dist/types/enums.js +70 -0
  201. package/dist/types/general.d.ts +92 -0
  202. package/dist/types/general.js +0 -0
  203. package/dist/types/index.d.ts +8 -0
  204. package/dist/types/index.js +8 -0
  205. package/dist/types/keys.d.ts +332 -0
  206. package/dist/types/keys.js +0 -0
  207. package/dist/types/manager.d.ts +249 -0
  208. package/dist/types/manager.js +0 -0
  209. package/dist/types/plugins.d.ts +1 -0
  210. package/dist/types/plugins.js +0 -0
  211. package/dist/types/shortcuts.d.ts +144 -0
  212. package/dist/types/shortcuts.js +0 -0
  213. package/dist/types/utils.d.ts +1 -0
  214. package/dist/types/utils.js +0 -0
  215. package/dist/types.d.mts +3 -0
  216. package/dist/utils/chainContainsSubset.d.ts +27 -0
  217. package/dist/utils/chainContainsSubset.js +45 -0
  218. package/dist/utils/cloneChain.d.ts +2 -0
  219. package/dist/utils/cloneChain.js +11 -0
  220. package/dist/utils/cloneKey.d.ts +3 -0
  221. package/dist/utils/cloneKey.js +26 -0
  222. package/dist/utils/containsKey.d.ts +7 -0
  223. package/dist/utils/containsKey.js +4 -0
  224. package/dist/utils/dedupeKeys.d.ts +9 -0
  225. package/dist/utils/dedupeKeys.js +9 -0
  226. package/dist/utils/equalsKey.d.ts +12 -0
  227. package/dist/utils/equalsKey.js +13 -0
  228. package/dist/utils/equalsKeys.d.ts +24 -0
  229. package/dist/utils/equalsKeys.js +15 -0
  230. package/dist/utils/index.d.ts +14 -0
  231. package/dist/utils/index.js +14 -0
  232. package/dist/utils/isAnyKey.d.ts +5 -0
  233. package/dist/utils/isAnyKey.js +5 -0
  234. package/dist/utils/isMouseKey.d.ts +5 -0
  235. package/dist/utils/isMouseKey.js +20 -0
  236. package/dist/utils/isNormalKey.d.ts +5 -0
  237. package/dist/utils/isNormalKey.js +5 -0
  238. package/dist/utils/isTriggerKey.d.ts +5 -0
  239. package/dist/utils/isTriggerKey.js +3 -0
  240. package/dist/utils/isWheelKey.d.ts +5 -0
  241. package/dist/utils/isWheelKey.js +11 -0
  242. package/dist/utils/mapKeys.d.ts +8 -0
  243. package/dist/utils/mapKeys.js +5 -0
  244. package/dist/utils/removeKeys.d.ts +7 -0
  245. package/dist/utils/removeKeys.js +4 -0
  246. package/package.json +156 -0
  247. package/src/core/EmulatedEvent.ts +29 -0
  248. package/src/core/Emulator.ts +185 -0
  249. package/src/core/ShortcutManagerManager.ts +380 -0
  250. package/src/core/addCommand.ts +24 -0
  251. package/src/core/addKey.ts +25 -0
  252. package/src/core/addShortcut.ts +24 -0
  253. package/src/core/attach.ts +27 -0
  254. package/src/core/createCommand.ts +24 -0
  255. package/src/core/createCommands.ts +45 -0
  256. package/src/core/createCondition.ts +21 -0
  257. package/src/core/createContext.ts +14 -0
  258. package/src/core/createKey.ts +59 -0
  259. package/src/core/createKeys.ts +68 -0
  260. package/src/core/createManager.ts +209 -0
  261. package/src/core/createManagerEventListeners.ts +139 -0
  262. package/src/core/createManagerOptions.ts +29 -0
  263. package/src/core/createShortcut.ts +49 -0
  264. package/src/core/createShortcuts.ts +51 -0
  265. package/src/core/detach.ts +21 -0
  266. package/src/core/index.ts +35 -0
  267. package/src/core/removeCommand.ts +25 -0
  268. package/src/core/removeKey.ts +25 -0
  269. package/src/core/removeShortcut.ts +24 -0
  270. package/src/core/setCommandProp.ts +140 -0
  271. package/src/core/setCommandsProp.ts +128 -0
  272. package/src/core/setKeyProp.ts +80 -0
  273. package/src/core/setKeysProp.ts +205 -0
  274. package/src/core/setManagerProp.ts +111 -0
  275. package/src/core/setShortcutProp.ts +89 -0
  276. package/src/core/setShortcutsProp.ts +124 -0
  277. package/src/defaults/KeysSorter.ts +55 -0
  278. package/src/defaults/Stringifier.ts +234 -0
  279. package/src/defaults/defaultConditionEquals.ts +29 -0
  280. package/src/defaults/defaultManagerCallback.ts +14 -0
  281. package/src/helpers/KnownError.ts +13 -0
  282. package/src/helpers/calculateAndSetPositionAndWidth.ts +30 -0
  283. package/src/helpers/calculateLayoutSize.ts +22 -0
  284. package/src/helpers/doesShortcutConflict.ts +72 -0
  285. package/src/helpers/equalsCommand.ts +18 -0
  286. package/src/helpers/equalsContext.ts +29 -0
  287. package/src/helpers/equalsShortcut.ts +34 -0
  288. package/src/helpers/forceClear.ts +31 -0
  289. package/src/helpers/forceUpdateNativeKeysState.ts +9 -0
  290. package/src/helpers/generateKeyShortcutMap.ts +113 -0
  291. package/src/helpers/getKeyFromEventCode.ts +67 -0
  292. package/src/helpers/getKeyFromIdOrVariant.ts +33 -0
  293. package/src/helpers/getKeyboardLayoutMap.ts +13 -0
  294. package/src/helpers/getLabel.ts +15 -0
  295. package/src/helpers/getTriggerableShortcut.ts +37 -0
  296. package/src/helpers/index.ts +30 -0
  297. package/src/helpers/isValidManager.ts +29 -0
  298. package/src/helpers/isValidShortcut.ts +20 -0
  299. package/src/helpers/labelWithEvent.ts +41 -0
  300. package/src/helpers/labelWithKeyboardMap.ts +37 -0
  301. package/src/helpers/managerToStorableClone.ts +26 -0
  302. package/src/helpers/onKeyboardLayoutChange.ts +17 -0
  303. package/src/helpers/safeSetManagerChain.ts +66 -0
  304. package/src/helpers/shortcutCanExecuteIn.ts +24 -0
  305. package/src/helpers/shortcutIsTriggerableBy.ts +15 -0
  306. package/src/helpers/shortcutSwapChords.ts +240 -0
  307. package/src/helpers/virtualPress.ts +34 -0
  308. package/src/helpers/virtualRelease.ts +25 -0
  309. package/src/helpers/virtualToggle.ts +28 -0
  310. package/src/internal/addToChain.ts +40 -0
  311. package/src/internal/areValidKeys.ts +40 -0
  312. package/src/internal/areValidVariants.ts +59 -0
  313. package/src/internal/checkTrigger.ts +55 -0
  314. package/src/internal/checkUntrigger.ts +26 -0
  315. package/src/internal/cloneLastChord.ts +10 -0
  316. package/src/internal/containsPossibleToggleChords.ts +91 -0
  317. package/src/internal/errorTextAdd.ts +18 -0
  318. package/src/internal/errorTextInUse.ts +10 -0
  319. package/src/internal/errorTextRemove.ts +10 -0
  320. package/src/internal/getModifierState.ts +15 -0
  321. package/src/internal/getPressedKeys.ts +26 -0
  322. package/src/internal/getPressedModifierKeys.ts +14 -0
  323. package/src/internal/getPressedNonModifierKeys.ts +19 -0
  324. package/src/internal/inChain.ts +25 -0
  325. package/src/internal/isValidChain.ts +42 -0
  326. package/src/internal/isValidChord.ts +87 -0
  327. package/src/internal/isValidCommand.ts +35 -0
  328. package/src/internal/keyOrder.ts +24 -0
  329. package/src/internal/removeFromChain.ts +46 -0
  330. package/src/internal/safeSetEmulatedToggleState.ts +23 -0
  331. package/src/internal/setKeysState.ts +71 -0
  332. package/src/internal/updateNativeKeysState.ts +84 -0
  333. package/src/layouts/createLayout.ts +328 -0
  334. package/src/module.ts +62 -0
  335. package/src/runtime/composables/useLabeledByKeyboardLayoutMap.ts +28 -0
  336. package/src/runtime/composables/usePointerCoords.ts +40 -0
  337. package/src/runtime/composables/useShortcutManagerContextCount.ts +81 -0
  338. package/src/runtime/composables/useShortcutManagerKeysLayout.ts +42 -0
  339. package/src/runtime/composables/useShortcutManagerVirtualPress.ts +30 -0
  340. package/src/runtime/types.ts +10 -0
  341. package/src/runtime/utils/shortcutToId.ts +14 -0
  342. package/src/types/commands.ts +148 -0
  343. package/src/types/condition.ts +35 -0
  344. package/src/types/context.ts +19 -0
  345. package/src/types/enums.ts +236 -0
  346. package/src/types/general.ts +117 -0
  347. package/src/types/index.ts +10 -0
  348. package/src/types/keys.ts +385 -0
  349. package/src/types/manager.ts +374 -0
  350. package/src/types/plugins.ts +32 -0
  351. package/src/types/shortcuts.ts +204 -0
  352. package/src/types/utils.ts +40 -0
  353. package/src/utils/chainContainsSubset.ts +97 -0
  354. package/src/utils/cloneChain.ts +13 -0
  355. package/src/utils/cloneKey.ts +33 -0
  356. package/src/utils/containsKey.ts +17 -0
  357. package/src/utils/dedupeKeys.ts +23 -0
  358. package/src/utils/equalsKey.ts +32 -0
  359. package/src/utils/equalsKeys.ts +50 -0
  360. package/src/utils/index.ts +16 -0
  361. package/src/utils/isAnyKey.ts +12 -0
  362. package/src/utils/isMouseKey.ts +27 -0
  363. package/src/utils/isNormalKey.ts +15 -0
  364. package/src/utils/isTriggerKey.ts +7 -0
  365. package/src/utils/isWheelKey.ts +18 -0
  366. package/src/utils/mapKeys.ts +21 -0
  367. package/src/utils/removeKeys.ts +16 -0
@@ -0,0 +1,26 @@
1
+ import { setManagerProp } from "../core/setManagerProp.js"
2
+ import type { AnyInputEvent, Manager, TriggerableShortcut } from "../types/index.js"
3
+
4
+
5
+ export function checkUntrigger(
6
+ manager: Manager,
7
+ e?: AnyInputEvent
8
+ ): void {
9
+ if (manager.state.untrigger) {
10
+ const untrigger: TriggerableShortcut = manager.state.untrigger
11
+ // MUST be set to false first or we can get into an infinite loop if the user uses `set(chain)` from inside the command execute function
12
+ setManagerProp(manager, "state.untrigger", false)
13
+ setManagerProp(manager, "state.nextIsChord", false)
14
+ if (!manager.options.enableShortcuts) return
15
+ const command = manager.commands.entries[untrigger.command]
16
+ command.execute?.({
17
+ isKeydown: false,
18
+ command,
19
+ shortcut: untrigger,
20
+ event: e,
21
+ manager,
22
+ context: manager.context
23
+ })
24
+ }
25
+ }
26
+
@@ -0,0 +1,10 @@
1
+ import { last } from "@alanscodelog/utils/last"
2
+
3
+ /**
4
+ * A copy of the last chord in the chain if it exists.
5
+ */
6
+ export function cloneLastChord(chain: string[][]): string[] | undefined {
7
+ const lastC = last(chain)
8
+ return lastC ? [...lastC] : undefined
9
+ }
10
+
@@ -0,0 +1,91 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
3
+
4
+ import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
5
+ import { KnownError } from "../helpers/KnownError.js"
6
+ import { type Manager, type PickManager, SHORTCUT_ERROR } from "../types/index.js"
7
+
8
+ /**
9
+ * If you have toggle keys in chords, their are "chains" of the same toggle key that would be physically impossible. For example: `toggle(on) toggle(on)`.
10
+ *
11
+ * This guards against that.
12
+ *
13
+ * This does NOT guard against duplicates and errors in the same chord, for that see {@link isValidChord}.
14
+ *
15
+ * @internal
16
+ */
17
+ export function containsPossibleToggleChords(
18
+ chain: string[][],
19
+ manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier">
20
+ ): Result<true, KnownError<typeof SHORTCUT_ERROR.IMPOSSIBLE_TOGGLE_SEQUENCE>> {
21
+ const keys = manager.keys
22
+ const s = manager.options.stringifier
23
+
24
+
25
+ const prevToggles: [string, number][] = []
26
+ let num = 0
27
+ let impossible: {
28
+ keyId: string
29
+ pos: number
30
+ } | undefined
31
+ for (const chord of chain) {
32
+ const toggleIds = chord.filter(keyId => (getKeyFromIdOrVariant(keyId, keys).unwrap()[0]).isToggle)
33
+ let found = false
34
+ for (let k = 0; k < toggleIds.length; k++) {
35
+ const currId = toggleIds[k]
36
+ // We've already been guaranteed there's maximum 1 of the same toggle per chord in isValidChord
37
+ // So we can use this to count if the amount of previous toggle states for each unique toggle key cancel out
38
+ const prevWithSameRootsIds = prevToggles.map(([prevId, j]) => {
39
+ const prev = getKeyFromIdOrVariant(prevId, keys).unwrap()[0]
40
+ const curr = getKeyFromIdOrVariant(currId, keys).unwrap()[0]
41
+ return (prev.id === curr.id || prev.variants?.includes(curr.id) || curr.variants?.includes(prev.id))
42
+ ? [prevId, j]
43
+ : undefined
44
+ }).filter(key => key !== undefined) as [string, number][]
45
+ prevWithSameRootsIds.push([currId, num + k])
46
+ let canBeOn = true
47
+ let canBeOff = true
48
+ for (const [keyId, j] of prevWithSameRootsIds) {
49
+ // no need to check variants
50
+ const key = (keys.entries[keyId] ?? keys.toggles[keyId])
51
+ if (key.id === keyId) {
52
+ // Flip them since a root toggle functions as the opposite of the last toggle key state
53
+ // Note they can be both true, which is why we must literally flip them
54
+ const couldOn: boolean = canBeOn
55
+ const couldOff: boolean = canBeOff
56
+ canBeOn = couldOff
57
+ canBeOff = couldOn
58
+ continue
59
+ }
60
+ const isOff = key.toggleOffId === keyId
61
+ const isOn = key.toggleOnId === keyId
62
+ if ((isOn && !canBeOn)
63
+ || (isOff && !canBeOff)) {
64
+ impossible = { keyId, pos: j }
65
+ break
66
+ }
67
+ canBeOn = isOff
68
+ canBeOff = isOn
69
+ }
70
+ if (impossible) {
71
+ found = true; break
72
+ }
73
+ const currKey = getKeyFromIdOrVariant(currId, keys).unwrap()[0]
74
+ if (currKey.isToggle) {
75
+ prevToggles.push([currId, num + k])
76
+ }
77
+ num += chord.length
78
+ }
79
+ if (found) break
80
+ }
81
+ if (impossible) {
82
+ const prettyShortcut = s.stringify(chain, manager)
83
+ const { pos, keyId: key } = impossible
84
+ const e = Err(new KnownError(SHORTCUT_ERROR.IMPOSSIBLE_TOGGLE_SEQUENCE, crop`
85
+ Chain "${prettyShortcut}" is impossible.
86
+ This chain has a toggle key state "${s.stringify(key, manager)}" at key #${pos + 1} that would be impossible to trigger.
87
+ `, { chain, key: keys.entries[key] ?? keys.toggles[key], i: pos }))
88
+ return e
89
+ }
90
+ return Ok(true)
91
+ }
@@ -0,0 +1,18 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { indent } from "@alanscodelog/utils/indent"
3
+
4
+
5
+ export function errorTextAdd(
6
+ type: "Key" | "Shortcut" | "Command",
7
+ existingIdentifier: string,
8
+ existingEntry: string,
9
+ newEntry: string
10
+ ): string {
11
+ return crop`
12
+ ${type} ${existingIdentifier} is already registered.
13
+ Existing ${type}:
14
+ ${indent(existingEntry.toString(), 3)}
15
+ New ${type}:
16
+ ${indent(newEntry.toString(), 3)}
17
+ `
18
+ }
@@ -0,0 +1,10 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { indent } from "@alanscodelog/utils/indent"
3
+
4
+
5
+ export function errorTextInUse(type: "command" | "key", identifier: string, list: string): string {
6
+ return crop`
7
+ Cannot remove ${type} ${identifier}, it is in use by the following shortcuts:
8
+ ${indent(list, 2)}
9
+ `
10
+ }
@@ -0,0 +1,10 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { indent } from "@alanscodelog/utils/indent"
3
+
4
+
5
+ export function errorTextRemove(type: string, identifier: string, entries: string): string {
6
+ return crop`
7
+ ${type} ${identifier} does not exist in entries:
8
+ ${indent(entries, 3)}
9
+ `
10
+ }
@@ -0,0 +1,15 @@
1
+ import type { AnyInputEvent, Key, PickManager } from "../types/index.js"
2
+
3
+
4
+ export function getModifierState(
5
+ key: Key,
6
+ e: AnyInputEvent,
7
+ manager: PickManager<"options", "updateStateOnAllEvents">
8
+ ): boolean | null {
9
+ if (!manager.options.updateStateOnAllEvents || !key.updateStateOnAllEvents) return null
10
+ for (const code of [key.id, ...key.variants ?? []]) {
11
+ if ("getModifierState" in e && e.getModifierState(code)) return true
12
+ }
13
+ return false
14
+ }
15
+
@@ -0,0 +1,26 @@
1
+ import type { Manager } from "../types/index.js"
2
+
3
+ /**
4
+ * A list of currently pressed keys for convenience.
5
+ *
6
+ * It's just a wrapper around querying the key set for pressed keys.
7
+ */
8
+ export function getPressedKeys(manager: Pick<Manager, "keys">): string[] {
9
+ const res: string[] = []
10
+ for (const keyId of Object.keys(manager.keys.entries)) {
11
+ const key = manager.keys.entries[keyId]
12
+ if (key.pressed) {
13
+ res.push(key.id)
14
+ }
15
+ if (key.isToggle) {
16
+ if (key.toggleOnPressed) {
17
+ res.push(key.toggleOnId)
18
+ }
19
+ if (key.toggleOffPressed) {
20
+ res.push(key.toggleOffId)
21
+ }
22
+ }
23
+ }
24
+ return res
25
+ }
26
+
@@ -0,0 +1,14 @@
1
+ import type { Manager } from "../types/index.js"
2
+
3
+
4
+ export function getPressedModifierKeys(manager: Pick<Manager, "keys">): string[] {
5
+ const res: string[] = []
6
+ for (const keyId of Object.keys(manager.keys.entries)) {
7
+ const key = manager.keys.entries[keyId]
8
+ if (key.pressed && key.isModifier) {
9
+ res.push(key.id)
10
+ }
11
+ }
12
+ return res
13
+ }
14
+
@@ -0,0 +1,19 @@
1
+ import type { Manager } from "../types/index.js"
2
+ import { isTriggerKey } from "../utils/isTriggerKey.js"
3
+
4
+ /**
5
+ * A list of currently pressed non-modifier keys. They can't be toggle state keys (i.e. `on`/`off`) either.
6
+ *
7
+ * This determines whether when we await keyup and when a new chord should be added.
8
+ */
9
+ export function getPressedNonModifierKeys(manager: Pick<Manager, "keys">): string[] {
10
+ const res: string[] = []
11
+ for (const keyId of Object.keys(manager.keys.entries)) {
12
+ const key = manager.keys.entries[keyId]
13
+ if (key.pressed && isTriggerKey(manager.keys.entries[keyId])) {
14
+ res.push(key.id)
15
+ }
16
+ }
17
+ return res
18
+ }
19
+
@@ -0,0 +1,25 @@
1
+ import { shortcutCanExecuteIn } from "../helpers/shortcutCanExecuteIn.js"
2
+ import type { Manager } from "../types/index.js"
3
+ import { equalsKeys } from "../utils/equalsKeys.js"
4
+
5
+
6
+ /**
7
+ * Returns true if there are shortcuts that could be executed given the state of the manager's chain. That is, except if {@link Manager.isAwaitingKeyup} is true, in which case it returns false.
8
+ */
9
+ export function inChain(manager: Manager): boolean {
10
+ if (manager.state.isAwaitingKeyup) return false
11
+ const shortcuts = manager.shortcuts.entries.filter(shortcut => (
12
+ shortcutCanExecuteIn(shortcut, manager, { allowEmptyCommand: true })
13
+ && manager.state.chain.length < shortcut.chain.length
14
+ && equalsKeys(
15
+ shortcut.chain,
16
+ manager.state.chain,
17
+ manager.keys,
18
+ manager.state.chain.length,
19
+ { allowVariants: true }
20
+ )
21
+ ))
22
+ if (!shortcuts) return false
23
+ return shortcuts.length > 0
24
+ }
25
+
@@ -0,0 +1,42 @@
1
+ import { Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { areValidKeys } from "./areValidKeys.js"
4
+ import { containsPossibleToggleChords } from "./containsPossibleToggleChords.js"
5
+ import { isValidChord } from "./isValidChord.js"
6
+
7
+ import type { ChainError, Manager, MultipleErrors, PickManager } from "../types/index.js"
8
+
9
+
10
+ /**
11
+ * Guards against invalid chains (for various reasons). See the {@link ChainError}.
12
+ *
13
+ * Checks {@link areValidKeys}, {@link isValidChord} and {@link containsPossibleToggleChords}
14
+ *
15
+ * @internal
16
+ */
17
+ export function isValidChain(
18
+ chain: string[][],
19
+ manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier" | "sorter">
20
+ ): Result<true,
21
+ MultipleErrors<ChainError>
22
+ > {
23
+ const sorter = manager.options.sorter
24
+
25
+ if ((chain.length === 0) || (chain.length === 1 && chain[0].length === 0)) {
26
+ return Ok(true)
27
+ }
28
+
29
+ const resIsValid = areValidKeys(chain, manager)
30
+ if (resIsValid.isError) return resIsValid
31
+
32
+ const val = []
33
+ for (let i = 0; i < chain.length; i++) {
34
+ const chord = chain[i]
35
+ const res = isValidChord(chain, chord, i, manager)
36
+ if (res.isError) return res
37
+ val.push(sorter.sort([...chord], manager.keys))
38
+ }
39
+ const res = containsPossibleToggleChords(chain, manager)
40
+ if (res.isError) return res
41
+ return Ok(true)
42
+ }
@@ -0,0 +1,87 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { findDuplicates } from "@alanscodelog/utils/findDuplicates"
3
+ import { indent } from "@alanscodelog/utils/indent"
4
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
5
+
6
+ import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
7
+ import { KnownError } from "../helpers/KnownError.js"
8
+ import { type Manager, type PickManager, SHORTCUT_ERROR } from "../types/index.js"
9
+ import { isTriggerKey } from "../utils/isTriggerKey.js"
10
+ import { isWheelKey } from "../utils/isWheelKey.js"
11
+
12
+
13
+ /**
14
+ * Guards against invalid chords (for various reasons). See the {@link ERROR}.CHORD_W_... errors.
15
+ *
16
+ * Does not check if the keys are valid.
17
+ *
18
+ * @internal
19
+ */
20
+ export function isValidChord(
21
+ chain: string[][],
22
+ chord: string[],
23
+ i: number,
24
+ manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier" | "sorter">
25
+ ): Result<true, KnownError<typeof SHORTCUT_ERROR.CHORD_W_DUPLICATE_KEY | typeof SHORTCUT_ERROR.CHORD_W_ONLY_MODIFIERS | typeof SHORTCUT_ERROR.CHORD_W_MULTIPLE_TRIGGER_KEYS | typeof SHORTCUT_ERROR.CHORD_W_MULTIPLE_WHEEL_KEYS>> {
26
+ const keys = manager.keys
27
+ const s = manager.options.stringifier
28
+
29
+ const prettyChord = s.stringify(chord, manager)
30
+ const prettyChain = s.stringify(chain, manager)
31
+
32
+ const repeated = findDuplicates(chord, { equals: (id, otherId) => {
33
+ const key = getKeyFromIdOrVariant(id, keys).unwrap()[0]
34
+ const other = getKeyFromIdOrVariant(otherId, keys).unwrap()[0]
35
+ if (key.id === other.id) return true
36
+ if (key.variants?.includes(other.id) || other.variants?.includes(key.id)) return true
37
+ // this is caught by multiple wheel key error
38
+ if (isWheelKey(key) && isWheelKey(other)) return false
39
+ return false
40
+ } })
41
+ if (repeated.length > 0) {
42
+ const prettyRepeated = s.stringifyList("keys", repeated, manager)
43
+
44
+ return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_DUPLICATE_KEY, crop`
45
+ Chord "${prettyChord}" in chain "${prettyChain}" contains duplicate or incompatible keys:
46
+ ${indent(prettyRepeated, 4)}
47
+ Chords cannot contain duplicate keys. This includes more than one of the same toggle, regardless of the state.
48
+ `, { chord, i, keys: repeated }))
49
+ }
50
+
51
+ const onlyModifiers = chord.filter(id => (getKeyFromIdOrVariant(id, keys).unwrap()[0]).isModifier)
52
+ const containsOnlyModifiers = onlyModifiers.length === chord.length
53
+ if (i < chain.length - 1 && containsOnlyModifiers) {
54
+ return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_ONLY_MODIFIERS, crop`
55
+ Chain "${prettyChain}" is impossible.
56
+ Chord #${i + 1} "${prettyChord}" cannot contain only modifiers if it is followed by another chord.
57
+ A chord can only consist of only modifiers if it's the last chord in a chain.
58
+ `, { chord, i, keys: onlyModifiers }))
59
+ }
60
+
61
+ /* It might actually be possible to allow this, similar to how emulated toggle keys are handled but it would be a pain for such an odd use case (even I don't have such weird shortcuts). */
62
+ const wheelKeys = chord.filter(id => isWheelKey(getKeyFromIdOrVariant(id, keys).unwrap()[0]))
63
+ const prettyWheelKeys = s.stringifyList("keys", wheelKeys, manager)
64
+
65
+ if (wheelKeys.length > 1) {
66
+ return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_MULTIPLE_WHEEL_KEYS, crop`
67
+ Chain "${prettyChain}" is impossible.
68
+ Chord #${i + 1} "${prettyChord}" contains multiple wheel keys: ${prettyWheelKeys}
69
+ Chords can only contain one.
70
+ `, { chord, i, keys: wheelKeys }))
71
+ }
72
+ // todo remove this limitation
73
+ // we should be able to have shortcuts like holding down both mouse buttons
74
+ // note it's fine to not check key.toggles in isTriggerKey because a trigger key cannot be a toggle *state* anyways
75
+ const triggerKeys = chord.filter(id => isTriggerKey(getKeyFromIdOrVariant(id, keys).unwrap()[0]))
76
+ const prettyTriggerKeys = s.stringifyList("keys", triggerKeys, manager)
77
+ if (triggerKeys.length > 1) {
78
+ return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_MULTIPLE_TRIGGER_KEYS, crop`
79
+ Chain "${prettyChain}" is impossible.
80
+ Chord #${i + 1} "${prettyChord}" contains multiple trigger (non-modifier/non-root toggle) keys: ${prettyTriggerKeys}
81
+ Chords can only contain one.
82
+ `, { chord, i, keys: triggerKeys }))
83
+ }
84
+
85
+
86
+ return Ok(true)
87
+ }
@@ -0,0 +1,35 @@
1
+ import { crop } from "@alanscodelog/utils/crop"
2
+ import { indent } from "@alanscodelog/utils/indent"
3
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
4
+
5
+ import { KnownError } from "../helpers/KnownError.js"
6
+ import type { Manager, MultipleErrors, PickManager, Shortcut } from "../types/index.js"
7
+ import { SHORTCUT_ERROR } from "../types/index.js"
8
+
9
+ /**
10
+ * @internal
11
+ */
12
+
13
+ export function isValidCommand(
14
+ commandName: string | undefined,
15
+ manager: Pick<Manager, "commands"> & Partial<Pick<Manager, "keys">> & PickManager<"options", "stringifier">,
16
+ /**
17
+ * Shortcut will only be added to error if the manager contains keys.
18
+ * Otherwise it will be ignored.
19
+ */
20
+ shortcut?: Shortcut
21
+ ): Result<true, MultipleErrors<typeof SHORTCUT_ERROR.UNKNOWN_COMMAND>> {
22
+ const commands = manager.commands
23
+ const s = manager.options.stringifier
24
+ if (commandName === undefined) return Ok(true)
25
+ const command = commands.entries[commandName]
26
+ if (command === undefined) {
27
+ const shortcutString = shortcut && "keys" in manager ? ` in shortcut ${s.stringify(shortcut, manager as Pick<Manager, "commands" | "keys">)}` : ""
28
+ return Err(new KnownError(SHORTCUT_ERROR.UNKNOWN_COMMAND, crop`
29
+ Unknown command: ${commandName}${shortcutString}. Cannot find in:
30
+
31
+ ${indent(s.stringifyList("commands", Object.values(commands.entries)), 3)}
32
+ `, { command: commandName, commands, shortcut }))
33
+ }
34
+ return Ok(true)
35
+ }
@@ -0,0 +1,24 @@
1
+ import type { Key, KEY_SORT_POS, KeySortPos } from "../types/index.js"
2
+ import { isMouseKey } from "../utils/isMouseKey.js"
3
+ import { isWheelKey } from "../utils/isWheelKey.js"
4
+
5
+
6
+ /**
7
+ *
8
+ * Determines the key order position based on a key's type and the given {@link KeySortPos}
9
+ */
10
+ export function keyOrder(
11
+ key: Key,
12
+ dictOrEnum: KeySortPos
13
+ ): number {
14
+ let type = [
15
+ key.isModifier ? "mod" : "",
16
+ key.isToggle ? "toggle" : "",
17
+ isMouseKey(key) ? "mouse" : "",
18
+ isWheelKey(key) ? "wheel" : ""
19
+ ].join("")
20
+ if (type === "") {
21
+ type = "normal"
22
+ }
23
+ return dictOrEnum[type as keyof typeof KEY_SORT_POS]
24
+ }
@@ -0,0 +1,46 @@
1
+ import { Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { checkTrigger } from "./checkTrigger.js"
4
+ import { checkUntrigger } from "./checkUntrigger.js"
5
+ import { cloneLastChord } from "./cloneLastChord.js"
6
+ import { getPressedNonModifierKeys } from "./getPressedNonModifierKeys.js"
7
+
8
+ import { setManagerProp } from "../core/setManagerProp.js"
9
+ import type { AnyInputEvent, Manager, ManagerSetEntries, MultipleErrors } from "../types/index.js"
10
+ import { cloneChain } from "../utils/cloneChain.js"
11
+
12
+
13
+ export function removeFromChain(
14
+ manager: Manager,
15
+ keys: string[],
16
+ e?: AnyInputEvent
17
+ ): Result<true, MultipleErrors<ManagerSetEntries["state.chain"]["error"]>> {
18
+ if (keys.length === 0) return Ok(true)
19
+ if (manager.state.isAwaitingKeyup) {
20
+ checkUntrigger(manager, e)
21
+ if (getPressedNonModifierKeys(manager).length === 0) {
22
+ setManagerProp(manager, "state.isAwaitingKeyup", false)
23
+ }
24
+ }
25
+
26
+ if (manager.state.nextIsChord) return Ok(true)
27
+ const lastChord = cloneLastChord(manager.state.chain) ?? []
28
+ for (const id of keys) {
29
+ const key = manager.keys.entries[id]
30
+ const i = lastChord.indexOf(key.id)
31
+
32
+ if (i > -1) {
33
+ lastChord.splice(i, 1)
34
+ const chain = manager.state.chain
35
+ const precedingChords = chain.slice(0, chain.length - 1)
36
+ const spreadableLastChord = precedingChords.length === 0 && lastChord.length === 0
37
+ ? []
38
+ : [lastChord]
39
+ const res = setManagerProp(manager, "state.chain", cloneChain([...precedingChords, ...spreadableLastChord]))
40
+ if (res.isError) return res as any
41
+ }
42
+ }
43
+ checkTrigger(manager, e)
44
+ return Ok(true)
45
+ }
46
+
@@ -0,0 +1,23 @@
1
+ import { setKeyProp } from "../core/setKeyProp.js"
2
+ import type { Key, KeySetEntries, Manager } from "../types/index.js"
3
+
4
+
5
+ export function safeSetEmulatedToggleState<
6
+ THooks extends Manager["hooks"]
7
+ >(
8
+ key: Key,
9
+ value: boolean,
10
+ manager: KeySetEntries["toggleOnPressed" | "toggleOffPressed"]["manager"] & { hooks?: THooks }
11
+ ) {
12
+ const res = setKeyProp(key, "toggleOnPressed", value, manager, { check: "only" })
13
+ if (res.isOk) {
14
+ const res2 = setKeyProp(key, "toggleOffPressed", !value, manager, { check: "only" })
15
+ if (res2.isOk) {
16
+ setKeyProp(key, "toggleOnPressed", value, manager, { check: false })
17
+ setKeyProp(key, "toggleOffPressed", !value, manager, { check: false })
18
+ }
19
+ return res2
20
+ }
21
+ return res
22
+ }
23
+
@@ -0,0 +1,71 @@
1
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { safeSetEmulatedToggleState } from "./safeSetEmulatedToggleState.js"
4
+
5
+ import { setKeyProp } from "../core/setKeyProp.js"
6
+ import { KnownError } from "../helpers/KnownError.js"
7
+ import type { CanHookErrors, KeySetEntries, Manager, MultipleErrors } from "../types/index.js"
8
+ import { SHORTCUT_ERROR } from "../types/index.js"
9
+
10
+
11
+ export function setKeysState<
12
+ THooks extends Manager["hooks"]
13
+ >(
14
+ keysList: string[],
15
+ manager: KeySetEntries["pressed" | "toggleOnPressed" | "toggleOffPressed"]["manager"] & { hooks?: THooks },
16
+ state: boolean,
17
+ {
18
+ ignoreToggleType = false
19
+ }: {
20
+ ignoreToggleType?: boolean
21
+ } = {}
22
+ ): Result<
23
+ true,
24
+ | MultipleErrors<
25
+ | typeof SHORTCUT_ERROR.CANNOT_SET_WHILE_DISABLED
26
+ | typeof SHORTCUT_ERROR.INCORRECT_TOGGLE_STATE
27
+ >
28
+ | CanHookErrors<THooks extends never ? never : THooks, "canSetKeyProp">
29
+ > {
30
+ const s = manager.options.stringifier
31
+ for (const id of keysList) {
32
+ const key = manager.keys.entries[id]
33
+ const isRoot = id === key.id
34
+ // this is only for the emulator
35
+ const isOn = id === key.toggleOnId
36
+ const isOff = id === key.toggleOffId
37
+ if ((isRoot && key.pressed === state)
38
+ || (isOn && key.toggleOnPressed === state)
39
+ || (isOff && key.toggleOffPressed === state)
40
+ ) {
41
+ continue
42
+ }
43
+ const res = setKeyProp(key, "pressed", state, manager)
44
+ if (res.isError) return res
45
+ if (isRoot && state) {
46
+ // toggles are considered active on keydown
47
+ if ((ignoreToggleType && key.isToggle) || key.isToggle === "emulated") {
48
+ // state was never set
49
+ if (key.toggleOnPressed && key.toggleOffPressed) {
50
+ return Err(new KnownError(
51
+ SHORTCUT_ERROR.INCORRECT_TOGGLE_STATE,
52
+ `Key ${s.stringify(key, manager)} is a toggle key whose on and off versions are both pressed, which is not a valid state. This should not happen if letting the manager manage the state.`,
53
+ { key }
54
+ ))
55
+ }
56
+
57
+ let res
58
+ if (!key.toggleOnPressed && !key.toggleOffPressed) {
59
+ res = safeSetEmulatedToggleState(key, true, manager)
60
+ } else if (key.toggleOnPressed) {
61
+ res = safeSetEmulatedToggleState(key, false, manager)
62
+ } else if (key.toggleOffPressed) {
63
+ res = safeSetEmulatedToggleState(key, true, manager)
64
+ }
65
+ if (res?.isError) return res
66
+ }
67
+ }
68
+ }
69
+ return Ok(true)
70
+ }
71
+