feat: 快捷键录入重绑(FR-K3,keymap:load/save IPC+config.json,SettingsPanel 录入 UI+冲突检测,use-hotkeys 读 store 实时生效,重开恢复)

This commit is contained in:
hunter-dev
2026-08-24 23:48:17 +08:00
parent cf2600901b
commit 49c174851e
9 changed files with 166 additions and 36 deletions
@@ -1,13 +1,27 @@
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import hotkeys from 'hotkeys-js';
import { useKeymap } from './keymap-service';
// 从 keymap store 读组合键绑定(FR-K3 重绑后实时生效);? 帮助键保留硬编码
export function useGlobalHotkeys(opts: { onNewDoc: () => void; onSave: () => void; onFocusSearch: () => void; onToggleOutline: () => void; onCheat: () => void; onSettings: () => void }) {
const optsRef = useRef(opts);
optsRef.current = opts;
const bindings = useKeymap(s => s.bindings);
const sig = bindings.map(b => b.id + '@' + b.combo).join('|');
useEffect(() => {
hotkeys('ctrl+n', e => { e.preventDefault(); opts.onNewDoc(); });
hotkeys('ctrl+s', e => { e.preventDefault(); opts.onSave(); });
hotkeys('ctrl+shift+f', e => { e.preventDefault(); opts.onFocusSearch(); });
hotkeys('ctrl+shift+l', e => { e.preventDefault(); opts.onToggleOutline(); });
hotkeys('?', e => { if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { e.preventDefault(); opts.onCheat(); } });
return () => { hotkeys.unbind('ctrl+n'); hotkeys.unbind('ctrl+s'); hotkeys.unbind('ctrl+shift+f'); hotkeys.unbind('ctrl+shift+l'); hotkeys.unbind('?'); };
}, [opts.onNewDoc, opts.onSave, opts.onFocusSearch, opts.onToggleOutline, opts.onCheat, opts.onSettings]);
const actions: Record<string, () => void> = {
newDoc: () => optsRef.current.onNewDoc(),
save: () => optsRef.current.onSave(),
focusSearch: () => optsRef.current.onFocusSearch(),
toggleOutline: () => optsRef.current.onToggleOutline(),
};
const bound: string[] = [];
for (const b of bindings) {
const act = actions[b.id];
if (act) { hotkeys(b.combo, (e) => { e.preventDefault(); act(); }); bound.push(b.combo); }
}
hotkeys('?', (e) => { if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { e.preventDefault(); optsRef.current.onCheat(); } });
bound.push('?');
return () => { for (const c of bound) hotkeys.unbind(c); };
}, [sig]);
}