fix: allow standard text shortcuts in command palette input (#73)

* fix: allow standard text shortcuts in command palette input

Two root causes prevented macOS text shortcuts (Cmd+A, Cmd+Backspace, etc.)
from working in the command palette input:

1. The Tauri native menu only had a View submenu — no Edit menu. On macOS,
   app.set_menu() replaces the entire menu bar, so Cmd+A/C/V/X/Z accelerators
   were removed. Added standard Edit menu with predefined items.

2. useAppKeyboard globally intercepted Cmd+Backspace (mapped to trash note)
   even when a text input was focused. Added an isTextInputFocused guard so
   Cmd+Backspace/Delete fall through to native text editing in inputs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: close missing }) in useAppKeyboard.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Luca Rossi
2026-02-25 18:30:20 +01:00
committed by GitHub
parent 19f35728e7
commit e95e85ceb1
3 changed files with 55 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
use tauri::{
menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder},
menu::{MenuBuilder, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder},
App, Emitter,
};
@@ -10,6 +10,16 @@ const VIEW_ITEMS: [(&str, &str, &str); 3] = [
];
pub fn setup_menu(app: &App) -> Result<(), Box<dyn std::error::Error>> {
let edit_submenu = SubmenuBuilder::new(app, "Edit")
.item(&PredefinedMenuItem::undo(app, Some("Undo"))?)
.item(&PredefinedMenuItem::redo(app, Some("Redo"))?)
.separator()
.item(&PredefinedMenuItem::cut(app, Some("Cut"))?)
.item(&PredefinedMenuItem::copy(app, Some("Copy"))?)
.item(&PredefinedMenuItem::paste(app, Some("Paste"))?)
.item(&PredefinedMenuItem::select_all(app, Some("Select All"))?)
.build()?;
let mut view_menu = SubmenuBuilder::new(app, "View");
for (id, label, accel) in &VIEW_ITEMS {
let item = MenuItemBuilder::new(*label)
@@ -20,7 +30,10 @@ pub fn setup_menu(app: &App) -> Result<(), Box<dyn std::error::Error>> {
}
let view_submenu = view_menu.build()?;
let menu = MenuBuilder::new(app).item(&view_submenu).build()?;
let menu = MenuBuilder::new(app)
.item(&edit_submenu)
.item(&view_submenu)
.build()?;
app.set_menu(menu)?;

View File

@@ -111,4 +111,36 @@ describe('useAppKeyboard', () => {
expect(actions.onQuickOpen).not.toHaveBeenCalled()
expect(actions.onCreateNote).not.toHaveBeenCalled()
})
function withFocusedInput(fn: () => void) {
const input = document.createElement('input')
document.body.appendChild(input)
input.focus()
try { fn() } finally { document.body.removeChild(input) }
}
it('Cmd+Backspace does not trash note when text input is focused', () => {
const actions = makeActions()
renderHook(() => useAppKeyboard(actions))
withFocusedInput(() => {
fireKey('Backspace', { metaKey: true })
expect(actions.onTrashNote).not.toHaveBeenCalled()
})
})
it('Cmd+Backspace trashes note when no text input is focused', () => {
const actions = makeActions()
renderHook(() => useAppKeyboard(actions))
fireKey('Backspace', { metaKey: true })
expect(actions.onTrashNote).toHaveBeenCalledWith('/vault/test.md')
})
it('Cmd+K still works when text input is focused', () => {
const actions = makeActions()
renderHook(() => useAppKeyboard(actions))
withFocusedInput(() => {
fireKey('k', { metaKey: true })
expect(actions.onCommandPalette).toHaveBeenCalled()
})
})
})

View File

@@ -17,6 +17,13 @@ interface KeyboardActions {
type ShortcutHandler = () => void
const TEXT_EDITING_KEYS = new Set(['Backspace', 'Delete'])
function isTextInputFocused(): boolean {
const tag = document.activeElement?.tagName
return tag === 'INPUT' || tag === 'TEXTAREA'
}
const VIEW_MODE_KEYS: Record<string, ViewMode> = {
'1': 'editor-only',
'2': 'editor-list',
@@ -41,6 +48,7 @@ function handleCmdKey(e: KeyboardEvent, keyMap: Record<string, ShortcutHandler>)
if (!mod) return false
const handler = keyMap[e.key]
if (!handler) return false
if (TEXT_EDITING_KEYS.has(e.key) && isTextInputFocused()) return false
e.preventDefault()
handler()
return true