Basic editable (not persisted) keybindings
This commit is contained in:
48
src-tauri/Cargo.lock
generated
48
src-tauri/Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -61,4 +61,5 @@ export const KCL_DEFAULT_LENGTH = `5`
|
||||
export const KEYBINDING_CATEGORIES = {
|
||||
MODELING: 'modeling',
|
||||
COMMAND_BAR: 'command-bar',
|
||||
USER_INTERFACE: 'user-interface',
|
||||
}
|
||||
|
||||
@ -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'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user