diff --git a/bun.lockb b/bun.lockb index 64ec2cf..39652e8 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index d4f3972..b4ab0d4 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@tauri-apps/plugin-clipboard-manager": "~2", "@tauri-apps/plugin-global-shortcut": "~2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-store": "~2", "@tauri-apps/plugin-stronghold": "~2", "@tauri-apps/plugin-upload": "~2", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index af0061c..f160ebd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -213,7 +213,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -245,7 +245,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 0.38.44", "tracing", ] @@ -272,7 +272,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix", + "rustix 0.38.44", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -1745,6 +1745,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "gethostname" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +dependencies = [ + "rustix 1.0.7", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1982,6 +1992,7 @@ dependencies = [ "tauri-plugin-global-shortcut", "tauri-plugin-macos-permissions", "tauri-plugin-opener", + "tauri-plugin-os", "tauri-plugin-single-instance", "tauri-plugin-store", "tokio", @@ -2663,6 +2674,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.4" @@ -3357,6 +3374,17 @@ dependencies = [ "ureq", ] +[[package]] +name = "os_info" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fc863e2ca13dc2d5c34fb22ea4a588248ac14db929616ba65c45f21744b1e9" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "pango" version = "0.18.3" @@ -3622,7 +3650,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -4134,7 +4162,20 @@ dependencies = [ "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -4684,6 +4725,15 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4973,6 +5023,24 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-os" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "424f19432397850c2ddd42aa58078630c15287bbce3866eb1d90e7dbee680637" +dependencies = [ + "gethostname 1.0.2", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + [[package]] name = "tauri-plugin-single-instance" version = "2.2.1" @@ -5106,7 +5174,7 @@ dependencies = [ "fastrand", "getrandom 0.3.1", "once_cell", - "rustix", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -5899,7 +5967,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] [[package]] @@ -6512,8 +6580,8 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ - "gethostname", - "rustix", + "gethostname 0.4.3", + "rustix 0.38.44", "x11rb-protocol", ] @@ -6530,8 +6598,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "linux-raw-sys 0.4.15", + "rustix 0.38.44", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 33871fc..bffb3e9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -38,6 +38,7 @@ log = "0.4.25" tokio = "1.43.0" vad-rs = "0.1.5" tauri-plugin-store = "2" +tauri-plugin-os = "2" [target.'cfg(target_os = "macos")'.dependencies] whisper-rs = { version = "0.13.2", features = ["metal"] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fd056ee..d7eb3ec 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -27,6 +27,7 @@ pub fn run() { env_logger::init(); tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_autostart::init( MacosLauncher::LaunchAgent, diff --git a/src/components/settings/KeyboardShortcuts.tsx b/src/components/settings/KeyboardShortcuts.tsx index dc36d03..9c5609f 100644 --- a/src/components/settings/KeyboardShortcuts.tsx +++ b/src/components/settings/KeyboardShortcuts.tsx @@ -7,6 +7,7 @@ import { ShortcutBindingsMap, } from "../../lib/types"; import { invoke } from "@tauri-apps/api/core"; +import { type } from "@tauri-apps/plugin-os"; import keycode from "keycode"; import ResetIcon from "../icons/ResetIcon"; @@ -16,11 +17,58 @@ export const KeyboardShortcuts: React.FC = () => { const [keyPressed, setKeyPressed] = useState([]); const [recordedKeys, setRecordedKeys] = useState([]); const [editingShortcutId, setEditingShortcutId] = useState( - null + null, ); const [originalBinding, setOriginalBinding] = useState(""); + const [isMacOS, setIsMacOS] = useState(false); const shortcutRefs = useRef>(new Map()); + // Check if running on macOS + useEffect(() => { + const checkOsType = async () => { + try { + const osType = type(); + setIsMacOS(osType === "macos"); + } catch (error) { + console.error("Error detecting OS type:", error); + setIsMacOS(false); + } + }; + + checkOsType(); + }, []); + + // 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 = { + 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(() => { load("settings_store.json", { autoSave: false }).then((r) => { console.log("loaded store", r); @@ -43,8 +91,11 @@ export const KeyboardShortcuts: React.FC = () => { const handleKeyDown = (e: KeyboardEvent) => { e.preventDefault(); - const key = keycode(e).toLowerCase(); - console.log("You pressed", key); + // Get the key and normalize it (unify left/right modifiers) + const rawKey = keycode(e).toLowerCase(); + const key = normalizeKey(rawKey); + + console.log("You pressed", rawKey, "normalized to", key); if (!keyPressed.includes(key)) { setKeyPressed((prev) => [...prev, key]); @@ -58,7 +109,9 @@ export const KeyboardShortcuts: React.FC = () => { const handleKeyUp = (e: KeyboardEvent) => { e.preventDefault(); - const key = keycode(e).toLowerCase(); + // Get the key and normalize it + const rawKey = keycode(e).toLowerCase(); + const key = normalizeKey(rawKey); // Remove from currently pressed keys setKeyPressed((prev) => prev.filter((k) => k !== key)); @@ -138,7 +191,14 @@ export const KeyboardShortcuts: React.FC = () => { // Format the current shortcut keys being recorded const formatCurrentKeys = () => { - return recordedKeys.length > 0 ? recordedKeys.join("+") : "Press keys..."; + 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(" + "); }; // Store references to shortcut elements @@ -185,16 +245,16 @@ export const KeyboardShortcuts: React.FC = () => { {editingShortcutId === id ? (
setShortcutRef(id, ref)} - className="px-2 py-1 text-sm font-semibold border border-logo-primary bg-logo-primary/30 rounded min-w-[100px] text-center" + className="px-2 py-1 text-sm font-semibold border border-logo-primary bg-logo-primary/30 rounded min-w-[120px] text-center" > {formatCurrentKeys()}
) : (
startRecording(id)} > - {binding.current_binding} + {formatKeyCombination(binding.current_binding)}
)}