diff --git a/extension/src/popup/components/item-detail.ts b/extension/src/popup/components/item-detail.ts index 54957ba..e00f121 100644 --- a/extension/src/popup/components/item-detail.ts +++ b/extension/src/popup/components/item-detail.ts @@ -207,7 +207,13 @@ function renderLogin(app: HTMLElement, item: Item): void { // --- Keyboard shortcuts --- const handler = async (e: KeyboardEvent) => { - if ((e.target as HTMLElement).tagName === 'INPUT') return; + // Bail if the user is typing into any editable field — don't steal + // printable keystrokes meant for an input/textarea/contenteditable element. + const t = e.target; + if (t instanceof HTMLElement) { + const tag = t.tagName; + if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || t.isContentEditable) return; + } switch (e.key) { case 'Escape': diff --git a/extension/src/popup/components/item-list.ts b/extension/src/popup/components/item-list.ts index 8e3a569..db62bd7 100644 --- a/extension/src/popup/components/item-list.ts +++ b/extension/src/popup/components/item-list.ts @@ -97,6 +97,7 @@ export function renderItemList(app: HTMLElement): void { rows.forEach(row => { row.addEventListener('click', async () => { const id = (row as HTMLElement).dataset.id!; + document.removeEventListener('keydown', handleListKeydown); await openItem(id); }); }); @@ -141,11 +142,35 @@ function getFilteredEntries(): Array<[ItemId, ManifestEntry]> { return filtered; } +/// True if the event target is an editable field (input/textarea/contenteditable). +/// Global shortcut handlers should bail when the user is typing into a field — +/// otherwise printable characters like "/" and "+" get eaten by the shortcut +/// routing and never reach the input. +function isEditableTarget(target: EventTarget | null): boolean { + if (!(target instanceof HTMLElement)) return false; + const tag = target.tagName; + if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true; + if (target.isContentEditable) return true; + return false; +} + function handleListKeydown(e: KeyboardEvent): void { const state = getState(); const target = e.target as HTMLElement; const isSearch = target.id === 'search-input'; + // If the user is typing into any input/textarea (other than the list's own + // search field, which we want to focus on "/" even from outside it), let the + // keystroke through. The "/" shortcut below is specifically "jump to search + // from the list," not "steal printable characters while typing." + if (isEditableTarget(target) && !isSearch) { + if (e.key === 'Escape') { + document.removeEventListener('keydown', handleListKeydown); + window.close(); + } + return; + } + if (e.key === '/' && !isSearch) { e.preventDefault(); (document.getElementById('search-input') as HTMLInputElement | null)?.focus();