128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
import { onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
export interface KeyboardShortcut {
|
|
key: string
|
|
ctrl?: boolean
|
|
shift?: boolean
|
|
alt?: boolean
|
|
meta?: boolean
|
|
description: string
|
|
action: () => void
|
|
}
|
|
|
|
// 快捷键管理组合式函数
|
|
export function useKeyboard(shortcuts: KeyboardShortcut[]) {
|
|
const activeKeys = ref<Set<string>>(new Set())
|
|
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
activeKeys.value.add(event.key.toLowerCase())
|
|
|
|
for (const shortcut of shortcuts) {
|
|
const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase()
|
|
const ctrlMatch = !!shortcut.ctrl === (event.ctrlKey || event.metaKey)
|
|
const shiftMatch = !!shortcut.shift === event.shiftKey
|
|
const altMatch = !!shortcut.alt === event.altKey
|
|
|
|
if (keyMatch && ctrlMatch && shiftMatch && altMatch) {
|
|
// 排除在输入框中的部分快捷键
|
|
const target = event.target as HTMLElement
|
|
const isInput = target.tagName === 'INPUT' ||
|
|
target.tagName === 'TEXTAREA' ||
|
|
target.isContentEditable
|
|
|
|
// 这些快捷键在输入框中也生效
|
|
const globalShortcuts = ['Escape', 'Enter']
|
|
const needsModifier = shortcut.ctrl || shortcut.alt || shortcut.meta
|
|
|
|
if (isInput && !globalShortcuts.includes(shortcut.key) && !needsModifier) {
|
|
continue
|
|
}
|
|
|
|
event.preventDefault()
|
|
shortcut.action()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleKeyUp = (event: KeyboardEvent) => {
|
|
activeKeys.value.delete(event.key.toLowerCase())
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('keydown', handleKeyDown)
|
|
window.addEventListener('keyup', handleKeyUp)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('keydown', handleKeyDown)
|
|
window.removeEventListener('keyup', handleKeyUp)
|
|
})
|
|
|
|
return {
|
|
activeKeys,
|
|
}
|
|
}
|
|
|
|
// 预定义的快捷键配置
|
|
export function getDefaultShortcuts(actions: {
|
|
newChat: () => void
|
|
toggleSidebar: () => void
|
|
focusInput: () => void
|
|
sendMessage: () => void
|
|
cancelStream: () => void
|
|
toggleTheme: () => void
|
|
showShortcuts: () => void
|
|
searchConversations: () => void
|
|
}): KeyboardShortcut[] {
|
|
return [
|
|
{
|
|
key: 'n',
|
|
ctrl: true,
|
|
description: '新建对话',
|
|
action: actions.newChat,
|
|
},
|
|
{
|
|
key: 'b',
|
|
ctrl: true,
|
|
description: '切换侧边栏',
|
|
action: actions.toggleSidebar,
|
|
},
|
|
{
|
|
key: '/',
|
|
ctrl: true,
|
|
description: '聚焦输入框',
|
|
action: actions.focusInput,
|
|
},
|
|
{
|
|
key: 'Enter',
|
|
ctrl: true,
|
|
description: '发送消息',
|
|
action: actions.sendMessage,
|
|
},
|
|
{
|
|
key: 'Escape',
|
|
description: '取消生成',
|
|
action: actions.cancelStream,
|
|
},
|
|
{
|
|
key: 'd',
|
|
ctrl: true,
|
|
shift: true,
|
|
description: '切换主题',
|
|
action: actions.toggleTheme,
|
|
},
|
|
{
|
|
key: '?',
|
|
ctrl: true,
|
|
description: '显示快捷键',
|
|
action: actions.showShortcuts,
|
|
},
|
|
{
|
|
key: 'k',
|
|
ctrl: true,
|
|
description: '搜索对话',
|
|
action: actions.searchConversations,
|
|
},
|
|
]
|
|
} |