diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 91edec5d6..eb55e8549 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -188,7 +188,7 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-updater", "tokio", - "toml 0.8.16", + "toml 0.8.19", "url", ] @@ -727,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" dependencies = [ "serde", - "toml 0.8.16", + "toml 0.8.19", ] [[package]] @@ -798,9 +798,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -808,9 +808,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -822,9 +822,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1389,7 +1389,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.16", + "toml 0.8.19", "vswhom", "winreg 0.52.0", ] @@ -2622,7 +2622,7 @@ dependencies = [ "thiserror", "tokio", "tokio-tungstenite", - "toml 0.8.16", + "toml 0.8.19", "tower-lsp", "ts-rs", "url", @@ -5088,7 +5088,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.16", + "toml 0.8.19", "version-compare", ] @@ -5241,7 +5241,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.16", + "toml 0.8.19", "walkdir", ] @@ -5299,7 +5299,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.16", + "toml 0.8.19", "walkdir", ] @@ -5583,7 +5583,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror", - "toml 0.8.16", + "toml 0.8.19", "url", "urlpattern", "walkdir", @@ -5830,21 +5830,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.17", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -5886,15 +5886,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.6", + "winnow 0.6.18", ] [[package]] @@ -6965,9 +6965,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 22ca3af39..18d1481c2 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -10,7 +10,6 @@ import { isSingleCursorInPipe } from 'lang/queryAst' import { useShouldDisableModelingActions } from 'hooks/useShouldDisableModelingActions' import { useInteractionMap } from 'hooks/useInteractionMap' import { ActionButtonDropdown } from 'components/ActionButtonDropdown' -import { useHotkeys } from 'react-hotkeys-hook' import Tooltip from 'components/Tooltip' import { KEYBINDING_CATEGORIES } from 'lib/constants' import { useAppState } from 'AppState' @@ -30,7 +29,6 @@ export function Toolbar({ }: React.HTMLAttributes) { const { state, send, context } = useModelingContext() const { commandBarSend } = useCommandsContext() - const shouldDisableModelingActions = useShouldDisableModelingActions() useInteractionMap( [ { @@ -39,7 +37,7 @@ export function Toolbar({ sequence: 'shift+s', action: () => send({ type: 'Enter sketch', data: { forceNewSketch: true } }), - guard: () => !shouldDisableModelingActions && state.matches('idle'), + guard: () => state.can('Enter sketch'), }, { name: 'extrude', @@ -50,10 +48,10 @@ export function Toolbar({ type: 'Find and select command', data: { name: 'Extrude', groupId: 'modeling' }, }), - guard: () => !shouldDisableModelingActions && state.matches('idle'), + guard: () => state.can('Extrude'), }, ], - [shouldDisableModelingActions, commandBarSend, state], + [commandBarSend, state], KEYBINDING_CATEGORIES.MODELING ) const iconClassName = @@ -286,19 +284,19 @@ const ToolbarItemContents = memo(function ToolbarItemContents({ itemConfig: ToolbarItemResolved configCallbackProps: ToolbarItemCallbackProps }) { - useHotkeys( - itemConfig.hotkey || '', - () => { - itemConfig.onClick(configCallbackProps) - }, - { - enabled: - itemConfig.status === 'available' && - !!itemConfig.hotkey && - !itemConfig.disabled && - !itemConfig.disableHotkey, - } - ) + // useHotkeys( + // itemConfig.hotkey || '', + // () => { + // itemConfig.onClick(configCallbackProps) + // }, + // { + // enabled: + // itemConfig.status === 'available' && + // !!itemConfig.hotkey && + // !itemConfig.disabled && + // !itemConfig.disableHotkey, + // } + // ) return ( <> diff --git a/src/components/InteractionMapMachineProvider.tsx b/src/components/InteractionMapMachineProvider.tsx index a08027015..c27dea791 100644 --- a/src/components/InteractionMapMachineProvider.tsx +++ b/src/components/InteractionMapMachineProvider.tsx @@ -10,6 +10,7 @@ import { import { MouseButtonName, interactionMapMachine, + makeOverrideKey, } from 'machines/interactionMapMachine' import { createContext, useEffect } from 'react' import toast from 'react-hot-toast' @@ -61,7 +62,9 @@ export function InteractionMapMachineProvider({ // normalize any interaction sequences to be sorted const normalizedInteractions = event.data.map((item) => ({ ...item, - sequence: item.sequence + sequence: ( + context.overrides[makeOverrideKey(item)] || item.sequence + ) .split(' ') .map((step) => step @@ -94,6 +97,17 @@ export function InteractionMapMachineProvider({ ] }, }), + 'Merge into overrides': assign({ + overrides: (context, event) => { + return { + ...context.overrides, + ...event.data, + } + }, + }), + 'Persist keybinding overrides': (context) => { + console.log('Persisting keybinding overrides', context.overrides) + }, }, services: { 'Resolve hotkey by prefix': (context, event) => { @@ -111,13 +125,16 @@ export function InteractionMapMachineProvider({ resolvedInteraction.asString const matches = context.interactionMap.filter((item) => - item.sequence.startsWith(searchString) + ( + context.overrides[makeOverrideKey(item)] || item.sequence + ).startsWith(searchString) ) console.log('matches', { matches, interactionMap: context.interactionMap, searchString, + overrides: context.overrides, }) // If we have no matches, reject the promise @@ -126,8 +143,11 @@ export function InteractionMapMachineProvider({ } const exactMatches = matches.filter( - (item) => item.sequence === searchString + (item) => + (context.overrides[makeOverrideKey(item)] || item.sequence) === + searchString ) + console.log('exactMatches', exactMatches) if (!exactMatches.length) { // We have a prefix match. // Reject the promise and return the step @@ -136,9 +156,11 @@ export function InteractionMapMachineProvider({ } // Resolve to just one exact match - const availableExactMatches = exactMatches.filter((item) => - item.guard(event.data) + const availableExactMatches = exactMatches.filter( + (item) => !item.guard || item.guard(event.data) ) + + console.log('availableExactMatches', availableExactMatches) if (availableExactMatches.length === 0) { return Promise.reject() } else { diff --git a/src/components/ModelingSidebar/ModelingPanes/index.ts b/src/components/ModelingSidebar/ModelingPanes/index.ts index d1c9d46b0..057678ad1 100644 --- a/src/components/ModelingSidebar/ModelingPanes/index.ts +++ b/src/components/ModelingSidebar/ModelingPanes/index.ts @@ -26,7 +26,7 @@ export type SidebarType = | 'lspMessages' | 'variables' -const PANE_KEYBINDING_PREFIX = 'alt+p ' as const +const PANE_KEYBINDING_PREFIX = 'ctrl+shift+p ' as const /** * This interface can be extended as more context is needed for the panes diff --git a/src/components/ModelingSidebar/ModelingSidebar.tsx b/src/components/ModelingSidebar/ModelingSidebar.tsx index 12783cd56..470a22f1d 100644 --- a/src/components/ModelingSidebar/ModelingSidebar.tsx +++ b/src/components/ModelingSidebar/ModelingSidebar.tsx @@ -12,6 +12,10 @@ import { CustomIconName } from 'components/CustomIcon' import { useCommandsContext } from 'hooks/useCommandsContext' import { IconDefinition } from '@fortawesome/free-solid-svg-icons' import { useKclContext } from 'lang/KclProvider' +import { KEYBINDING_CATEGORIES } from 'lib/constants' +import { useInteractionMap } from 'hooks/useInteractionMap' +import { useInteractionMapContext } from 'hooks/useInteractionMapContext' +import { InteractionSequence } from 'components/Settings/AllKeybindingsFields' interface ModelingSidebarProps { paneOpacity: '' | 'opacity-20' | 'opacity-40' @@ -63,6 +67,18 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { [sidebarPanes, showDebugPanel.current] ) + useInteractionMap( + filteredPanes.map((pane) => ({ + name: pane.id, + action: () => togglePane(pane.id), + keybinding: pane.keybinding, + title: `Toggle ${pane.title} pane`, + sequence: pane.keybinding, + })), + [filteredPanes, context.store?.openPanes], + KEYBINDING_CATEGORIES.USER_INTERFACE + ) + const paneBadgeMap: Record = useMemo(() => { return filteredPanes.reduce((acc, pane) => { if (pane.showBadge) { @@ -207,6 +223,16 @@ function ModelingPaneButton({ showBadge, ...props }: ModelingPaneButtonProps) { + const { state: interactionMapState } = useInteractionMapContext() + + const resolvedKeybinding = useMemo( + () => + interactionMapState.context.overrides[ + `${KEYBINDING_CATEGORIES.USER_INTERFACE}.${paneConfig.id}` + ] || paneConfig.keybinding, + [interactionMapState.context.overrides] + ) + return ( ) diff --git a/src/components/Settings/AllKeybindingsFields.tsx b/src/components/Settings/AllKeybindingsFields.tsx index 33821fe48..5aa16b546 100644 --- a/src/components/Settings/AllKeybindingsFields.tsx +++ b/src/components/Settings/AllKeybindingsFields.tsx @@ -1,13 +1,11 @@ import { ActionIcon } from 'components/ActionIcon' import { useInteractionMapContext } from 'hooks/useInteractionMapContext' +import { resolveInteractionEvent } from 'lib/keyboard' import { - isModifierKey, - mapKey, - mouseButtonToName, - resolveInteractionEvent, -} from 'lib/keyboard' -import { InteractionMapItem } from 'machines/interactionMapMachine' -import { useEffect, useState } from 'react' + InteractionMapItem, + makeOverrideKey, +} from 'machines/interactionMapMachine' +import { FormEvent, HTMLProps, useEffect, useRef, useState } from 'react' export function AllKeybindingsFields() { const { state } = useInteractionMapContext() @@ -23,8 +21,23 @@ export function AllKeybindingsFields() { } function KeybindingField({ item }: { item: InteractionMapItem }) { + const { send, state } = useInteractionMapContext() const [isEditing, setIsEditing] = useState(false) const [newSequence, setNewSequence] = useState('') + const submitRef = useRef(null) + + function handleSubmit(e: FormEvent) { + e.preventDefault() + if (newSequence !== item.sequence) { + send({ + type: 'Update overrides', + data: { + [makeOverrideKey(item)]: newSequence, + }, + }) + } + setIsEditing(false) + } useEffect(() => { const blockOtherEvents = (e: KeyboardEvent | MouseEvent) => { @@ -34,13 +47,25 @@ function KeybindingField({ item }: { item: InteractionMapItem }) { } const handleInteraction = (e: KeyboardEvent | MouseEvent) => { + if (e instanceof KeyboardEvent && e.key === 'Escape') { + blockOtherEvents(e) + setIsEditing(false) + return + } else if (e instanceof KeyboardEvent && e.key === 'Enter') { + return + } else if (e instanceof MouseEvent && e.target === submitRef.current) { + return + } blockOtherEvents(e) const resolvedInteraction = resolveInteractionEvent(e) if (resolvedInteraction.isModifier) return - setNewSequence( - (prev) => prev + (prev.length ? ' ' : '') + resolvedInteraction.asString - ) + setNewSequence((prev) => { + const newSequence = + prev + (prev.length ? ' ' : '') + resolvedInteraction.asString + console.log('newSequence', newSequence) + return newSequence + }) } const handleContextMenu = (e: MouseEvent) => { @@ -49,47 +74,100 @@ function KeybindingField({ item }: { item: InteractionMapItem }) { if (!isEditing) { setNewSequence('') - globalThis?.window?.removeEventListener('keydown', handleInteraction) - globalThis?.window?.removeEventListener('mousedown', handleInteraction) - globalThis?.window?.removeEventListener('contextmenu', handleContextMenu) + globalThis?.window?.removeEventListener('keydown', handleInteraction, { + capture: true, + }) + globalThis?.window?.removeEventListener('mousedown', handleInteraction, { + capture: true, + }) + globalThis?.window?.removeEventListener( + 'contextmenu', + handleContextMenu, + { capture: true } + ) } else { - globalThis?.window?.addEventListener('keydown', handleInteraction) - globalThis?.window?.addEventListener('mousedown', handleInteraction) - globalThis?.window?.addEventListener('contextmenu', handleContextMenu) + globalThis?.window?.addEventListener('keydown', handleInteraction, { + capture: true, + }) + globalThis?.window?.addEventListener('mousedown', handleInteraction, { + capture: true, + }) + globalThis?.window?.addEventListener('contextmenu', handleContextMenu, { + capture: true, + }) } return () => { - globalThis?.window?.removeEventListener('keydown', handleInteraction) - globalThis?.window?.removeEventListener('mousedown', handleInteraction) - globalThis?.window?.removeEventListener('contextmenu', handleContextMenu) + globalThis?.window?.removeEventListener('keydown', handleInteraction, { + capture: true, + }) + globalThis?.window?.removeEventListener('mousedown', handleInteraction, { + capture: true, + }) + globalThis?.window?.removeEventListener( + 'contextmenu', + handleContextMenu, + { capture: true } + ) } - }, [isEditing]) + }, [isEditing, setNewSequence]) - return ( + return isEditing ? ( +
+

