@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,26 @@
|
|
|
1
|
+
import { setManagerProp } from "../core/setManagerProp.js"
|
|
2
|
+
import type { AnyInputEvent, Manager, TriggerableShortcut } from "../types/index.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function checkUntrigger(
|
|
6
|
+
manager: Manager,
|
|
7
|
+
e?: AnyInputEvent
|
|
8
|
+
): void {
|
|
9
|
+
if (manager.state.untrigger) {
|
|
10
|
+
const untrigger: TriggerableShortcut = manager.state.untrigger
|
|
11
|
+
// MUST be set to false first or we can get into an infinite loop if the user uses `set(chain)` from inside the command execute function
|
|
12
|
+
setManagerProp(manager, "state.untrigger", false)
|
|
13
|
+
setManagerProp(manager, "state.nextIsChord", false)
|
|
14
|
+
if (!manager.options.enableShortcuts) return
|
|
15
|
+
const command = manager.commands.entries[untrigger.command]
|
|
16
|
+
command.execute?.({
|
|
17
|
+
isKeydown: false,
|
|
18
|
+
command,
|
|
19
|
+
shortcut: untrigger,
|
|
20
|
+
event: e,
|
|
21
|
+
manager,
|
|
22
|
+
context: manager.context
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { last } from "@alanscodelog/utils/last"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A copy of the last chord in the chain if it exists.
|
|
5
|
+
*/
|
|
6
|
+
export function cloneLastChord(chain: string[][]): string[] | undefined {
|
|
7
|
+
const lastC = last(chain)
|
|
8
|
+
return lastC ? [...lastC] : undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
3
|
+
|
|
4
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
|
|
5
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
6
|
+
import { type Manager, type PickManager, SHORTCUT_ERROR } from "../types/index.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* If you have toggle keys in chords, their are "chains" of the same toggle key that would be physically impossible. For example: `toggle(on) toggle(on)`.
|
|
10
|
+
*
|
|
11
|
+
* This guards against that.
|
|
12
|
+
*
|
|
13
|
+
* This does NOT guard against duplicates and errors in the same chord, for that see {@link isValidChord}.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export function containsPossibleToggleChords(
|
|
18
|
+
chain: string[][],
|
|
19
|
+
manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier">
|
|
20
|
+
): Result<true, KnownError<typeof SHORTCUT_ERROR.IMPOSSIBLE_TOGGLE_SEQUENCE>> {
|
|
21
|
+
const keys = manager.keys
|
|
22
|
+
const s = manager.options.stringifier
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
const prevToggles: [string, number][] = []
|
|
26
|
+
let num = 0
|
|
27
|
+
let impossible: {
|
|
28
|
+
keyId: string
|
|
29
|
+
pos: number
|
|
30
|
+
} | undefined
|
|
31
|
+
for (const chord of chain) {
|
|
32
|
+
const toggleIds = chord.filter(keyId => (getKeyFromIdOrVariant(keyId, keys).unwrap()[0]).isToggle)
|
|
33
|
+
let found = false
|
|
34
|
+
for (let k = 0; k < toggleIds.length; k++) {
|
|
35
|
+
const currId = toggleIds[k]
|
|
36
|
+
// We've already been guaranteed there's maximum 1 of the same toggle per chord in isValidChord
|
|
37
|
+
// So we can use this to count if the amount of previous toggle states for each unique toggle key cancel out
|
|
38
|
+
const prevWithSameRootsIds = prevToggles.map(([prevId, j]) => {
|
|
39
|
+
const prev = getKeyFromIdOrVariant(prevId, keys).unwrap()[0]
|
|
40
|
+
const curr = getKeyFromIdOrVariant(currId, keys).unwrap()[0]
|
|
41
|
+
return (prev.id === curr.id || prev.variants?.includes(curr.id) || curr.variants?.includes(prev.id))
|
|
42
|
+
? [prevId, j]
|
|
43
|
+
: undefined
|
|
44
|
+
}).filter(key => key !== undefined) as [string, number][]
|
|
45
|
+
prevWithSameRootsIds.push([currId, num + k])
|
|
46
|
+
let canBeOn = true
|
|
47
|
+
let canBeOff = true
|
|
48
|
+
for (const [keyId, j] of prevWithSameRootsIds) {
|
|
49
|
+
// no need to check variants
|
|
50
|
+
const key = (keys.entries[keyId] ?? keys.toggles[keyId])
|
|
51
|
+
if (key.id === keyId) {
|
|
52
|
+
// Flip them since a root toggle functions as the opposite of the last toggle key state
|
|
53
|
+
// Note they can be both true, which is why we must literally flip them
|
|
54
|
+
const couldOn: boolean = canBeOn
|
|
55
|
+
const couldOff: boolean = canBeOff
|
|
56
|
+
canBeOn = couldOff
|
|
57
|
+
canBeOff = couldOn
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
const isOff = key.toggleOffId === keyId
|
|
61
|
+
const isOn = key.toggleOnId === keyId
|
|
62
|
+
if ((isOn && !canBeOn)
|
|
63
|
+
|| (isOff && !canBeOff)) {
|
|
64
|
+
impossible = { keyId, pos: j }
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
canBeOn = isOff
|
|
68
|
+
canBeOff = isOn
|
|
69
|
+
}
|
|
70
|
+
if (impossible) {
|
|
71
|
+
found = true; break
|
|
72
|
+
}
|
|
73
|
+
const currKey = getKeyFromIdOrVariant(currId, keys).unwrap()[0]
|
|
74
|
+
if (currKey.isToggle) {
|
|
75
|
+
prevToggles.push([currId, num + k])
|
|
76
|
+
}
|
|
77
|
+
num += chord.length
|
|
78
|
+
}
|
|
79
|
+
if (found) break
|
|
80
|
+
}
|
|
81
|
+
if (impossible) {
|
|
82
|
+
const prettyShortcut = s.stringify(chain, manager)
|
|
83
|
+
const { pos, keyId: key } = impossible
|
|
84
|
+
const e = Err(new KnownError(SHORTCUT_ERROR.IMPOSSIBLE_TOGGLE_SEQUENCE, crop`
|
|
85
|
+
Chain "${prettyShortcut}" is impossible.
|
|
86
|
+
This chain has a toggle key state "${s.stringify(key, manager)}" at key #${pos + 1} that would be impossible to trigger.
|
|
87
|
+
`, { chain, key: keys.entries[key] ?? keys.toggles[key], i: pos }))
|
|
88
|
+
return e
|
|
89
|
+
}
|
|
90
|
+
return Ok(true)
|
|
91
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { indent } from "@alanscodelog/utils/indent"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function errorTextAdd(
|
|
6
|
+
type: "Key" | "Shortcut" | "Command",
|
|
7
|
+
existingIdentifier: string,
|
|
8
|
+
existingEntry: string,
|
|
9
|
+
newEntry: string
|
|
10
|
+
): string {
|
|
11
|
+
return crop`
|
|
12
|
+
${type} ${existingIdentifier} is already registered.
|
|
13
|
+
Existing ${type}:
|
|
14
|
+
${indent(existingEntry.toString(), 3)}
|
|
15
|
+
New ${type}:
|
|
16
|
+
${indent(newEntry.toString(), 3)}
|
|
17
|
+
`
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { indent } from "@alanscodelog/utils/indent"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function errorTextInUse(type: "command" | "key", identifier: string, list: string): string {
|
|
6
|
+
return crop`
|
|
7
|
+
Cannot remove ${type} ${identifier}, it is in use by the following shortcuts:
|
|
8
|
+
${indent(list, 2)}
|
|
9
|
+
`
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { indent } from "@alanscodelog/utils/indent"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function errorTextRemove(type: string, identifier: string, entries: string): string {
|
|
6
|
+
return crop`
|
|
7
|
+
${type} ${identifier} does not exist in entries:
|
|
8
|
+
${indent(entries, 3)}
|
|
9
|
+
`
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AnyInputEvent, Key, PickManager } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function getModifierState(
|
|
5
|
+
key: Key,
|
|
6
|
+
e: AnyInputEvent,
|
|
7
|
+
manager: PickManager<"options", "updateStateOnAllEvents">
|
|
8
|
+
): boolean | null {
|
|
9
|
+
if (!manager.options.updateStateOnAllEvents || !key.updateStateOnAllEvents) return null
|
|
10
|
+
for (const code of [key.id, ...key.variants ?? []]) {
|
|
11
|
+
if ("getModifierState" in e && e.getModifierState(code)) return true
|
|
12
|
+
}
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Manager } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A list of currently pressed keys for convenience.
|
|
5
|
+
*
|
|
6
|
+
* It's just a wrapper around querying the key set for pressed keys.
|
|
7
|
+
*/
|
|
8
|
+
export function getPressedKeys(manager: Pick<Manager, "keys">): string[] {
|
|
9
|
+
const res: string[] = []
|
|
10
|
+
for (const keyId of Object.keys(manager.keys.entries)) {
|
|
11
|
+
const key = manager.keys.entries[keyId]
|
|
12
|
+
if (key.pressed) {
|
|
13
|
+
res.push(key.id)
|
|
14
|
+
}
|
|
15
|
+
if (key.isToggle) {
|
|
16
|
+
if (key.toggleOnPressed) {
|
|
17
|
+
res.push(key.toggleOnId)
|
|
18
|
+
}
|
|
19
|
+
if (key.toggleOffPressed) {
|
|
20
|
+
res.push(key.toggleOffId)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return res
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Manager } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function getPressedModifierKeys(manager: Pick<Manager, "keys">): string[] {
|
|
5
|
+
const res: string[] = []
|
|
6
|
+
for (const keyId of Object.keys(manager.keys.entries)) {
|
|
7
|
+
const key = manager.keys.entries[keyId]
|
|
8
|
+
if (key.pressed && key.isModifier) {
|
|
9
|
+
res.push(key.id)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return res
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Manager } from "../types/index.js"
|
|
2
|
+
import { isTriggerKey } from "../utils/isTriggerKey.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A list of currently pressed non-modifier keys. They can't be toggle state keys (i.e. `on`/`off`) either.
|
|
6
|
+
*
|
|
7
|
+
* This determines whether when we await keyup and when a new chord should be added.
|
|
8
|
+
*/
|
|
9
|
+
export function getPressedNonModifierKeys(manager: Pick<Manager, "keys">): string[] {
|
|
10
|
+
const res: string[] = []
|
|
11
|
+
for (const keyId of Object.keys(manager.keys.entries)) {
|
|
12
|
+
const key = manager.keys.entries[keyId]
|
|
13
|
+
if (key.pressed && isTriggerKey(manager.keys.entries[keyId])) {
|
|
14
|
+
res.push(key.id)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return res
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { shortcutCanExecuteIn } from "../helpers/shortcutCanExecuteIn.js"
|
|
2
|
+
import type { Manager } from "../types/index.js"
|
|
3
|
+
import { equalsKeys } from "../utils/equalsKeys.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if there are shortcuts that could be executed given the state of the manager's chain. That is, except if {@link Manager.isAwaitingKeyup} is true, in which case it returns false.
|
|
8
|
+
*/
|
|
9
|
+
export function inChain(manager: Manager): boolean {
|
|
10
|
+
if (manager.state.isAwaitingKeyup) return false
|
|
11
|
+
const shortcuts = manager.shortcuts.entries.filter(shortcut => (
|
|
12
|
+
shortcutCanExecuteIn(shortcut, manager, { allowEmptyCommand: true })
|
|
13
|
+
&& manager.state.chain.length < shortcut.chain.length
|
|
14
|
+
&& equalsKeys(
|
|
15
|
+
shortcut.chain,
|
|
16
|
+
manager.state.chain,
|
|
17
|
+
manager.keys,
|
|
18
|
+
manager.state.chain.length,
|
|
19
|
+
{ allowVariants: true }
|
|
20
|
+
)
|
|
21
|
+
))
|
|
22
|
+
if (!shortcuts) return false
|
|
23
|
+
return shortcuts.length > 0
|
|
24
|
+
}
|
|
25
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { areValidKeys } from "./areValidKeys.js"
|
|
4
|
+
import { containsPossibleToggleChords } from "./containsPossibleToggleChords.js"
|
|
5
|
+
import { isValidChord } from "./isValidChord.js"
|
|
6
|
+
|
|
7
|
+
import type { ChainError, Manager, MultipleErrors, PickManager } from "../types/index.js"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Guards against invalid chains (for various reasons). See the {@link ChainError}.
|
|
12
|
+
*
|
|
13
|
+
* Checks {@link areValidKeys}, {@link isValidChord} and {@link containsPossibleToggleChords}
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export function isValidChain(
|
|
18
|
+
chain: string[][],
|
|
19
|
+
manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier" | "sorter">
|
|
20
|
+
): Result<true,
|
|
21
|
+
MultipleErrors<ChainError>
|
|
22
|
+
> {
|
|
23
|
+
const sorter = manager.options.sorter
|
|
24
|
+
|
|
25
|
+
if ((chain.length === 0) || (chain.length === 1 && chain[0].length === 0)) {
|
|
26
|
+
return Ok(true)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resIsValid = areValidKeys(chain, manager)
|
|
30
|
+
if (resIsValid.isError) return resIsValid
|
|
31
|
+
|
|
32
|
+
const val = []
|
|
33
|
+
for (let i = 0; i < chain.length; i++) {
|
|
34
|
+
const chord = chain[i]
|
|
35
|
+
const res = isValidChord(chain, chord, i, manager)
|
|
36
|
+
if (res.isError) return res
|
|
37
|
+
val.push(sorter.sort([...chord], manager.keys))
|
|
38
|
+
}
|
|
39
|
+
const res = containsPossibleToggleChords(chain, manager)
|
|
40
|
+
if (res.isError) return res
|
|
41
|
+
return Ok(true)
|
|
42
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { findDuplicates } from "@alanscodelog/utils/findDuplicates"
|
|
3
|
+
import { indent } from "@alanscodelog/utils/indent"
|
|
4
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
5
|
+
|
|
6
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
|
|
7
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
8
|
+
import { type Manager, type PickManager, SHORTCUT_ERROR } from "../types/index.js"
|
|
9
|
+
import { isTriggerKey } from "../utils/isTriggerKey.js"
|
|
10
|
+
import { isWheelKey } from "../utils/isWheelKey.js"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Guards against invalid chords (for various reasons). See the {@link ERROR}.CHORD_W_... errors.
|
|
15
|
+
*
|
|
16
|
+
* Does not check if the keys are valid.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export function isValidChord(
|
|
21
|
+
chain: string[][],
|
|
22
|
+
chord: string[],
|
|
23
|
+
i: number,
|
|
24
|
+
manager: Pick<Manager, "keys"> & PickManager<"options", "stringifier" | "sorter">
|
|
25
|
+
): Result<true, KnownError<typeof SHORTCUT_ERROR.CHORD_W_DUPLICATE_KEY | typeof SHORTCUT_ERROR.CHORD_W_ONLY_MODIFIERS | typeof SHORTCUT_ERROR.CHORD_W_MULTIPLE_TRIGGER_KEYS | typeof SHORTCUT_ERROR.CHORD_W_MULTIPLE_WHEEL_KEYS>> {
|
|
26
|
+
const keys = manager.keys
|
|
27
|
+
const s = manager.options.stringifier
|
|
28
|
+
|
|
29
|
+
const prettyChord = s.stringify(chord, manager)
|
|
30
|
+
const prettyChain = s.stringify(chain, manager)
|
|
31
|
+
|
|
32
|
+
const repeated = findDuplicates(chord, { equals: (id, otherId) => {
|
|
33
|
+
const key = getKeyFromIdOrVariant(id, keys).unwrap()[0]
|
|
34
|
+
const other = getKeyFromIdOrVariant(otherId, keys).unwrap()[0]
|
|
35
|
+
if (key.id === other.id) return true
|
|
36
|
+
if (key.variants?.includes(other.id) || other.variants?.includes(key.id)) return true
|
|
37
|
+
// this is caught by multiple wheel key error
|
|
38
|
+
if (isWheelKey(key) && isWheelKey(other)) return false
|
|
39
|
+
return false
|
|
40
|
+
} })
|
|
41
|
+
if (repeated.length > 0) {
|
|
42
|
+
const prettyRepeated = s.stringifyList("keys", repeated, manager)
|
|
43
|
+
|
|
44
|
+
return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_DUPLICATE_KEY, crop`
|
|
45
|
+
Chord "${prettyChord}" in chain "${prettyChain}" contains duplicate or incompatible keys:
|
|
46
|
+
${indent(prettyRepeated, 4)}
|
|
47
|
+
Chords cannot contain duplicate keys. This includes more than one of the same toggle, regardless of the state.
|
|
48
|
+
`, { chord, i, keys: repeated }))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const onlyModifiers = chord.filter(id => (getKeyFromIdOrVariant(id, keys).unwrap()[0]).isModifier)
|
|
52
|
+
const containsOnlyModifiers = onlyModifiers.length === chord.length
|
|
53
|
+
if (i < chain.length - 1 && containsOnlyModifiers) {
|
|
54
|
+
return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_ONLY_MODIFIERS, crop`
|
|
55
|
+
Chain "${prettyChain}" is impossible.
|
|
56
|
+
Chord #${i + 1} "${prettyChord}" cannot contain only modifiers if it is followed by another chord.
|
|
57
|
+
A chord can only consist of only modifiers if it's the last chord in a chain.
|
|
58
|
+
`, { chord, i, keys: onlyModifiers }))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* It might actually be possible to allow this, similar to how emulated toggle keys are handled but it would be a pain for such an odd use case (even I don't have such weird shortcuts). */
|
|
62
|
+
const wheelKeys = chord.filter(id => isWheelKey(getKeyFromIdOrVariant(id, keys).unwrap()[0]))
|
|
63
|
+
const prettyWheelKeys = s.stringifyList("keys", wheelKeys, manager)
|
|
64
|
+
|
|
65
|
+
if (wheelKeys.length > 1) {
|
|
66
|
+
return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_MULTIPLE_WHEEL_KEYS, crop`
|
|
67
|
+
Chain "${prettyChain}" is impossible.
|
|
68
|
+
Chord #${i + 1} "${prettyChord}" contains multiple wheel keys: ${prettyWheelKeys}
|
|
69
|
+
Chords can only contain one.
|
|
70
|
+
`, { chord, i, keys: wheelKeys }))
|
|
71
|
+
}
|
|
72
|
+
// todo remove this limitation
|
|
73
|
+
// we should be able to have shortcuts like holding down both mouse buttons
|
|
74
|
+
// note it's fine to not check key.toggles in isTriggerKey because a trigger key cannot be a toggle *state* anyways
|
|
75
|
+
const triggerKeys = chord.filter(id => isTriggerKey(getKeyFromIdOrVariant(id, keys).unwrap()[0]))
|
|
76
|
+
const prettyTriggerKeys = s.stringifyList("keys", triggerKeys, manager)
|
|
77
|
+
if (triggerKeys.length > 1) {
|
|
78
|
+
return Err(new KnownError(SHORTCUT_ERROR.CHORD_W_MULTIPLE_TRIGGER_KEYS, crop`
|
|
79
|
+
Chain "${prettyChain}" is impossible.
|
|
80
|
+
Chord #${i + 1} "${prettyChord}" contains multiple trigger (non-modifier/non-root toggle) keys: ${prettyTriggerKeys}
|
|
81
|
+
Chords can only contain one.
|
|
82
|
+
`, { chord, i, keys: triggerKeys }))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
return Ok(true)
|
|
87
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { crop } from "@alanscodelog/utils/crop"
|
|
2
|
+
import { indent } from "@alanscodelog/utils/indent"
|
|
3
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
4
|
+
|
|
5
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
6
|
+
import type { Manager, MultipleErrors, PickManager, Shortcut } from "../types/index.js"
|
|
7
|
+
import { SHORTCUT_ERROR } from "../types/index.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export function isValidCommand(
|
|
14
|
+
commandName: string | undefined,
|
|
15
|
+
manager: Pick<Manager, "commands"> & Partial<Pick<Manager, "keys">> & PickManager<"options", "stringifier">,
|
|
16
|
+
/**
|
|
17
|
+
* Shortcut will only be added to error if the manager contains keys.
|
|
18
|
+
* Otherwise it will be ignored.
|
|
19
|
+
*/
|
|
20
|
+
shortcut?: Shortcut
|
|
21
|
+
): Result<true, MultipleErrors<typeof SHORTCUT_ERROR.UNKNOWN_COMMAND>> {
|
|
22
|
+
const commands = manager.commands
|
|
23
|
+
const s = manager.options.stringifier
|
|
24
|
+
if (commandName === undefined) return Ok(true)
|
|
25
|
+
const command = commands.entries[commandName]
|
|
26
|
+
if (command === undefined) {
|
|
27
|
+
const shortcutString = shortcut && "keys" in manager ? ` in shortcut ${s.stringify(shortcut, manager as Pick<Manager, "commands" | "keys">)}` : ""
|
|
28
|
+
return Err(new KnownError(SHORTCUT_ERROR.UNKNOWN_COMMAND, crop`
|
|
29
|
+
Unknown command: ${commandName}${shortcutString}. Cannot find in:
|
|
30
|
+
|
|
31
|
+
${indent(s.stringifyList("commands", Object.values(commands.entries)), 3)}
|
|
32
|
+
`, { command: commandName, commands, shortcut }))
|
|
33
|
+
}
|
|
34
|
+
return Ok(true)
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Key, KEY_SORT_POS, KeySortPos } from "../types/index.js"
|
|
2
|
+
import { isMouseKey } from "../utils/isMouseKey.js"
|
|
3
|
+
import { isWheelKey } from "../utils/isWheelKey.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* Determines the key order position based on a key's type and the given {@link KeySortPos}
|
|
9
|
+
*/
|
|
10
|
+
export function keyOrder(
|
|
11
|
+
key: Key,
|
|
12
|
+
dictOrEnum: KeySortPos
|
|
13
|
+
): number {
|
|
14
|
+
let type = [
|
|
15
|
+
key.isModifier ? "mod" : "",
|
|
16
|
+
key.isToggle ? "toggle" : "",
|
|
17
|
+
isMouseKey(key) ? "mouse" : "",
|
|
18
|
+
isWheelKey(key) ? "wheel" : ""
|
|
19
|
+
].join("")
|
|
20
|
+
if (type === "") {
|
|
21
|
+
type = "normal"
|
|
22
|
+
}
|
|
23
|
+
return dictOrEnum[type as keyof typeof KEY_SORT_POS]
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { checkTrigger } from "./checkTrigger.js"
|
|
4
|
+
import { checkUntrigger } from "./checkUntrigger.js"
|
|
5
|
+
import { cloneLastChord } from "./cloneLastChord.js"
|
|
6
|
+
import { getPressedNonModifierKeys } from "./getPressedNonModifierKeys.js"
|
|
7
|
+
|
|
8
|
+
import { setManagerProp } from "../core/setManagerProp.js"
|
|
9
|
+
import type { AnyInputEvent, Manager, ManagerSetEntries, MultipleErrors } from "../types/index.js"
|
|
10
|
+
import { cloneChain } from "../utils/cloneChain.js"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export function removeFromChain(
|
|
14
|
+
manager: Manager,
|
|
15
|
+
keys: string[],
|
|
16
|
+
e?: AnyInputEvent
|
|
17
|
+
): Result<true, MultipleErrors<ManagerSetEntries["state.chain"]["error"]>> {
|
|
18
|
+
if (keys.length === 0) return Ok(true)
|
|
19
|
+
if (manager.state.isAwaitingKeyup) {
|
|
20
|
+
checkUntrigger(manager, e)
|
|
21
|
+
if (getPressedNonModifierKeys(manager).length === 0) {
|
|
22
|
+
setManagerProp(manager, "state.isAwaitingKeyup", false)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (manager.state.nextIsChord) return Ok(true)
|
|
27
|
+
const lastChord = cloneLastChord(manager.state.chain) ?? []
|
|
28
|
+
for (const id of keys) {
|
|
29
|
+
const key = manager.keys.entries[id]
|
|
30
|
+
const i = lastChord.indexOf(key.id)
|
|
31
|
+
|
|
32
|
+
if (i > -1) {
|
|
33
|
+
lastChord.splice(i, 1)
|
|
34
|
+
const chain = manager.state.chain
|
|
35
|
+
const precedingChords = chain.slice(0, chain.length - 1)
|
|
36
|
+
const spreadableLastChord = precedingChords.length === 0 && lastChord.length === 0
|
|
37
|
+
? []
|
|
38
|
+
: [lastChord]
|
|
39
|
+
const res = setManagerProp(manager, "state.chain", cloneChain([...precedingChords, ...spreadableLastChord]))
|
|
40
|
+
if (res.isError) return res as any
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
checkTrigger(manager, e)
|
|
44
|
+
return Ok(true)
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { setKeyProp } from "../core/setKeyProp.js"
|
|
2
|
+
import type { Key, KeySetEntries, Manager } from "../types/index.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function safeSetEmulatedToggleState<
|
|
6
|
+
THooks extends Manager["hooks"]
|
|
7
|
+
>(
|
|
8
|
+
key: Key,
|
|
9
|
+
value: boolean,
|
|
10
|
+
manager: KeySetEntries["toggleOnPressed" | "toggleOffPressed"]["manager"] & { hooks?: THooks }
|
|
11
|
+
) {
|
|
12
|
+
const res = setKeyProp(key, "toggleOnPressed", value, manager, { check: "only" })
|
|
13
|
+
if (res.isOk) {
|
|
14
|
+
const res2 = setKeyProp(key, "toggleOffPressed", !value, manager, { check: "only" })
|
|
15
|
+
if (res2.isOk) {
|
|
16
|
+
setKeyProp(key, "toggleOnPressed", value, manager, { check: false })
|
|
17
|
+
setKeyProp(key, "toggleOffPressed", !value, manager, { check: false })
|
|
18
|
+
}
|
|
19
|
+
return res2
|
|
20
|
+
}
|
|
21
|
+
return res
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { safeSetEmulatedToggleState } from "./safeSetEmulatedToggleState.js"
|
|
4
|
+
|
|
5
|
+
import { setKeyProp } from "../core/setKeyProp.js"
|
|
6
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
7
|
+
import type { CanHookErrors, KeySetEntries, Manager, MultipleErrors } from "../types/index.js"
|
|
8
|
+
import { SHORTCUT_ERROR } from "../types/index.js"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export function setKeysState<
|
|
12
|
+
THooks extends Manager["hooks"]
|
|
13
|
+
>(
|
|
14
|
+
keysList: string[],
|
|
15
|
+
manager: KeySetEntries["pressed" | "toggleOnPressed" | "toggleOffPressed"]["manager"] & { hooks?: THooks },
|
|
16
|
+
state: boolean,
|
|
17
|
+
{
|
|
18
|
+
ignoreToggleType = false
|
|
19
|
+
}: {
|
|
20
|
+
ignoreToggleType?: boolean
|
|
21
|
+
} = {}
|
|
22
|
+
): Result<
|
|
23
|
+
true,
|
|
24
|
+
| MultipleErrors<
|
|
25
|
+
| typeof SHORTCUT_ERROR.CANNOT_SET_WHILE_DISABLED
|
|
26
|
+
| typeof SHORTCUT_ERROR.INCORRECT_TOGGLE_STATE
|
|
27
|
+
>
|
|
28
|
+
| CanHookErrors<THooks extends never ? never : THooks, "canSetKeyProp">
|
|
29
|
+
> {
|
|
30
|
+
const s = manager.options.stringifier
|
|
31
|
+
for (const id of keysList) {
|
|
32
|
+
const key = manager.keys.entries[id]
|
|
33
|
+
const isRoot = id === key.id
|
|
34
|
+
// this is only for the emulator
|
|
35
|
+
const isOn = id === key.toggleOnId
|
|
36
|
+
const isOff = id === key.toggleOffId
|
|
37
|
+
if ((isRoot && key.pressed === state)
|
|
38
|
+
|| (isOn && key.toggleOnPressed === state)
|
|
39
|
+
|| (isOff && key.toggleOffPressed === state)
|
|
40
|
+
) {
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
const res = setKeyProp(key, "pressed", state, manager)
|
|
44
|
+
if (res.isError) return res
|
|
45
|
+
if (isRoot && state) {
|
|
46
|
+
// toggles are considered active on keydown
|
|
47
|
+
if ((ignoreToggleType && key.isToggle) || key.isToggle === "emulated") {
|
|
48
|
+
// state was never set
|
|
49
|
+
if (key.toggleOnPressed && key.toggleOffPressed) {
|
|
50
|
+
return Err(new KnownError(
|
|
51
|
+
SHORTCUT_ERROR.INCORRECT_TOGGLE_STATE,
|
|
52
|
+
`Key ${s.stringify(key, manager)} is a toggle key whose on and off versions are both pressed, which is not a valid state. This should not happen if letting the manager manage the state.`,
|
|
53
|
+
{ key }
|
|
54
|
+
))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let res
|
|
58
|
+
if (!key.toggleOnPressed && !key.toggleOffPressed) {
|
|
59
|
+
res = safeSetEmulatedToggleState(key, true, manager)
|
|
60
|
+
} else if (key.toggleOnPressed) {
|
|
61
|
+
res = safeSetEmulatedToggleState(key, false, manager)
|
|
62
|
+
} else if (key.toggleOffPressed) {
|
|
63
|
+
res = safeSetEmulatedToggleState(key, true, manager)
|
|
64
|
+
}
|
|
65
|
+
if (res?.isError) return res
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return Ok(true)
|
|
70
|
+
}
|
|
71
|
+
|