import { useCallback, useEffect, useId, useMemo, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, } from 'react' import { useNavigate } from 'react-router-dom' import { machineRegistry } from '../../data/machineRegistry' import { createCommandPaletteItems, filterCommandPaletteItems, type CommandPaletteSearchResult, } from '../../utils/commandPalette' const MAX_VISIBLE_RESULTS = 10 const FOCUSABLE_SELECTOR = [ 'a[href]', 'button:not([disabled])', 'textarea:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', '[tabindex]:not([tabindex="-1"])', ].join(',') export function CommandPalette() { const navigate = useNavigate() const headingId = useId() const descriptionId = useId() const inputId = useId() const listboxId = useId() const panelRef = useRef(null) const inputRef = useRef(null) const [isOpen, setIsOpen] = useState(false) const [query, setQuery] = useState('') const [activeIndex, setActiveIndex] = useState(0) const [isApplePlatform, setIsApplePlatform] = useState(false) const commandItems = useMemo( () => createCommandPaletteItems(machineRegistry), [], ) const results = useMemo( () => filterCommandPaletteItems(commandItems, query, { maxResults: MAX_VISIBLE_RESULTS, }), [commandItems, query], ) const activeResult = results[activeIndex] const activeOptionId = activeResult ? getOptionId(listboxId, activeResult.id, activeIndex) : undefined const primaryShortcutLabel = isApplePlatform ? '⌘K' : 'Ctrl K' const spokenShortcutLabel = isApplePlatform ? 'Command K' : 'Control K' const resultStatus = query.trim() ? `${results.length} ${results.length === 1 ? 'result' : 'results'} for ${query}` : `${results.length} quick ${results.length === 1 ? 'destination' : 'destinations'} available` const openPalette = useCallback(() => { setIsOpen(true) }, []) const closePalette = useCallback(() => { setIsOpen(false) setQuery('') setActiveIndex(0) }, []) const selectResult = useCallback( (result: CommandPaletteSearchResult) => { closePalette() navigate(result.href) }, [closePalette, navigate], ) useEffect(() => { setIsApplePlatform(detectApplePlatform()) }, []) useEffect(() => { const handleShortcut = (event: KeyboardEvent) => { if (event.defaultPrevented) { return } const key = event.key.toLocaleLowerCase() const isCommandK = key === 'k' && (event.metaKey || event.ctrlKey) const isSlashSearch = event.key === '/' && !event.metaKey && !event.ctrlKey && !event.altKey && !isEditableTarget(event.target) if (!isCommandK && !isSlashSearch) { return } event.preventDefault() openPalette() } window.addEventListener('keydown', handleShortcut) return () => { window.removeEventListener('keydown', handleShortcut) } }, [openPalette]) useEffect(() => { setActiveIndex(0) }, [query]) useEffect(() => { setActiveIndex((currentIndex) => { if (results.length === 0) { return 0 } return Math.min(currentIndex, results.length - 1) }) }, [results.length]) useEffect(() => { if (!isOpen) { return } const previousActiveElement = document.activeElement instanceof HTMLElement ? document.activeElement : null const previousBodyOverflow = document.body.style.overflow const focusTimer = window.setTimeout(() => { inputRef.current?.focus() }, 0) document.body.style.overflow = 'hidden' const handleDialogKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { event.preventDefault() closePalette() return } if (event.key === 'Tab') { trapFocus(event, panelRef.current) } } document.addEventListener('keydown', handleDialogKeyDown) return () => { window.clearTimeout(focusTimer) document.removeEventListener('keydown', handleDialogKeyDown) document.body.style.overflow = previousBodyOverflow if (previousActiveElement?.isConnected) { previousActiveElement.focus({ preventScroll: true }) } } }, [closePalette, isOpen]) const handleInputKeyDown = ( event: ReactKeyboardEvent, ) => { if (event.nativeEvent.isComposing) { return } if (event.key === 'ArrowDown') { event.preventDefault() if (results.length === 0) { return } setActiveIndex((currentIndex) => (currentIndex + 1) % results.length) return } if (event.key === 'ArrowUp') { event.preventDefault() if (results.length === 0) { return } setActiveIndex( (currentIndex) => (currentIndex - 1 + results.length) % results.length, ) return } if (event.key === 'Enter') { const result = results[activeIndex] if (!result) { return } event.preventDefault() selectResult(result) } } return ( <> {isOpen ? (
{ if (event.target === event.currentTarget) { closePalette() } }} >

Command palette

Search machines, categories, difficulties, and catalogue destinations. Use arrow keys to move through results and Enter to open the selected destination.

setQuery(event.target.value)} onKeyDown={handleInputKeyDown} />

{resultStatus}

    {results.map((result, index) => { const isActive = index === activeIndex const optionId = getOptionId(listboxId, result.id, index) return (
  • setActiveIndex(index)} onPointerDown={(event) => { event.preventDefault() selectResult(result) }} > {result.label} {result.category ? ( {result.category} ) : null} {result.difficulty ? ( {result.difficulty} ) : null} {result.subtitle ? ( {result.subtitle} ) : null} {result.href}
  • ) })}
{results.length === 0 ? (

No matching destination

Try a machine name, category, difficulty, or engineering keyword.

) : null}
Navigate Enter Open Esc Close Also opens with /
) : null} ) } export default CommandPalette function detectApplePlatform(): boolean { if (typeof navigator === 'undefined') { return false } return /Mac|iPhone|iPad|iPod/.test(navigator.platform) } function isEditableTarget(target: EventTarget | null): boolean { if (!(target instanceof HTMLElement)) { return false } return ( target.isContentEditable || target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT' ) } function trapFocus(event: KeyboardEvent, container: HTMLElement | null): void { if (!container) { return } const focusableElements = Array.from( container.querySelectorAll(FOCUSABLE_SELECTOR), ).filter( (element) => element.tabIndex !== -1 && element.getAttribute('aria-hidden') !== 'true', ) if (focusableElements.length === 0) { event.preventDefault() return } const firstElement = focusableElements[0] const lastElement = focusableElements[focusableElements.length - 1] const activeElement = document.activeElement if (!container.contains(activeElement)) { event.preventDefault() firstElement.focus() return } if (event.shiftKey && activeElement === firstElement) { event.preventDefault() lastElement.focus() return } if (!event.shiftKey && activeElement === lastElement) { event.preventDefault() firstElement.focus() } } function getOptionId(listboxId: string, itemId: string, index: number): string { const safeItemId = itemId.replace(/[^a-zA-Z0-9_-]+/g, '-') return `${listboxId}-option-${index}-${safeItemId}` } function cx(...classes: Array): string { return classes.filter(Boolean).join(' ') }