fix(ext/popup): don't eat '/' and other keystrokes while typing in inputs
Bug: item-list's global "/" shortcut (focus search) and "+" shortcut (new item) fired even when focus was inside any input/textarea other than the list's own search field. This ate forward-slashes typed into the setup wizard's host-url field and the add/edit form's notes area, and would have done the same for any printable shortcut in a future text field. Root cause: the handler was attached to `document`, stays attached when the user opens an item (and its click-handler navigated without removing the listener), and only excluded the search field by id. Fix: - Add isEditableTarget() helper — returns true for INPUT/TEXTAREA/SELECT and contenteditable elements. Global shortcut handlers bail early when this fires, passing the keystroke through to the field. - Apply the same guard in item-detail.ts (previously only guarded against INPUT, missing TEXTAREA + contenteditable). - Remove handleListKeydown on row-click so it doesn't linger on detail/edit views even before the route-transition keydown listeners install. - Escape in the list view still works from inside an editable field — only the printable-character interceptions are gated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -207,7 +207,13 @@ function renderLogin(app: HTMLElement, item: Item): void {
|
|||||||
|
|
||||||
// --- Keyboard shortcuts ---
|
// --- Keyboard shortcuts ---
|
||||||
const handler = async (e: KeyboardEvent) => {
|
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) {
|
switch (e.key) {
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
row.addEventListener('click', async () => {
|
row.addEventListener('click', async () => {
|
||||||
const id = (row as HTMLElement).dataset.id!;
|
const id = (row as HTMLElement).dataset.id!;
|
||||||
|
document.removeEventListener('keydown', handleListKeydown);
|
||||||
await openItem(id);
|
await openItem(id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -141,11 +142,35 @@ function getFilteredEntries(): Array<[ItemId, ManifestEntry]> {
|
|||||||
return filtered;
|
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 {
|
function handleListKeydown(e: KeyboardEvent): void {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const isSearch = target.id === 'search-input';
|
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) {
|
if (e.key === '/' && !isSearch) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
(document.getElementById('search-input') as HTMLInputElement | null)?.focus();
|
(document.getElementById('search-input') as HTMLInputElement | null)?.focus();
|
||||||
|
|||||||
Reference in New Issue
Block a user