♻️(frontend) centralize shortcuts in a catalog

Centralize shortcuts into a single source of truth, making them easier to
discover and manage, and laying the groundwork for future override support
and the ability to revert to default definitions if needed.

Shortcuts are now retrieved by identifier, while leaving each component
responsible for declaring when a shortcut should be enabled and which
handler should be called;
This commit is contained in:
Ovgodd
2026-02-10 19:02:41 +01:00
committed by aleb_the_flash
parent 89031abb63
commit a2c7becaf4
5 changed files with 71 additions and 20 deletions

View File

@@ -18,6 +18,7 @@ import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
import { useDeviceIcons } from '../../../hooks/useDeviceIcons'
import { useDeviceShortcut } from '../../../hooks/useDeviceShortcut'
import { ToggleSource, CaptureOptionsBySource } from '@livekit/components-core'
import { getShortcutDescriptorById } from '@/features/shortcuts/catalog'
type ToggleDeviceStyleProps = {
variant?: NonNullable<ButtonRecipeProps>['variant']
@@ -88,12 +89,14 @@ export const ToggleDevice = <T extends ToggleSource>({
const deviceShortcut = useDeviceShortcut(kind)
useRegisterKeyboardShortcut({
shortcut: deviceShortcut,
id: deviceShortcut?.id,
handler: async () => await toggle(),
isDisabled: cannotUseDevice,
})
const pushToTalkShortcut = getShortcutDescriptorById('push-to-talk')
useLongPress({
keyCode: kind === 'audioinput' ? 'KeyV' : undefined,
keyCode: kind === 'audioinput' ? pushToTalkShortcut?.code : undefined,
onKeyDown,
onKeyUp,
isDisabled: cannotUseDevice,
@@ -103,7 +106,9 @@ export const ToggleDevice = <T extends ToggleSource>({
const label = t(enabled ? 'disable' : 'enable', {
keyPrefix: `selectDevice.${kind}`,
})
return deviceShortcut ? appendShortcutLabel(label, deviceShortcut) : label
return deviceShortcut?.shortcut
? appendShortcutLabel(label, deviceShortcut.shortcut)
: label
}, [enabled, kind, deviceShortcut, t])
const Icon =

View File

@@ -1,19 +1,16 @@
import { useMemo } from 'react'
import { Shortcut } from '@/features/shortcuts/types'
import {
getShortcutDescriptorById,
ShortcutDescriptor,
} from '@/features/shortcuts/catalog'
export const useDeviceShortcut = (kind: MediaDeviceKind) => {
return useMemo<Shortcut | undefined>(() => {
return useMemo<ShortcutDescriptor | undefined>(() => {
switch (kind) {
case 'audioinput':
return {
key: 'd',
ctrlKey: true,
}
return getShortcutDescriptorById('toggle-microphone')
case 'videoinput':
return {
key: 'e',
ctrlKey: true,
}
return getShortcutDescriptorById('toggle-camera')
default:
return undefined
}

View File

@@ -22,7 +22,7 @@ export function DesktopControlBar({
const desktopControlBarEl = useRef<HTMLDivElement>(null)
useRegisterKeyboardShortcut({
shortcut: { key: 'F2' },
id: 'focus-toolbar',
handler: () => {
const root = desktopControlBarEl.current
if (!root) return

View File

@@ -0,0 +1,47 @@
import { Shortcut } from './types'
// Central list of current keyboard shortcuts.
// Keep a single source of truth for display and, later, customization
export type ShortcutCategory = 'navigation' | 'media' | 'interaction'
export type ShortcutId =
| 'focus-toolbar'
| 'toggle-microphone'
| 'toggle-camera'
| 'push-to-talk'
export const getShortcutDescriptorById = (id: ShortcutId) =>
shortcutCatalog.find((item) => item.id === id)
export type ShortcutDescriptor = {
id: ShortcutId
category: ShortcutCategory
shortcut?: Shortcut
kind?: 'press' | 'longPress'
code?: string // used when kind === 'longPress' (KeyboardEvent.code)
description?: string
}
export const shortcutCatalog: ShortcutDescriptor[] = [
{
id: 'focus-toolbar',
category: 'navigation',
shortcut: { key: 'F2' },
},
{
id: 'toggle-microphone',
category: 'media',
shortcut: { key: 'd', ctrlKey: true },
},
{
id: 'toggle-camera',
category: 'media',
shortcut: { key: 'e', ctrlKey: true },
},
{
id: 'push-to-talk',
category: 'media',
kind: 'longPress',
code: 'KeyV',
},
]

View File

@@ -1,26 +1,28 @@
import { useEffect } from 'react'
import { keyboardShortcutsStore } from '@/stores/keyboardShortcuts'
import { formatShortcutKey } from '@/features/shortcuts/utils'
import { Shortcut } from '@/features/shortcuts/types'
import { ShortcutId, getShortcutDescriptorById } from './catalog'
export type useRegisterKeyboardShortcutProps = {
shortcut?: Shortcut
id?: ShortcutId
handler: () => Promise<void | boolean | undefined> | void
isDisabled?: boolean
}
export const useRegisterKeyboardShortcut = ({
shortcut,
id,
handler,
isDisabled = false,
}: useRegisterKeyboardShortcutProps) => {
useEffect(() => {
if (!shortcut) return
const formattedKey = formatShortcutKey(shortcut)
if (!id) return
const descriptor = getShortcutDescriptorById(id)
if (!descriptor?.shortcut) return
const formattedKey = formatShortcutKey(descriptor.shortcut)
if (isDisabled) {
keyboardShortcutsStore.shortcuts.delete(formattedKey)
} else {
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
}
}, [handler, shortcut, isDisabled])
}, [handler, id, isDisabled])
}