Basic editable (not persisted) keybindings

This commit is contained in:
Frank Noirot
2024-08-01 15:04:35 -04:00
parent b5f2e0ea3e
commit cac848ab22
9 changed files with 230 additions and 95 deletions

48
src-tauri/Cargo.lock generated
View File

@ -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",
]

View File

@ -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<HTMLElement>) {
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 (
<>

View File

@ -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 {

View File

@ -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

View File

@ -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<SidebarType, number | boolean> = 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 (
<button
className="pointer-events-auto flex items-center justify-center border-transparent dark:border-transparent p-0 m-0 rounded-sm !outline-0 focus-visible:border-primary"
@ -258,7 +284,10 @@ function ModelingPaneButton({
{paneConfig.title}
{paneIsOpen !== undefined ? ` pane` : ''}
</span>
<kbd className="hotkey text-xs capitalize">{paneConfig.keybinding}</kbd>
<InteractionSequence
sequence={resolvedKeybinding}
className="flex-nowrap !gap-1"
/>
</Tooltip>
</button>
)

View File

@ -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<HTMLButtonElement>(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 ? (
<form
key={item.ownerId + '-' + item.name}
className="flex gap-2 justify-between items-start"
onSubmit={handleSubmit}
>
<h3>{item.title}</h3>
<InteractionSequence sequence={newSequence} showNoSequence />
<input type="hidden" value={item.sequence} name="sequence" />
<button ref={submitRef} className="p-0 m-0" type="submit">
<ActionIcon icon="checkmark" />
</button>
</form>
) : (
<div
key={item.ownerId + '-' + item.name}
className="flex gap-2 justify-between items-start"
>
<h3>{item.title}</h3>
<div className="flex-1 flex flex-wrap justify-end gap-3">
{(isEditing ? newSequence : item.sequence)
.split(' ')
.map((chord, i) => (
<kbd
key={`${item.ownerId}-${item.name}-${chord}-${i}`}
className="py-0.5 px-1.5 rounded bg-primary/10 dark:bg-chalkboard-80"
>
{chord}
</kbd>
))}
</div>
<InteractionSequence
sequence={
state.context.overrides[makeOverrideKey(item)] || item.sequence
}
showNoSequence
/>
<button
onClick={() => setIsEditing((prev) => !prev)}
ref={submitRef}
className="p-0 m-0"
type={isEditing ? 'submit' : 'button'}
onClick={() => setIsEditing(true)}
>
<ActionIcon icon={isEditing ? 'checkmark' : 'sketch'} />
<ActionIcon icon="sketch" />
</button>
</div>
)
}
export function InteractionSequence({
sequence,
className = '',
showNoSequence = false,
...props
}: HTMLProps<HTMLDivElement> & { sequence: string; showNoSequence?: boolean }) {
return sequence.length ? (
<div
className={'flex-1 flex flex-wrap justify-end gap-3 ' + className}
{...props}
>
{sequence.split(' ').map((chord, i) => (
<kbd key={`sequence-${sequence}-${chord}-${i}`} className="hotkey">
{chord}
</kbd>
))}
</div>
) : (
showNoSequence && (
<div className="flex-1 flex justify-end text-xs">No sequence set</div>
)
)
}

View File

@ -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
}

View File

@ -61,4 +61,5 @@ export const KCL_DEFAULT_LENGTH = `5`
export const KEYBINDING_CATEGORIES = {
MODELING: 'modeling',
COMMAND_BAR: 'command-bar',
USER_INTERFACE: 'user-interface',
}

View File

@ -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'],
},
},
})