@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,89 @@
|
|
|
1
|
+
import { castType } from "@alanscodelog/utils/castType"
|
|
2
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
3
|
+
|
|
4
|
+
import { addShortcut } from "./addShortcut.js"
|
|
5
|
+
import { removeShortcut } from "./removeShortcut.js"
|
|
6
|
+
|
|
7
|
+
import { isValidChain } from "../internal/isValidChain.js"
|
|
8
|
+
import { isValidCommand } from "../internal/isValidCommand.js"
|
|
9
|
+
import { type CanHookErrors, type CanHookShortcutProps, type Hooks, type Manager, type MultipleErrors, type Shortcut, SHORTCUT_ERROR, type Shortcuts, type ShortcutSetEntries } from "../types/index.js"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const canHookable: CanHookShortcutProps[] = ["chain", "command", "condition", "enabled"]
|
|
13
|
+
|
|
14
|
+
/* Sets a settable shortcut property.
|
|
15
|
+
*
|
|
16
|
+
* You should not use this to set properties the manager manages (those tagged with @Managed in the docs) unless you've forgone using the manager.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export function setShortcutProp<
|
|
21
|
+
TEntries extends ShortcutSetEntries,
|
|
22
|
+
TProp extends keyof ShortcutSetEntries,
|
|
23
|
+
TEntry extends TEntries[TProp],
|
|
24
|
+
THooks extends Manager["hooks"],
|
|
25
|
+
TCheck extends boolean | "only" = true
|
|
26
|
+
>(
|
|
27
|
+
/** Shortcut is mutated if check is not "only". */
|
|
28
|
+
shortcut: Shortcut,
|
|
29
|
+
prop: TProp,
|
|
30
|
+
val: TEntry["val"],
|
|
31
|
+
manager: (TEntry["manager"] extends never ? unknown : TEntry["manager"]) & { hooks?: THooks },
|
|
32
|
+
{ check = true as TCheck }: { check?: TCheck } = {}
|
|
33
|
+
):
|
|
34
|
+
Result<
|
|
35
|
+
TCheck extends "only" ? true : Shortcut,
|
|
36
|
+
MultipleErrors<
|
|
37
|
+
TEntry["error"]
|
|
38
|
+
>
|
|
39
|
+
| CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetShortcutProp">
|
|
40
|
+
> {
|
|
41
|
+
if (check) {
|
|
42
|
+
switch (prop) {
|
|
43
|
+
case "chain": {
|
|
44
|
+
castType<TEntries["chain"]["val"]>(val)
|
|
45
|
+
castType<TEntries["chain"]["manager"]>(manager)
|
|
46
|
+
|
|
47
|
+
const res = isValidChain(val, manager)
|
|
48
|
+
if (res.isError) return res
|
|
49
|
+
|
|
50
|
+
const shortcutsShallowClone: Shortcuts = { ...manager.shortcuts, entries: [...manager.shortcuts.entries] }
|
|
51
|
+
const managerClone = { ...manager, shortcuts: shortcutsShallowClone } as any as Manager
|
|
52
|
+
// todo better way
|
|
53
|
+
const resRemove = removeShortcut(shortcut, managerClone)
|
|
54
|
+
// we could be setting a shortcut not in the set
|
|
55
|
+
if (resRemove.isError && "code" in resRemove.error && resRemove.error.code !== SHORTCUT_ERROR.MISSING) return resRemove as any
|
|
56
|
+
const resAdd = addShortcut({ ...shortcut, chain: val }, managerClone)
|
|
57
|
+
if (resAdd.isError) return resAdd as any
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
case "command": {
|
|
61
|
+
castType<TEntries["command"]["val"]>(val)
|
|
62
|
+
castType<TEntries["command"]["manager"]>(manager)
|
|
63
|
+
|
|
64
|
+
const res = isValidCommand(val, manager, shortcut)
|
|
65
|
+
if (res.isError) return res
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
default: {
|
|
69
|
+
break
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (manager?.hooks && "canSetShortcutProp" in manager.hooks && canHookable.includes(prop as any)) {
|
|
73
|
+
const canHook = (manager.hooks as Hooks).canSetShortcutProp?.(shortcut, prop as any, val)
|
|
74
|
+
if (canHook instanceof Error) {
|
|
75
|
+
return Err(canHook) as any
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (check === "only") {
|
|
80
|
+
return Ok(true) satisfies Result<true, never> as any
|
|
81
|
+
}
|
|
82
|
+
shortcut[prop] = val as any
|
|
83
|
+
|
|
84
|
+
(manager?.hooks as Hooks)?.onSetShortcutProp?.(shortcut, prop, val)
|
|
85
|
+
manager?.hooks?.onSetShortcutProp?.(shortcut, prop, val)
|
|
86
|
+
|
|
87
|
+
return Ok(shortcut) satisfies Result<Shortcut, never> as any
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { castType } from "@alanscodelog/utils/castType"
|
|
2
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
3
|
+
|
|
4
|
+
import { doesShortcutConflict } from "../helpers/doesShortcutConflict.js"
|
|
5
|
+
import { equalsShortcut } from "../helpers/equalsShortcut.js"
|
|
6
|
+
import { isValidShortcut } from "../helpers/isValidShortcut.js"
|
|
7
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
8
|
+
import { errorTextAdd } from "../internal/errorTextAdd.js"
|
|
9
|
+
import { errorTextRemove } from "../internal/errorTextRemove.js"
|
|
10
|
+
import { type CanHookErrors, type CanHookShortcutsProps, type Manager, type MultipleErrors, type Shortcut, SHORTCUT_ERROR, type Shortcuts, type ShortcutsSetEntries } from "../types/index.js"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const canHookable: CanHookShortcutsProps[] = ["entries@add", "entries@remove"]
|
|
14
|
+
/**
|
|
15
|
+
* Sets a settable {@link Shortcuts} property.
|
|
16
|
+
*/
|
|
17
|
+
export function setShortcutsProp<
|
|
18
|
+
TEntries extends ShortcutsSetEntries,
|
|
19
|
+
TProp extends keyof ShortcutsSetEntries,
|
|
20
|
+
TEntry extends TEntries[TProp],
|
|
21
|
+
THooks extends Manager["hooks"],
|
|
22
|
+
TCheck extends boolean | "only" = true
|
|
23
|
+
>(
|
|
24
|
+
prop: TProp,
|
|
25
|
+
val: TEntry["val"],
|
|
26
|
+
/** Shortcuts is mutated if check is not "only". */
|
|
27
|
+
manager: TEntry["manager"] & { hooks?: THooks },
|
|
28
|
+
{
|
|
29
|
+
check = true as TCheck
|
|
30
|
+
}: { check?: TCheck } = {}
|
|
31
|
+
): Result<
|
|
32
|
+
TCheck extends "only" ? true : Shortcut,
|
|
33
|
+
MultipleErrors<TEntry["error"]>
|
|
34
|
+
| CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetShortcutsProp">
|
|
35
|
+
> {
|
|
36
|
+
const s = manager.options.stringifier
|
|
37
|
+
const shortcuts = manager.shortcuts
|
|
38
|
+
if (check) {
|
|
39
|
+
switch (prop) {
|
|
40
|
+
case "entries@add": {
|
|
41
|
+
castType<TEntries["entries@add"]["val"]>(val)
|
|
42
|
+
castType<TEntries["entries@add"]["manager"]>(manager)
|
|
43
|
+
|
|
44
|
+
const shortcut = val as any as Shortcut
|
|
45
|
+
const existing = (shortcuts.entries).find(_ =>
|
|
46
|
+
equalsShortcut(shortcut, _, manager, { ignoreCommand: true })
|
|
47
|
+
|| doesShortcutConflict(_, shortcut, manager)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if (existing) {
|
|
51
|
+
return Err(new KnownError(
|
|
52
|
+
SHORTCUT_ERROR.DUPLICATE_SHORTCUT,
|
|
53
|
+
errorTextAdd(
|
|
54
|
+
"Shortcut",
|
|
55
|
+
s.stringify(existing.chain, manager),
|
|
56
|
+
s.stringify(existing, manager),
|
|
57
|
+
s.stringify(shortcut, manager)
|
|
58
|
+
),
|
|
59
|
+
{ existing: (existing as any), self: shortcuts }
|
|
60
|
+
))
|
|
61
|
+
}
|
|
62
|
+
const isValid = isValidShortcut(shortcut, manager)
|
|
63
|
+
if (isValid.isError) return isValid
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
case "entries@remove": {
|
|
67
|
+
const shortcut = val as any as Shortcut
|
|
68
|
+
// note we don't ignore the command here, we want to find an exact match
|
|
69
|
+
const existing = (shortcuts.entries).find(_ =>
|
|
70
|
+
_ === shortcut || equalsShortcut(shortcut, _, manager, { ignoreCommand: false })
|
|
71
|
+
)
|
|
72
|
+
if (existing === undefined) {
|
|
73
|
+
return Err(new KnownError(
|
|
74
|
+
SHORTCUT_ERROR.MISSING,
|
|
75
|
+
errorTextRemove(
|
|
76
|
+
"Shortcut",
|
|
77
|
+
s.stringify(shortcut, manager),
|
|
78
|
+
s.stringifyList("shortcuts", shortcuts.entries, manager)
|
|
79
|
+
),
|
|
80
|
+
{ entry: shortcut, self: shortcuts }
|
|
81
|
+
))
|
|
82
|
+
}
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
default: break
|
|
87
|
+
}
|
|
88
|
+
if (manager?.hooks && "canSetShortcutsProp" in manager.hooks && canHookable.includes(prop as any)) {
|
|
89
|
+
const canHook = manager.hooks.canSetShortcutsProp?.(shortcuts, prop as any, val as any)
|
|
90
|
+
if (canHook instanceof Error) {
|
|
91
|
+
return Err(canHook) as any
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (check === "only") {
|
|
97
|
+
return Ok(true) satisfies Result<true, never> as any
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
switch (prop) {
|
|
101
|
+
case "entries@add": {
|
|
102
|
+
const shortcut = val
|
|
103
|
+
shortcuts.entries.push(shortcut)
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
case "entries@remove": {
|
|
107
|
+
const shortcut = val
|
|
108
|
+
const i = shortcuts.entries.findIndex(_ => _ === shortcut || equalsShortcut(shortcut, _, manager, { ignoreCommand: false }))
|
|
109
|
+
if (i < 0) {
|
|
110
|
+
throw new Error("If used correctly, shortcut should exist at this point, but it does not.")
|
|
111
|
+
}
|
|
112
|
+
shortcuts.entries.splice(i, 1)
|
|
113
|
+
break
|
|
114
|
+
}
|
|
115
|
+
default:
|
|
116
|
+
(shortcuts as any)[prop] = val
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
manager.hooks?.onSetShortcutsProp?.(shortcuts, prop as any, val as any)
|
|
121
|
+
|
|
122
|
+
return Ok(shortcuts) satisfies Result<Shortcuts, never> as any
|
|
123
|
+
}
|
|
124
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
|
|
2
|
+
import { keyOrder } from "../internal/keyOrder.js"
|
|
3
|
+
import { type IKeysSorter, KEY_SORT_POS, type Keys, type KeySortPos } from "../types/index.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The default class based implementation of the {@link IKeysSorter} interface.
|
|
8
|
+
*
|
|
9
|
+
* Creates a keys sorter for shortcut chains.
|
|
10
|
+
*
|
|
11
|
+
* Can either be passed some (object) enum of KeySortPos or it's keys with the values changed, for example:
|
|
12
|
+
* ```ts
|
|
13
|
+
* const MyKeySortPos = {
|
|
14
|
+
* [KEY_SORT_POS.modmouse]: 0,
|
|
15
|
+
* [KEY_SORT_POS.mod]: 1,
|
|
16
|
+
* // or
|
|
17
|
+
* modmouse: 0,
|
|
18
|
+
* mod: 1,
|
|
19
|
+
* //...
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* const mySorter = new KeysSorter(MyKeySortPos)
|
|
23
|
+
* new Shortcut([...keys], {sorter: mySorter})
|
|
24
|
+
* ```
|
|
25
|
+
* 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.
|
|
26
|
+
*
|
|
27
|
+
* Or you can extend from the class and implement a custom sort function.
|
|
28
|
+
*
|
|
29
|
+
* 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.
|
|
30
|
+
*
|
|
31
|
+
* The order of the default sorter can be changed without creating a new class by importing it early and changing it's `order` property.
|
|
32
|
+
*/
|
|
33
|
+
export class KeysSorter implements IKeysSorter {
|
|
34
|
+
private _sort(aId: string, bId: string, keys: Keys, order: KeySortPos): number {
|
|
35
|
+
const a = getKeyFromIdOrVariant(aId, keys).unwrap()[0]
|
|
36
|
+
const b = getKeyFromIdOrVariant(bId, keys).unwrap()[0]
|
|
37
|
+
// -1 = a b
|
|
38
|
+
if (keyOrder(a, order) < keyOrder(b, order)) return -1
|
|
39
|
+
// 1 = b a
|
|
40
|
+
if (keyOrder(b, order) < keyOrder(a, order)) return 1
|
|
41
|
+
return aId.localeCompare(bId) // => alphabetical
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
order: KeySortPos
|
|
45
|
+
|
|
46
|
+
constructor(order: KeysSorter["order"] = KEY_SORT_POS) {
|
|
47
|
+
this.order = order
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sort(keyList: string[], keys: Keys): string[] {
|
|
51
|
+
return keyList.sort((a, b) => this._sort!(a, b, keys, this.order))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const defaultSorter = new KeysSorter()
|
|
@@ -0,0 +1,234 @@
|
|
|
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
|
+
|
|
9
|
+
import { getKeyFromIdOrVariant } from "../helpers/getKeyFromIdOrVariant.js"
|
|
10
|
+
import { getLabel } from "../helpers/getLabel.js"
|
|
11
|
+
import type { Command, Condition, DefaultStringifierOptions, IStringifier, Key, Manager, Shortcut } from "../types/index.js"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The default class based implementation of the {@link IStringifier} interface.
|
|
16
|
+
*
|
|
17
|
+
* It can be passed (and a default instance is passed by default) to most functions to specify how to stringify items in errors.
|
|
18
|
+
*
|
|
19
|
+
* 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.
|
|
20
|
+
*
|
|
21
|
+
* 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)
|
|
22
|
+
*
|
|
23
|
+
* 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)
|
|
24
|
+
*
|
|
25
|
+
* 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 ` `.
|
|
26
|
+
* ```
|
|
27
|
+
* Key+Key Key+Key+Key
|
|
28
|
+
* ^Chord^ ^Chord ^
|
|
29
|
+
* ^Chain ^
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* For shortcuts, the default is:
|
|
33
|
+
* ```
|
|
34
|
+
* Shortcut Key+Key Key+Key+Key (command: command_name, condition: condition_text)
|
|
35
|
+
* ```
|
|
36
|
+
* If the condition or command are undefined:
|
|
37
|
+
* - For command it will still say it's undefined.
|
|
38
|
+
* - The condition is removed entirely if it's undefined.
|
|
39
|
+
* ```
|
|
40
|
+
* Shortcut Key+Key Key+Key+Key (command: undefined)
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* `stringifyLists` returns a lists joined by a comma and new line:
|
|
44
|
+
*
|
|
45
|
+
* ```
|
|
46
|
+
* item,
|
|
47
|
+
* item
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ## Customizing
|
|
51
|
+
* 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.
|
|
52
|
+
*
|
|
53
|
+
* You can just import it early and change it's options.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
export class Stringifier implements IStringifier {
|
|
57
|
+
opts: DefaultStringifierOptions
|
|
58
|
+
|
|
59
|
+
constructor(opts: DefaultStringifierOptions = {}) {
|
|
60
|
+
this.opts = opts
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
stringify(
|
|
64
|
+
entry: string | string[] | string[][],
|
|
65
|
+
manager: Pick<Manager, "keys">
|
|
66
|
+
): string
|
|
67
|
+
|
|
68
|
+
stringify(
|
|
69
|
+
entry: Shortcut,
|
|
70
|
+
manager: Pick<Manager, "keys" | "commands">
|
|
71
|
+
): string
|
|
72
|
+
|
|
73
|
+
stringify(
|
|
74
|
+
entry: Key | Key[] | Command | Condition,
|
|
75
|
+
): string
|
|
76
|
+
|
|
77
|
+
stringify(
|
|
78
|
+
entry: string | string[] | string[][] | Key | Key[] | Shortcut | Command | Condition,
|
|
79
|
+
manager?: Pick<Manager, "keys" | "commands"> | Pick<Manager, "keys">
|
|
80
|
+
|
|
81
|
+
): string {
|
|
82
|
+
if (isObject(entry) && "type" in entry) {
|
|
83
|
+
switch (entry.type) {
|
|
84
|
+
case "key": return this.stringifyKey(entry)
|
|
85
|
+
case "shortcut": return this.stringifyShortcut(entry, manager as any)
|
|
86
|
+
case "command": return this.stringifyCommand(entry as any, manager as any)
|
|
87
|
+
case "condition": return this.stringifyCondition(entry)
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
if (isArray(entry)) {
|
|
91
|
+
if (entry.length === 0) return this.stringifyChord([] as any)
|
|
92
|
+
// not getting correctly narrow :/
|
|
93
|
+
if (!isArray(entry[0])) {
|
|
94
|
+
castType<string[] | Key[]>(entry)
|
|
95
|
+
return this.stringifyChord(entry as any, manager!)
|
|
96
|
+
}
|
|
97
|
+
castType<string[][] | Key[][]>(entry)
|
|
98
|
+
return this.stringifyChain(entry as any, manager!)
|
|
99
|
+
} else {
|
|
100
|
+
return this.stringifyKey(entry, manager!)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
unreachable()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
stringifyPropertyValue(entry: any): string {
|
|
108
|
+
let res = ""
|
|
109
|
+
try {
|
|
110
|
+
res = this.stringify(entry)
|
|
111
|
+
return res
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// ignore
|
|
115
|
+
}
|
|
116
|
+
const type = typeof entry
|
|
117
|
+
|
|
118
|
+
switch (type) {
|
|
119
|
+
case "string": return entry
|
|
120
|
+
case "number":
|
|
121
|
+
case "boolean":
|
|
122
|
+
return `${entry}`
|
|
123
|
+
case "function":
|
|
124
|
+
return `function "${entry.constructor.name}"`
|
|
125
|
+
case "object":
|
|
126
|
+
return pretty(entry)
|
|
127
|
+
default:
|
|
128
|
+
return entry.toString()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
protected stringifyShortcut(
|
|
134
|
+
shortcut: Shortcut,
|
|
135
|
+
manager: Pick<Manager, "keys" | "commands">
|
|
136
|
+
): string {
|
|
137
|
+
if (this.opts.shortcut) return this.opts.shortcut(shortcut)
|
|
138
|
+
const command = `command: ${this.stringifyCommand(shortcut.command ? manager.commands.entries[shortcut.command] : undefined)}`
|
|
139
|
+
const chain = this.stringifyChain(shortcut.chain.map(chord => chord.map(id => manager.keys.entries[id] ?? manager.keys.toggles[id] ?? id)) as any, manager)
|
|
140
|
+
const condition = this.stringifyCondition(shortcut.condition)
|
|
141
|
+
return crop`Shortcut ${chain} (${command}${shortcut.condition ? `, ${condition}` : ""})`
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
protected stringifyCondition(condition?: Condition): string {
|
|
145
|
+
if (this.opts.condition) return this.opts.condition(condition)
|
|
146
|
+
return condition ? `condition: "${condition?.text}"` : `condition: undefined`
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
stringifyCommand(name: string, manager: Pick<Manager, "commands">): string
|
|
150
|
+
|
|
151
|
+
stringifyCommand(command?: Command): string
|
|
152
|
+
|
|
153
|
+
stringifyCommand(nameOrCommand?: string | Command | undefined, manager?: Pick<Manager, "commands">): string {
|
|
154
|
+
const command = typeof nameOrCommand === "string"
|
|
155
|
+
? manager!.commands.entries[nameOrCommand]
|
|
156
|
+
: nameOrCommand
|
|
157
|
+
|
|
158
|
+
if (this.opts.command) return this.opts.command(command)
|
|
159
|
+
return command ? command.name : "(None)"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected stringifyKey(key: Key): string
|
|
163
|
+
|
|
164
|
+
protected stringifyKey(key: string, manager: Pick<Manager, "keys">): string
|
|
165
|
+
|
|
166
|
+
protected stringifyKey(keyOrId: Key | string, manager?: Pick<Manager, "keys">): string {
|
|
167
|
+
if (typeof keyOrId === "string") {
|
|
168
|
+
const res = getKeyFromIdOrVariant(keyOrId, manager!.keys)
|
|
169
|
+
const key = res.isOk ? res.value[0] : undefined
|
|
170
|
+
if (key) {
|
|
171
|
+
if (this.opts.key) return this.opts.key(keyOrId, key)
|
|
172
|
+
return getLabel(keyOrId, key)
|
|
173
|
+
} else {
|
|
174
|
+
if (this.opts.key) return this.opts.key(keyOrId)
|
|
175
|
+
return keyOrId
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
if (this.opts.key) return this.opts.key(keyOrId.id, keyOrId)
|
|
179
|
+
return getLabel(keyOrId.id, keyOrId)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected stringifyChord(chord: Key[]): string
|
|
184
|
+
|
|
185
|
+
protected stringifyChord(chord: string[], manager: Pick<Manager, "keys">): string
|
|
186
|
+
|
|
187
|
+
protected stringifyChord(chord: Key[] | string[], manager?: Pick<Manager, "keys">): string {
|
|
188
|
+
const stringified = dedupe(chord.map(key => this.stringifyKey(key satisfies string | Key as any, manager!)), { mutate: true })
|
|
189
|
+
if (this.opts.chord) return this.opts.chord(stringified)
|
|
190
|
+
return stringified.join("+")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected stringifyChain(chain: Key[][]): string
|
|
194
|
+
|
|
195
|
+
protected stringifyChain(chain: string[][], manager: Pick<Manager, "keys">): string
|
|
196
|
+
|
|
197
|
+
protected stringifyChain(chain: Key[][] | string[][], manager?: Pick<Manager, "keys">): string {
|
|
198
|
+
const stringified = chain.map(chord => this.stringifyChord(chord satisfies string[] | Key[] as any, manager!))
|
|
199
|
+
|
|
200
|
+
if (this.opts.chain) return this.opts.chain(stringified)
|
|
201
|
+
return stringified.join(" ")
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
stringifyList(type: "keys", entries: Key[]): string
|
|
205
|
+
|
|
206
|
+
stringifyList(type: "commands", entries: Command[]): string
|
|
207
|
+
|
|
208
|
+
stringifyList(type: "keys", entries: string[], manager: Pick<Manager, "keys">): string
|
|
209
|
+
|
|
210
|
+
stringifyList(type: "commands", entries: string[], manager: Pick<Manager, "commands">): string
|
|
211
|
+
|
|
212
|
+
stringifyList(type: "shortcuts", entries: Shortcut[], manager: Pick<Manager, "keys" | "commands">): string
|
|
213
|
+
|
|
214
|
+
stringifyList(
|
|
215
|
+
type: "keys" | "commands" | "shortcuts",
|
|
216
|
+
entries: Key[] | Command[] | Shortcut[] | string[],
|
|
217
|
+
manager?: Pick<Manager, "commands"> | Pick<Manager, "keys"> | Pick<Manager, "keys">
|
|
218
|
+
): string {
|
|
219
|
+
let stringified: string[]
|
|
220
|
+
if (typeof entries[0] === "string") {
|
|
221
|
+
stringified = (entries as string[]).map(entry => type === "keys"
|
|
222
|
+
? this.stringify(entry, manager as any)
|
|
223
|
+
: this.stringifyCommand(entry, manager as any)
|
|
224
|
+
)
|
|
225
|
+
} else {
|
|
226
|
+
stringified = (entries as any[]).map(entry => this.stringify(entry, manager as any))
|
|
227
|
+
}
|
|
228
|
+
if (this.opts.list) return this.opts.list(stringified, type)
|
|
229
|
+
return stringified.join(",\n")
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const defaultStringifier = new Stringifier()
|
|
234
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Condition } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns whether the condition passed is equal to this one.
|
|
5
|
+
*
|
|
6
|
+
* 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.
|
|
7
|
+
*
|
|
8
|
+
* If you override the default conditionEquals option you will likely want it to just always return false.
|
|
9
|
+
*
|
|
10
|
+
* 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.
|
|
11
|
+
*
|
|
12
|
+
* 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.
|
|
13
|
+
*
|
|
14
|
+
* 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.
|
|
15
|
+
*
|
|
16
|
+
* 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.
|
|
17
|
+
*
|
|
18
|
+
* 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).
|
|
19
|
+
*/
|
|
20
|
+
export function defaultConditionEquals<TCondition extends Condition>(
|
|
21
|
+
conditionA?: TCondition,
|
|
22
|
+
conditionB?: Condition
|
|
23
|
+
): conditionB is TCondition {
|
|
24
|
+
// both are the same object or both undefined
|
|
25
|
+
if (conditionA === conditionB) return true
|
|
26
|
+
// one if undefined
|
|
27
|
+
if (!conditionA || !conditionB) return false
|
|
28
|
+
return conditionA.text === conditionB.text
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { setManagerProp } from "../core/setManagerProp.js"
|
|
2
|
+
import type { Manager } from "../types/index.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The default callback for the manager which logs the error, the event that caused it, and clears the chain.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export const defaultManagerCallback: Manager["options"]["cb"] = (manager: Manager, error, e): void => {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.warn(error, e)
|
|
13
|
+
setManagerProp(manager, "state.chain", [])
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TypedError } from "@alanscodelog/utils"
|
|
2
|
+
|
|
3
|
+
import type { ErrorInfo, ShortcutError, TypeError } from "../types/index.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a known error that extends the base Error with some extra information.
|
|
8
|
+
* 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.
|
|
9
|
+
*/
|
|
10
|
+
export class KnownError<
|
|
11
|
+
T extends ShortcutError | TypeError = ShortcutError | TypeError
|
|
12
|
+
// TInfo extends ErrorInfo<T> = ErrorInfo<T>,
|
|
13
|
+
> extends TypedError<T, ErrorInfo<T>> {}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { setReadOnly } from "@alanscodelog/utils/setReadOnly"
|
|
2
|
+
|
|
3
|
+
import type { RawKey } from "../types/keys.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 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.
|
|
7
|
+
*
|
|
8
|
+
* Useful for creating layouts.
|
|
9
|
+
*
|
|
10
|
+
* This is for *RAW* keys, so it mutates the key directly without going through {@link setKeyProp}
|
|
11
|
+
*/
|
|
12
|
+
export function calculateAndSetPositionAndSize<
|
|
13
|
+
T extends RawKey
|
|
14
|
+
>(row: T[]): (T & { x: number, width: number, height: 1 })[] {
|
|
15
|
+
let x = 0
|
|
16
|
+
for (const key of row) {
|
|
17
|
+
if (key.x) x = key.x
|
|
18
|
+
setReadOnly(key, "x", x)
|
|
19
|
+
if (key.width) {
|
|
20
|
+
x += key.width
|
|
21
|
+
} else {
|
|
22
|
+
setReadOnly(key, "width", 1)
|
|
23
|
+
x++
|
|
24
|
+
}
|
|
25
|
+
if (key.height === undefined) {
|
|
26
|
+
setReadOnly(key, "height", 1)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return row as any
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Key, Keys } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 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}).
|
|
5
|
+
*
|
|
6
|
+
* See {@link Keys.autoManageLayout} and {@link Keys.layout}.
|
|
7
|
+
*
|
|
8
|
+
* 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}.
|
|
9
|
+
*/
|
|
10
|
+
export function calculateLayoutSize(keys: Keys): Keys["layout"] {
|
|
11
|
+
let x = 0
|
|
12
|
+
let y = 0
|
|
13
|
+
for (const key of Object.values<Key>(keys.entries)) {
|
|
14
|
+
if (key.render) {
|
|
15
|
+
const xLimit = key.x + key.width
|
|
16
|
+
x = xLimit > x ? xLimit : x
|
|
17
|
+
const yLimit = key.y + key.height
|
|
18
|
+
y = yLimit > y ? yLimit : y
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { x, y }
|
|
22
|
+
}
|