{item.title}

+ + + + + ) : (

{item.title}

-
- {(isEditing ? newSequence : item.sequence) - .split(' ') - .map((chord, i) => ( - - {chord} - - ))} -
+
) } + +export function InteractionSequence({ + sequence, + className = '', + showNoSequence = false, + ...props +}: HTMLProps & { sequence: string; showNoSequence?: boolean }) { + return sequence.length ? ( +
+ {sequence.split(' ').map((chord, i) => ( + + {chord} + + ))} +
+ ) : ( + showNoSequence && ( +
No sequence set
+ ) + ) +} diff --git a/src/hooks/useShouldDisableModelingActions.ts b/src/hooks/useShouldDisableModelingActions.ts index 5c76ae856..a441f0002 100644 --- a/src/hooks/useShouldDisableModelingActions.ts +++ b/src/hooks/useShouldDisableModelingActions.ts @@ -1,9 +1,6 @@ -import { - NetworkHealthState, - useNetworkStatus, -} from 'components/NetworkHealthIndicator' +import { useAppState } from 'AppState' +import { NetworkHealthState, useNetworkStatus } from 'hooks/useNetworkStatus' import { useKclContext } from 'lang/KclProvider' -import { useStore } from 'useStore' /** * Custom hook to determine if modeling actions should be disabled @@ -13,9 +10,7 @@ import { useStore } from 'useStore' export function useShouldDisableModelingActions() { const { overallState } = useNetworkStatus() const { isExecuting } = useKclContext() - const { isStreamReady } = useStore((s) => ({ - isStreamReady: s.isStreamReady, - })) + const { isStreamReady } = useAppState() return overallState !== NetworkHealthState.Ok || isExecuting || !isStreamReady } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 5fa6f4cf9..ec598435d 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -61,4 +61,5 @@ export const KCL_DEFAULT_LENGTH = `5` export const KEYBINDING_CATEGORIES = { MODELING: 'modeling', COMMAND_BAR: 'command-bar', + USER_INTERFACE: 'user-interface', } diff --git a/src/machines/interactionMapMachine.ts b/src/machines/interactionMapMachine.ts index b934c65af..68f61377a 100644 --- a/src/machines/interactionMapMachine.ts +++ b/src/machines/interactionMapMachine.ts @@ -6,15 +6,20 @@ export type InteractionMapItem = { name: string title: string sequence: string - guard: (e: MouseEvent | KeyboardEvent) => boolean + guard?: (e: MouseEvent | KeyboardEvent) => boolean action: () => void ownerId: string } +export function makeOverrideKey(interactionMapItem: InteractionMapItem) { + return `${interactionMapItem.ownerId}.${interactionMapItem.name}` +} + export const interactionMapMachine = createMachine({ - /** @xstate-layout N4IgpgJg5mDOIC5QEkB2AXMAnAhgY3QEsB7VAAgFkcAHMgQQOKwGI6IIz1izCNt8ipMgFsaAbQAMAXUShqxWIUGpZIAB6IAzAE4AbADoATBIAcAdk0AWCQFYJmiRMMAaEAE9EAWgCMN7-t0JbRNdS0tDM29dbQBfGNc0TFwCEnIqWgYuFgAlMGFiADcwMgAzLGJhHj5k5RFxaVV5RWVVDQRow30TU29rbrsrb1cPBB8-AKDtXytg7R0TOITqgVTKGnpGLH0AGUJYTFReKFKmKqSV0mYAMUIsYrAijEkZJBAmpVTWxDmbfTMI7wmEyWYGGbThYZaUJGKw2SxwmwWSJmRYgRL8FJCdIbLL6XKwYgAGyKZAAFsR0ABrMBuZgQUhgfS8ArEan6O4E4lgAASFOpbgAQm4AAp3EqENTPRoKD6kL4IbzeTSaLreMyWXS6RWaXRmOaQhB2Aw2KyGawOEyInWo9E1VbYzJMPFwIkk8lUmnMbDlLbUQk4dAlJjCdkurm8j2CkViiVS17vFqvNreCSArq6Yw6iLaRyGGwGqK-bRzUKGbyGM0mYuGG3LTFpdaOrb413Fd38r1YH36P0BoNYEMc1sR-lC0VgcWS7wvOQyxOgZNOfxWeHRDPzMsGgFdPSObrKsxwywo+Jouu1B2bfQAUTUYDwAFdMGR+aJaA8wBg6QymagWWywDvR9MAAaRpN9MlSONZ2aT4k0QMIzH0YJvGCGYbGCVNdANTQ4X0DVUzsCswW6WJT1tC4GwyK9b3vJ9ilfdYPy-b0nV7QNg30QC6NA8CaEg0hoLeOc4IXBCQQCGxjDVawKz1eEt0tdMHDMboU20M0zBPU9UGICA4FUCj6zWaismlWC5Xg0YhncRBfH0csdRTWwTEMaIbF0TRa3OYzL1xXZ9k-I4TiwM4MXnYSLJUKz-iQwwTFwzQEvhM1bC3DTVSBTzHBNdzvPC+1GyvFsuTJPkaXM2VorEhVj1+CRdBMctLHUk03JwzR-HLHMLECDyEu8fK7SxIrcVo4CGL499HnQSqIraY9FJVUJgQ1cJUP+CRLDiOIgA */ + /** @xstate-layout N4IgpgJg5mDOIC5QEkB2AXMAnAhgY3QEsB7VAAgFkcAHMgQQOKwGI6IIz1izCNt8ipMgFsaAbQAMAXUShqxWIUGpZIAB6IAzAE4AbADoATBIAcAdk0AWCQFYJmiRMMAaEAE9EAWgCMN7-t0JbRNdS0tDM29dbQBfGNc0TFwCEnIqWgYuFgAlMGFiADcwMgAzLGJhHj5k5RFxaVV5RWVVDQRow30TU29rbrsrb1cPBB8-AKDtXytg7R0TOITqgVTKGnpGLH0AGUJYTFReKFKmKqSV0mYAMUIsYrAijEkZJBAmpVTWxDmbfTMI7wmEyWYGGbThYZaUJGKw2SxwmwWSJmRYgRL8FJCdIbLL6XKwYgAGyKZAAFsR0ABrMBuZgQUhgfS8ArEan6O4E4lgAASFOpbgAQm4AAp3EqENTPRoKD6kL4IbzeTSaLreMyWXS6RWaXRmOaQhB2Aw2KyGawOEyInWo9E1VbYzJMPFwIkk8lUmnMbDlLbUQk4dAlJjCdkurm8j2CkViiVS17vFqvNreCSArq6Yw6iLaRyGGwGqK-bRzUKGbyGM0mYuGG3LTFpdaOrb413Fd38r1YH36P0BoNYEMc1sR-lC0VgcWS7wvOQyxOgZNOfxWeHRDPzMsGgFdPSObrKsxwywo+Jouu1B2bfQAUTUYDwAFdMGR+aJaA8wBg6QymagWWywDvR9MAAaRpN9MlSONZ2aT4k0QMIzH0YJvGCGYbGCVNdANTQ4X0DVUzsCswW6WJT1tC4GwyK9b3vJ9ilfdYPy-b0nV7QNg30QC6NA8CaEg0hoLeOc4IXBCQQCGxjDVawKz1eEt0tdMHDMboU20M0zBPJZznrNZqKyZgAFVqAgANikKb1CAgOAhITUT1EQbUVRBA9gRsEw1SGdwvHLExkLLCR1yCzVyxPU9UGIGz4FeCi9MvLJpVguV4NGbyRl8fRyx1XCTDBQxNH+MJa10i9GyvXZ9k-I4TiwM4MXnYTkpUVL-iQwwTFwzROvhM1bC3DTVSBXQHFsHVtBsEqGvtcrcRbLkyT5GkktlFqxIVY9fiCzyzXUk1DGwnyEGVfxyxzCxAhsXROu8Ka7SxWanVo4CGL499HnQFbGraY9FJVUJgQ1cJUP+CRLDiOIgA */ context: { interactionMap: [] as InteractionMapItem[], + overrides: {} as { [key: string]: string }, currentSequence: '' as string, }, predictableActionArguments: true, @@ -35,6 +40,7 @@ export const interactionMapMachine = createMachine({ | { type: 'Update prefix matrix' } | { type: 'Add last interaction to sequence' } | { type: 'Clear sequence' } + | { type: 'Update overrides'; data: { [key: string]: string } } | { type: 'Resolve hotkey by prefix'; data: MouseEvent | KeyboardEvent } | { type: 'done.invoke.resolveHotkeyByPrefix'; data: InteractionMapItem } | { @@ -115,5 +121,11 @@ export const interactionMapMachine = createMachine({ }, ], }, + + 'Update overrides': { + target: '#Interaction Map Actor', + internal: true, + actions: ['Merge into overrides', 'Persist keybinding overrides'], + }, }, })