potentially fix cross-platform keybinding handling Naming at the very
least.
This commit is contained in:
parent
98eb336ab0
commit
32a630cc52
2 changed files with 110 additions and 70 deletions
|
|
@ -1,7 +1,12 @@
|
|||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { BindingResponseSchema, ShortcutBindingsMap } from "../../lib/types";
|
||||
import { type } from "@tauri-apps/plugin-os";
|
||||
import { getKeyName } from "../../lib/utils/keyboard";
|
||||
import {
|
||||
getKeyName,
|
||||
formatKeyCombination,
|
||||
normalizeKey,
|
||||
type OSType,
|
||||
} from "../../lib/utils/keyboard";
|
||||
import ResetIcon from "../icons/ResetIcon";
|
||||
import { SettingContainer } from "../ui/SettingContainer";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
|
|
@ -24,57 +29,42 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
null,
|
||||
);
|
||||
const [originalBinding, setOriginalBinding] = useState<string>("");
|
||||
const [isMacOS, setIsMacOS] = useState<boolean>(false);
|
||||
const [osType, setOsType] = useState<OSType>("unknown");
|
||||
const shortcutRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());
|
||||
|
||||
const bindings = getSetting("bindings") || {};
|
||||
|
||||
// Check if running on macOS
|
||||
// Detect and store OS type
|
||||
useEffect(() => {
|
||||
const checkOsType = async () => {
|
||||
const detectOsType = async () => {
|
||||
try {
|
||||
const osType = await type();
|
||||
setIsMacOS(osType === "macos");
|
||||
const detectedType = await type();
|
||||
let normalizedType: OSType;
|
||||
|
||||
switch (detectedType) {
|
||||
case "macos":
|
||||
normalizedType = "macos";
|
||||
break;
|
||||
case "windows":
|
||||
normalizedType = "windows";
|
||||
break;
|
||||
case "linux":
|
||||
normalizedType = "linux";
|
||||
break;
|
||||
default:
|
||||
normalizedType = "unknown";
|
||||
}
|
||||
|
||||
setOsType(normalizedType);
|
||||
} catch (error) {
|
||||
console.error("Error detecting OS type:", error);
|
||||
setIsMacOS(false);
|
||||
setOsType("unknown");
|
||||
}
|
||||
};
|
||||
|
||||
checkOsType();
|
||||
detectOsType();
|
||||
}, []);
|
||||
|
||||
// Normalize modifier keys (unify left/right variants)
|
||||
const normalizeKey = (key: string): string => {
|
||||
// Handle left/right variants of modifier keys
|
||||
if (key.startsWith("left ") || key.startsWith("right ")) {
|
||||
const parts = key.split(" ");
|
||||
if (parts.length === 2) {
|
||||
// Return just the modifier name without left/right prefix
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
// Format keys for macOS display
|
||||
const formatMacOSKeys = (key: string): string => {
|
||||
if (!isMacOS) return key; // Only format for macOS
|
||||
|
||||
const keyMap: Record<string, string> = {
|
||||
alt: "option",
|
||||
};
|
||||
|
||||
return keyMap[key.toLowerCase()] || key;
|
||||
};
|
||||
|
||||
// Format a key combination for display
|
||||
const formatKeyCombination = (combination: string): string => {
|
||||
if (!isMacOS) return combination; // Only format for macOS
|
||||
|
||||
return combination.split("+").map(formatMacOSKeys).join(" + ");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Only add event listeners when we're in editing mode
|
||||
if (editingShortcutId === null) return;
|
||||
|
|
@ -99,8 +89,8 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
}
|
||||
e.preventDefault();
|
||||
|
||||
// Get the key and normalize it (unify left/right modifiers)
|
||||
const rawKey = getKeyName(e);
|
||||
// Get the key with OS-specific naming and normalize it
|
||||
const rawKey = getKeyName(e, osType);
|
||||
const key = normalizeKey(rawKey);
|
||||
|
||||
console.log("You pressed", rawKey, "normalized to", key);
|
||||
|
|
@ -117,8 +107,8 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
const handleKeyUp = async (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Get the key and normalize it
|
||||
const rawKey = getKeyName(e);
|
||||
// Get the key with OS-specific naming and normalize it
|
||||
const rawKey = getKeyName(e, osType);
|
||||
const key = normalizeKey(rawKey);
|
||||
|
||||
// Remove from currently pressed keys
|
||||
|
|
@ -183,6 +173,7 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
bindings,
|
||||
originalBinding,
|
||||
updateBinding,
|
||||
osType,
|
||||
]);
|
||||
|
||||
// Start recording a new shortcut
|
||||
|
|
@ -200,15 +191,11 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
};
|
||||
|
||||
// Format the current shortcut keys being recorded
|
||||
const formatCurrentKeys = () => {
|
||||
const formatCurrentKeys = (): string => {
|
||||
if (recordedKeys.length === 0) return "Press keys...";
|
||||
|
||||
if (!isMacOS) {
|
||||
return recordedKeys.join("+");
|
||||
}
|
||||
|
||||
// Map each key to its macOS-friendly name for display
|
||||
return recordedKeys.map(formatMacOSKeys).join(" + ");
|
||||
// Use the same formatting as the display to ensure consistency
|
||||
return formatKeyCombination(recordedKeys.join("+"), osType);
|
||||
};
|
||||
|
||||
// Store references to shortcut elements
|
||||
|
|
@ -275,7 +262,7 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
className="px-2 py-1 text-sm font-semibold bg-mid-gray/10 border border-mid-gray/80 hover:bg-logo-primary/10 rounded cursor-pointer hover:border-logo-primary"
|
||||
onClick={() => startRecording(primaryId)}
|
||||
>
|
||||
{formatKeyCombination(primaryBinding.current_binding)}
|
||||
{formatKeyCombination(primaryBinding.current_binding, osType)}
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -2,12 +2,17 @@
|
|||
* Keyboard utility functions for handling keyboard events
|
||||
*/
|
||||
|
||||
export type OSType = "macos" | "windows" | "linux" | "unknown";
|
||||
|
||||
/**
|
||||
* Extract a consistent key name from a KeyboardEvent
|
||||
* This function replaces the keycode library and provides better support
|
||||
* for extended function keys (F14+) and cross-platform compatibility
|
||||
* This function provides cross-platform keyboard event handling
|
||||
* and returns key names appropriate for the target operating system
|
||||
*/
|
||||
export const getKeyName = (e: KeyboardEvent): string => {
|
||||
export const getKeyName = (
|
||||
e: KeyboardEvent,
|
||||
osType: OSType = "unknown",
|
||||
): string => {
|
||||
// Handle special cases first
|
||||
if (e.code) {
|
||||
const code = e.code;
|
||||
|
|
@ -32,18 +37,36 @@ export const getKeyName = (e: KeyboardEvent): string => {
|
|||
return code.replace("Numpad", "numpad ").toLowerCase();
|
||||
}
|
||||
|
||||
// Handle modifier keys - normalize left/right variants
|
||||
// Handle modifier keys - OS-specific naming
|
||||
const getModifierName = (baseModifier: string): string => {
|
||||
switch (baseModifier) {
|
||||
case "shift":
|
||||
return "shift";
|
||||
case "ctrl":
|
||||
return osType === "macos" ? "ctrl" : "ctrl";
|
||||
case "alt":
|
||||
return osType === "macos" ? "option" : "alt";
|
||||
case "meta":
|
||||
// Windows key on Windows/Linux, Command key on Mac
|
||||
if (osType === "macos") return "command";
|
||||
if (osType === "windows") return "win";
|
||||
return "super"; // Linux convention
|
||||
default:
|
||||
return baseModifier;
|
||||
}
|
||||
};
|
||||
|
||||
const modifierMap: Record<string, string> = {
|
||||
ShiftLeft: "shift",
|
||||
ShiftRight: "shift",
|
||||
ControlLeft: "ctrl",
|
||||
ControlRight: "ctrl",
|
||||
AltLeft: "alt",
|
||||
AltRight: "alt",
|
||||
MetaLeft: "command",
|
||||
MetaRight: "command",
|
||||
OSLeft: "command",
|
||||
OSRight: "command",
|
||||
ShiftLeft: getModifierName("shift"),
|
||||
ShiftRight: getModifierName("shift"),
|
||||
ControlLeft: getModifierName("ctrl"),
|
||||
ControlRight: getModifierName("ctrl"),
|
||||
AltLeft: getModifierName("alt"),
|
||||
AltRight: getModifierName("alt"),
|
||||
MetaLeft: getModifierName("meta"),
|
||||
MetaRight: getModifierName("meta"),
|
||||
OSLeft: getModifierName("meta"),
|
||||
OSRight: getModifierName("meta"),
|
||||
CapsLock: "caps lock",
|
||||
Tab: "tab",
|
||||
Enter: "enter",
|
||||
|
|
@ -103,13 +126,15 @@ export const getKeyName = (e: KeyboardEvent): string => {
|
|||
if (e.key) {
|
||||
const key = e.key;
|
||||
|
||||
// Handle special key names
|
||||
// Handle special key names with OS-specific formatting
|
||||
const keyMap: Record<string, string> = {
|
||||
Control: "ctrl",
|
||||
Alt: "alt",
|
||||
Control: osType === "macos" ? "ctrl" : "ctrl",
|
||||
Alt: osType === "macos" ? "option" : "alt",
|
||||
Shift: "shift",
|
||||
Meta: "command",
|
||||
OS: "command",
|
||||
Meta:
|
||||
osType === "macos" ? "command" : osType === "windows" ? "win" : "super",
|
||||
OS:
|
||||
osType === "macos" ? "command" : osType === "windows" ? "win" : "super",
|
||||
CapsLock: "caps lock",
|
||||
ArrowUp: "up",
|
||||
ArrowDown: "down",
|
||||
|
|
@ -129,3 +154,31 @@ export const getKeyName = (e: KeyboardEvent): string => {
|
|||
// Last resort fallback
|
||||
return `unknown-${e.keyCode || e.which || 0}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get display-friendly key combination string for the current OS
|
||||
* Returns basic plus-separated format with correct platform key names
|
||||
*/
|
||||
export const formatKeyCombination = (
|
||||
combination: string,
|
||||
osType: OSType,
|
||||
): string => {
|
||||
// Simply return the combination as-is since getKeyName already provides
|
||||
// the correct platform-specific key names
|
||||
return combination;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize modifier keys to handle left/right variants
|
||||
*/
|
||||
export const normalizeKey = (key: string): string => {
|
||||
// Handle left/right variants of modifier keys
|
||||
if (key.startsWith("left ") || key.startsWith("right ")) {
|
||||
const parts = key.split(" ");
|
||||
if (parts.length === 2) {
|
||||
// Return just the modifier name without left/right prefix
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue