mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	refactor(hotkeys): use own (rough) implementation
This commit is contained in:
		| @@ -13,7 +13,6 @@ import type ElectronRemote from "@electron/remote"; | ||||
| import type Electron from "electron"; | ||||
| import "./stylesheets/bootstrap.scss"; | ||||
| import "boxicons/css/boxicons.min.css"; | ||||
| import "jquery-hotkeys"; | ||||
| import "autocomplete.js/index_jquery.js"; | ||||
|  | ||||
| await appContext.earlyInit(); | ||||
|   | ||||
| @@ -1,7 +1,18 @@ | ||||
| import utils from "./utils.js"; | ||||
|  | ||||
| type ElementType = HTMLElement | Document; | ||||
| type Handler = (e: JQuery.TriggeredEvent<ElementType | Element, string, ElementType | Element, ElementType | Element>) => void; | ||||
| type Handler = () => void; | ||||
|  | ||||
| interface ShortcutBinding { | ||||
|     element: HTMLElement | Document; | ||||
|     shortcut: string; | ||||
|     handler: Handler; | ||||
|     namespace: string | null; | ||||
|     listener: (evt: Event) => void; | ||||
| } | ||||
|  | ||||
| // Store all active shortcut bindings for management | ||||
| const activeBindings: Map<string, ShortcutBinding[]> = new Map(); | ||||
|  | ||||
| function removeGlobalShortcut(namespace: string) { | ||||
|     bindGlobalShortcut("", null, namespace); | ||||
| @@ -15,38 +26,141 @@ function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: st | ||||
|     if (utils.isDesktop()) { | ||||
|         keyboardShortcut = normalizeShortcut(keyboardShortcut); | ||||
|  | ||||
|         let eventName = "keydown"; | ||||
|  | ||||
|         // If namespace is provided, remove all previous bindings for this namespace | ||||
|         if (namespace) { | ||||
|             eventName += `.${namespace}`; | ||||
|  | ||||
|             // if there's a namespace, then we replace the existing event handler with the new one | ||||
|             $el.off(eventName); | ||||
|             removeNamespaceBindings(namespace); | ||||
|         } | ||||
|  | ||||
|         // method can be called to remove the shortcut (e.g. when keyboardShortcut label is deleted) | ||||
|         if (keyboardShortcut) { | ||||
|             $el.bind(eventName, keyboardShortcut, (e) => { | ||||
|                 if (handler) { | ||||
|                     handler(e); | ||||
|                 } | ||||
|         // Method can be called to remove the shortcut (e.g. when keyboardShortcut label is deleted) | ||||
|         if (keyboardShortcut && handler) { | ||||
|             const element = $el.length > 0 ? $el[0] as (HTMLElement | Document) : document; | ||||
|  | ||||
|             const listener = (evt: Event) => { | ||||
|                 const e = evt as KeyboardEvent; | ||||
|                 if (matchesShortcut(e, keyboardShortcut)) { | ||||
|                     e.preventDefault(); | ||||
|                     e.stopPropagation(); | ||||
|             }); | ||||
|                     handler(); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             // Add the event listener | ||||
|             element.addEventListener('keydown', listener); | ||||
|  | ||||
|             // Store the binding for later cleanup | ||||
|             const binding: ShortcutBinding = { | ||||
|                 element, | ||||
|                 shortcut: keyboardShortcut, | ||||
|                 handler, | ||||
|                 namespace, | ||||
|                 listener | ||||
|             }; | ||||
|  | ||||
|             const key = namespace || 'global'; | ||||
|             if (!activeBindings.has(key)) { | ||||
|                 activeBindings.set(key, []); | ||||
|             } | ||||
|             activeBindings.get(key)!.push(binding); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function removeNamespaceBindings(namespace: string) { | ||||
|     const bindings = activeBindings.get(namespace); | ||||
|     if (bindings) { | ||||
|         // Remove all event listeners for this namespace | ||||
|         bindings.forEach(binding => { | ||||
|             binding.element.removeEventListener('keydown', binding.listener); | ||||
|         }); | ||||
|         activeBindings.delete(namespace); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function matchesShortcut(e: KeyboardEvent, shortcut: string): boolean { | ||||
|     if (!shortcut) return false; | ||||
|  | ||||
|     const parts = shortcut.toLowerCase().split('+'); | ||||
|     const key = parts[parts.length - 1]; // Last part is the actual key | ||||
|     const modifiers = parts.slice(0, -1); // Everything before is modifiers | ||||
|  | ||||
|     // Check if the main key matches | ||||
|     if (!keyMatches(e, key)) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // Check modifiers | ||||
|     const expectedCtrl = modifiers.includes('ctrl') || modifiers.includes('control'); | ||||
|     const expectedAlt = modifiers.includes('alt'); | ||||
|     const expectedShift = modifiers.includes('shift'); | ||||
|     const expectedMeta = modifiers.includes('meta') || modifiers.includes('cmd') || modifiers.includes('command'); | ||||
|  | ||||
|     return e.ctrlKey === expectedCtrl && | ||||
|            e.altKey === expectedAlt && | ||||
|            e.shiftKey === expectedShift && | ||||
|            e.metaKey === expectedMeta; | ||||
| } | ||||
|  | ||||
| function keyMatches(e: KeyboardEvent, key: string): boolean { | ||||
|     // Handle special key mappings | ||||
|     const keyMap: { [key: string]: string[] } = { | ||||
|         'return': ['Enter'], | ||||
|         'del': ['Delete'], | ||||
|         'esc': ['Escape'], | ||||
|         'space': [' ', 'Space'], | ||||
|         'tab': ['Tab'], | ||||
|         'backspace': ['Backspace'], | ||||
|         'home': ['Home'], | ||||
|         'end': ['End'], | ||||
|         'pageup': ['PageUp'], | ||||
|         'pagedown': ['PageDown'], | ||||
|         'up': ['ArrowUp'], | ||||
|         'down': ['ArrowDown'], | ||||
|         'left': ['ArrowLeft'], | ||||
|         'right': ['ArrowRight'] | ||||
|     }; | ||||
|  | ||||
|     // Function keys | ||||
|     for (let i = 1; i <= 19; i++) { | ||||
|         keyMap[`f${i}`] = [`F${i}`]; | ||||
|     } | ||||
|  | ||||
|     const mappedKeys = keyMap[key.toLowerCase()]; | ||||
|     if (mappedKeys) { | ||||
|         return mappedKeys.includes(e.key) || mappedKeys.includes(e.code); | ||||
|     } | ||||
|  | ||||
|     // For regular keys, check both key and code | ||||
|     return e.key.toLowerCase() === key.toLowerCase() || | ||||
|            e.code.toLowerCase() === key.toLowerCase(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Normalize to the form expected by the jquery.hotkeys.js | ||||
|  * Normalize to a consistent format for our custom shortcut parser | ||||
|  */ | ||||
| function normalizeShortcut(shortcut: string): string { | ||||
|     if (!shortcut) { | ||||
|         return shortcut; | ||||
|     } | ||||
|  | ||||
|     return shortcut.toLowerCase().replace("enter", "return").replace("delete", "del").replace("ctrl+alt", "alt+ctrl").replace("meta+alt", "alt+meta"); // alt needs to be first; | ||||
|     return shortcut | ||||
|         .toLowerCase() | ||||
|         .trim() | ||||
|         .replace(/\s+/g, '') // Remove any spaces | ||||
|         .replace("enter", "return") | ||||
|         .replace("delete", "del") | ||||
|         .replace("escape", "esc") | ||||
|         // Normalize modifier order: ctrl, alt, shift, meta, then key | ||||
|         .split('+') | ||||
|         .sort((a, b) => { | ||||
|             const order = ['ctrl', 'control', 'alt', 'shift', 'meta', 'cmd', 'command']; | ||||
|             const aIndex = order.indexOf(a); | ||||
|             const bIndex = order.indexOf(b); | ||||
|             if (aIndex === -1 && bIndex === -1) return 0; | ||||
|             if (aIndex === -1) return 1; | ||||
|             if (bIndex === -1) return -1; | ||||
|             return aIndex - bIndex; | ||||
|         }) | ||||
|         .join('+'); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import "jquery"; | ||||
| import "jquery-hotkeys"; | ||||
| import utils from "./services/utils.js"; | ||||
| import ko from "knockout"; | ||||
| import "./stylesheets/bootstrap.scss"; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user