@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,93 @@
|
|
|
1
|
+
import { castType } from "@alanscodelog/utils/castType";
|
|
2
|
+
import { Err, Ok } from "@alanscodelog/utils/Result";
|
|
3
|
+
import { doesShortcutConflict } from "../helpers/doesShortcutConflict.js";
|
|
4
|
+
import { equalsShortcut } from "../helpers/equalsShortcut.js";
|
|
5
|
+
import { isValidShortcut } from "../helpers/isValidShortcut.js";
|
|
6
|
+
import { KnownError } from "../helpers/KnownError.js";
|
|
7
|
+
import { errorTextAdd } from "../internal/errorTextAdd.js";
|
|
8
|
+
import { errorTextRemove } from "../internal/errorTextRemove.js";
|
|
9
|
+
import { SHORTCUT_ERROR } from "../types/index.js";
|
|
10
|
+
const canHookable = ["entries@add", "entries@remove"];
|
|
11
|
+
export function setShortcutsProp(prop, val, manager, {
|
|
12
|
+
check = true
|
|
13
|
+
} = {}) {
|
|
14
|
+
const s = manager.options.stringifier;
|
|
15
|
+
const shortcuts = manager.shortcuts;
|
|
16
|
+
if (check) {
|
|
17
|
+
switch (prop) {
|
|
18
|
+
case "entries@add": {
|
|
19
|
+
castType(val);
|
|
20
|
+
castType(manager);
|
|
21
|
+
const shortcut = val;
|
|
22
|
+
const existing = shortcuts.entries.find(
|
|
23
|
+
(_) => equalsShortcut(shortcut, _, manager, { ignoreCommand: true }) || doesShortcutConflict(_, shortcut, manager)
|
|
24
|
+
);
|
|
25
|
+
if (existing) {
|
|
26
|
+
return Err(new KnownError(
|
|
27
|
+
SHORTCUT_ERROR.DUPLICATE_SHORTCUT,
|
|
28
|
+
errorTextAdd(
|
|
29
|
+
"Shortcut",
|
|
30
|
+
s.stringify(existing.chain, manager),
|
|
31
|
+
s.stringify(existing, manager),
|
|
32
|
+
s.stringify(shortcut, manager)
|
|
33
|
+
),
|
|
34
|
+
{ existing, self: shortcuts }
|
|
35
|
+
));
|
|
36
|
+
}
|
|
37
|
+
const isValid = isValidShortcut(shortcut, manager);
|
|
38
|
+
if (isValid.isError) return isValid;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "entries@remove": {
|
|
42
|
+
const shortcut = val;
|
|
43
|
+
const existing = shortcuts.entries.find(
|
|
44
|
+
(_) => _ === shortcut || equalsShortcut(shortcut, _, manager, { ignoreCommand: false })
|
|
45
|
+
);
|
|
46
|
+
if (existing === void 0) {
|
|
47
|
+
return Err(new KnownError(
|
|
48
|
+
SHORTCUT_ERROR.MISSING,
|
|
49
|
+
errorTextRemove(
|
|
50
|
+
"Shortcut",
|
|
51
|
+
s.stringify(shortcut, manager),
|
|
52
|
+
s.stringifyList("shortcuts", shortcuts.entries, manager)
|
|
53
|
+
),
|
|
54
|
+
{ entry: shortcut, self: shortcuts }
|
|
55
|
+
));
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default:
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
if (manager?.hooks && "canSetShortcutsProp" in manager.hooks && canHookable.includes(prop)) {
|
|
63
|
+
const canHook = manager.hooks.canSetShortcutsProp?.(shortcuts, prop, val);
|
|
64
|
+
if (canHook instanceof Error) {
|
|
65
|
+
return Err(canHook);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (check === "only") {
|
|
70
|
+
return Ok(true);
|
|
71
|
+
}
|
|
72
|
+
switch (prop) {
|
|
73
|
+
case "entries@add": {
|
|
74
|
+
const shortcut = val;
|
|
75
|
+
shortcuts.entries.push(shortcut);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "entries@remove": {
|
|
79
|
+
const shortcut = val;
|
|
80
|
+
const i = shortcuts.entries.findIndex((_) => _ === shortcut || equalsShortcut(shortcut, _, manager, { ignoreCommand: false }));
|
|
81
|
+
if (i < 0) {
|
|
82
|
+
throw new Error("If used correctly, shortcut should exist at this point, but it does not.");
|
|
83
|
+
}
|
|
84
|
+
shortcuts.entries.splice(i, 1);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
shortcuts[prop] = val;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
manager.hooks?.onSetShortcutsProp?.(shortcuts, prop, val);
|
|
92
|
+
return Ok(shortcuts);
|
|
93
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type IKeysSorter, type Keys, type KeySortPos } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The default class based implementation of the {@link IKeysSorter} interface.
|
|
4
|
+
*
|
|
5
|
+
* Creates a keys sorter for shortcut chains.
|
|
6
|
+
*
|
|
7
|
+
* Can either be passed some (object) enum of KeySortPos or it's keys with the values changed, for example:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const MyKeySortPos = {
|
|
10
|
+
* [KEY_SORT_POS.modmouse]: 0,
|
|
11
|
+
* [KEY_SORT_POS.mod]: 1,
|
|
12
|
+
* // or
|
|
13
|
+
* modmouse: 0,
|
|
14
|
+
* mod: 1,
|
|
15
|
+
* //...
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* const mySorter = new KeysSorter(MyKeySortPos)
|
|
19
|
+
* new Shortcut([...keys], {sorter: mySorter})
|
|
20
|
+
* ```
|
|
21
|
+
* They way this works is the enum should contain every possible combination of key "types". All sort does is determine the type and use it's position in the enum to know where to sort it. If two keys are of the same type, they are sorted alphabetically by their id.
|
|
22
|
+
*
|
|
23
|
+
* Or you can extend from the class and implement a custom sort function.
|
|
24
|
+
*
|
|
25
|
+
* Ideally a single sorter should be created and shared amongst all instances. This is already taken care of if you do not pass a custom sorter, a default sorter instance is re-used throughout.
|
|
26
|
+
*
|
|
27
|
+
* The order of the default sorter can be changed without creating a new class by importing it early and changing it's `order` property.
|
|
28
|
+
*/
|
|
29
|
+
export declare class KeysSorter implements IKeysSorter {
|
|
30
|
+
private _sort;
|
|
31
|
+
order: KeySortPos;
|
|
32
|
+
constructor(order?: KeysSorter["order"]);
|
|
33
|
+
sort(keyList: string[], keys: Keys): string[];
|
|
34
|
+
}
|
|
35
|
+
export declare const defaultSorter: KeysSorter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js";
|
|
2
|
+
import { keyOrder } from "../internal/keyOrder.js";
|
|
3
|
+
import { KEY_SORT_POS } from "../types/index.js";
|
|
4
|
+
export class KeysSorter {
|
|
5
|
+
_sort(aId, bId, keys, order) {
|
|
6
|
+
const a = getKeyFromIdOrVariant(aId, keys).unwrap()[0];
|
|
7
|
+
const b = getKeyFromIdOrVariant(bId, keys).unwrap()[0];
|
|
8
|
+
if (keyOrder(a, order) < keyOrder(b, order)) return -1;
|
|
9
|
+
if (keyOrder(b, order) < keyOrder(a, order)) return 1;
|
|
10
|
+
return aId.localeCompare(bId);
|
|
11
|
+
}
|
|
12
|
+
order;
|
|
13
|
+
constructor(order = KEY_SORT_POS) {
|
|
14
|
+
this.order = order;
|
|
15
|
+
}
|
|
16
|
+
sort(keyList, keys) {
|
|
17
|
+
return keyList.sort((a, b) => this._sort(a, b, keys, this.order));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const defaultSorter = new KeysSorter();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Command, Condition, DefaultStringifierOptions, IStringifier, Key, Manager, Shortcut } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The default class based implementation of the {@link IStringifier} interface.
|
|
4
|
+
*
|
|
5
|
+
* It can be passed (and a default instance is passed by default) to most functions to specify how to stringify items in errors.
|
|
6
|
+
*
|
|
7
|
+
* The default method `stringify` can be called with any key, chord, chain, shortcut, etc. and calls the respective {@link DefaultStringifierOptions} method depending on the type property or in the case of chains, chords, and keys, the array depth.
|
|
8
|
+
*
|
|
9
|
+
* That method called then calls any others it needs (e.g. if you pass a shortcut, it will call `stringifyShortcut` which will call `stringifyCommand`, `stringifyCondition`, and `stringifyChain`, which will call `stringifyChord` and so on)
|
|
10
|
+
*
|
|
11
|
+
* These can be customized by changing the options of the default instance (see Customizing below). These options are methods that describe in simpler term how items should be joined, without handling all the logic (the class pieces it all together)
|
|
12
|
+
*
|
|
13
|
+
* For chains the default method uses a key's label and combines keys inside chords with `+` and the chords of shortcut chains with a space ` `.
|
|
14
|
+
* ```
|
|
15
|
+
* Key+Key Key+Key+Key
|
|
16
|
+
* ^Chord^ ^Chord ^
|
|
17
|
+
* ^Chain ^
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* For shortcuts, the default is:
|
|
21
|
+
* ```
|
|
22
|
+
* Shortcut Key+Key Key+Key+Key (command: command_name, condition: condition_text)
|
|
23
|
+
* ```
|
|
24
|
+
* If the condition or command are undefined:
|
|
25
|
+
* - For command it will still say it's undefined.
|
|
26
|
+
* - The condition is removed entirely if it's undefined.
|
|
27
|
+
* ```
|
|
28
|
+
* Shortcut Key+Key Key+Key+Key (command: undefined)
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* `stringifyLists` returns a lists joined by a comma and new line:
|
|
32
|
+
*
|
|
33
|
+
* ```
|
|
34
|
+
* item,
|
|
35
|
+
* item
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ## Customizing
|
|
39
|
+
* Ideally a single stringifier should be created and shared amongst all instances. This is already taken care of if you do not pass a custom stringifier, a default stringifier instance is re-used throughout. Unless you're implementing your own {@link IStringifier}, you should not need to pass the default one around.
|
|
40
|
+
*
|
|
41
|
+
* You can just import it early and change it's options.
|
|
42
|
+
*/
|
|
43
|
+
export declare class Stringifier implements IStringifier {
|
|
44
|
+
opts: DefaultStringifierOptions;
|
|
45
|
+
constructor(opts?: DefaultStringifierOptions);
|
|
46
|
+
stringify(entry: string | string[] | string[][], manager: Pick<Manager, "keys">): string;
|
|
47
|
+
stringify(entry: Shortcut, manager: Pick<Manager, "keys" | "commands">): string;
|
|
48
|
+
stringify(entry: Key | Key[] | Command | Condition): string;
|
|
49
|
+
stringifyPropertyValue(entry: any): string;
|
|
50
|
+
protected stringifyShortcut(shortcut: Shortcut, manager: Pick<Manager, "keys" | "commands">): string;
|
|
51
|
+
protected stringifyCondition(condition?: Condition): string;
|
|
52
|
+
stringifyCommand(name: string, manager: Pick<Manager, "commands">): string;
|
|
53
|
+
stringifyCommand(command?: Command): string;
|
|
54
|
+
protected stringifyKey(key: Key): string;
|
|
55
|
+
protected stringifyKey(key: string, manager: Pick<Manager, "keys">): string;
|
|
56
|
+
protected stringifyChord(chord: Key[]): string;
|
|
57
|
+
protected stringifyChord(chord: string[], manager: Pick<Manager, "keys">): string;
|
|
58
|
+
protected stringifyChain(chain: Key[][]): string;
|
|
59
|
+
protected stringifyChain(chain: string[][], manager: Pick<Manager, "keys">): string;
|
|
60
|
+
stringifyList(type: "keys", entries: Key[]): string;
|
|
61
|
+
stringifyList(type: "commands", entries: Command[]): string;
|
|
62
|
+
stringifyList(type: "keys", entries: string[], manager: Pick<Manager, "keys">): string;
|
|
63
|
+
stringifyList(type: "commands", entries: string[], manager: Pick<Manager, "commands">): string;
|
|
64
|
+
stringifyList(type: "shortcuts", entries: Shortcut[], manager: Pick<Manager, "keys" | "commands">): string;
|
|
65
|
+
}
|
|
66
|
+
export declare const defaultStringifier: Stringifier;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { castType } from "@alanscodelog/utils/castType";
|
|
2
|
+
import { crop } from "@alanscodelog/utils/crop";
|
|
3
|
+
import { dedupe } from "@alanscodelog/utils/dedupe";
|
|
4
|
+
import { isArray } from "@alanscodelog/utils/isArray";
|
|
5
|
+
import { isObject } from "@alanscodelog/utils/isObject";
|
|
6
|
+
import { pretty } from "@alanscodelog/utils/pretty";
|
|
7
|
+
import { unreachable } from "@alanscodelog/utils/unreachable";
|
|
8
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js";
|
|
9
|
+
import { getLabel } from "../helpers/getLabel.js";
|
|
10
|
+
export class Stringifier {
|
|
11
|
+
opts;
|
|
12
|
+
constructor(opts = {}) {
|
|
13
|
+
this.opts = opts;
|
|
14
|
+
}
|
|
15
|
+
stringify(entry, manager) {
|
|
16
|
+
if (isObject(entry) && "type" in entry) {
|
|
17
|
+
switch (entry.type) {
|
|
18
|
+
case "key":
|
|
19
|
+
return this.stringifyKey(entry);
|
|
20
|
+
case "shortcut":
|
|
21
|
+
return this.stringifyShortcut(entry, manager);
|
|
22
|
+
case "command":
|
|
23
|
+
return this.stringifyCommand(entry, manager);
|
|
24
|
+
case "condition":
|
|
25
|
+
return this.stringifyCondition(entry);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
if (isArray(entry)) {
|
|
29
|
+
if (entry.length === 0) return this.stringifyChord([]);
|
|
30
|
+
if (!isArray(entry[0])) {
|
|
31
|
+
castType(entry);
|
|
32
|
+
return this.stringifyChord(entry, manager);
|
|
33
|
+
}
|
|
34
|
+
castType(entry);
|
|
35
|
+
return this.stringifyChain(entry, manager);
|
|
36
|
+
} else {
|
|
37
|
+
return this.stringifyKey(entry, manager);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
unreachable();
|
|
41
|
+
}
|
|
42
|
+
stringifyPropertyValue(entry) {
|
|
43
|
+
let res = "";
|
|
44
|
+
try {
|
|
45
|
+
res = this.stringify(entry);
|
|
46
|
+
return res;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
}
|
|
49
|
+
const type = typeof entry;
|
|
50
|
+
switch (type) {
|
|
51
|
+
case "string":
|
|
52
|
+
return entry;
|
|
53
|
+
case "number":
|
|
54
|
+
case "boolean":
|
|
55
|
+
return `${entry}`;
|
|
56
|
+
case "function":
|
|
57
|
+
return `function "${entry.constructor.name}"`;
|
|
58
|
+
case "object":
|
|
59
|
+
return pretty(entry);
|
|
60
|
+
default:
|
|
61
|
+
return entry.toString();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
stringifyShortcut(shortcut, manager) {
|
|
65
|
+
if (this.opts.shortcut) return this.opts.shortcut(shortcut);
|
|
66
|
+
const command = `command: ${this.stringifyCommand(shortcut.command ? manager.commands.entries[shortcut.command] : void 0)}`;
|
|
67
|
+
const chain = this.stringifyChain(shortcut.chain.map((chord) => chord.map((id) => manager.keys.entries[id] ?? manager.keys.toggles[id] ?? id)), manager);
|
|
68
|
+
const condition = this.stringifyCondition(shortcut.condition);
|
|
69
|
+
return crop`Shortcut ${chain} (${command}${shortcut.condition ? `, ${condition}` : ""})`;
|
|
70
|
+
}
|
|
71
|
+
stringifyCondition(condition) {
|
|
72
|
+
if (this.opts.condition) return this.opts.condition(condition);
|
|
73
|
+
return condition ? `condition: "${condition?.text}"` : `condition: undefined`;
|
|
74
|
+
}
|
|
75
|
+
stringifyCommand(nameOrCommand, manager) {
|
|
76
|
+
const command = typeof nameOrCommand === "string" ? manager.commands.entries[nameOrCommand] : nameOrCommand;
|
|
77
|
+
if (this.opts.command) return this.opts.command(command);
|
|
78
|
+
return command ? command.name : "(None)";
|
|
79
|
+
}
|
|
80
|
+
stringifyKey(keyOrId, manager) {
|
|
81
|
+
if (typeof keyOrId === "string") {
|
|
82
|
+
const res = getKeyFromIdOrVariant(keyOrId, manager.keys);
|
|
83
|
+
const key = res.isOk ? res.value[0] : void 0;
|
|
84
|
+
if (key) {
|
|
85
|
+
if (this.opts.key) return this.opts.key(keyOrId, key);
|
|
86
|
+
return getLabel(keyOrId, key);
|
|
87
|
+
} else {
|
|
88
|
+
if (this.opts.key) return this.opts.key(keyOrId);
|
|
89
|
+
return keyOrId;
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
if (this.opts.key) return this.opts.key(keyOrId.id, keyOrId);
|
|
93
|
+
return getLabel(keyOrId.id, keyOrId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
stringifyChord(chord, manager) {
|
|
97
|
+
const stringified = dedupe(chord.map((key) => this.stringifyKey(key, manager)), { mutate: true });
|
|
98
|
+
if (this.opts.chord) return this.opts.chord(stringified);
|
|
99
|
+
return stringified.join("+");
|
|
100
|
+
}
|
|
101
|
+
stringifyChain(chain, manager) {
|
|
102
|
+
const stringified = chain.map((chord) => this.stringifyChord(chord, manager));
|
|
103
|
+
if (this.opts.chain) return this.opts.chain(stringified);
|
|
104
|
+
return stringified.join(" ");
|
|
105
|
+
}
|
|
106
|
+
stringifyList(type, entries, manager) {
|
|
107
|
+
let stringified;
|
|
108
|
+
if (typeof entries[0] === "string") {
|
|
109
|
+
stringified = entries.map(
|
|
110
|
+
(entry) => type === "keys" ? this.stringify(entry, manager) : this.stringifyCommand(entry, manager)
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
stringified = entries.map((entry) => this.stringify(entry, manager));
|
|
114
|
+
}
|
|
115
|
+
if (this.opts.list) return this.opts.list(stringified, type);
|
|
116
|
+
return stringified.join(",\n");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export const defaultStringifier = new Stringifier();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Condition } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the condition passed is equal to this one.
|
|
4
|
+
*
|
|
5
|
+
* The default method does a simplistic object check, and otherwise a check of the `text` property for equality. If both conditions are undefined, note this will return true.
|
|
6
|
+
*
|
|
7
|
+
* If you override the default conditionEquals option you will likely want it to just always return false.
|
|
8
|
+
*
|
|
9
|
+
* Why? Because unless you're using simple single variable conditions that you can presort to make them uniquely identifiable (i.e. not boolean expressions, e.g. `!a b !c`), this will return A LOT of false negatives.
|
|
10
|
+
*
|
|
11
|
+
* Why the false negatives? Because two conditions might be functionally equal but have differing representations (e.g: `a && b`, `b && a`). You might think, lets normalize them all, but normalizing boolean expressions (converting them to CNF) can be dangerous with very long expressions because it can take exponential time.
|
|
12
|
+
*
|
|
13
|
+
* Now the main reason for checking the equality of two conditions is to check if two shortcuts might conflict. If we're using boolean expressions it just can't be done safely.
|
|
14
|
+
*
|
|
15
|
+
* This is a personal preference, but if we have a method that gives false negatives it can be confusing that some shortcuts immediately error when added because their conditions are simple, while others don't until triggered. The simpler, more consistent alternative is to only have them error on triggering. Aditionally conflicting conditions can be shown on the keyboard layout when then user picks contexts to check against.
|
|
16
|
+
*
|
|
17
|
+
* Why use the default implementation at all then? Well, shortcuts aren't the only ones that have conditions, commands can too, but unlike shortcuts, usually it's developers who are in charge of assigning a command's condition, and since they are usually simple, it's more possible to make sure the conditions are unique (e.g. tests could enforce they're unique by converting them all to CNF and pre-checking them for equality).
|
|
18
|
+
*/
|
|
19
|
+
export declare function defaultConditionEquals<TCondition extends Condition>(conditionA?: TCondition, conditionB?: Condition): conditionB is TCondition;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TypedError } from "@alanscodelog/utils";
|
|
2
|
+
import type { ErrorInfo, ShortcutError, TypeError } from "../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a known error that extends the base Error with some extra information.
|
|
5
|
+
* All the variables used to create the error message are stored in it's info property so you can easily craft your own error messages to show to users.
|
|
6
|
+
*/
|
|
7
|
+
export declare class KnownError<T extends ShortcutError | TypeError = ShortcutError | TypeError> extends TypedError<T, ErrorInfo<T>> {
|
|
8
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RawKey } from "../types/keys.js";
|
|
2
|
+
/**
|
|
3
|
+
* Auto calculate and set the x positions of a row of raw keys based on their width if set (otherwise width is set to 1). If the key already has an x position it takes priority and "shifts" the rest of the keys.
|
|
4
|
+
*
|
|
5
|
+
* Useful for creating layouts.
|
|
6
|
+
*
|
|
7
|
+
* This is for *RAW* keys, so it mutates the key directly without going through {@link setKeyProp}
|
|
8
|
+
*/
|
|
9
|
+
export declare function calculateAndSetPositionAndSize<T extends RawKey>(row: T[]): (T & {
|
|
10
|
+
x: number;
|
|
11
|
+
width: number;
|
|
12
|
+
height: 1;
|
|
13
|
+
})[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { setReadOnly } from "@alanscodelog/utils/setReadOnly";
|
|
2
|
+
export function calculateAndSetPositionAndSize(row) {
|
|
3
|
+
let x = 0;
|
|
4
|
+
for (const key of row) {
|
|
5
|
+
if (key.x) x = key.x;
|
|
6
|
+
setReadOnly(key, "x", x);
|
|
7
|
+
if (key.width) {
|
|
8
|
+
x += key.width;
|
|
9
|
+
} else {
|
|
10
|
+
setReadOnly(key, "width", 1);
|
|
11
|
+
x++;
|
|
12
|
+
}
|
|
13
|
+
if (key.height === void 0) {
|
|
14
|
+
setReadOnly(key, "height", 1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return row;
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Keys } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the layout size in key units for a set of {@link Keys}. This will only look at keys which are set to ({@link Key.render}).
|
|
4
|
+
*
|
|
5
|
+
* See {@link Keys.autoManageLayout} and {@link Keys.layout}.
|
|
6
|
+
*
|
|
7
|
+
* This should be done after creating {@link Keys} then on any user changes to the size/pos properties of keys. You can hook into these changed with the manager's {@link Manager.hooks.onSetKeyProp}.
|
|
8
|
+
*/
|
|
9
|
+
export declare function calculateLayoutSize(keys: Keys): Keys["layout"];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function calculateLayoutSize(keys) {
|
|
2
|
+
let x = 0;
|
|
3
|
+
let y = 0;
|
|
4
|
+
for (const key of Object.values(keys.entries)) {
|
|
5
|
+
if (key.render) {
|
|
6
|
+
const xLimit = key.x + key.width;
|
|
7
|
+
x = xLimit > x ? xLimit : x;
|
|
8
|
+
const yLimit = key.y + key.height;
|
|
9
|
+
y = yLimit > y ? yLimit : y;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return { x, y };
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Manager, PickManager, Shortcut } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A shortcut conflicts with another if their conditions are equal and their chains are in conflict.
|
|
4
|
+
*
|
|
5
|
+
* Chains can be in conflict in any of the following situations:
|
|
6
|
+
*
|
|
7
|
+
* - The shortcuts are the same instance.
|
|
8
|
+
* - All the keys are the same.
|
|
9
|
+
* - They share starting chords and the last shared chord conflicts. (e.g. [[A], [B]] and [[A]]), i.e. they have a chain conflict.
|
|
10
|
+
* - 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.
|
|
11
|
+
*
|
|
12
|
+
* Some of these can be ignored with the experimental ignore* options.
|
|
13
|
+
*
|
|
14
|
+
* 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.
|
|
15
|
+
*/
|
|
16
|
+
export declare function doesShortcutConflict<TShortcut extends Shortcut>(shortcutA: TShortcut, shortcutB: Shortcut, manager: Pick<Manager, "keys" | "commands" | "shortcuts"> & {
|
|
17
|
+
context?: Manager["context"];
|
|
18
|
+
} & PickManager<"options", "evaluateCondition" | "conditionEquals">): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { equalsShortcut } from "./equalsShortcut.js";
|
|
2
|
+
import { getKeyFromIdOrVariant } from "./getKeyFromIdOrVariant.js";
|
|
3
|
+
import { equalsKey } from "../utils/equalsKey.js";
|
|
4
|
+
import { equalsKeys } from "../utils/equalsKeys.js";
|
|
5
|
+
export function doesShortcutConflict(shortcutA, shortcutB, manager) {
|
|
6
|
+
const context = manager.context;
|
|
7
|
+
if (!context && manager.shortcuts.useContextInConflictCheck) {
|
|
8
|
+
throw new Error("Manager must have a context if `useContextInConflictCheck` is true.");
|
|
9
|
+
}
|
|
10
|
+
const {
|
|
11
|
+
ignoreChainConflicts,
|
|
12
|
+
ignoreModifierConflicts
|
|
13
|
+
} = manager.shortcuts;
|
|
14
|
+
if (ignoreChainConflicts && ignoreModifierConflicts) return false;
|
|
15
|
+
if (shortcutA.forceUnequal || shortcutB.forceUnequal) return false;
|
|
16
|
+
if (equalsShortcut(shortcutA, shortcutB, manager)) return true;
|
|
17
|
+
const evaluateCondition = manager.options.evaluateCondition;
|
|
18
|
+
const conditionEquals = manager.options.conditionEquals;
|
|
19
|
+
if (context) {
|
|
20
|
+
if (shortcutA.condition && shortcutB.condition) {
|
|
21
|
+
const shortcutCondition = evaluateCondition(shortcutA.condition, context);
|
|
22
|
+
const otherCondition = evaluateCondition(shortcutB.condition, context);
|
|
23
|
+
if (shortcutCondition !== otherCondition) return false;
|
|
24
|
+
if (!shortcutCondition) return false;
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
if (!conditionEquals(shortcutA.condition, shortcutB.condition)) return false;
|
|
28
|
+
}
|
|
29
|
+
const { keys } = manager;
|
|
30
|
+
if (shortcutA.chain.length === 0 || shortcutB.chain.length === 0) {
|
|
31
|
+
return ignoreChainConflicts === true && ignoreModifierConflicts === true;
|
|
32
|
+
}
|
|
33
|
+
const lastSharedIndex = Math.max(0, Math.min(shortcutA.chain.length - 1, shortcutB.chain.length - 1));
|
|
34
|
+
const lastIsModOnly = shortcutA.chain[lastSharedIndex].find((id) => !getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier) === void 0;
|
|
35
|
+
const otherLastIsModOnly = shortcutB.chain[lastSharedIndex].find((id) => !getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier) === void 0;
|
|
36
|
+
const sharedModifiers = shortcutA.chain[lastSharedIndex].filter(
|
|
37
|
+
(id) => getKeyFromIdOrVariant(id, keys).unwrap()[0].isModifier && shortcutB.chain[lastSharedIndex].some((otherId) => equalsKey(otherId, id, keys, { allowVariants: true }))
|
|
38
|
+
);
|
|
39
|
+
const lastSharedChordConflicts = (lastIsModOnly || otherLastIsModOnly) && sharedModifiers.length > 0;
|
|
40
|
+
const conflictsWithChain = equalsKeys(shortcutA.chain, shortcutB.chain, keys, lastSharedIndex + 1, { allowVariants: true });
|
|
41
|
+
return !ignoreChainConflicts && conflictsWithChain || !ignoreModifierConflicts && lastSharedChordConflicts;
|
|
42
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command, PickManager } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the command passed is equal to this one.
|
|
4
|
+
*
|
|
5
|
+
* The name, execute, description, and conditions must be equal.
|
|
6
|
+
*/
|
|
7
|
+
export declare function equalsCommand<TCommand extends Command>(commandA: TCommand, commandB: Command, manager: PickManager<"options", "conditionEquals">): commandB is TCommand;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export function equalsCommand(commandA, commandB, manager) {
|
|
2
|
+
if (commandA === commandB) return true;
|
|
3
|
+
return commandA.name === commandB.name && commandA.execute === commandB.execute && manager.options.conditionEquals(commandA.condition, commandB.condition) && commandA.description === commandB.description;
|
|
4
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Context } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the context passed is equal to this one.
|
|
4
|
+
*
|
|
5
|
+
* 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.
|
|
6
|
+
*/
|
|
7
|
+
export declare function equalsContext(contextA: Context, contextB: Context): boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function fastIsEqual(obj, other) {
|
|
2
|
+
const keys1 = Object.keys(obj);
|
|
3
|
+
const keys2 = Object.keys(other);
|
|
4
|
+
if (keys1.length !== keys2.length) return false;
|
|
5
|
+
for (const key of keys1) {
|
|
6
|
+
const val1 = obj[key];
|
|
7
|
+
const val2 = other[key];
|
|
8
|
+
if (typeof val1 === "object" && typeof val2 === "object") {
|
|
9
|
+
if (!fastIsEqual(val1, val2)) return false;
|
|
10
|
+
}
|
|
11
|
+
if (val1 !== val2) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
export function equalsContext(contextA, contextB) {
|
|
18
|
+
return fastIsEqual(contextA.value, contextB.value);
|
|
19
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Manager, PickManager, Shortcut } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the shortcut passed is equal to shortcutA one.
|
|
4
|
+
*
|
|
5
|
+
* 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.
|
|
6
|
+
*/
|
|
7
|
+
export declare function equalsShortcut<TShortcut extends Shortcut>(shortcutA: TShortcut, shortcutB: Shortcut, manager: Pick<Manager, "keys" | "commands"> & PickManager<"options", "evaluateCondition" | "conditionEquals">, { ignoreCommand }?: {
|
|
8
|
+
ignoreCommand?: boolean;
|
|
9
|
+
}): shortcutB is TShortcut;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { equalsCommand } from "./equalsCommand.js";
|
|
2
|
+
import { equalsKeys } from "../utils/equalsKeys.js";
|
|
3
|
+
export function equalsShortcut(shortcutA, shortcutB, manager, { ignoreCommand = false } = {}) {
|
|
4
|
+
if (shortcutA.forceUnequal || shortcutB.forceUnequal) return false;
|
|
5
|
+
if (shortcutA === shortcutB) return true;
|
|
6
|
+
return equalsKeys(shortcutA.chain, shortcutB.chain, manager.keys, void 0, { allowVariants: true }) && manager.options.conditionEquals(shortcutA.condition, shortcutB.condition) && (ignoreCommand || shortcutA.command === shortcutB.command && shortcutA.command === void 0 || shortcutA.command !== void 0 && shortcutB.command !== void 0 && equalsCommand(manager.commands.entries[shortcutA.command], manager.commands.entries[shortcutB.command], manager));
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Manager } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Force clears/resets all state. Clears the chain and sets all keys to unpressed.
|
|
4
|
+
*
|
|
5
|
+
* Useful for testing.
|
|
6
|
+
*
|
|
7
|
+
* @param opts
|
|
8
|
+
* @param {false} opts.ignoreNative If true, does not change state of native modifier/toggle keys.
|
|
9
|
+
*/
|
|
10
|
+
export declare function forceClear(manager: Manager, { ignoreNative }?: {
|
|
11
|
+
ignoreNative?: boolean;
|
|
12
|
+
}): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { setKeyProp } from "../core/setKeyProp.js";
|
|
2
|
+
import { setManagerProp } from "../core/setManagerProp.js";
|
|
3
|
+
export function forceClear(manager, { ignoreNative = false } = {}) {
|
|
4
|
+
setManagerProp(manager, "state.chain", []);
|
|
5
|
+
setManagerProp(manager, "state.nextIsChord", true);
|
|
6
|
+
setManagerProp(manager, "state.untrigger", false);
|
|
7
|
+
setManagerProp(manager, "state.isAwaitingKeyup", false);
|
|
8
|
+
for (const key of Object.values(manager.keys.entries)) {
|
|
9
|
+
if ((key.isModifier === "native" || key.isToggle === "native") && ignoreNative) return;
|
|
10
|
+
setKeyProp(key, "pressed", false, manager);
|
|
11
|
+
if (key.isToggle) {
|
|
12
|
+
setKeyProp(key, "toggleOnPressed", false, manager);
|
|
13
|
+
setKeyProp(key, "toggleOffPressed", false, manager);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { KeyInfo, Manager, Shortcut, ShortcutInfo } from "../types/index.js";
|
|
2
|
+
import type { Key } from "../types/keys.js";
|
|
3
|
+
/**
|
|
4
|
+
* 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}).
|
|
5
|
+
*
|
|
6
|
+
* 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}).
|
|
7
|
+
*
|
|
8
|
+
* It is recommended you pre-filter the shortcut's list to remove any shortcuts that are disabled or not executable.
|
|
9
|
+
*
|
|
10
|
+
* You can also post filter depending on the generated {@link ShortcutInfo} entry. See the postFilter param.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateKeyShortcutMap(chain: string[][], shortcutsList: Shortcut[], manager: Manager,
|
|
13
|
+
/**
|
|
14
|
+
* Filter entries by their {@link ShortcutInfo}, before adding/modifying the corresponding keys. Return false to filter the entry out, true to keep it.
|
|
15
|
+
*
|
|
16
|
+
* The second argument also provides some additional info the function looks at to generate the result.
|
|
17
|
+
*/
|
|
18
|
+
postFilter?: (entry: ShortcutInfo, info: {
|
|
19
|
+
pressableKeys: Key[];
|
|
20
|
+
unpressedModifiers: Key[];
|
|
21
|
+
isPressed: boolean;
|
|
22
|
+
containsSubset: boolean;
|
|
23
|
+
}) => boolean): Record<Key["id"], KeyInfo>;
|