Skip to content

macOS: fn cannot be bound to a capture chord — key_from_str has no "Function" arm, and the resulting Err disables all hotkeys #941

Description

@daniel-1988-cb

Summary

On macOS the fn key cannot be bound to a capture chord, even though every other layer of the stack already supports it. Worse than a no-op: a chord containing Function makes build_chord_bindings return Err, so the HotkeyMonitor is never spawned and both push-to-talk and toggle-to-talk stop working — global dictation is silently dead until the chord is changed back.

Root cause

tauri/src-tauri/src/key_codes.rs::key_from_str() has no "Function" arm, so it falls through to _ => return None.

main.rs:891 treats that as fatal:

let key = key_codes::key_from_str(raw)
    .ok_or_else(|| format!("Unsupported key in {name} chord: {raw}"))?;

The ? propagates out of build_chord, build_chord_bindings returns Err, and HotkeyMonitor::apply is never reached — so there's no ChordMatcher build failed line on stderr either, which makes this hard to diagnose from logs.

Every other layer already handles fn

  • keytap 0.4src/platform/macos/keycodes.rs: const FUNCTION: u32 = 63; // kVK_Function and FUNCTION => Key::Function. The tap observes the key.
  • keytapKey::Function exists (src/key.rs).
  • frontendapp/src/lib/utils/keyCodes.ts: 'Function' is in the passthrough allowlist of canonicalKeyFromEvent, and displayLabelForKey maps 'Function''fn', which reads as an intent to support it.

Only the string→Key bridge drops it.

Proposed fix

One arm in key_codes.rs, next to the other modifiers:

"Function" => Key::Function,

Happy to open a PR if useful.

Repro

macOS 26.5.2 (build 25F84), Apple M1, Voicebox 0.5.0 (prebuilt aarch64 DMG), MLX backend.

  1. Quit Voicebox.
  2. sqlite3 "$HOME/Library/Application Support/sh.voicebox.app/voicebox.db" "UPDATE capture_settings SET chord_push_to_talk_keys='[\"Function\"]' WHERE id=1;"
  3. Relaunch.

Result: no chord fires at all — neither the fn chord nor the untouched toggle chord. Reverting chord_push_to_talk_keys to a mapped key (e.g. ["MetaRight"]) restores hotkeys immediately.

I set the value through the DB rather than the chord picker, so I can't say from this repro whether the picker itself can capture fn on macOS — WebKit may not deliver a keydown for it, in which case the picker would need to source the key from the native tap for fn to be selectable in the UI even after the bridge is fixed.

Why it matters

fn is the dictation key people coming from Wispr Flow expect, and it's the one key on a Mac keyboard with no competing role in most apps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions