Files
resolutionflow/frontend/src/hooks/useKeyboardShortcuts.ts
chihlasm 2fc4e69c38 feat: UX improvements — copy buttons, shortcuts modal, breadcrumb rewind, create tree CTA
- Add copy-to-clipboard buttons on command blocks (action + custom step nodes)
- Add keyboard shortcuts modal (?) with option loading spinners and [Esc] hint
- Restyle session timer as a pill badge for better visibility
- Add prominent "Create Tree" CTA to MyTreesPage header and empty state
- Make breadcrumb items clickable to rewind navigation to any previous step

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:19:24 -05:00

107 lines
2.6 KiB
TypeScript

import { useEffect, useCallback } from 'react'
interface ShortcutConfig {
key: string
ctrl?: boolean
shift?: boolean
alt?: boolean
handler: () => void
enabled?: boolean
}
export function useKeyboardShortcuts(shortcuts: ShortcutConfig[]) {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
// Don't trigger shortcuts when typing in inputs
const target = e.target as HTMLElement
if (
target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.isContentEditable
) {
return
}
for (const shortcut of shortcuts) {
if (shortcut.enabled === false) continue
const keyMatch = e.key === shortcut.key || e.key === shortcut.key.toLowerCase()
const ctrlMatch = !!shortcut.ctrl === (e.ctrlKey || e.metaKey)
const shiftMatch = !!shortcut.shift === e.shiftKey
const altMatch = !!shortcut.alt === e.altKey
if (keyMatch && ctrlMatch && shiftMatch && altMatch) {
e.preventDefault()
shortcut.handler()
return
}
}
},
[shortcuts]
)
useEffect(() => {
document.addEventListener('keydown', handleKeyDown)
return () => document.removeEventListener('keydown', handleKeyDown)
}, [handleKeyDown])
}
// Convenience hook for tree navigation specifically
export interface TreeNavigationShortcutsConfig {
onSelectOption: (index: number) => void
onGoBack: () => void
onContinue: () => void
onShowShortcuts?: () => void
optionCount: number
canGoBack: boolean
canContinue: boolean
}
export function useTreeNavigationShortcuts({
onSelectOption,
onGoBack,
onContinue,
onShowShortcuts,
optionCount,
canGoBack,
canContinue,
}: TreeNavigationShortcutsConfig) {
const shortcuts: ShortcutConfig[] = [
// Number keys 1-9 for options
...Array.from({ length: Math.min(optionCount, 9) }, (_, i) => ({
key: String(i + 1),
handler: () => onSelectOption(i),
})),
// Escape to go back
{
key: 'Escape',
handler: onGoBack,
enabled: canGoBack,
},
// Enter to continue (for action nodes)
{
key: 'Enter',
handler: onContinue,
enabled: canContinue,
},
// Tab to focus notes
{
key: 'Tab',
handler: () => {
document.getElementById('session-notes')?.focus()
},
},
// ? to show shortcuts modal
{
key: '?',
shift: true,
handler: () => onShowShortcuts?.(),
enabled: !!onShowShortcuts,
},
]
useKeyboardShortcuts(shortcuts)
}
export default useKeyboardShortcuts