@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.
- package/README.md +579 -0
- package/dist/core/EmulatedEvent.d.ts +10 -0
- package/dist/core/EmulatedEvent.js +19 -0
- package/dist/core/Emulator.d.ts +74 -0
- package/dist/core/Emulator.js +153 -0
- package/dist/core/ShortcutManagerManager.d.ts +103 -0
- package/dist/core/ShortcutManagerManager.js +267 -0
- package/dist/core/addCommand.d.ts +7 -0
- package/dist/core/addCommand.js +7 -0
- package/dist/core/addKey.d.ts +7 -0
- package/dist/core/addKey.js +7 -0
- package/dist/core/addShortcut.d.ts +7 -0
- package/dist/core/addShortcut.js +7 -0
- package/dist/core/attach.d.ts +10 -0
- package/dist/core/attach.js +13 -0
- package/dist/core/createCommand.d.ts +4 -0
- package/dist/core/createCommand.js +11 -0
- package/dist/core/createCommands.d.ts +11 -0
- package/dist/core/createCommands.js +20 -0
- package/dist/core/createCondition.d.ts +7 -0
- package/dist/core/createCondition.js +11 -0
- package/dist/core/createContext.d.ts +2 -0
- package/dist/core/createContext.js +8 -0
- package/dist/core/createKey.d.ts +10 -0
- package/dist/core/createKey.js +35 -0
- package/dist/core/createKeys.d.ts +5 -0
- package/dist/core/createKeys.js +36 -0
- package/dist/core/createManager.d.ts +81 -0
- package/dist/core/createManager.js +83 -0
- package/dist/core/createManagerEventListeners.d.ts +4 -0
- package/dist/core/createManagerEventListeners.js +130 -0
- package/dist/core/createManagerOptions.d.ts +8 -0
- package/dist/core/createManagerOptions.js +17 -0
- package/dist/core/createShortcut.d.ts +8 -0
- package/dist/core/createShortcut.js +24 -0
- package/dist/core/createShortcuts.d.ts +13 -0
- package/dist/core/createShortcuts.js +20 -0
- package/dist/core/detach.d.ts +10 -0
- package/dist/core/detach.js +6 -0
- package/dist/core/index.d.ts +32 -0
- package/dist/core/index.js +32 -0
- package/dist/core/removeCommand.d.ts +7 -0
- package/dist/core/removeCommand.js +7 -0
- package/dist/core/removeKey.d.ts +7 -0
- package/dist/core/removeKey.js +7 -0
- package/dist/core/removeShortcut.d.ts +7 -0
- package/dist/core/removeShortcut.js +7 -0
- package/dist/core/setCommandProp.d.ts +17 -0
- package/dist/core/setCommandProp.js +96 -0
- package/dist/core/setCommandsProp.d.ts +15 -0
- package/dist/core/setCommandsProp.js +94 -0
- package/dist/core/setKeyProp.d.ts +16 -0
- package/dist/core/setKeyProp.js +47 -0
- package/dist/core/setKeysProp.d.ts +10 -0
- package/dist/core/setKeysProp.js +166 -0
- package/dist/core/setManagerProp.d.ts +34 -0
- package/dist/core/setManagerProp.js +54 -0
- package/dist/core/setShortcutProp.d.ts +9 -0
- package/dist/core/setShortcutProp.js +50 -0
- package/dist/core/setShortcutsProp.d.ts +12 -0
- package/dist/core/setShortcutsProp.js +93 -0
- package/dist/defaults/KeysSorter.d.ts +35 -0
- package/dist/defaults/KeysSorter.js +20 -0
- package/dist/defaults/Stringifier.d.ts +66 -0
- package/dist/defaults/Stringifier.js +119 -0
- package/dist/defaults/defaultConditionEquals.d.ts +19 -0
- package/dist/defaults/defaultConditionEquals.js +5 -0
- package/dist/defaults/defaultManagerCallback.d.ts +7 -0
- package/dist/defaults/defaultManagerCallback.js +5 -0
- package/dist/helpers/KnownError.d.ts +8 -0
- package/dist/helpers/KnownError.js +3 -0
- package/dist/helpers/calculateAndSetPositionAndWidth.d.ts +13 -0
- package/dist/helpers/calculateAndSetPositionAndWidth.js +18 -0
- package/dist/helpers/calculateLayoutSize.d.ts +9 -0
- package/dist/helpers/calculateLayoutSize.js +13 -0
- package/dist/helpers/doesShortcutConflict.d.ts +18 -0
- package/dist/helpers/doesShortcutConflict.js +42 -0
- package/dist/helpers/equalsCommand.d.ts +7 -0
- package/dist/helpers/equalsCommand.js +4 -0
- package/dist/helpers/equalsContext.d.ts +7 -0
- package/dist/helpers/equalsContext.js +19 -0
- package/dist/helpers/equalsShortcut.d.ts +9 -0
- package/dist/helpers/equalsShortcut.js +7 -0
- package/dist/helpers/forceClear.d.ts +12 -0
- package/dist/helpers/forceClear.js +16 -0
- package/dist/helpers/forceUpdateNativeKeysState.d.ts +5 -0
- package/dist/helpers/forceUpdateNativeKeysState.js +4 -0
- package/dist/helpers/generateKeyShortcutMap.d.ts +23 -0
- package/dist/helpers/generateKeyShortcutMap.js +64 -0
- package/dist/helpers/getKeyFromEventCode.d.ts +15 -0
- package/dist/helpers/getKeyFromEventCode.js +36 -0
- package/dist/helpers/getKeyFromIdOrVariant.d.ts +4 -0
- package/dist/helpers/getKeyFromIdOrVariant.js +26 -0
- package/dist/helpers/getKeyboardLayoutMap.d.ts +5 -0
- package/dist/helpers/getKeyboardLayoutMap.js +7 -0
- package/dist/helpers/getLabel.d.ts +9 -0
- package/dist/helpers/getLabel.js +6 -0
- package/dist/helpers/getTriggerableShortcut.d.ts +6 -0
- package/dist/helpers/getTriggerableShortcut.js +25 -0
- package/dist/helpers/index.d.ts +28 -0
- package/dist/helpers/index.js +28 -0
- package/dist/helpers/isValidManager.d.ts +3 -0
- package/dist/helpers/isValidManager.js +20 -0
- package/dist/helpers/isValidShortcut.d.ts +3 -0
- package/dist/helpers/isValidShortcut.js +10 -0
- package/dist/helpers/labelWithEvent.d.ts +13 -0
- package/dist/helpers/labelWithEvent.js +20 -0
- package/dist/helpers/labelWithKeyboardMap.d.ts +15 -0
- package/dist/helpers/labelWithKeyboardMap.js +17 -0
- package/dist/helpers/managerToStorableClone.d.ts +12 -0
- package/dist/helpers/managerToStorableClone.js +13 -0
- package/dist/helpers/onKeyboardLayoutChange.d.ts +10 -0
- package/dist/helpers/onKeyboardLayoutChange.js +7 -0
- package/dist/helpers/safeSetManagerChain.d.ts +20 -0
- package/dist/helpers/safeSetManagerChain.js +37 -0
- package/dist/helpers/shortcutCanExecuteIn.d.ts +4 -0
- package/dist/helpers/shortcutCanExecuteIn.js +9 -0
- package/dist/helpers/shortcutIsTriggerableBy.d.ts +2 -0
- package/dist/helpers/shortcutIsTriggerableBy.js +5 -0
- package/dist/helpers/shortcutSwapChords.d.ts +75 -0
- package/dist/helpers/shortcutSwapChords.js +118 -0
- package/dist/helpers/virtualPress.d.ts +13 -0
- package/dist/helpers/virtualPress.js +15 -0
- package/dist/helpers/virtualRelease.d.ts +5 -0
- package/dist/helpers/virtualRelease.js +15 -0
- package/dist/helpers/virtualToggle.d.ts +7 -0
- package/dist/helpers/virtualToggle.js +16 -0
- package/dist/internal/addToChain.d.ts +3 -0
- package/dist/internal/addToChain.js +27 -0
- package/dist/internal/areValidKeys.d.ts +7 -0
- package/dist/internal/areValidKeys.js +28 -0
- package/dist/internal/areValidVariants.d.ts +4 -0
- package/dist/internal/areValidVariants.js +45 -0
- package/dist/internal/checkTrigger.d.ts +2 -0
- package/dist/internal/checkTrigger.js +47 -0
- package/dist/internal/checkUntrigger.d.ts +2 -0
- package/dist/internal/checkUntrigger.js +18 -0
- package/dist/internal/cloneLastChord.d.ts +4 -0
- package/dist/internal/cloneLastChord.js +5 -0
- package/dist/internal/containsPossibleToggleChords.d.ts +13 -0
- package/dist/internal/containsPossibleToggleChords.js +65 -0
- package/dist/internal/errorTextAdd.d.ts +1 -0
- package/dist/internal/errorTextAdd.js +11 -0
- package/dist/internal/errorTextInUse.d.ts +1 -0
- package/dist/internal/errorTextInUse.js +8 -0
- package/dist/internal/errorTextRemove.d.ts +1 -0
- package/dist/internal/errorTextRemove.js +8 -0
- package/dist/internal/getModifierState.d.ts +2 -0
- package/dist/internal/getModifierState.js +7 -0
- package/dist/internal/getPressedKeys.d.ts +7 -0
- package/dist/internal/getPressedKeys.js +18 -0
- package/dist/internal/getPressedModifierKeys.d.ts +2 -0
- package/dist/internal/getPressedModifierKeys.js +10 -0
- package/dist/internal/getPressedNonModifierKeys.d.ts +7 -0
- package/dist/internal/getPressedNonModifierKeys.js +11 -0
- package/dist/internal/inChain.d.ts +5 -0
- package/dist/internal/inChain.js +14 -0
- package/dist/internal/isValidChain.d.ts +10 -0
- package/dist/internal/isValidChain.js +22 -0
- package/dist/internal/isValidChord.d.ts +11 -0
- package/dist/internal/isValidChord.js +59 -0
- package/dist/internal/isValidCommand.d.ts +12 -0
- package/dist/internal/isValidCommand.js +20 -0
- package/dist/internal/keyOrder.d.ts +6 -0
- package/dist/internal/keyOrder.js +14 -0
- package/dist/internal/removeFromChain.d.ts +3 -0
- package/dist/internal/removeFromChain.js +32 -0
- package/dist/internal/safeSetEmulatedToggleState.d.ts +4 -0
- package/dist/internal/safeSetEmulatedToggleState.js +13 -0
- package/dist/internal/setKeysState.d.ts +8 -0
- package/dist/internal/setKeysState.js +42 -0
- package/dist/internal/updateNativeKeysState.d.ts +14 -0
- package/dist/internal/updateNativeKeysState.js +58 -0
- package/dist/layouts/createLayout.d.ts +25 -0
- package/dist/layouts/createLayout.js +221 -0
- package/dist/module.d.mts +13 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +40 -0
- package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.d.ts +9 -0
- package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.js +19 -0
- package/dist/runtime/composables/usePointerCoords.d.ts +32 -0
- package/dist/runtime/composables/usePointerCoords.js +17 -0
- package/dist/runtime/composables/useShortcutManagerContextCount.d.ts +14 -0
- package/dist/runtime/composables/useShortcutManagerContextCount.js +61 -0
- package/dist/runtime/composables/useShortcutManagerKeysLayout.d.ts +18 -0
- package/dist/runtime/composables/useShortcutManagerKeysLayout.js +22 -0
- package/dist/runtime/composables/useShortcutManagerVirtualPress.d.ts +6 -0
- package/dist/runtime/composables/useShortcutManagerVirtualPress.js +24 -0
- package/dist/runtime/types.d.ts +10 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/utils/shortcutToId.d.ts +5 -0
- package/dist/runtime/utils/shortcutToId.js +6 -0
- package/dist/types/commands.d.ts +113 -0
- package/dist/types/commands.js +0 -0
- package/dist/types/condition.d.ts +29 -0
- package/dist/types/condition.js +0 -0
- package/dist/types/context.d.ts +18 -0
- package/dist/types/context.js +0 -0
- package/dist/types/enums.d.ts +186 -0
- package/dist/types/enums.js +70 -0
- package/dist/types/general.d.ts +92 -0
- package/dist/types/general.js +0 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +8 -0
- package/dist/types/keys.d.ts +332 -0
- package/dist/types/keys.js +0 -0
- package/dist/types/manager.d.ts +249 -0
- package/dist/types/manager.js +0 -0
- package/dist/types/plugins.d.ts +1 -0
- package/dist/types/plugins.js +0 -0
- package/dist/types/shortcuts.d.ts +144 -0
- package/dist/types/shortcuts.js +0 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.js +0 -0
- package/dist/types.d.mts +3 -0
- package/dist/utils/chainContainsSubset.d.ts +27 -0
- package/dist/utils/chainContainsSubset.js +45 -0
- package/dist/utils/cloneChain.d.ts +2 -0
- package/dist/utils/cloneChain.js +11 -0
- package/dist/utils/cloneKey.d.ts +3 -0
- package/dist/utils/cloneKey.js +26 -0
- package/dist/utils/containsKey.d.ts +7 -0
- package/dist/utils/containsKey.js +4 -0
- package/dist/utils/dedupeKeys.d.ts +9 -0
- package/dist/utils/dedupeKeys.js +9 -0
- package/dist/utils/equalsKey.d.ts +12 -0
- package/dist/utils/equalsKey.js +13 -0
- package/dist/utils/equalsKeys.d.ts +24 -0
- package/dist/utils/equalsKeys.js +15 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.js +14 -0
- package/dist/utils/isAnyKey.d.ts +5 -0
- package/dist/utils/isAnyKey.js +5 -0
- package/dist/utils/isMouseKey.d.ts +5 -0
- package/dist/utils/isMouseKey.js +20 -0
- package/dist/utils/isNormalKey.d.ts +5 -0
- package/dist/utils/isNormalKey.js +5 -0
- package/dist/utils/isTriggerKey.d.ts +5 -0
- package/dist/utils/isTriggerKey.js +3 -0
- package/dist/utils/isWheelKey.d.ts +5 -0
- package/dist/utils/isWheelKey.js +11 -0
- package/dist/utils/mapKeys.d.ts +8 -0
- package/dist/utils/mapKeys.js +5 -0
- package/dist/utils/removeKeys.d.ts +7 -0
- package/dist/utils/removeKeys.js +4 -0
- package/package.json +156 -0
- package/src/core/EmulatedEvent.ts +29 -0
- package/src/core/Emulator.ts +185 -0
- package/src/core/ShortcutManagerManager.ts +380 -0
- package/src/core/addCommand.ts +24 -0
- package/src/core/addKey.ts +25 -0
- package/src/core/addShortcut.ts +24 -0
- package/src/core/attach.ts +27 -0
- package/src/core/createCommand.ts +24 -0
- package/src/core/createCommands.ts +45 -0
- package/src/core/createCondition.ts +21 -0
- package/src/core/createContext.ts +14 -0
- package/src/core/createKey.ts +59 -0
- package/src/core/createKeys.ts +68 -0
- package/src/core/createManager.ts +209 -0
- package/src/core/createManagerEventListeners.ts +139 -0
- package/src/core/createManagerOptions.ts +29 -0
- package/src/core/createShortcut.ts +49 -0
- package/src/core/createShortcuts.ts +51 -0
- package/src/core/detach.ts +21 -0
- package/src/core/index.ts +35 -0
- package/src/core/removeCommand.ts +25 -0
- package/src/core/removeKey.ts +25 -0
- package/src/core/removeShortcut.ts +24 -0
- package/src/core/setCommandProp.ts +140 -0
- package/src/core/setCommandsProp.ts +128 -0
- package/src/core/setKeyProp.ts +80 -0
- package/src/core/setKeysProp.ts +205 -0
- package/src/core/setManagerProp.ts +111 -0
- package/src/core/setShortcutProp.ts +89 -0
- package/src/core/setShortcutsProp.ts +124 -0
- package/src/defaults/KeysSorter.ts +55 -0
- package/src/defaults/Stringifier.ts +234 -0
- package/src/defaults/defaultConditionEquals.ts +29 -0
- package/src/defaults/defaultManagerCallback.ts +14 -0
- package/src/helpers/KnownError.ts +13 -0
- package/src/helpers/calculateAndSetPositionAndWidth.ts +30 -0
- package/src/helpers/calculateLayoutSize.ts +22 -0
- package/src/helpers/doesShortcutConflict.ts +72 -0
- package/src/helpers/equalsCommand.ts +18 -0
- package/src/helpers/equalsContext.ts +29 -0
- package/src/helpers/equalsShortcut.ts +34 -0
- package/src/helpers/forceClear.ts +31 -0
- package/src/helpers/forceUpdateNativeKeysState.ts +9 -0
- package/src/helpers/generateKeyShortcutMap.ts +113 -0
- package/src/helpers/getKeyFromEventCode.ts +67 -0
- package/src/helpers/getKeyFromIdOrVariant.ts +33 -0
- package/src/helpers/getKeyboardLayoutMap.ts +13 -0
- package/src/helpers/getLabel.ts +15 -0
- package/src/helpers/getTriggerableShortcut.ts +37 -0
- package/src/helpers/index.ts +30 -0
- package/src/helpers/isValidManager.ts +29 -0
- package/src/helpers/isValidShortcut.ts +20 -0
- package/src/helpers/labelWithEvent.ts +41 -0
- package/src/helpers/labelWithKeyboardMap.ts +37 -0
- package/src/helpers/managerToStorableClone.ts +26 -0
- package/src/helpers/onKeyboardLayoutChange.ts +17 -0
- package/src/helpers/safeSetManagerChain.ts +66 -0
- package/src/helpers/shortcutCanExecuteIn.ts +24 -0
- package/src/helpers/shortcutIsTriggerableBy.ts +15 -0
- package/src/helpers/shortcutSwapChords.ts +240 -0
- package/src/helpers/virtualPress.ts +34 -0
- package/src/helpers/virtualRelease.ts +25 -0
- package/src/helpers/virtualToggle.ts +28 -0
- package/src/internal/addToChain.ts +40 -0
- package/src/internal/areValidKeys.ts +40 -0
- package/src/internal/areValidVariants.ts +59 -0
- package/src/internal/checkTrigger.ts +55 -0
- package/src/internal/checkUntrigger.ts +26 -0
- package/src/internal/cloneLastChord.ts +10 -0
- package/src/internal/containsPossibleToggleChords.ts +91 -0
- package/src/internal/errorTextAdd.ts +18 -0
- package/src/internal/errorTextInUse.ts +10 -0
- package/src/internal/errorTextRemove.ts +10 -0
- package/src/internal/getModifierState.ts +15 -0
- package/src/internal/getPressedKeys.ts +26 -0
- package/src/internal/getPressedModifierKeys.ts +14 -0
- package/src/internal/getPressedNonModifierKeys.ts +19 -0
- package/src/internal/inChain.ts +25 -0
- package/src/internal/isValidChain.ts +42 -0
- package/src/internal/isValidChord.ts +87 -0
- package/src/internal/isValidCommand.ts +35 -0
- package/src/internal/keyOrder.ts +24 -0
- package/src/internal/removeFromChain.ts +46 -0
- package/src/internal/safeSetEmulatedToggleState.ts +23 -0
- package/src/internal/setKeysState.ts +71 -0
- package/src/internal/updateNativeKeysState.ts +84 -0
- package/src/layouts/createLayout.ts +328 -0
- package/src/module.ts +62 -0
- package/src/runtime/composables/useLabeledByKeyboardLayoutMap.ts +28 -0
- package/src/runtime/composables/usePointerCoords.ts +40 -0
- package/src/runtime/composables/useShortcutManagerContextCount.ts +81 -0
- package/src/runtime/composables/useShortcutManagerKeysLayout.ts +42 -0
- package/src/runtime/composables/useShortcutManagerVirtualPress.ts +30 -0
- package/src/runtime/types.ts +10 -0
- package/src/runtime/utils/shortcutToId.ts +14 -0
- package/src/types/commands.ts +148 -0
- package/src/types/condition.ts +35 -0
- package/src/types/context.ts +19 -0
- package/src/types/enums.ts +236 -0
- package/src/types/general.ts +117 -0
- package/src/types/index.ts +10 -0
- package/src/types/keys.ts +385 -0
- package/src/types/manager.ts +374 -0
- package/src/types/plugins.ts +32 -0
- package/src/types/shortcuts.ts +204 -0
- package/src/types/utils.ts +40 -0
- package/src/utils/chainContainsSubset.ts +97 -0
- package/src/utils/cloneChain.ts +13 -0
- package/src/utils/cloneKey.ts +33 -0
- package/src/utils/containsKey.ts +17 -0
- package/src/utils/dedupeKeys.ts +23 -0
- package/src/utils/equalsKey.ts +32 -0
- package/src/utils/equalsKeys.ts +50 -0
- package/src/utils/index.ts +16 -0
- package/src/utils/isAnyKey.ts +12 -0
- package/src/utils/isMouseKey.ts +27 -0
- package/src/utils/isNormalKey.ts +15 -0
- package/src/utils/isTriggerKey.ts +7 -0
- package/src/utils/isWheelKey.ts +18 -0
- package/src/utils/mapKeys.ts +21 -0
- 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
|
+
|