Configuring and Customizing Global Hotkeys in Clash Verge Rev: A Complete Technical Guide

Clash Verge Rev allows users to bind predefined actions—such as opening the dashboard, switching proxy modes, or toggling system proxy—to system-wide keyboard shortcuts that function even when the application window is not focused, implemented through a React frontend and Rust backend architecture.

Configuring and customizing global hotkeys in Clash Verge Rev enables efficient workflow automation for proxy management. The implementation spans TypeScript type definitions, React UI components, and Rust system-level integration using Tauri's global-shortcut plugin. This guide examines the complete architecture based on the clash-verge-rev/clash-verge-rev source code.

Understanding the Global Hotkey Architecture

The hotkey system operates through three distinct layers that handle configuration persistence, user interaction, and OS-level registration.

Configuration Layer stores user preferences in the IVergeConfig interface defined in src/types/global.d.ts (lines 924-927). This includes the enable_global_hotkey boolean flag and the hotkeys string array.

Frontend UI Layer provides the interactive interface through React components. The HotkeyViewer component in src/components/setting/mods/hotkey-viewer.tsx (lines 43-55) renders the settings dialog, while HotkeyInput in src/components/setting/mods/hotkey-input.tsx (lines 65-88) captures keyboard input.

Backend Layer handles OS-level registration in src-tauri/src/core/hotkey.rs. The Hotkey::init function reads stored configurations and registers shortcuts using tauri_plugin_global_shortcut, respecting the global flag to determine whether shortcuts work when the window is unfocused.

Configuring Hotkeys Through the Settings UI

The primary method for customizing shortcuts uses the React-based settings interface.

When you navigate to Settings → Hotkeys, the HotkeyViewer component initializes by fetching the current configuration via the useVerge hook. It constructs a hotkeyMap by parsing the hotkeys array from verge?.hotkeys, splitting each "func,key" string (lines 60-70 in hotkey-viewer.tsx).

The HotkeyInput component captures raw KeyboardEvent data and normalizes it using parseHotkey from src/utils/parse-hotkey.ts. This converts complex key combinations into standardized strings like "CTRL+ALT+S" that the backend can parse.

When you save changes, the onSave function concatenates the map back into ["func,key"] strings (lines 77-90) and calls patchVerge with the updated hotkeys and enable_global_hotkey values. The backend then triggers Hotkey::update to re-register the modified shortcuts.

Manual Configuration via verge.json

For advanced customization or batch configuration, you can edit the configuration file directly.

The settings persist in verge.json in the application's user data directory. The configuration uses two key fields defined in src/types/global.d.ts (lines 924-927):

  • enable_global_hotkey: Boolean flag controlling whether shortcuts work globally
  • hotkeys: Array of strings in "function_name,key_combination" format

Example configuration:

{
  "enable_global_hotkey": true,
  "hotkeys": [
    "open_or_close_dashboard,CTRL+ALT+D",
    "clash_mode_rule,CTRL+ALT+R",
    "toggle_system_proxy,CTRL+ALT+P",
    "toggle_tun_mode,CTRL+SHIFT+T"
  ]
}

Available function names correspond to variants in the HotkeyFunction enum defined in src-tauri/src/core/hotkey.rs (lines 13-27), including open_or_close_dashboard, toggle_system_proxy, clash_mode_rule, clash_mode_global, clash_mode_direct, and toggle_tun_mode.

Extending Hotkey Functions in Rust

To add custom actions beyond the built-in set, you must extend the Rust backend.

First, modify the HotkeyFunction enum in src-tauri/src/core/hotkey.rs (lines 13-27) to include your new variant:

impl fmt::Display for HotkeyFunction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Self::OpenOrCloseDashboard => "open_or_close_dashboard",
            Self::ToggleSystemProxy => "toggle_system_proxy",
            Self::ToggleTunMode => "toggle_tun_mode",
            Self::ClashModeRule => "clash_mode_rule",
            Self::ClashModeGlobal => "clash_mode_global",
            Self::ClashModeDirect => "clash_mode_direct",
            Self::ToggleWindowVisibility => "toggle_window_visibility",
            Self::MyNewAction => "my_new_action",   // New variant
        };
        write!(f, "{s}")
    }
}

