feat(GlobalState, KeyboardShortcuts): enhance global state management with new properties and improve keyboard shortcut handling for better modifier key detection
This commit is contained in:
@@ -5,6 +5,8 @@ const globalState = reactive({
|
|||||||
unreadConversationsCount: 0,
|
unreadConversationsCount: 0,
|
||||||
activeCallTab: "phone",
|
activeCallTab: "phone",
|
||||||
blockedDestinations: [],
|
blockedDestinations: [],
|
||||||
|
modifiedInterfaceNames: new Set(),
|
||||||
|
hasPendingInterfaceChanges: false,
|
||||||
config: {
|
config: {
|
||||||
banished_effect_enabled: true,
|
banished_effect_enabled: true,
|
||||||
banished_text: "BANISHED",
|
banished_text: "BANISHED",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class KeyboardShortcuts {
|
|||||||
window.addEventListener("keydown", (e) => this.handleKeyDown(e));
|
window.addEventListener("keydown", (e) => this.handleKeyDown(e));
|
||||||
window.addEventListener("keyup", (e) => this.handleKeyUp(e));
|
window.addEventListener("keyup", (e) => this.handleKeyUp(e));
|
||||||
window.addEventListener("blur", () => this.activeKeys.clear());
|
window.addEventListener("blur", () => this.activeKeys.clear());
|
||||||
|
window.addEventListener("mousedown", () => this.activeKeys.clear()); // Clear on mouse click to prevent stuck modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultShortcuts() {
|
getDefaultShortcuts() {
|
||||||
@@ -30,21 +31,10 @@ class KeyboardShortcuts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
|
// Always update modifier states regardless of other conditions
|
||||||
|
this.updateModifiers(e);
|
||||||
|
|
||||||
const key = e.key.toLowerCase();
|
const key = e.key.toLowerCase();
|
||||||
|
|
||||||
// Update active keys from modifiers to ensure they are in sync
|
|
||||||
if (e.ctrlKey) this.activeKeys.add("control");
|
|
||||||
else if (!this.isRecording) this.activeKeys.delete("control");
|
|
||||||
|
|
||||||
if (e.altKey) this.activeKeys.add("alt");
|
|
||||||
else if (!this.isRecording) this.activeKeys.delete("alt");
|
|
||||||
|
|
||||||
if (e.shiftKey) this.activeKeys.add("shift");
|
|
||||||
else if (!this.isRecording) this.activeKeys.delete("shift");
|
|
||||||
|
|
||||||
if (e.metaKey) this.activeKeys.add("meta");
|
|
||||||
else if (!this.isRecording) this.activeKeys.delete("meta");
|
|
||||||
|
|
||||||
if (!["control", "alt", "shift", "meta"].includes(key)) {
|
if (!["control", "alt", "shift", "meta"].includes(key)) {
|
||||||
this.activeKeys.add(key);
|
this.activeKeys.add(key);
|
||||||
}
|
}
|
||||||
@@ -60,14 +50,23 @@ class KeyboardShortcuts {
|
|||||||
// Check for matches
|
// Check for matches
|
||||||
for (const shortcut of this.shortcuts) {
|
for (const shortcut of this.shortcuts) {
|
||||||
if (this.matches(shortcut.keys, e)) {
|
if (this.matches(shortcut.keys, e)) {
|
||||||
// Don't trigger if user is typing in an input, unless it's a global shortcut
|
// Check if we should ignore because we're in an input
|
||||||
const isInput = ["INPUT", "TEXTAREA"].includes(document.activeElement.tagName);
|
const isInput =
|
||||||
|
["INPUT", "TEXTAREA"].includes(document.activeElement.tagName) ||
|
||||||
|
document.activeElement.isContentEditable ||
|
||||||
|
document.activeElement.closest(".v-input"); // Better Vuetify detection
|
||||||
|
|
||||||
const hasModifier = shortcut.keys.some((k) => ["control", "alt", "meta"].includes(k));
|
const hasModifier = shortcut.keys.some((k) => ["control", "alt", "meta"].includes(k));
|
||||||
|
|
||||||
|
// If it's an input, only allow shortcuts with modifiers (like Alt+1, Ctrl+K)
|
||||||
|
// but ignore simple keys if they're not explicitly allowed in inputs
|
||||||
if (isInput && !hasModifier) {
|
if (isInput && !hasModifier) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specifically allow navigation shortcuts (Alt+number) even in inputs
|
||||||
|
// as they don't usually conflict with typing (unless using special chars)
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.executeAction(shortcut.action);
|
this.executeAction(shortcut.action);
|
||||||
@@ -76,6 +75,20 @@ class KeyboardShortcuts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateModifiers(e) {
|
||||||
|
if (e.ctrlKey) this.activeKeys.add("control");
|
||||||
|
else if (!this.isRecording) this.activeKeys.delete("control");
|
||||||
|
|
||||||
|
if (e.altKey) this.activeKeys.add("alt");
|
||||||
|
else if (!this.isRecording) this.activeKeys.delete("alt");
|
||||||
|
|
||||||
|
if (e.shiftKey) this.activeKeys.add("shift");
|
||||||
|
else if (!this.isRecording) this.activeKeys.delete("shift");
|
||||||
|
|
||||||
|
if (e.metaKey) this.activeKeys.add("meta");
|
||||||
|
else if (!this.isRecording) this.activeKeys.delete("meta");
|
||||||
|
}
|
||||||
|
|
||||||
handleKeyUp(e) {
|
handleKeyUp(e) {
|
||||||
const key = e.key.toLowerCase();
|
const key = e.key.toLowerCase();
|
||||||
this.activeKeys.delete(key);
|
this.activeKeys.delete(key);
|
||||||
@@ -94,24 +107,42 @@ class KeyboardShortcuts {
|
|||||||
const hasControl = shortcutKeys.includes("control");
|
const hasControl = shortcutKeys.includes("control");
|
||||||
const hasAlt = shortcutKeys.includes("alt");
|
const hasAlt = shortcutKeys.includes("alt");
|
||||||
const hasShift = shortcutKeys.includes("shift");
|
const hasShift = shortcutKeys.includes("shift");
|
||||||
|
const hasMeta = shortcutKeys.includes("meta");
|
||||||
|
|
||||||
// Allow Cmd (Meta) to act as Control on Mac for improved compatibility
|
// Browsers handle Alt and Control differently on different OSs.
|
||||||
const ctrlMatch = hasControl ? e.ctrlKey || e.metaKey : !e.ctrlKey && !e.metaKey;
|
// We use a normalized approach here.
|
||||||
const altMatch = hasAlt ? e.altKey : !e.altKey;
|
const isControlPressed = e.ctrlKey;
|
||||||
const shiftMatch = hasShift ? e.shiftKey : !e.shiftKey;
|
const isAltPressed = e.altKey;
|
||||||
|
const isShiftPressed = e.shiftKey;
|
||||||
|
const isMetaPressed = e.metaKey;
|
||||||
|
|
||||||
if (!ctrlMatch || !altMatch || !shiftMatch) return false;
|
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
||||||
|
const ctrlMatch = hasControl
|
||||||
|
? isControlPressed || (isMac && isMetaPressed)
|
||||||
|
: !isControlPressed && !(isMac && isMetaPressed);
|
||||||
|
if (!ctrlMatch) return false;
|
||||||
|
|
||||||
|
if (isAltPressed !== hasAlt) return false;
|
||||||
|
if (isShiftPressed !== hasShift) return false;
|
||||||
|
if (!isMac && isMetaPressed !== hasMeta) return false;
|
||||||
|
|
||||||
// Find the non-modifier key in the shortcut
|
// Find the non-modifier key in the shortcut
|
||||||
const mainKey = shortcutKeys.find((k) => !["control", "alt", "shift", "meta"].includes(k));
|
const mainKey = shortcutKeys.find((k) => !["control", "alt", "shift", "meta"].includes(k));
|
||||||
if (!mainKey) return true; // Modifier-only shortcut (rare but possible)
|
if (!mainKey) return true; // Modifier-only shortcut (rare but possible)
|
||||||
|
|
||||||
const pressedKey = e.key.toLowerCase();
|
const pressedKey = e.key.toLowerCase();
|
||||||
|
|
||||||
|
// Direct key match
|
||||||
if (pressedKey === mainKey.toLowerCase()) return true;
|
if (pressedKey === mainKey.toLowerCase()) return true;
|
||||||
|
|
||||||
// Layout independence: check e.code as well (handles Alt+key layout changes)
|
// Layout independence: check e.code as well (handles Alt+key layout changes)
|
||||||
if (e.code === `Digit${mainKey}`) return true;
|
// e.g. Alt+1 might be captured as Digit1 regardless of layout
|
||||||
if (e.code === `Key${mainKey.toUpperCase()}`) return true;
|
const codeMatch =
|
||||||
|
e.code === `Digit${mainKey}` ||
|
||||||
|
e.code === `Key${mainKey.toUpperCase()}` ||
|
||||||
|
e.code === mainKey.toUpperCase(); // For keys like 'Enter', 'Escape'
|
||||||
|
|
||||||
|
if (codeMatch) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user