fix: make ai panel shortcut command-only
This commit is contained in:
@@ -295,7 +295,7 @@ fn build_note_menu(app: &App) -> MenuResult {
|
||||
.build(app)?;
|
||||
let toggle_ai_chat = MenuItemBuilder::new("Toggle AI Panel")
|
||||
.id(VIEW_TOGGLE_AI_CHAT)
|
||||
.accelerator("CmdOrCtrl+Shift+L")
|
||||
.accelerator("Cmd+Shift+L")
|
||||
.build(app)?;
|
||||
let toggle_backlinks = MenuItemBuilder::new("Toggle Backlinks")
|
||||
.id(VIEW_TOGGLE_BACKLINKS)
|
||||
|
||||
@@ -38,8 +38,10 @@ const VIEW_MODE_KEYS: Record<string, ViewMode> = {
|
||||
}
|
||||
|
||||
function isTextInputFocused(): boolean {
|
||||
const tag = document.activeElement?.tagName
|
||||
return tag === 'INPUT' || tag === 'TEXTAREA'
|
||||
const active = document.activeElement
|
||||
if (!(active instanceof HTMLElement)) return false
|
||||
if (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA') return true
|
||||
return active.isContentEditable || active.closest('[contenteditable="true"]') !== null
|
||||
}
|
||||
|
||||
function isCommandOrCtrlOnly(e: KeyboardEvent): boolean {
|
||||
|
||||
@@ -3,6 +3,14 @@ import { renderHook } from '@testing-library/react'
|
||||
import { useAppKeyboard } from './useAppKeyboard'
|
||||
|
||||
function fireKey(key: string, mods: { altKey?: boolean; metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean } = {}) {
|
||||
fireKeyOnTarget(window, key, mods)
|
||||
}
|
||||
|
||||
function fireKeyOnTarget(
|
||||
target: EventTarget,
|
||||
key: string,
|
||||
mods: { altKey?: boolean; metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean } = {},
|
||||
) {
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key,
|
||||
altKey: mods.altKey ?? false,
|
||||
@@ -12,7 +20,7 @@ function fireKey(key: string, mods: { altKey?: boolean; metaKey?: boolean; ctrlK
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
target.dispatchEvent(event)
|
||||
}
|
||||
|
||||
function makeActions() {
|
||||
@@ -130,6 +138,14 @@ describe('useAppKeyboard', () => {
|
||||
try { fn() } finally { document.body.removeChild(input) }
|
||||
}
|
||||
|
||||
function withFocusedContentEditable(fn: (editable: HTMLDivElement) => void) {
|
||||
const editable = document.createElement('div')
|
||||
editable.setAttribute('contenteditable', 'true')
|
||||
document.body.appendChild(editable)
|
||||
editable.focus()
|
||||
try { fn(editable) } finally { document.body.removeChild(editable) }
|
||||
}
|
||||
|
||||
it('Cmd+Backspace does not delete note when text input is focused', () => {
|
||||
const actions = makeActions()
|
||||
renderHook(() => useAppKeyboard(actions))
|
||||
@@ -139,6 +155,15 @@ describe('useAppKeyboard', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('Cmd+Backspace does not delete note when contenteditable is focused', () => {
|
||||
const actions = makeActions()
|
||||
renderHook(() => useAppKeyboard(actions))
|
||||
withFocusedContentEditable((editable) => {
|
||||
fireKeyOnTarget(editable, 'Backspace', { metaKey: true })
|
||||
expect(actions.onDeleteNote).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('Cmd+Backspace deletes note when no text input is focused', () => {
|
||||
const actions = makeActions()
|
||||
renderHook(() => useAppKeyboard(actions))
|
||||
@@ -201,6 +226,17 @@ describe('useAppKeyboard', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('Cmd+Shift+L works when editor stops propagation', () => {
|
||||
const actions = makeActions()
|
||||
const onToggleAIChat = vi.fn()
|
||||
renderHook(() => useAppKeyboard({ ...actions, onToggleAIChat }))
|
||||
withFocusedContentEditable((editable) => {
|
||||
editable.addEventListener('keydown', (event) => event.stopPropagation())
|
||||
fireKeyOnTarget(editable, 'l', { metaKey: true, shiftKey: true })
|
||||
expect(onToggleAIChat).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('Ctrl+Shift+L does not trigger toggle AI chat', () => {
|
||||
const actions = makeActions()
|
||||
const onToggleAIChat = vi.fn()
|
||||
|
||||
@@ -14,7 +14,7 @@ export function useAppKeyboard(actions: KeyboardActions) {
|
||||
onKeyDown(event)
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleWindowKeyDown)
|
||||
return () => window.removeEventListener('keydown', handleWindowKeyDown)
|
||||
window.addEventListener('keydown', handleWindowKeyDown, true)
|
||||
return () => window.removeEventListener('keydown', handleWindowKeyDown, true)
|
||||
}, [])
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ test.describe('AI panel shortcut', () => {
|
||||
await expect(page.locator('[data-testid="note-list-container"]')).toBeVisible({ timeout: 5_000 })
|
||||
})
|
||||
|
||||
test('Cmd/Ctrl+Shift+L opens the AI panel', async ({ page }) => {
|
||||
test('Cmd+Shift+L opens the AI panel from the editor', async ({ page }) => {
|
||||
await page.locator('.app__note-list .cursor-pointer').first().click()
|
||||
await sendShortcut(page, 'L', ['Control', 'Shift'])
|
||||
await page.locator('.bn-editor').click()
|
||||
await sendShortcut(page, 'L', ['Meta', 'Shift'])
|
||||
await expect(page.getByTestId('ai-panel')).toBeVisible({ timeout: 3_000 })
|
||||
await expect(page.getByTitle('Close AI panel')).toBeVisible()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user