@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,380 @@
|
|
|
1
|
+
import type { DeepPartial } from "@alanscodelog/utils"
|
|
2
|
+
import { debounce } from "@alanscodelog/utils/debounce"
|
|
3
|
+
import { isArray } from "@alanscodelog/utils/isArray"
|
|
4
|
+
import { keys } from "@alanscodelog/utils/keys"
|
|
5
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
6
|
+
import { setReadOnly } from "@alanscodelog/utils/setReadOnly"
|
|
7
|
+
|
|
8
|
+
import { managerToStorableClone } from "../helpers/managerToStorableClone.js"
|
|
9
|
+
import type { Manager, PickManager } from "../types/index.js"
|
|
10
|
+
|
|
11
|
+
export class ShortcutManagerManager {
|
|
12
|
+
storageKeys: {
|
|
13
|
+
/** Prefix for all keys. */
|
|
14
|
+
prefix: string
|
|
15
|
+
/** @default "shortcut-manager:names" */
|
|
16
|
+
managerNames: string
|
|
17
|
+
/** @default "shortcut-manager:manager." */
|
|
18
|
+
managerPrefix: string
|
|
19
|
+
/** @default "shortcut-manager:active" */
|
|
20
|
+
activeManager: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
readonly managerNames: string[] = []
|
|
24
|
+
|
|
25
|
+
readonly managers: Readonly<Record<string, Manager>> = {}
|
|
26
|
+
|
|
27
|
+
readonly activeManagerName: string = "default"
|
|
28
|
+
|
|
29
|
+
/** A debounced version of the save function. Is already bound to the instance. */
|
|
30
|
+
debouncedSave: (name: string) => void
|
|
31
|
+
|
|
32
|
+
storage: {
|
|
33
|
+
setItem: (key: string, value: string) => void
|
|
34
|
+
getItem: (key: string) => string | null
|
|
35
|
+
removeItem: (key: string) => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* All new managers will be created using this function. It should be capable of taking as little information as `{name}` (when a new manager is created) and returining a full manger.
|
|
40
|
+
*
|
|
41
|
+
* It's suggested you not do validation here, use the `onParse` hook instead.
|
|
42
|
+
*
|
|
43
|
+
* If you return an error, `onError` will be called, and no manager will be created.
|
|
44
|
+
*/
|
|
45
|
+
createManager: (
|
|
46
|
+
raw: Partial<Omit<Manager, "options" | "hooks" | "listener" | "state">> & {
|
|
47
|
+
options: PickManager<"options", "enableShortcuts" | "enableListeners" | "updateStateOnAllEvents">
|
|
48
|
+
},
|
|
49
|
+
isNew: boolean
|
|
50
|
+
) => Manager | Error
|
|
51
|
+
|
|
52
|
+
hooks: {
|
|
53
|
+
/**
|
|
54
|
+
* Called on any errors loading/saving/etc, should be used to notify the user.
|
|
55
|
+
*/
|
|
56
|
+
onError: (err: Error) => void
|
|
57
|
+
/**
|
|
58
|
+
* Called when a manager is parsed from storage after being sucessfully JSON.parsed.
|
|
59
|
+
*
|
|
60
|
+
* You can validate the parsed object here so it's in the correct shape to pass to `createManager`.
|
|
61
|
+
*
|
|
62
|
+
* If you return an error, `onError` will be called and `createManager` will be skipped.
|
|
63
|
+
*/
|
|
64
|
+
onParse?: (parsed: object) => undefined | Error
|
|
65
|
+
/**
|
|
66
|
+
* Called when a manager is saved or exported. It's called with the cloned version of the manager that has been stripped of properties that should not be saved (see {@link managerToStorableClone}).
|
|
67
|
+
*
|
|
68
|
+
* You can delete/add properties here (it will not modify the original manager).
|
|
69
|
+
*/
|
|
70
|
+
onSave?: (clone: DeepPartial<Manager>) => void
|
|
71
|
+
/**
|
|
72
|
+
* Called when a manager is exported. Can be used to actually do the export.
|
|
73
|
+
*/
|
|
74
|
+
onExport?: (res: object) => void
|
|
75
|
+
/**
|
|
76
|
+
* Called when a manager is set. Can be used to update your state.
|
|
77
|
+
*/
|
|
78
|
+
onSetManager?: (name: string, clone: Manager) => void
|
|
79
|
+
/**
|
|
80
|
+
* Called when the manager names are set. Can be used to update your state.
|
|
81
|
+
*/
|
|
82
|
+
onSetManagerNames?: (names: string[]) => void
|
|
83
|
+
/**
|
|
84
|
+
* Called when the active manager is set. Can be used to update your state.
|
|
85
|
+
*/
|
|
86
|
+
onSetActiveManager?: (name: string) => void
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
constructor(
|
|
90
|
+
createManager: ShortcutManagerManager["createManager"],
|
|
91
|
+
hooks: ShortcutManagerManager["hooks"],
|
|
92
|
+
{
|
|
93
|
+
storageKeys = {} as any,
|
|
94
|
+
storage = localStorage
|
|
95
|
+
}:
|
|
96
|
+
{
|
|
97
|
+
storageKeys?: Partial<ShortcutManagerManager["storageKeys"]>
|
|
98
|
+
storage?: ShortcutManagerManager["storage"]
|
|
99
|
+
} = {}
|
|
100
|
+
) {
|
|
101
|
+
this.createManager = createManager
|
|
102
|
+
this.hooks = hooks
|
|
103
|
+
storageKeys.prefix ??= ""
|
|
104
|
+
storageKeys.managerNames ??= "shortcut-manager:names"
|
|
105
|
+
storageKeys.managerPrefix ??= "shortcut-manager:manager."
|
|
106
|
+
storageKeys.activeManager ??= "shortcut-manager:active"
|
|
107
|
+
if (storageKeys.prefix) {
|
|
108
|
+
for (const key of keys(storageKeys)) {
|
|
109
|
+
if (key === "prefix") continue
|
|
110
|
+
storageKeys[key] = `${storageKeys.prefix}${storageKeys[key]}`
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.storageKeys = storageKeys as ShortcutManagerManager["storageKeys"]
|
|
114
|
+
this.storage = storage
|
|
115
|
+
|
|
116
|
+
this.debouncedSave = debounce(this.save.bind(this), 500)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
init(): void {
|
|
120
|
+
const list = this.storage.getItem(this.storageKeys.managerNames)
|
|
121
|
+
const active = this.storage.getItem(this.storageKeys.activeManager)
|
|
122
|
+
this.setManagerNames(list ? JSON.parse(list) : [])
|
|
123
|
+
|
|
124
|
+
if (this.managerNames.length > 0) {
|
|
125
|
+
const res = this.managerNames
|
|
126
|
+
.map(m => {
|
|
127
|
+
const r = this.storageReadManager(m, { force: true })
|
|
128
|
+
this.notifyIfError(r)
|
|
129
|
+
if (r.isOk) {
|
|
130
|
+
this.load(r.value)
|
|
131
|
+
}
|
|
132
|
+
return r
|
|
133
|
+
})
|
|
134
|
+
const ok = res.filter(r => r.isOk)
|
|
135
|
+
if (ok.length > 0) {
|
|
136
|
+
this.loadOrCreateDefault()
|
|
137
|
+
}
|
|
138
|
+
if (active && this.managerNames.includes(active)) {
|
|
139
|
+
this.setActiveManager(active)
|
|
140
|
+
} else {
|
|
141
|
+
this.setActiveManager(this.managerNames[0]!)
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
this.loadOrCreateDefault()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
setManager(name: string, clone?: any): void {
|
|
149
|
+
if (clone === undefined) {
|
|
150
|
+
// @ts-expect-error setting readonly
|
|
151
|
+
delete this.managers[name]
|
|
152
|
+
} else {
|
|
153
|
+
setReadOnly(this.managers, name, clone)
|
|
154
|
+
}
|
|
155
|
+
this.hooks.onSetManager?.(name, clone)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setManagerNames(names: string[]): void {
|
|
159
|
+
setReadOnly(this, "managerNames", names)
|
|
160
|
+
this.hooks.onSetManagerNames?.(names)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setActiveManager(name: string): void {
|
|
164
|
+
setReadOnly(this, "activeManagerName", name)
|
|
165
|
+
this.storage.setItem(this.storageKeys.activeManager, name)
|
|
166
|
+
this.hooks.onSetActiveManager?.(name)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
protected addManager(name: string, clone: any): void {
|
|
170
|
+
this.setManager(name, clone)
|
|
171
|
+
if (!this.managerNames.includes(name)) {
|
|
172
|
+
this.setManagerNames([...this.managerNames, name])
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected removeManager(name: string): void {
|
|
177
|
+
this.setManager(name, undefined)
|
|
178
|
+
const index = this.managerNames.indexOf(name)
|
|
179
|
+
if (index === -1) return
|
|
180
|
+
this.setManagerNames(this.managerNames.toSpliced(index, 1))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
protected storageSaveManager(name: string, clone: any): void {
|
|
185
|
+
this.storage.setItem(`${this.storageKeys.managerPrefix}${name}`, JSON.stringify(clone))
|
|
186
|
+
if (!this.managerNames.includes(name)) {
|
|
187
|
+
this.managerNames.push(name)
|
|
188
|
+
this.storage.setItem(
|
|
189
|
+
this.storageKeys.managerNames,
|
|
190
|
+
JSON.stringify(this.managerNames)
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
protected storageRemoveManager(name: string): void {
|
|
196
|
+
this.storage.removeItem(`${this.storageKeys.managerPrefix}${name}`)
|
|
197
|
+
const index = this.managerNames.indexOf(name)
|
|
198
|
+
if (index > -1) {
|
|
199
|
+
this.managerNames.splice(index, 1)
|
|
200
|
+
this.storage.setItem(
|
|
201
|
+
this.storageKeys.managerNames,
|
|
202
|
+
JSON.stringify(this.managerNames)
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
protected storageReadManager(
|
|
208
|
+
name: string,
|
|
209
|
+
{ force = false }: { force?: boolean } = {}
|
|
210
|
+
): Result<Manager, Error> {
|
|
211
|
+
const raw = this.storage.getItem(`${this.storageKeys.managerPrefix}${name}`)
|
|
212
|
+
if (!raw && !force) {
|
|
213
|
+
const res = Err(new Error(`No manager found by the name of ${name}.`))
|
|
214
|
+
return this.notifyIfError(res)
|
|
215
|
+
}
|
|
216
|
+
const r = this.createManager(
|
|
217
|
+
!raw && force ? { name } : this.parseJsonManager(raw!).unwrap(),
|
|
218
|
+
force
|
|
219
|
+
)
|
|
220
|
+
if (r instanceof Error) return Err(r)
|
|
221
|
+
return Ok(r)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
parseJsonManager(
|
|
225
|
+
raw: string
|
|
226
|
+
): Result<any, Error> {
|
|
227
|
+
if (!raw) {
|
|
228
|
+
return Err(new Error(`Nothing to parse.`))
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const parsed = JSON.parse(raw)
|
|
232
|
+
const res = this.hooks.onParse?.(parsed)
|
|
233
|
+
if (res instanceof Error) {
|
|
234
|
+
return this.notifyIfError(Err(res))
|
|
235
|
+
}
|
|
236
|
+
if (res === undefined) throw new Error(`onParse must return a value.`)
|
|
237
|
+
return Ok(res ?? parsed)
|
|
238
|
+
} catch (e) {
|
|
239
|
+
return Err(e as Error)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
load(
|
|
244
|
+
manager: Manager,
|
|
245
|
+
{
|
|
246
|
+
doActivate = true
|
|
247
|
+
}: {
|
|
248
|
+
doActivate?: boolean
|
|
249
|
+
} = {}
|
|
250
|
+
): void {
|
|
251
|
+
this.setManager(manager.name, manager)
|
|
252
|
+
if (doActivate) {
|
|
253
|
+
this.setActiveManager(manager.name)
|
|
254
|
+
}
|
|
255
|
+
this.save(manager.name)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
protected loadOrCreateDefault(): void {
|
|
259
|
+
const r = this.storageReadManager("default", { force: true })
|
|
260
|
+
this.notifyIfError(r)
|
|
261
|
+
if (r.isOk) {
|
|
262
|
+
this.load(r.value)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
save(name: string): void {
|
|
267
|
+
if (!this.managers[name]) {
|
|
268
|
+
this.notifyIfError(Err(new Error(`Manager ${name} not found`)))
|
|
269
|
+
}
|
|
270
|
+
const clone = managerToStorableClone(this.managers[name])
|
|
271
|
+
this.hooks.onSave?.(clone)
|
|
272
|
+
this.storageSaveManager(name, clone)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
protected notifyIfError<T extends Result<any, Error>>(res: T): T {
|
|
276
|
+
if (!res.isOk) {
|
|
277
|
+
this.hooks.onError?.(res.error)
|
|
278
|
+
}
|
|
279
|
+
return res
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
changeManager(name: string, opts: { force?: boolean } = {}): Result<void, Error> {
|
|
283
|
+
if (this.managerNames.includes(name)) {
|
|
284
|
+
this.setActiveManager(name)
|
|
285
|
+
return Ok()
|
|
286
|
+
} else {
|
|
287
|
+
const m = this.storageReadManager(name, opts)
|
|
288
|
+
if (m.isOk) {
|
|
289
|
+
this.load(m.value)
|
|
290
|
+
this.setActiveManager(name)
|
|
291
|
+
}
|
|
292
|
+
return Ok()
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
deleteManager(name: string): Result<void, Error> {
|
|
297
|
+
const index = this.managerNames.indexOf(name)
|
|
298
|
+
let nextName = this.managerNames[index - 1] ?? this.managerNames[index + 1] ?? "default"
|
|
299
|
+
this.storageRemoveManager(name)
|
|
300
|
+
this.setManager(name, undefined)
|
|
301
|
+
|
|
302
|
+
if (nextName === name) nextName = "default"
|
|
303
|
+
const res = this.changeManager(nextName, { force: true })
|
|
304
|
+
this.notifyIfError(res)
|
|
305
|
+
return res
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
renameManager(name: string): void {
|
|
309
|
+
this.storageRemoveManager(this.activeManagerName)
|
|
310
|
+
this.setManager(name, this.managers[this.activeManagerName])
|
|
311
|
+
this.setManager(this.activeManagerName, undefined)
|
|
312
|
+
this.setActiveManager(name)
|
|
313
|
+
this.save(name)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
duplicateManager(oldName: string, newName: string): Result<Manager, Error> {
|
|
317
|
+
const m = this.managers[oldName]
|
|
318
|
+
if (!m) {
|
|
319
|
+
const res = Err(new Error(`No manager found by the name of ${oldName}.`))
|
|
320
|
+
return this.notifyIfError(res)
|
|
321
|
+
}
|
|
322
|
+
const clone = managerToStorableClone(m)
|
|
323
|
+
clone.name = newName
|
|
324
|
+
const instance = this.createManager(clone as any, false)
|
|
325
|
+
if (instance instanceof Error) return this.notifyIfError(Err(instance))
|
|
326
|
+
else if (instance) this.load(instance)
|
|
327
|
+
|
|
328
|
+
return Ok(instance)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
exportManagers(names: string[]): Result<object, Error> {
|
|
332
|
+
const obj = { managers: [] as any[] }
|
|
333
|
+
for (const name of names) {
|
|
334
|
+
const m = this.managers[name]
|
|
335
|
+
if (!m) {
|
|
336
|
+
const res = Err(new Error(`No manager found by the name of ${name}.`))
|
|
337
|
+
return this.notifyIfError(res)
|
|
338
|
+
}
|
|
339
|
+
const clone = managerToStorableClone(m)
|
|
340
|
+
this.hooks.onSave?.(clone)
|
|
341
|
+
obj.managers.push(clone)
|
|
342
|
+
}
|
|
343
|
+
this.hooks.onExport?.(obj)
|
|
344
|
+
return Ok(obj)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
importManagers(content: string): Result<void, Error> {
|
|
348
|
+
const p = JSON.parse(content)
|
|
349
|
+
if (!p.managers || !isArray(p.manager)) {
|
|
350
|
+
return Err(new Error(`Not a valid manager file.`))
|
|
351
|
+
}
|
|
352
|
+
const ok: Manager[] = []
|
|
353
|
+
for (const parsedM of p.managers) {
|
|
354
|
+
const m = this.parseJsonManager(JSON.stringify(parsedM))
|
|
355
|
+
this.notifyIfError(m)
|
|
356
|
+
|
|
357
|
+
if (m.isOk) {
|
|
358
|
+
const instance = this.createManager(m.value as any, false)
|
|
359
|
+
if (instance instanceof Error) return this.notifyIfError(Err(instance))
|
|
360
|
+
ok.push(instance)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
for (const instance of ok) {
|
|
364
|
+
this.load(instance)
|
|
365
|
+
}
|
|
366
|
+
return Ok()
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Clears all managers and resets the state. `init` must be called again if you want to use the class instance again. */
|
|
370
|
+
clearAll(): void {
|
|
371
|
+
// todo, check we can init again after this
|
|
372
|
+
this.storage.removeItem(this.storageKeys.managerNames)
|
|
373
|
+
this.storage.removeItem(this.storageKeys.activeManager)
|
|
374
|
+
for (const name of this.managerNames) {
|
|
375
|
+
this.storageRemoveManager(name)
|
|
376
|
+
}
|
|
377
|
+
this.setManagerNames([])
|
|
378
|
+
this.setActiveManager("default")
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { setCommandsProp } from "./setCommandsProp.js"
|
|
4
|
+
|
|
5
|
+
import type { CanHookErrors, Command, CommandsSetEntries, Manager, MultipleErrors } from "../types/index.js"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function addCommand<
|
|
9
|
+
THooks extends Manager["hooks"],
|
|
10
|
+
TCheck extends boolean | "only" = true
|
|
11
|
+
>(
|
|
12
|
+
command: Command,
|
|
13
|
+
manager: CommandsSetEntries["entries@add"]["manager"] & { hooks?: THooks },
|
|
14
|
+
opts: { check?: TCheck } = {}
|
|
15
|
+
): Result<
|
|
16
|
+
TCheck extends "only" ? true : Command,
|
|
17
|
+
MultipleErrors<
|
|
18
|
+
CommandsSetEntries["entries@add"]["error"]
|
|
19
|
+
> | CanHookErrors<THooks extends never ? never : THooks, "canSetCommandsProp">
|
|
20
|
+
> {
|
|
21
|
+
const res = setCommandsProp("entries@add", command, manager, opts)
|
|
22
|
+
if (res.isError) return res
|
|
23
|
+
return Ok(command) satisfies Result<Command, never> as any
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { setKeysProp } from "./setKeysProp.js"
|
|
4
|
+
|
|
5
|
+
import type { CanHookErrors, Key, KeysSetEntries, Manager, MultipleErrors } from "../types/index.js"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function addKey<
|
|
9
|
+
THooks extends Manager["hooks"],
|
|
10
|
+
TCheck extends boolean | "only" = true
|
|
11
|
+
>(
|
|
12
|
+
key: Key,
|
|
13
|
+
manager: KeysSetEntries["entries@add"]["manager"] & { hooks?: THooks },
|
|
14
|
+
opts: { check?: TCheck } = {}
|
|
15
|
+
): Result<
|
|
16
|
+
TCheck extends "only" ? true : Key,
|
|
17
|
+
MultipleErrors<
|
|
18
|
+
KeysSetEntries["entries@add"]["error"]
|
|
19
|
+
> | CanHookErrors<THooks extends never ? never : THooks, "canSetKeysProp">
|
|
20
|
+
> {
|
|
21
|
+
const res = setKeysProp("entries@add", key, manager, opts)
|
|
22
|
+
if (res.isError) return res
|
|
23
|
+
return Ok(key) satisfies Result<Key, never> as any
|
|
24
|
+
}
|
|
25
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { setShortcutsProp } from "./setShortcutsProp.js"
|
|
4
|
+
|
|
5
|
+
import type { CanHookErrors, Manager, MultipleErrors, Shortcut, ShortcutsSetEntries } from "../types/index.js"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function addShortcut<
|
|
9
|
+
THooks extends Manager["hooks"],
|
|
10
|
+
TCheck extends boolean | "only" = true
|
|
11
|
+
>(
|
|
12
|
+
shortcut: Shortcut,
|
|
13
|
+
manager: ShortcutsSetEntries["entries@add"]["manager"] & { hooks?: THooks },
|
|
14
|
+
opts: { check?: TCheck } = {}
|
|
15
|
+
): Result<
|
|
16
|
+
TCheck extends "only" ? true : Shortcut,
|
|
17
|
+
MultipleErrors<
|
|
18
|
+
ShortcutsSetEntries["entries@add"]["error"]
|
|
19
|
+
> | CanHookErrors<THooks extends never ? never : THooks, "canSetShortcutsProp">
|
|
20
|
+
> {
|
|
21
|
+
const res = setShortcutsProp("entries@add", shortcut, manager, opts)
|
|
22
|
+
if (res.isError) return res
|
|
23
|
+
return Ok(shortcut) satisfies Result<Shortcut, never> as any
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { keys } from "@alanscodelog/utils/keys"
|
|
2
|
+
import type { AnyFunction } from "@alanscodelog/utils/types"
|
|
3
|
+
|
|
4
|
+
import type { AttachTarget, EventTypes } from "../types/index.js"
|
|
5
|
+
/**
|
|
6
|
+
* Attach the event listeners created by {@link createManagerEventListeners} to an element or {@link Emulator} so the manager can listen to the needed event hooks.
|
|
7
|
+
*
|
|
8
|
+
* They can be removed with {@link detach} or by aborting the controller returned by the function.
|
|
9
|
+
*/
|
|
10
|
+
export function attach(
|
|
11
|
+
el: AttachTarget,
|
|
12
|
+
listeners: Record<EventTypes, AnyFunction>,
|
|
13
|
+
/** Add options to some listeners. An abort controller is added by default. You can use the `*` key to set a default for all. */
|
|
14
|
+
opts: Partial<Record<EventTypes | "*", AddEventListenerOptions>> = { wheel: { passive: true } }
|
|
15
|
+
): AbortController {
|
|
16
|
+
const controller = new AbortController()
|
|
17
|
+
const defaultOpts = opts["*"] ?? {}
|
|
18
|
+
for (const listenerName of keys(listeners)) {
|
|
19
|
+
el.addEventListener(listenerName, listeners[listenerName], {
|
|
20
|
+
signal: controller.signal,
|
|
21
|
+
...defaultOpts,
|
|
22
|
+
...opts[listenerName]
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
return controller
|
|
26
|
+
}
|
|
27
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Command, CommandExecute, RawCommand } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function createCommand<
|
|
5
|
+
TName extends string = string,
|
|
6
|
+
TCommand extends RawCommand<TName> = RawCommand<TName>
|
|
7
|
+
>(
|
|
8
|
+
name: TName,
|
|
9
|
+
// we only type the execute here so the user gets type safety but doesn't
|
|
10
|
+
// pay the price of Command being too strictly typed
|
|
11
|
+
rawCommand: Omit<TCommand, "name" | "execute"> & { execute: CommandExecute<TName> } = {} as any
|
|
12
|
+
// TName is not passed on purpose! making the command so strictly types is annoying
|
|
13
|
+
// since typescript will complain the command returned does not satisfy the base command (due to execute)
|
|
14
|
+
): Command {
|
|
15
|
+
const command: Command = {
|
|
16
|
+
...rawCommand as any,
|
|
17
|
+
type: "command",
|
|
18
|
+
name,
|
|
19
|
+
execute: rawCommand.execute as any,
|
|
20
|
+
description: rawCommand.description ?? "",
|
|
21
|
+
condition: rawCommand.condition ?? { type: "condition", text: "" }
|
|
22
|
+
}
|
|
23
|
+
return command as any
|
|
24
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { addCommand } from "./addCommand.js"
|
|
4
|
+
|
|
5
|
+
import { defaultStringifier } from "../defaults/Stringifier.js"
|
|
6
|
+
import type { CanHookErrors, Command, Commands, CommandsSetEntries, Manager, MultipleErrors, PickManager } from "../types/index.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* # Commands
|
|
10
|
+
* Creates a set of commands.
|
|
11
|
+
*/
|
|
12
|
+
export function createCommands<
|
|
13
|
+
THooks extends Manager["hooks"],
|
|
14
|
+
TEntries extends Record<string, Command>,
|
|
15
|
+
TCheck extends boolean | "only" = true
|
|
16
|
+
|
|
17
|
+
>(
|
|
18
|
+
commandsList: Command[],
|
|
19
|
+
manager: PickManager<"options", "stringifier"> & { hooks?: THooks } = {
|
|
20
|
+
options: { stringifier: defaultStringifier }
|
|
21
|
+
},
|
|
22
|
+
{ check = true as TCheck }: { check?: TCheck } = {}
|
|
23
|
+
): Result<
|
|
24
|
+
TCheck extends "only" ? true : Commands<TEntries>,
|
|
25
|
+
MultipleErrors<
|
|
26
|
+
CommandsSetEntries["entries@add"]["error"]
|
|
27
|
+
> | CanHookErrors<THooks extends never ? never : THooks, "canSetCommandsProp">
|
|
28
|
+
> {
|
|
29
|
+
const commands: Commands = {
|
|
30
|
+
type: "commands",
|
|
31
|
+
entries: {}
|
|
32
|
+
}
|
|
33
|
+
const managerClone = { ...manager, commands }
|
|
34
|
+
if (check) {
|
|
35
|
+
for (const command of commandsList) {
|
|
36
|
+
// we check all first to avoid erroring mid-way through
|
|
37
|
+
const res = addCommand(command, managerClone)
|
|
38
|
+
if (res.isError) return res
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (check === "only") return Ok(true) satisfies Result<true, never> as any
|
|
42
|
+
|
|
43
|
+
return Ok(commands) satisfies Result<Commands, never> as any
|
|
44
|
+
}
|
|
45
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Condition } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function createCondition<TTextCondition extends string, TReturn extends Condition>(
|
|
5
|
+
rawCondition: TTextCondition,
|
|
6
|
+
/**
|
|
7
|
+
* Optionally modify the shape of the condition and add your own properties.
|
|
8
|
+
* For example, you can append a parsed AST like representation of the condition.
|
|
9
|
+
*/
|
|
10
|
+
parse?: (raw: TTextCondition, condition: Condition) => TReturn
|
|
11
|
+
): TReturn {
|
|
12
|
+
let condition: Condition = {
|
|
13
|
+
...rawCondition as any,
|
|
14
|
+
type: "condition",
|
|
15
|
+
text: rawCondition ?? ""
|
|
16
|
+
}
|
|
17
|
+
if (parse) {
|
|
18
|
+
condition = parse(rawCondition, condition)
|
|
19
|
+
}
|
|
20
|
+
return condition as any
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context, RawContext } from "../types/index.js"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function createContext<TContext extends RawContext>(
|
|
5
|
+
value: TContext["value"],
|
|
6
|
+
rawContext: Omit<TContext, "value"> = {} as any
|
|
7
|
+
): Context<TContext["value"]> {
|
|
8
|
+
const context: Context = {
|
|
9
|
+
type: "context",
|
|
10
|
+
...rawContext,
|
|
11
|
+
value
|
|
12
|
+
}
|
|
13
|
+
return context
|
|
14
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
|
|
2
|
+
|
|
3
|
+
import { KnownError } from "../helpers/KnownError.js"
|
|
4
|
+
import { type Key, type MultipleErrors, type RawKey, SHORTCUT_ERROR } from "../types/index.js"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a key.
|
|
9
|
+
*
|
|
10
|
+
* @template TId **@internal** See {@link ./README.md Collection Entries}
|
|
11
|
+
* @param id See {@link Key.id}
|
|
12
|
+
* @param opts Set {@link Key}.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export function createKey<
|
|
16
|
+
TId extends string = string,
|
|
17
|
+
TKey extends RawKey<TId> = RawKey<TId>
|
|
18
|
+
>(
|
|
19
|
+
id: TId,
|
|
20
|
+
rawKey: Omit<TKey, "id"> = {} as any
|
|
21
|
+
): Result<
|
|
22
|
+
Key<TId>,
|
|
23
|
+
MultipleErrors<
|
|
24
|
+
| typeof SHORTCUT_ERROR.INVALID_VARIANT
|
|
25
|
+
>
|
|
26
|
+
> {
|
|
27
|
+
const k = rawKey
|
|
28
|
+
const key: Key<TId> = {
|
|
29
|
+
type: "key",
|
|
30
|
+
id,
|
|
31
|
+
label: k.label ?? id,
|
|
32
|
+
classes: [...(k.classes as any ?? [])],
|
|
33
|
+
x: k.x ?? 0,
|
|
34
|
+
y: k.y ?? 0,
|
|
35
|
+
width: k.width ?? 0,
|
|
36
|
+
height: k.height ?? 0,
|
|
37
|
+
enabled: k.enabled ?? true,
|
|
38
|
+
pressed: false,
|
|
39
|
+
isModifier: k.isModifier ?? false,
|
|
40
|
+
isToggle: (k.isToggle ?? false) satisfies Key["isToggle"] as false,
|
|
41
|
+
...(k.isToggle
|
|
42
|
+
? {
|
|
43
|
+
toggleOnId: `${id}On`,
|
|
44
|
+
toggleOffId: `${id}Off`,
|
|
45
|
+
toggleOnPressed: false,
|
|
46
|
+
toggleOffPressed: false
|
|
47
|
+
} satisfies Partial<Key<string>>
|
|
48
|
+
: {}) as any,
|
|
49
|
+
variants: k.variants ?? [],
|
|
50
|
+
render: k.render ?? true,
|
|
51
|
+
updateStateOnAllEvents: k.updateStateOnAllEvents ?? true
|
|
52
|
+
}
|
|
53
|
+
if (key.variants?.includes(key.id)) {
|
|
54
|
+
return Err(
|
|
55
|
+
new KnownError(SHORTCUT_ERROR.INVALID_VARIANT, `A key variant cannot be the key id itself. Attempted to use "${key.id}" in variants:[ ${key.variants.join(",")} ]`, { variants: key.variants as any /* todo */, id: key.id })
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
return Ok(key)
|
|
59
|
+
}
|