Then implement the execution logic in the execute_function method:

match function {
    HotkeyFunction::OpenOrCloseDashboard => {
        AsyncHandler::spawn(async move {
            feat::open_or_close_dashboard().await;
        });
    }
    // ... existing cases ...
    HotkeyFunction::MyNewAction => {
        AsyncHandler::spawn(async move {
            feat::run_my_new_action().await;
            notify_event(NotificationEvent::MyNewActionExecuted).await;
        });
    }
}

After recompiling the application, users can bind the new function using "my_new_action,CTRL+ALT+M" in the configuration.

Enabling and Disabling Global Mode

The enable_global_hotkey flag determines whether shortcuts work system-wide or only when the application window is focused.

In the React frontend, a switch component in HotkeyViewer (src/components/setting/mods/hotkey-viewer.tsx, lines 18-22) controls this setting:

<Switch
  edge="end"
  checked={enableGlobalHotkey}
  onChange={(e) => setEnableGlobalHotkey(e.target.checked)}
/>

When disabled, the Rust backend in src-tauri/src/core/hotkey.rs (around lines 57-71) checks this flag before dispatching shortcut events. If the flag is false and the window is not focused, the shortcut trigger is ignored.

On macOS, the backend additionally handles system-wide shortcuts like Cmd+Q and Cmd+W (lines 69-95 in hotkey.rs) to ensure proper integration with macOS window management.

Summary

  • Clash Verge Rev implements global hotkeys through a three-layer stack: TypeScript configuration types, React UI components, and Rust OS-level registration.
  • Configuration persists in IVergeConfig (src/types/global.d.ts, lines 924-927) and stores data in verge.json using the format "function_name,key_combination".
  • UI Components HotkeyViewer and HotkeyInput (src/components/setting/mods/) manage the settings interface, utilizing useVerge for state and parseHotkey for key normalization.
  • Backend Registration occurs in src-tauri/src/core/hotkey.rs, where Hotkey::init registers shortcuts with Tauri's global-shortcut plugin and respects the enable_global_hotkey flag.
  • Customization is possible by extending the HotkeyFunction enum in Rust to add new executable actions.

Frequently Asked Questions

Where are the global hotkey settings stored in Clash Verge Rev?

The settings are stored in the verge.json file located in the application's user data directory. This file contains the enable_global_hotkey boolean flag and the hotkeys array, where each entry follows the format "function_name,key_combination" (for example, "toggle_system_proxy,CTRL+ALT+P"). The TypeScript interface IVergeConfig in src/types/global.d.ts (lines 924-927) defines these fields.

Why do my hotkeys only work when the Clash Verge Rev window is focused?

This behavior occurs when the Enable Global Hotkey setting is disabled. In src-tauri/src/core/hotkey.rs (around lines 57-71), the backend checks the enable_global_hotkey flag before dispatching shortcut events. When this flag is false, the application ignores global shortcut triggers unless the main window currently has focus. Enable the global mode in Settings → Hotkeys to allow shortcuts to work from any application.

Can I add custom hotkey actions beyond the built-in functions?

Yes, but extending functionality requires modifying the Rust source code. You must add a new variant to the HotkeyFunction enum in src-tauri/src/core/hotkey.rs (lines 13-27), implement the Display trait to map it to a string identifier, and add execution logic in the execute_function method. After recompiling the application, users can bind the new function using your defined string identifier in the JSON configuration or UI.

How does the frontend capture and normalize keyboard input?

The React frontend uses the HotkeyInput component located in src/components/setting/mods/hotkey-input.tsx (lines 65-88) to capture raw KeyboardEvent data. This component utilizes the parseHotkey utility from src/utils/parse-hotkey.ts to normalize the input into a standardized string format (such as "CTRL+ALT+S"). This normalized format ensures consistency between the frontend display, JSON configuration storage, and Rust backend parsing when registering shortcuts with the OS.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →