mirror of
https://github.com/sotam0316/drawNET.git
synced 2026-04-25 03:58:37 +09:00
95 lines
3.4 KiB
JavaScript
95 lines
3.4 KiB
JavaScript
import { state } from '../state.js';
|
|
import { actionHandlers } from './handlers.js';
|
|
import { logger } from '../utils/logger.js';
|
|
|
|
/**
|
|
* initHotkeys - Initializes the keyboard shortcut system.
|
|
*/
|
|
export async function initHotkeys() {
|
|
if (window.__drawNET_Hotkeys_Initialized) {
|
|
logger.high("Hotkeys already initialized. Skipping.");
|
|
return;
|
|
}
|
|
window.__drawNET_Hotkeys_Initialized = true;
|
|
|
|
try {
|
|
const response = await fetch('/static/hotkeys.json');
|
|
const config = await response.json();
|
|
const hotkeys = config.hotkeys;
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
if (e.repeat) return;
|
|
|
|
const editableElements = ['TEXTAREA', 'INPUT'];
|
|
const isTyping = editableElements.includes(e.target.tagName) && e.target.id !== 'dark-mode-toggle';
|
|
|
|
const keyPressed = e.key.toLowerCase();
|
|
const keyCode = e.code;
|
|
|
|
for (const hk of hotkeys) {
|
|
// Match by physical code (preferred) or key string
|
|
const matchCode = hk.code && hk.code === keyCode;
|
|
const matchKey = hk.key && hk.key.toLowerCase() === keyPressed;
|
|
|
|
if (!(matchCode || matchKey)) continue;
|
|
|
|
// Match modifiers strictly
|
|
const matchCtrl = !!hk.ctrl === e.ctrlKey;
|
|
const matchAlt = !!hk.alt === e.altKey;
|
|
const matchShift = !!hk.shift === e.shiftKey;
|
|
|
|
if (matchCtrl && matchAlt && matchShift) {
|
|
const allowWhileTyping = hk.alwaysEnabled === true;
|
|
if (isTyping && !allowWhileTyping) break;
|
|
|
|
logger.info(`Hotkey Match - ${hk.action} (${hk.key})`);
|
|
|
|
if (allowWhileTyping || !isTyping) {
|
|
e.preventDefault();
|
|
}
|
|
const handler = actionHandlers[hk.action];
|
|
if (handler) {
|
|
handler();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
logger.high("Hotkeys module initialized (X6).");
|
|
} catch (err) {
|
|
logger.critical("Failed to load hotkeys configuration (X6).", err);
|
|
}
|
|
|
|
// Ctrl + Wheel: Zoom handling
|
|
window.addEventListener('wheel', (e) => {
|
|
if (e.ctrlKey) {
|
|
e.preventDefault();
|
|
if (!state.graph) return;
|
|
const delta = e.deltaY > 0 ? -0.1 : 0.1;
|
|
state.graph.zoom(delta, { center: { x: e.clientX, y: e.clientY } });
|
|
}
|
|
}, { passive: false });
|
|
|
|
// Global Key State Tracking (for Ctrl+Drag copy)
|
|
// Extra guard: Check e.ctrlKey/metaKey on mouse events to auto-heal mismatched state
|
|
const syncCtrlState = (e) => {
|
|
const isModifier = e.ctrlKey || e.metaKey;
|
|
if (state.isCtrlPressed !== isModifier) {
|
|
state.isCtrlPressed = isModifier;
|
|
}
|
|
};
|
|
window.addEventListener('mousedown', syncCtrlState, true);
|
|
window.addEventListener('mousemove', syncCtrlState, true);
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Control' || e.key === 'Meta') state.isCtrlPressed = true;
|
|
}, true);
|
|
window.addEventListener('keyup', (e) => {
|
|
if (e.key === 'Control' || e.key === 'Meta') state.isCtrlPressed = false;
|
|
}, true);
|
|
window.addEventListener('blur', () => {
|
|
state.isCtrlPressed = false; // Reset on window blur to avoid stuck state
|
|
});
|
|
}
|