@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,72 @@
1
+ import { equalsShortcut } from "./equalsShortcut.js"
2
+ import { getKeyFromIdOrVariant } from "./getKeyFromIdOrVariant.js"
3
+
4
+ import type { Manager, PickManager, Shortcut } from "../types/index.js"
5
+ import { equalsKey } from "../utils/equalsKey.js"
6
+ import { equalsKeys } from "../utils/equalsKeys.js"
7
+
8
+ /**
9
+ * A shortcut conflicts with another if their conditions are equal and their chains are in conflict.
10
+ *
11
+ * Chains can be in conflict in any of the following situations:
12
+ *
13
+ * - The shortcuts are the same instance.
14
+ * - All the keys are the same.
15
+ * - They share starting chords and the last shared chord conflicts. (e.g. [[A], [B]] and [[A]]), i.e. they have a chain conflict.
16
+ * - The last chords shares all modifiers and one of the chords is all modifiers. (e.g. [[A], [Ctrl]] and [[A], [Ctrl, A]]), i.e. they have a modifier conflict.
17
+ *
18
+ * Some of these can be ignored with the experimental ignore* options.
19
+ *
20
+ * A context can be passed for a more accurate test, otherwise depending on what type of conditions you use (see {@link ConditionComparer}) you cannot truly tell if shortcuts with complex conditions will conflict. See the option itself for more details.
21
+ */
22
+ export function doesShortcutConflict<TShortcut extends Shortcut>(
23
+ shortcutA: TShortcut,
24
+ shortcutB: Shortcut,
25
+ manager: Pick<Manager, "keys" | "commands" | "shortcuts"> & { context?: Manager["context"] }
26
+ & PickManager<"options", "evaluateCondition" | "conditionEquals">
27
+ ): boolean {
28
+ const context = manager.context
29
+ if (!context && manager.shortcuts.useContextInConflictCheck) {
30
+ throw new Error("Manager must have a context if `useContextInConflictCheck` is true.")
31
+ }
32
+ const {
33
+ ignoreChainConflicts,
34
+ ignoreModifierConflicts
35
+ } = manager.shortcuts
36
+ if (ignoreChainConflicts && ignoreModifierConflicts) return false
37
+
38
+ if (shortcutA.forceUnequal || shortcutB.forceUnequal) return false
39
+ if (equalsShortcut(shortcutA, shortcutB, manager)) return true
40
+ const evaluateCondition = manager.options.evaluateCondition
41
+ const conditionEquals = manager.options.conditionEquals
42
+ if (context) {
43
+ if (shortcutA.condition && shortcutB.condition) {
44
+ const shortcutCondition = evaluateCondition(shortcutA.condition, context)
45
+ const otherCondition = evaluateCondition(shortcutB.condition, context)
46
+ if (shortcutCondition !== otherCondition) return false
47
+ if (!shortcutCondition) return false
48
+ }
49
+ } else {
50
+ if (!conditionEquals(shortcutA.condition, shortcutB.condition)) return false
51
+ }
52
+ const { keys } = manager
53
+ // an empty chain is always in conflict ?
54
+ if (shortcutA.chain.length === 0 || shortcutB.chain.length === 0) {
55
+ return ignoreChainConflicts === true && ignoreModifierConflicts === true
56
+ }
57
+
58
+ const lastSharedIndex = Math.max(0, Math.min(shortcutA.chain.length - 1, shortcutB.chain.length - 1))
59
+ const lastIsModOnly = shortcutA.chain[lastSharedIndex].find(id => !getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier) === undefined
60
+ const otherLastIsModOnly = shortcutB.chain[lastSharedIndex].find(id => !getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier) === undefined
61
+ const sharedModifiers = shortcutA.chain[lastSharedIndex].filter(id => getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier
62
+ && shortcutB.chain[lastSharedIndex].some(otherId => equalsKey(otherId, id, keys, { allowVariants: true }))
63
+ )
64
+
65
+
66
+ const lastSharedChordConflicts = (lastIsModOnly || otherLastIsModOnly) && sharedModifiers.length > 0
67
+ const conflictsWithChain = equalsKeys(shortcutA.chain, shortcutB.chain, keys, lastSharedIndex + 1, { allowVariants: true })
68
+ return (
69
+ (!ignoreChainConflicts && conflictsWithChain)
70
+ || (!ignoreModifierConflicts && lastSharedChordConflicts)
71
+ )
72
+ }
@@ -0,0 +1,18 @@
1
+ import type { Command, PickManager } from "../types/index.js"
2
+
3
+ /**
4
+ * Returns whether the command passed is equal to this one.
5
+ *
6
+ * The name, execute, description, and conditions must be equal.
7
+ */
8
+ export function equalsCommand<TCommand extends Command>(
9
+ commandA: TCommand,
10
+ commandB: Command,
11
+ manager: PickManager<"options", "conditionEquals">
12
+ ): commandB is TCommand {
13
+ if (commandA === commandB) return true
14
+ return commandA.name === commandB.name
15
+ && commandA.execute === commandB.execute
16
+ && manager.options.conditionEquals(commandA.condition, commandB.condition)
17
+ && commandA.description === commandB.description
18
+ }
@@ -0,0 +1,29 @@
1
+ import type { Context, RecursiveRecord } from "../types/index.js"
2
+
3
+
4
+ function fastIsEqual(obj: RecursiveRecord, other: RecursiveRecord): boolean {
5
+ const keys1 = Object.keys(obj)
6
+ const keys2 = Object.keys(other)
7
+ if (keys1.length !== keys2.length) return false
8
+ for (const key of keys1) {
9
+ const val1 = obj[key]
10
+ const val2 = other[key]
11
+ if (typeof val1 === "object" && typeof val2 === "object") {
12
+ if (!fastIsEqual(val1, val2)) return false
13
+ }
14
+ if (val1 !== val2) {
15
+ return false
16
+ }
17
+ }
18
+ return true
19
+ }
20
+
21
+ /**
22
+ * Returns whether the context passed is equal to this one.
23
+ *
24
+ * The default methods provides a simple comparison that can handle simple flat or nested objects (simple as in it assumes values are not arrays). If you need something more complex you will need to provide your own function.
25
+ */
26
+
27
+ export function equalsContext(contextA: Context, contextB: Context): boolean {
28
+ return fastIsEqual(contextA.value, contextB.value)
29
+ }
@@ -0,0 +1,34 @@
1
+ import { equalsCommand } from "./equalsCommand.js"
2
+
3
+ import type { Manager, PickManager, Shortcut } from "../types/index.js"
4
+ import { equalsKeys } from "../utils/equalsKeys.js"
5
+
6
+ /**
7
+ * Returns whether the shortcut passed is equal to shortcutA one.
8
+ *
9
+ * To return true, their keys and command must be equal (unless ignoreCommand is passed), their condition must be equal according to shortcutA shortcut's condition.
10
+ */
11
+ export function equalsShortcut<TShortcut extends Shortcut>(
12
+ shortcutA: TShortcut,
13
+ shortcutB: Shortcut,
14
+ manager: Pick<Manager, "keys" | "commands"> & PickManager<"options", | "evaluateCondition" | "conditionEquals">,
15
+ { ignoreCommand = false }: { ignoreCommand?: boolean } = {}
16
+ ): shortcutB is TShortcut {
17
+ if (shortcutA.forceUnequal || shortcutB.forceUnequal) return false
18
+ if (shortcutA === shortcutB) return true
19
+ return (
20
+ equalsKeys(shortcutA.chain, shortcutB.chain, manager.keys, undefined, { allowVariants: true })
21
+ && manager.options.conditionEquals(shortcutA.condition, shortcutB.condition)
22
+ && (ignoreCommand
23
+ || (
24
+ shortcutA.command === shortcutB.command
25
+ && shortcutA.command === undefined
26
+ )
27
+ || (shortcutA.command !== undefined
28
+ && shortcutB.command !== undefined
29
+ && equalsCommand(manager.commands.entries[shortcutA.command], manager.commands.entries[shortcutB.command], manager)
30
+ )
31
+ )
32
+ )
33
+ }
34
+
@@ -0,0 +1,31 @@
1
+ import { setKeyProp } from "../core/setKeyProp.js"
2
+ import { setManagerProp } from "../core/setManagerProp.js"
3
+ import type { Manager } from "../types/index.js"
4
+
5
+ /**
6
+ * Force clears/resets all state. Clears the chain and sets all keys to unpressed.
7
+ *
8
+ * Useful for testing.
9
+ *
10
+ * @param opts
11
+ * @param {false} opts.ignoreNative If true, does not change state of native modifier/toggle keys.
12
+ */
13
+
14
+
15
+ export function forceClear(manager: Manager, { ignoreNative = false }: { ignoreNative?: boolean } = {}): void {
16
+ setManagerProp(manager, "state.chain", [])
17
+ setManagerProp(manager, "state.nextIsChord", true)
18
+ setManagerProp(manager, "state.untrigger", false)
19
+ setManagerProp(manager, "state.isAwaitingKeyup", false)
20
+
21
+ for (const key of Object.values(manager.keys.entries)) {
22
+ if ((key.isModifier === "native" || key.isToggle === "native") && ignoreNative) return
23
+ setKeyProp(key, "pressed", false, manager)
24
+ if (key.isToggle) {
25
+ // safe not to check return because setting to false will never error
26
+ setKeyProp(key, "toggleOnPressed", false, manager)
27
+ setKeyProp(key, "toggleOffPressed", false, manager)
28
+ }
29
+ }
30
+ }
31
+
@@ -0,0 +1,9 @@
1
+ import { updateNativeKeysState } from "../internal/updateNativeKeysState.js"
2
+ import type { AnyInputEvent, Manager } from "../types/index.js"
3
+
4
+ /**
5
+ * Force the manager to update the state of modifier/toggle keys during a keyboard or mouse event.
6
+ */
7
+ export function forceUpdateNativeKeysState(manager: Manager, e: AnyInputEvent): void {
8
+ updateNativeKeysState(manager, e)
9
+ }
@@ -0,0 +1,113 @@
1
+ import { last } from "@alanscodelog/utils/last"
2
+
3
+ import { getKeyFromIdOrVariant } from "./getKeyFromIdOrVariant.js"
4
+
5
+ import type { KeyInfo, Manager, Shortcut, ShortcutInfo } from "../types/index.js"
6
+ import type { Key } from "../types/keys.js"
7
+ import { chainContainsSubset } from "../utils/chainContainsSubset.js"
8
+ import { equalsKeys } from "../utils/equalsKeys.js"
9
+ import { removeKeys } from "../utils/removeKeys.js"
10
+
11
+
12
+ /**
13
+ * Given a chain, a shortcut list, and a manager, this will return a record with information for each key id regarding what shortcuts can be pressed and some additional information useful for visual displaying them on a keyboard (See {@link KeyInfo}).
14
+ *
15
+ * The record will contain all renderable keys by their id (see {@link Key.render}). It will not contain variants or toggle states, only full keys (i.e. {@link Keys.entries}).
16
+ *
17
+ * It is recommended you pre-filter the shortcut's list to remove any shortcuts that are disabled or not executable.
18
+ *
19
+ * You can also post filter depending on the generated {@link ShortcutInfo} entry. See the postFilter param.
20
+ */
21
+
22
+ export function generateKeyShortcutMap(
23
+ chain: string[][],
24
+ shortcutsList: Shortcut[],
25
+ manager: Manager,
26
+ /**
27
+ * Filter entries by their {@link ShortcutInfo}, before adding/modifying the corresponding keys. Return false to filter the entry out, true to keep it.
28
+ *
29
+ * The second argument also provides some additional info the function looks at to generate the result.
30
+ */
31
+ postFilter?: (
32
+ entry: ShortcutInfo,
33
+ info: {
34
+ pressableKeys: Key[]
35
+ unpressedModifiers: Key[]
36
+ isPressed: boolean
37
+ containsSubset: boolean
38
+ }
39
+ ) => boolean
40
+ ): Record<Key["id"], KeyInfo> {
41
+ const obj: Record<string, KeyInfo> = {}
42
+ const nextIsChord = manager.state.nextIsChord
43
+
44
+ const isEmpty = chain.length === 0 || (chain.length === 1 && chain?.[0].length === 0)
45
+ const index = isEmpty ? 0 : nextIsChord ? chain.length : chain.length - 1
46
+ for (const shortcut of shortcutsList) {
47
+ const containsSubset = chainContainsSubset(shortcut.chain, chain, manager.keys, { onlySubset: true, onlyPressable: false })
48
+ const isPressed = equalsKeys(shortcut.chain, chain, manager.keys)
49
+ if (!containsSubset && !isPressed) continue
50
+
51
+ const keysLeftInChord = removeKeys((shortcut.chain[index] ?? []), (chain[index] ?? []), manager.keys)
52
+ // normalize to Key[] so that we are only looking at "full" keys
53
+ .map(_ => getKeyFromIdOrVariant(_, manager.keys).unwrap()[0])
54
+
55
+ const isPressableChord = keysLeftInChord.length === 1
56
+ const isPressable = isPressableChord && shortcut.chain.length - 1 === index
57
+ const isPressableChain = isPressableChord && shortcut.chain.length - 1 > index
58
+
59
+ const unpressedModifiers = keysLeftInChord.length > 1 ? keysLeftInChord.filter(_ => _.isModifier) : []
60
+ const hasUnpressedModifiers = unpressedModifiers.length > 0
61
+
62
+ if (!isPressable && !isPressableChain && !isPressed && unpressedModifiers.length <= 0) continue
63
+
64
+ const pressableKeys = isPressed
65
+ ? (last(shortcut.chain) ?? []).map(_ => getKeyFromIdOrVariant(_, manager.keys).unwrap()[0])
66
+ : unpressedModifiers.length === 0
67
+ ? keysLeftInChord
68
+ : []
69
+
70
+ const entry = {
71
+ shortcut,
72
+ isPressed,
73
+ isPressable,
74
+ isPressableChain,
75
+ hasUnpressedModifiers
76
+ }
77
+ if (postFilter !== undefined && !postFilter(entry, { unpressedModifiers, pressableKeys, isPressed, containsSubset })) continue
78
+ if (!hasUnpressedModifiers || isPressed) {
79
+ for (const k of pressableKeys) {
80
+ if (k.render) {
81
+ obj[k.id] ??= {
82
+ pressableEntries: [] as ShortcutInfo[],
83
+ modifierEntries: [] as ShortcutInfo[],
84
+ containsConflicting: false,
85
+ isModifierHint: false
86
+ }
87
+ if (!obj[k.id].containsConflicting) {
88
+ const containsConflicting = obj[k.id].pressableEntries
89
+ .some(_ => _.isPressable || _.isPressableChain)
90
+ obj[k.id].containsConflicting = containsConflicting
91
+ }
92
+ obj[k.id].pressableEntries.push({ ...entry })
93
+ }
94
+ }
95
+ }
96
+ if (!isPressed && !isPressable && !isPressableChain) {
97
+ for (const k of unpressedModifiers) {
98
+ if (k.render) {
99
+ obj[k.id] ??= {
100
+ pressableEntries: [] as ShortcutInfo[],
101
+ modifierEntries: [] as ShortcutInfo[],
102
+ containsConflicting: false,
103
+ isModifierHint: false
104
+ }
105
+ obj[k.id].modifierEntries.push({ ...entry })
106
+ obj[k.id].isModifierHint ||= true
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ return obj
113
+ }
@@ -0,0 +1,67 @@
1
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { KnownError } from "./KnownError.js"
4
+
5
+ import { type Key, SHORTCUT_ERROR } from "../types/index.js"
6
+
7
+
8
+ export function getKeyFromEventCode(
9
+ code: string,
10
+ e: { code: string } | { key: string } | { button: number } | { deltaY: number },
11
+ keys: Record<string, Key>,
12
+ {
13
+ pressedState: pressState,
14
+ includeDisabled = false
15
+ }: {
16
+ includeDisabled?: boolean
17
+ pressedState?: boolean
18
+ } = {}
19
+ ): Result<string[], KnownError<typeof SHORTCUT_ERROR.UNKNOWN_KEY_EVENT>> {
20
+ const keyIds = []
21
+ const disabledIds = []
22
+ for (const key of Object.values(keys)) {
23
+ if (
24
+ (pressState === undefined ? true : key.pressed === pressState)
25
+ && (
26
+ key.id === code
27
+ || key.variants?.includes(code)
28
+ )
29
+ ) {
30
+ if (key.enabled) {
31
+ keyIds.push(key.id)
32
+ } else if (!key.enabled) {
33
+ if (includeDisabled) {
34
+ keyIds.push(key.id)
35
+ } else {
36
+ disabledIds.push(key.id)
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ if (keyIds.length === 0 && disabledIds.length === 0) {
43
+ const withCode = "code" in e && e.code !== undefined
44
+ ? `code: ${e.code}`
45
+ : undefined
46
+ const withKey = "key" in e && e.key !== undefined
47
+ ? `key: ${e.key}`
48
+ : undefined
49
+ const withButton = "button" in e && e.button !== undefined
50
+ ? `button: ${e.button}`
51
+ : undefined
52
+ const withDeltaY = "deltaY" in e && e.deltaY !== undefined
53
+ ? `deltaY: ${e.deltaY}`
54
+ : undefined
55
+
56
+ const info = [withCode, withKey, withButton, withDeltaY]
57
+ .filter(_ => _ !== "undefined")
58
+ .join(", ")
59
+
60
+ return Err(new KnownError(
61
+ SHORTCUT_ERROR.UNKNOWN_KEY_EVENT,
62
+ `An unknown key (${info}) was pressed.`,
63
+ { e: e as any, button: withButton, code: withCode, key: withKey, deltaY: withDeltaY }
64
+ ))
65
+ }
66
+ return Ok(keyIds)
67
+ }
@@ -0,0 +1,33 @@
1
+ import { isArray } from "@alanscodelog/utils/isArray"
2
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
3
+
4
+ import { KnownError } from "./KnownError.js"
5
+
6
+ import { type Key, type Keys, SHORTCUT_ERROR } from "../types/index.js"
7
+
8
+
9
+ export function getKeyFromIdOrVariant(
10
+ id: string,
11
+ keys: Keys
12
+ ): Result<Key[], KnownError<typeof SHORTCUT_ERROR.UNKNOWN_KEY_ID>> {
13
+ let k: Key | Key[] = keys.entries[id] ?? keys.toggles[id]
14
+ if (k === undefined) {
15
+ if (keys.variants[id]) {
16
+ const variants = []
17
+ for (const variant of keys.variants[id]) {
18
+ const v = keys.entries[variant] ?? keys.toggles[variant]
19
+ if (v !== undefined) variants.push(v)
20
+ }
21
+ if (variants.length > 0) k = variants
22
+ }
23
+ }
24
+ if (!isArray(k) && k !== undefined) k = [k]
25
+ if (k === undefined) {
26
+ return Err(new KnownError(
27
+ SHORTCUT_ERROR.UNKNOWN_KEY_ID,
28
+ `Tried to get unknown key (${id}).`,
29
+ { id }
30
+ ))
31
+ }
32
+ return Ok(k)
33
+ }
@@ -0,0 +1,13 @@
1
+ import type { KeyboardLayoutMap } from "../types/general.js"
2
+
3
+ /**
4
+ * Safely get the [KeyboardLayoutMap](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardLayoutMap) with [navigator.getLayoutMap](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/navigator.getLayoutMap).
5
+ */
6
+ export async function getKeyboardLayoutMap(): Promise<KeyboardLayoutMap | undefined> {
7
+ // castType<Navigator>(navigator) // not working during build
8
+ if (typeof navigator !== "undefined" && "keyboard" in navigator) {
9
+ return (navigator.keyboard as any).getLayoutMap()
10
+ } else {
11
+ return undefined
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { Key } from "../types/index.js"
2
+ /**
3
+ * Returns the label, adding an `(On)` or `(Off)` suffix to any toggle keys.
4
+ *
5
+ * If the key has not label, uses the id
6
+ *
7
+ * Used by the {@link defaultStringifier}
8
+ */
9
+ export function getLabel(id: string | undefined, key: Key): string {
10
+ const name = key.label ?? key.id
11
+ if (id === key.toggleOnId) return `${name} (On)`
12
+ if (id === key.toggleOffId) return `${name} (Off)`
13
+
14
+ return name
15
+ }
@@ -0,0 +1,37 @@
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 "./KnownError.js"
6
+ import { shortcutIsTriggerableBy } from "./shortcutIsTriggerableBy.js"
7
+
8
+ import type { Manager, PickManager, TriggerableShortcut } from "../types/index.js"
9
+ import { SHORTCUT_ERROR } from "../types/index.js"
10
+
11
+
12
+ /** Gets all triggerable shortcuts in a manager. */
13
+ export function getTriggerableShortcut(
14
+ manager: Pick<Manager, "shortcuts" | "keys" | "context" | "commands">
15
+ & PickManager<"options", "stringifier" | "evaluateCondition">
16
+ & PickManager<"state", "chain" | "isRecording">
17
+ ): Result<false | TriggerableShortcut, KnownError<typeof SHORTCUT_ERROR.MULTIPLE_MATCHING_SHORTCUTS>> {
18
+ const s = manager.options.stringifier
19
+
20
+ if (manager.state.isRecording) return Ok(false)
21
+
22
+ const shortcuts = manager.shortcuts.entries.filter(shortcut => shortcutIsTriggerableBy(manager.state.chain, shortcut, manager))
23
+
24
+ if (shortcuts.length === 0) return Ok(false)
25
+ if (shortcuts.length > 1) {
26
+ return Err(new KnownError(SHORTCUT_ERROR.MULTIPLE_MATCHING_SHORTCUTS,
27
+ crop`
28
+ Multiple commands are assigned to the key combination ${s.stringify(manager.state.chain, manager)}:
29
+
30
+ ${indent(s.stringifyList("shortcuts", shortcuts, manager), 4)}
31
+ `,
32
+ { shortcuts }))
33
+ } else {
34
+ return Ok(shortcuts[0] as TriggerableShortcut)
35
+ }
36
+ }
37
+
@@ -0,0 +1,30 @@
1
+ /* Autogenerated Index [Ignore] */
2
+
3
+ export { calculateAndSetPositionAndSize } from "./calculateAndSetPositionAndWidth.js"
4
+ export { calculateLayoutSize } from "./calculateLayoutSize.js"
5
+ export { doesShortcutConflict } from "./doesShortcutConflict.js"
6
+ export { equalsCommand } from "./equalsCommand.js"
7
+ export { equalsContext } from "./equalsContext.js"
8
+ export { equalsShortcut } from "./equalsShortcut.js"
9
+ export { forceClear } from "./forceClear.js"
10
+ export { forceUpdateNativeKeysState } from "./forceUpdateNativeKeysState.js"
11
+ export { generateKeyShortcutMap } from "./generateKeyShortcutMap.js"
12
+ export { getKeyboardLayoutMap } from "./getKeyboardLayoutMap.js"
13
+ export { getKeyFromEventCode } from "./getKeyFromEventCode.js"
14
+ export { getKeyFromIdOrVariant } from "./getKeyFromIdOrVariant.js"
15
+ export { getLabel } from "./getLabel.js"
16
+ export { getTriggerableShortcut } from "./getTriggerableShortcut.js"
17
+ export { isValidManager } from "./isValidManager.js"
18
+ export { isValidShortcut } from "./isValidShortcut.js"
19
+ export { KnownError } from "./KnownError.js"
20
+ export { labelWithEvent } from "./labelWithEvent.js"
21
+ export { labelWithKeyboardMap } from "./labelWithKeyboardMap.js"
22
+ export { managerToStorableClone } from "./managerToStorableClone.js"
23
+ export { onKeyboardLayoutChange } from "./onKeyboardLayoutChange.js"
24
+ export { safeSetManagerChain } from "./safeSetManagerChain.js"
25
+ export { shortcutCanExecuteIn } from "./shortcutCanExecuteIn.js"
26
+ export { shortcutIsTriggerableBy } from "./shortcutIsTriggerableBy.js"
27
+ export { shortcutSwapChords } from "./shortcutSwapChords.js"
28
+ export { virtualPress } from "./virtualPress.js"
29
+ export { virtualRelease } from "./virtualRelease.js"
30
+ export { virtualToggle } from "./virtualToggle.js"
@@ -0,0 +1,29 @@
1
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { isValidShortcut } from "./isValidShortcut.js"
4
+ import { KnownError } from "./KnownError.js"
5
+
6
+ import { type ChainError, type Manager, type MultipleErrors, SHORTCUT_ERROR } from "../types/index.js"
7
+
8
+
9
+ export function isValidManager(manager: Manager): Result<true, MultipleErrors<
10
+ | ChainError
11
+ | typeof SHORTCUT_ERROR.INVALID_MANAGER
12
+ | typeof SHORTCUT_ERROR.UNKNOWN_COMMAND
13
+ >> {
14
+ const required = ["shortcuts", "keys", "commands"] as const
15
+ if (required.some(key => manager[key] === undefined)) {
16
+ return Err(new KnownError(
17
+ SHORTCUT_ERROR.INVALID_MANAGER,
18
+ "Manager is missing required properties.",
19
+ { keys: Object.keys(manager) }
20
+ ))
21
+ }
22
+ let res
23
+ for (const shortcut of manager.shortcuts.entries) {
24
+ res = isValidShortcut(shortcut, manager)
25
+ if (res.isError) return res
26
+ }
27
+
28
+ return Ok(true)
29
+ }
@@ -0,0 +1,20 @@
1
+ import { Ok, type Result } from "@alanscodelog/utils/Result"
2
+
3
+ import { isValidChain } from "../internal/isValidChain.js"
4
+ import { isValidCommand } from "../internal/isValidCommand.js"
5
+ import type { ChainError, Manager, MultipleErrors, PickManager, Shortcut, SHORTCUT_ERROR } from "../types/index.js"
6
+
7
+
8
+ export function isValidShortcut(
9
+ shortcut: Shortcut,
10
+ manager: Pick<Manager, "keys" | "commands"> & PickManager<"options", "stringifier" | "sorter">
11
+ ): Result<true, MultipleErrors<
12
+ | ChainError
13
+ | typeof SHORTCUT_ERROR.UNKNOWN_COMMAND
14
+ >> {
15
+ const resCommandsValid = isValidCommand(shortcut.command, manager, shortcut)
16
+ if (resCommandsValid.isError) return resCommandsValid
17
+ const resChainValid = isValidChain(shortcut.chain, manager)
18
+ if (resChainValid.isError) return resChainValid
19
+ return Ok(true)
20
+ }
@@ -0,0 +1,41 @@
1
+ import { setKeyProp } from "../core/setKeyProp.js"
2
+ import type { AnyInputEvent, KeySetEntries, LabelOptions, Manager, MinimalInputEvent, PickManager } from "../types/index.js"
3
+ /**
4
+ * Labels keys using events.
5
+ *
6
+ * For keys uses `KeyboardEvent.key`. For mouse buttons it will label them `Button x` where x is `MouseEvent.button`, and for the mouse wheel, `WheelEvent.deltaY` is used to label them `Wheel Up`/`Wheel Down`.
7
+ *
8
+ * This is intended to be used for labeling keys as they are pressed or as a fallback to {@link labelWithKeyboardMap}. See it for the recommended labeling strategy.
9
+ *
10
+ * A filter can be provided to avoid labeling some keys.
11
+ */
12
+ export function labelWithEvent<
13
+ T extends MinimalInputEvent | AnyInputEvent = MinimalInputEvent | AnyInputEvent,
14
+ THooks extends KeySetEntries["label"]["hooks"] = KeySetEntries["label"]["hooks"]
15
+ >(
16
+ e: T,
17
+ keyIds: string[],
18
+ manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier"> & { hooks?: THooks },
19
+ options: Partial<Omit<LabelOptions, "map">> = {}
20
+ ): string[] {
21
+ const set = []
22
+ for (const id of keyIds) {
23
+ let label = ""
24
+ // we don't use instanceof so that we can still be compatible with emulated events
25
+ if ("deltaY" in e) {
26
+ label = e.deltaY < 0 ? "Wheel Up" : "Wheel Down"
27
+ } else if ("button" in e) {
28
+ label = `Button ${e.button}`
29
+ } else if ("key" in e) {
30
+ label = e.key
31
+ }
32
+ if (!options.labelFilter || options.labelFilter(e, id, label, manager.keys)) {
33
+ const key = manager.keys.entries[id]
34
+ set.push(key.id)
35
+ // its fine to ignore any errors even if they're custom errors
36
+ setKeyProp(key, "label", label, manager)
37
+ }
38
+ }
39
+ return set
40
+ }
41
+
@@ -0,0 +1,37 @@
1
+ import { setKeyProp } from "../core/setKeyProp.js"
2
+ import type { KeySetEntries, LabelOptions, Manager } from "../types/index.js"
3
+
4
+ /**
5
+ * Labels keys using the experimental navigator keyboard [map](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardLayoutMap) (you can get it using {@link getKeyboardLayoutMap} which safely gets it if it's available, otherwise you can use [navigator.getLayoutMap](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/navigator.getLayoutMap)).
6
+ *
7
+ * It will check for the key in the map first by the id, then by their variants.
8
+ *
9
+ *It returns a list of key ids that were set.
10
+ *
11
+ * Note that not all keys can be auto labeled with the navigator. Modifier keys and some other keys are not available.
12
+ *
13
+ * You can use {@link labelWithEvent} as a fallback, by using it with a filter and only labeling the keys that were not set by this function.
14
+ */
15
+ export function labelWithKeyboardMap<
16
+ THooks extends KeySetEntries["label"]["hooks"]
17
+ >(
18
+ manager: Pick<Manager, "keys"> & { hooks?: THooks },
19
+ opts: LabelOptions
20
+ ): string[] {
21
+ const set = []
22
+ for (const key of Object.values(manager.keys.entries)) {
23
+ const codes = [key.id, ...(key.variants ?? [])]
24
+ for (const code of codes) {
25
+ const label = opts.map.get(code)
26
+ if (label) {
27
+ if (!opts.labelFilter || opts.labelFilter({ key: label }, key.id, label, manager.keys)) {
28
+ // its fine to ignore any errors even if they're custom errors
29
+ setKeyProp(key, "label", label, manager)
30
+ set.push(key.id)
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return set
36
+ }
37
+