Skip to content

Commit 34b2e13

Browse files
committed
fix: allow app shortcuts in Reader AI composer
1 parent f9e492f commit 34b2e13

3 files changed

Lines changed: 49 additions & 1 deletion

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import test from 'ava';
2+
import { JSDOM } from 'jsdom';
3+
import { APP_SHORTCUTS_ALLOWED_ATTR, isEditableShortcutTarget } from '../../src/keyboard_shortcuts.ts';
4+
5+
function withDom<T>(callback: () => T): T {
6+
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'https://input.test/doc' });
7+
const previous = {
8+
window: globalThis.window,
9+
document: globalThis.document,
10+
HTMLElement: globalThis.HTMLElement,
11+
};
12+
13+
Object.assign(globalThis, {
14+
window: dom.window,
15+
document: dom.window.document,
16+
HTMLElement: dom.window.HTMLElement,
17+
});
18+
19+
try {
20+
return callback();
21+
} finally {
22+
Object.assign(globalThis, previous);
23+
dom.window.close();
24+
}
25+
}
26+
27+
test('isEditableShortcutTarget keeps regular textareas opt-out for app shortcuts', (t) => {
28+
withDom(() => {
29+
const input = document.createElement('textarea');
30+
document.body.append(input);
31+
32+
t.true(isEditableShortcutTarget(input));
33+
});
34+
});
35+
36+
test('isEditableShortcutTarget allows marked composer targets to receive app shortcuts', (t) => {
37+
withDom(() => {
38+
const input = document.createElement('textarea');
39+
input.setAttribute(APP_SHORTCUTS_ALLOWED_ATTR, 'true');
40+
document.body.append(input);
41+
42+
t.false(isEditableShortcutTarget(input));
43+
});
44+
});

src/components/ReaderAiPanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ArrowLeft, ArrowRight, ChevronDown, ChevronRight, CircleStop, MoreHoriz
33
import type { ComponentChildren } from 'preact';
44
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks';
55
import { blurOnClose } from '../dom_utils';
6-
import { matchesPrimaryShortcut } from '../keyboard_shortcuts';
6+
import { APP_SHORTCUTS_ALLOWED_ATTR, matchesPrimaryShortcut } from '../keyboard_shortcuts';
77
import { parseMarkdownToHtml } from '../markdown';
88
import type { ReaderAiEditProposal, ReaderAiStagedChange } from '../reader_ai';
99
import type { ReaderAiRunRecord } from '../reader_ai_ledger';
@@ -1299,6 +1299,7 @@ export function ReaderAiPanel({
12991299
<textarea
13001300
ref={composerInputRef}
13011301
class={`reader-ai-input${hasMessages ? ' reader-ai-input--bottom' : ''}`}
1302+
{...{ [APP_SHORTCUTS_ALLOWED_ATTR]: 'true' }}
13021303
value={draft}
13031304
placeholder={composerPlaceholder}
13041305
onInput={(event) => {

src/keyboard_shortcuts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
export const APP_SHORTCUTS_ALLOWED_ATTR = 'data-allow-app-shortcuts';
2+
13
export function isEditableShortcutTarget(target: EventTarget | null): boolean {
24
if (!(target instanceof HTMLElement)) return false;
5+
if (target.closest(`[${APP_SHORTCUTS_ALLOWED_ATTR}="true"]`) !== null) return false;
36
return target.isContentEditable || target.closest('input, textarea, select, [contenteditable="true"]') !== null;
47
}
58

0 commit comments

Comments
 (0)