mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
Merge branch 'main' of https://github.com/infinilabs/coco-app
This commit is contained in:
19
src-tauri/Cargo.lock
generated
19
src-tauri/Cargo.lock
generated
@@ -595,6 +595,7 @@ dependencies = [
|
||||
"tauri-plugin-oauth",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-single-instance",
|
||||
"tauri-plugin-store",
|
||||
"tauri-plugin-theme",
|
||||
"tauri-plugin-websocket",
|
||||
]
|
||||
@@ -2417,7 +2418,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
@@ -4295,6 +4296,22 @@ dependencies = [
|
||||
"zbus 4.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-store"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c0c08fae6995909f5e9a0da6038273b750221319f2c0f3b526d6de1cde21505"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.6",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-theme"
|
||||
version = "2.1.2"
|
||||
|
||||
@@ -30,6 +30,7 @@ tauri-plugin-theme = "2.1.2"
|
||||
tauri-plugin-oauth = { git = "https://github.com/FabianLars/tauri-plugin-oauth", branch = "v2" }
|
||||
tauri-plugin-deep-link = "2.0.0"
|
||||
tauri-plugin-single-instance = "2.0.0"
|
||||
tauri-plugin-store = "2.2.0"
|
||||
|
||||
# tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
|
||||
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
use std::{fs::create_dir, io::Read};
|
||||
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow};
|
||||
// use tauri_nspanel::{panel_delegate, ManagerExt, WebviewWindowExt};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut};
|
||||
|
||||
mod autostart;
|
||||
mod shortcut;
|
||||
|
||||
use autostart::{change_autostart, enable_autostart};
|
||||
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::ActivationPolicy;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const DEFAULT_SHORTCUT: &str = "command+shift+space";
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const DEFAULT_SHORTCUT: &str = "ctrl+shift+space";
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, WebviewWindow};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
|
||||
#[tauri::command]
|
||||
fn change_window_height(handle: AppHandle, height: u32) {
|
||||
@@ -88,22 +71,24 @@ pub fn run() {
|
||||
app.emit("single-instance", Payload { args: argv, cwd })
|
||||
.unwrap();
|
||||
}))
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
greet,
|
||||
change_window_height,
|
||||
change_shortcut,
|
||||
get_current_shortcut,
|
||||
shortcut::change_shortcut,
|
||||
shortcut::unregister_shortcut,
|
||||
shortcut::get_current_shortcut,
|
||||
change_autostart,
|
||||
hide_coco,
|
||||
switch_tray_icon,
|
||||
// show_panel,
|
||||
// hide_panel,
|
||||
// close_panel
|
||||
shortcut::check_shortcut_available,
|
||||
])
|
||||
.setup(|app| {
|
||||
init(app.app_handle());
|
||||
|
||||
enable_shortcut(app);
|
||||
shortcut::enable_shortcut(app);
|
||||
enable_tray(app);
|
||||
enable_autostart(app);
|
||||
|
||||
@@ -111,7 +96,7 @@ pub fn run() {
|
||||
app.set_activation_policy(ActivationPolicy::Accessory);
|
||||
|
||||
app.listen("theme-changed", move |event| {
|
||||
if let Ok(payload) = serde_json::from_str::<ThemeChangedPayload>(&event.payload()) {
|
||||
if let Ok(payload) = serde_json::from_str::<ThemeChangedPayload>(event.payload()) {
|
||||
// switch_tray_icon(app.app_handle(), payload.is_dark_mode);
|
||||
println!("Theme changed: is_dark_mode = {}", payload.is_dark_mode);
|
||||
}
|
||||
@@ -159,101 +144,6 @@ fn init(_app_handle: &AppHandle) {
|
||||
// panel.set_delegate(delegate);
|
||||
}
|
||||
|
||||
fn enable_shortcut(app: &mut tauri::App) {
|
||||
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
|
||||
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
|
||||
let command_shortcut: Shortcut = current_shortcut(app.app_handle()).unwrap();
|
||||
|
||||
app.handle()
|
||||
.plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |_app, shortcut, event| {
|
||||
//println!("{:?}", shortcut);
|
||||
if shortcut == &command_shortcut {
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
app.global_shortcut().register(command_shortcut).unwrap();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn change_shortcut<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
_window: tauri::Window<R>,
|
||||
key: String,
|
||||
) -> Result<(), String> {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use tauri_plugin_global_shortcut::ShortcutState;
|
||||
|
||||
if let Err(e) = remove_shortcut(&app) {
|
||||
eprintln!("Failed to remove old shortcut: {}", e);
|
||||
}
|
||||
|
||||
let main_window = app.get_webview_window("main").unwrap();
|
||||
|
||||
if key.trim().is_empty() {
|
||||
let path = app.path().app_config_dir().unwrap();
|
||||
if !path.exists() {
|
||||
create_dir(&path).unwrap();
|
||||
}
|
||||
|
||||
let file_path = path.join("shortcut.txt");
|
||||
let mut file = File::create(file_path).unwrap();
|
||||
file.write_all(b"").unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shortcut: Shortcut = key
|
||||
.parse()
|
||||
.map_err(|_| "The format of the shortcut key is incorrect".to_owned())?;
|
||||
app.global_shortcut()
|
||||
.on_shortcut(shortcut, move |_app, scut, event| {
|
||||
if scut == &shortcut {
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if main_window.is_visible().unwrap() {
|
||||
main_window.hide().unwrap();
|
||||
} else {
|
||||
main_window.show().unwrap();
|
||||
main_window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(|_| "Failed to register new shortcut key".to_owned())?;
|
||||
|
||||
let path = app.path().app_config_dir().unwrap();
|
||||
if path.exists() == false {
|
||||
create_dir(&path).unwrap();
|
||||
}
|
||||
|
||||
let file_path = path.join("shortcut.txt");
|
||||
let mut file = File::create(file_path).unwrap();
|
||||
file.write_all(key.as_bytes()).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_current_shortcut<R: Runtime>(app: tauri::AppHandle<R>) -> Result<String, String> {
|
||||
let res = current_shortcut(&app)?;
|
||||
|
||||
Ok(res.into_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn hide_coco(app: tauri::AppHandle) {
|
||||
if let Some(window) = app.get_window("main") {
|
||||
@@ -277,38 +167,6 @@ fn hide_coco(app: tauri::AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
fn current_shortcut<R: Runtime>(app: &tauri::AppHandle<R>) -> Result<Shortcut, String> {
|
||||
use std::fs::File;
|
||||
|
||||
let path = app.path().app_config_dir().unwrap();
|
||||
let mut old_value = DEFAULT_SHORTCUT.to_owned();
|
||||
|
||||
if path.exists() {
|
||||
let file_path = path.join("shortcut.txt");
|
||||
if file_path.exists() {
|
||||
let mut file = File::open(file_path).unwrap();
|
||||
let mut data = String::new();
|
||||
if let Ok(_) = file.read_to_string(&mut data) {
|
||||
if data.is_empty() == false {
|
||||
old_value = data
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let short: Shortcut = old_value.parse().unwrap();
|
||||
|
||||
Ok(short)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn remove_shortcut<R: Runtime>(app: &tauri::AppHandle<R>) -> Result<(), String> {
|
||||
let short = current_shortcut(app)?;
|
||||
|
||||
app.global_shortcut().unregister(short).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_open_coco(app: &AppHandle) {
|
||||
println!("Open Coco menu clicked!");
|
||||
|
||||
|
||||
190
src-tauri/src/shortcut.rs
Normal file
190
src-tauri/src/shortcut.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use tauri::App;
|
||||
use tauri::AppHandle;
|
||||
use tauri::Manager;
|
||||
use tauri::Runtime;
|
||||
use tauri_plugin_global_shortcut::GlobalShortcutExt;
|
||||
use tauri_plugin_global_shortcut::Shortcut;
|
||||
use tauri_plugin_global_shortcut::ShortcutState;
|
||||
use tauri_plugin_store::JsonValue;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
|
||||
/// Tauri store name
|
||||
const COCO_TAURI_STORE: &str = "coco_tauri_store";
|
||||
|
||||
/// Tauri's store is a key-value database, we use it to store our registered
|
||||
/// global shortcut.
|
||||
///
|
||||
/// This is the key we use to store it.
|
||||
const COCO_GLOBAL_SHORTCUT: &str = "coco_global_shortcut";
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const DEFAULT_SHORTCUT: &str = "command+shift+space";
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const DEFAULT_SHORTCUT: &str = "ctrl+shift+space";
|
||||
|
||||
/// Set up the shortcut upon app start.
|
||||
pub fn enable_shortcut(app: &App) {
|
||||
let store = app
|
||||
.store(COCO_TAURI_STORE)
|
||||
.expect("creating a store should not fail");
|
||||
|
||||
if let Some(stored_shortcut) = store.get(COCO_GLOBAL_SHORTCUT) {
|
||||
let stored_shortcut_str = match stored_shortcut {
|
||||
JsonValue::String(str) => str,
|
||||
unexpected_type => panic!(
|
||||
"COCO shortcut should be stored as a string, found: {} ",
|
||||
unexpected_type
|
||||
),
|
||||
};
|
||||
let stored_shortcut = stored_shortcut_str
|
||||
.parse::<Shortcut>()
|
||||
.expect("stored shortcut string should be valid");
|
||||
_register_shortcut_upon_start(app, stored_shortcut);
|
||||
} else {
|
||||
store.set(
|
||||
COCO_GLOBAL_SHORTCUT,
|
||||
JsonValue::String(DEFAULT_SHORTCUT.to_string()),
|
||||
);
|
||||
let default_shortcut = DEFAULT_SHORTCUT
|
||||
.parse::<Shortcut>()
|
||||
.expect("default shortcut should never be invalid");
|
||||
_register_shortcut_upon_start(app, default_shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the stored shortcut as a string, same as [`_get_shortcut()`], except that
|
||||
/// this is a `tauri::command` interface.
|
||||
#[tauri::command]
|
||||
pub fn get_current_shortcut<R: Runtime>(app: AppHandle<R>) -> Result<String, String> {
|
||||
let shortcut = _get_shortcut(&app);
|
||||
Ok(shortcut)
|
||||
}
|
||||
|
||||
/// Get the current shortcut and unregister it on the tauri side.
|
||||
#[tauri::command]
|
||||
pub fn unregister_shortcut<R: Runtime>(app: AppHandle<R>) {
|
||||
let shortcut_str = _get_shortcut(&app);
|
||||
let shortcut = shortcut_str
|
||||
.parse::<Shortcut>()
|
||||
.expect("stored shortcut string should be valid");
|
||||
|
||||
app.global_shortcut()
|
||||
.unregister(shortcut)
|
||||
.expect("failed to unregister shortcut")
|
||||
}
|
||||
|
||||
/// Change the global shortcut to `key`.
|
||||
#[tauri::command]
|
||||
pub fn change_shortcut<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
_window: tauri::Window<R>,
|
||||
key: String,
|
||||
) -> Result<(), String> {
|
||||
println!("key {}:", key);
|
||||
let shortcut = match key.parse::<Shortcut>() {
|
||||
Ok(shortcut) => shortcut,
|
||||
Err(_) => return Err(format!("invalid shortcut {}", key)),
|
||||
};
|
||||
|
||||
// Store it
|
||||
let store = app
|
||||
.get_store(COCO_TAURI_STORE)
|
||||
.expect("store should be loaded or created");
|
||||
store.set(COCO_GLOBAL_SHORTCUT, JsonValue::String(key));
|
||||
|
||||
// Register it
|
||||
_register_shortcut(&app, shortcut);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function to register a shortcut, used for shortcut updates.
|
||||
fn _register_shortcut<R: Runtime>(app: &AppHandle<R>, shortcut: Shortcut) {
|
||||
let main_window = app.get_webview_window("main").unwrap();
|
||||
app.global_shortcut()
|
||||
.on_shortcut(shortcut, move |_app, scut, event| {
|
||||
if scut == &shortcut {
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if main_window.is_visible().unwrap() {
|
||||
main_window.hide().unwrap();
|
||||
} else {
|
||||
main_window.show().unwrap();
|
||||
main_window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(|err| format!("Failed to register new shortcut key '{}'", err))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Helper function to register a shortcut, used to set up the shortcut up App's first start.
|
||||
fn _register_shortcut_upon_start(app: &App, shortcut: Shortcut) {
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
app.handle()
|
||||
.plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |_app, scut, event| {
|
||||
if scut == &shortcut {
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.unwrap();
|
||||
app.global_shortcut().register(shortcut).unwrap();
|
||||
}
|
||||
|
||||
/// Helper function to get the stored global shortcut, as a string.
|
||||
pub fn _get_shortcut<R: Runtime>(app: &AppHandle<R>) -> String {
|
||||
let store = app
|
||||
.get_store(COCO_TAURI_STORE)
|
||||
.expect("store should be loaded or created");
|
||||
|
||||
match store
|
||||
.get(COCO_GLOBAL_SHORTCUT)
|
||||
.expect("shortcut should be stored")
|
||||
{
|
||||
JsonValue::String(str) => str,
|
||||
unexpected_type => panic!(
|
||||
"COCO shortcut should be stored as a string, found: {} ",
|
||||
unexpected_type
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn check_shortcut_available(key: String) -> bool {
|
||||
// 这里可以实现系统级的快捷键检查
|
||||
// 可以检查是否与其他应用的全局快捷键冲突
|
||||
// 返回 true 表示可用,false 表示已被占用
|
||||
|
||||
// 简单实现示例:
|
||||
!is_system_shortcut(&key)
|
||||
}
|
||||
|
||||
fn is_system_shortcut(key: &str) -> bool {
|
||||
let system_shortcuts = vec![
|
||||
"Command+C",
|
||||
"Command+V",
|
||||
"Command+X",
|
||||
"Command+A",
|
||||
"Command+Z",
|
||||
"Control+C",
|
||||
"Control+V",
|
||||
"Control+X",
|
||||
"Control+A",
|
||||
"Control+Z",
|
||||
// 添加更多系统快捷键
|
||||
];
|
||||
|
||||
system_shortcuts.contains(&key)
|
||||
}
|
||||
@@ -67,34 +67,39 @@ export default function GeneralSettings() {
|
||||
setLaunchAtLogin(false);
|
||||
};
|
||||
|
||||
const [shortcut, setShortcut] = useState<Shortcut>([]);
|
||||
|
||||
async function getCurrentShortcut() {
|
||||
const res: any = await invoke("get_current_shortcut");
|
||||
setShortcut(res?.split("+"));
|
||||
try {
|
||||
const res: any = await invoke("get_current_shortcut");
|
||||
console.log("DBG: ", res);
|
||||
setShortcut(res?.split("+"));
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch shortcut:", err);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getCurrentShortcut();
|
||||
}, []);
|
||||
|
||||
const [shortcut, setShortcut] = useState<Shortcut>([]);
|
||||
const changeShortcut =(key: Shortcut) => {
|
||||
setShortcut(key)
|
||||
//
|
||||
if (key.length === 0) return;
|
||||
invoke("change_shortcut", { key: key?.join("+") }).catch((err) => {
|
||||
console.error("Failed to save hotkey:", err);
|
||||
});
|
||||
}
|
||||
|
||||
const { isEditing, currentKeys, startEditing, saveShortcut, cancelEditing } =
|
||||
useShortcutEditor(shortcut, setShortcut);
|
||||
|
||||
useEffect(() => {
|
||||
if (shortcut.length === 0) return;
|
||||
invoke("change_shortcut", { key: shortcut?.join("+") }).catch((err) => {
|
||||
console.error("Failed to save hotkey:", err);
|
||||
startEditing();
|
||||
});
|
||||
}, [shortcut]);
|
||||
useShortcutEditor(shortcut, changeShortcut);
|
||||
|
||||
const onEditShortcut = async () => {
|
||||
startEditing();
|
||||
//
|
||||
invoke("change_shortcut", { key: "" }).catch((err) => {
|
||||
invoke("unregister_shortcut").catch((err) => {
|
||||
console.error("Failed to save hotkey:", err);
|
||||
startEditing();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -103,7 +108,6 @@ export default function GeneralSettings() {
|
||||
//
|
||||
invoke("change_shortcut", { key: shortcut?.join("+") }).catch((err) => {
|
||||
console.error("Failed to save hotkey:", err);
|
||||
startEditing();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,31 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { Shortcut } from '@/components/Settings/shortcut';
|
||||
import { normalizeKey, isModifierKey, sortKeys } from '@/utils/keyboardUtils';
|
||||
|
||||
const RESERVED_SHORTCUTS = [
|
||||
["Command", "C"],
|
||||
["Command", "V"],
|
||||
["Command", "X"],
|
||||
["Command", "A"],
|
||||
["Command", "Z"],
|
||||
["Command", "Q"],
|
||||
// Windows/Linux
|
||||
["Control", "C"],
|
||||
["Control", "V"],
|
||||
["Control", "X"],
|
||||
["Control", "A"],
|
||||
["Control", "Z"],
|
||||
// Coco
|
||||
["Command", "I"],
|
||||
["Command", "T"],
|
||||
["Command", "N"],
|
||||
["Command", "G"],
|
||||
["Command", "O"],
|
||||
["Command", "U"],
|
||||
["Command", "M"],
|
||||
["Command", "Enter"],
|
||||
["Command", "ArrowLeft"],
|
||||
];
|
||||
|
||||
export function useShortcutEditor(shortcut: Shortcut, onChange: (shortcut: Shortcut) => void) {
|
||||
console.log("shortcut", shortcut)
|
||||
|
||||
@@ -16,7 +41,7 @@ export function useShortcutEditor(shortcut: Shortcut, onChange: (shortcut: Short
|
||||
setCurrentKeys([]);
|
||||
}, []);
|
||||
|
||||
const saveShortcut = useCallback(() => {
|
||||
const saveShortcut = async () => {
|
||||
if (!isEditing || currentKeys.length < 2) return;
|
||||
|
||||
const hasModifier = currentKeys.some(isModifierKey);
|
||||
@@ -24,13 +49,29 @@ export function useShortcutEditor(shortcut: Shortcut, onChange: (shortcut: Short
|
||||
|
||||
if (!hasModifier || !hasNonModifier) return;
|
||||
|
||||
console.log(111111, currentKeys)
|
||||
|
||||
const isReserved = RESERVED_SHORTCUTS.some(reserved =>
|
||||
reserved.length === currentKeys.length &&
|
||||
reserved.every((key, index) => key.toLowerCase() === currentKeys[index].toLowerCase())
|
||||
);
|
||||
|
||||
console.log(22222, isReserved)
|
||||
|
||||
|
||||
if (isReserved) {
|
||||
console.error("This is a system reserved shortcut");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort keys to ensure consistent order (modifiers first)
|
||||
const sortedKeys = sortKeys(currentKeys);
|
||||
|
||||
|
||||
|
||||
onChange(sortedKeys);
|
||||
setIsEditing(false);
|
||||
setCurrentKeys([]);
|
||||
}, [isEditing, currentKeys, onChange]);
|
||||
};
|
||||
|
||||
const cancelEditing = useCallback(() => {
|
||||
setIsEditing(false);
|
||||
|
||||
@@ -11,10 +11,13 @@ export const KEY_SYMBOLS: Record<string, string> = {
|
||||
Alt: isMac ? '⌥' : 'Alt',
|
||||
alt: isMac ? '⌥' : 'Alt',
|
||||
Meta: isMac ? '⌘' : 'Win',
|
||||
meta: isMac ? '⌘' : 'Win',
|
||||
Command: isMac ? '⌘' : 'Win',
|
||||
command: isMac ? '⌘' : 'Win',
|
||||
super: isMac ? '⌘' : 'Win',
|
||||
// Special keys
|
||||
Space: 'Space',
|
||||
space: 'Space',
|
||||
Enter: '↵',
|
||||
Backspace: '⌫',
|
||||
Delete: 'Del',
|
||||
|
||||
Reference in New Issue
Block a user