diff --git a/src/components/ui/HotkeysHelper.tsx b/src/components/ui/HotkeysHelper.tsx new file mode 100644 index 0000000..9e960b2 --- /dev/null +++ b/src/components/ui/HotkeysHelper.tsx @@ -0,0 +1,207 @@ +'use client'; + +import { useEffect, useState, useMemo } from 'react'; +import { X, Keyboard } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { RegisteredHotkey, HotkeyCategory } from '@/types/hotkeys'; +import { formatHotkey } from '@/types/hotkeys'; + +interface HotkeysHelperProps { + isOpen: boolean; + onClose: () => void; + registeredHotkeys?: RegisteredHotkey[]; +} + +// 预定义的快捷键列表(包括系统内置的) +const BUILTIN_HOTKEYS: RegisteredHotkey[] = [ + { + id: 'new-chat', + config: { + key: 'n', + description: '新建对话', + category: '导航', + action: () => {}, + }, + }, + { + id: 'open-search', + config: { + key: 'k', + modifiers: ['meta'], + description: '打开搜索', + category: '导航', + action: () => {}, + }, + }, + { + id: 'help', + config: { + key: '?', + description: '显示快捷键帮助', + category: '通用', + action: () => {}, + }, + }, + { + id: 'close-modal', + config: { + key: 'Escape', + description: '关闭弹窗', + category: '通用', + action: () => {}, + }, + }, +]; + +/** + * 快捷键帮助面板 + */ +export function HotkeysHelper({ isOpen, onClose, registeredHotkeys = [] }: HotkeysHelperProps) { + // 检测是否为 Mac 系统 + const [isMac, setIsMac] = useState(true); + + useEffect(() => { + setIsMac(navigator.platform.toLowerCase().includes('mac')); + }, []); + + // 合并内置和动态快捷键,按分类分组 + const groupedHotkeys = useMemo(() => { + const allHotkeys = [...BUILTIN_HOTKEYS, ...registeredHotkeys]; + + // 去重(以 id 为准,优先保留 BUILTIN) + const uniqueHotkeys = allHotkeys.reduce((acc, hotkey) => { + if (!acc.find((h) => h.id === hotkey.id)) { + acc.push(hotkey); + } + return acc; + }, [] as RegisteredHotkey[]); + + // 按分类分组 + const groups: Record = { + 导航: [], + 编辑: [], + 通用: [], + }; + + uniqueHotkeys.forEach((hotkey) => { + const category = hotkey.config.category || '通用'; + if (groups[category]) { + groups[category].push(hotkey); + } else { + groups['通用'].push(hotkey); + } + }); + + // 过滤空分类 + return Object.entries(groups).filter(([, items]) => items.length > 0); + }, [registeredHotkeys]); + + if (!isOpen) return null; + + return ( + <> + {/* 遮罩层 */} +
+ + {/* 帮助面板 */} +
+
+ {/* 头部 */} +
+
+
+ +
+
+

+ 键盘快捷键 +

+

+ 使用快捷键提高效率 +

+
+
+ +
+ + {/* 快捷键列表 */} +
+ {groupedHotkeys.map(([category, hotkeys], groupIndex) => ( +
0 && 'mt-6')} + > + {/* 分类标题 */} +

+ {category} +

+ + {/* 快捷键项 */} +
+ {hotkeys.map((hotkey) => ( +
+ + {hotkey.config.description} + + +
+ ))} +
+
+ ))} +
+ + {/* 底部提示 */} +
+
+ Esc 关闭 + {isMac ? 'macOS' : 'Windows/Linux'} +
+
+
+
+ + ); +} + +/** + * 快捷键徽章组件 + */ +function HotkeyBadge({ + config, + isMac, +}: { + config: RegisteredHotkey['config']; + isMac: boolean; +}) { + const keys = formatHotkey(config, isMac).split(isMac ? ' ' : '+'); + + return ( +
+ {keys.map((key, index) => ( + + {key} + + ))} +
+ ); +}