diff --git a/packages/app/e2e/browser/chat-find.spec.ts b/packages/app/e2e/browser/chat-find.spec.ts index 5d94b5166a..2a223f37e2 100644 --- a/packages/app/e2e/browser/chat-find.spec.ts +++ b/packages/app/e2e/browser/chat-find.spec.ts @@ -6,6 +6,14 @@ import { expectReconnectingToastVisible, } from "../support/helpers/workspace-ui"; import { trackPromptJumpRequests } from "../support/helpers/agent-timeline-gate"; +import { + composerLocator, + fillComposerDraft, + expectComposerDraft, + expectComposerFocused, +} from "../support/helpers/composer"; + +import { openCommandCenter, closeCommandCenter } from "../support/helpers/command-center"; const RESPONSE = 'İ😀hello **world**; hello *world*.\n\n[hello & world](https://example.com/hidden-destination)\n\n```ts\nconst a = "a.b";\n```\n\n- first cell\n- second cell'; @@ -203,3 +211,62 @@ test("finds text beyond the normal render cap and restores the cap when Find clo await agent.cleanup(); } }); + +for (const shortcut of ["Control+f", "Meta+f"]) { + test(`opens and refocuses Find with ${shortcut} from the composer`, async ({ + page, + }, testInfo) => { + const agent = await seedMockAgentWorkspace({ + repoPrefix: "chat-find-composer-", + title: "Chat Find from composer", + initialPrompt: "Render the response", + featureValues: { mockAssistantResponse: RESPONSE }, + }); + try { + await agent.client.waitForFinish(agent.agentId, 15_000); + await openAgentRoute(page, agent); + await searchFromComposerWithoutLosingDraft(page, shortcut); + await refocusFindAndReplaceQuery(page, shortcut); + await page.screenshot({ path: testInfo.outputPath("find-from-composer.png") }); + await closeFind(page); + await expectCommandCenterOwnsFindShortcut(page, shortcut); + await fillComposerDraft(page, "Draft after closing Find"); + await expectComposerDraft(page, "Draft after closing Find"); + } finally { + await agent.cleanup(); + } + }); +} + +async function openFindFromComposer(page: Page, shortcut: string) { + await page.keyboard.press(shortcut); + await expect(query(page)).toBeFocused(); +} + +async function searchFromComposerWithoutLosingDraft(page: Page, shortcut: string) { + await fillComposerDraft(page, "Keep this draft"); + await expectComposerFocused(page); + await openFindFromComposer(page, shortcut); + await expectComposerDraft(page, "Keep this draft"); + await query(page).fill("hello world"); + await expectHighlight(page, "hello world"); +} + +async function refocusFindAndReplaceQuery(page: Page, shortcut: string) { + await composerLocator(page).click(); + await expectComposerFocused(page); + await openFindFromComposer(page, shortcut); + await expect(query(page)).toHaveValue("hello world"); + await expectComposerDraft(page, "Keep this draft"); + await page.keyboard.type("a.b"); + await expect(query(page)).toHaveValue("a.b"); + await expectHighlight(page, "a.b"); +} + +async function expectCommandCenterOwnsFindShortcut(page: Page, shortcut: string) { + await openCommandCenter(page); + await page.keyboard.press(shortcut); + await expect(page.getByTestId("command-center-panel")).toBeVisible(); + await expect(query(page)).toHaveCount(0); + await closeCommandCenter(page); +} diff --git a/packages/app/src/agent-stream/chat-find/index.web.tsx b/packages/app/src/agent-stream/chat-find/index.web.tsx index ca9f7537b1..19539cff77 100644 --- a/packages/app/src/agent-stream/chat-find/index.web.tsx +++ b/packages/app/src/agent-stream/chat-find/index.web.tsx @@ -103,8 +103,9 @@ export function ChatFind({ widget.current?.focus(); } }; - document.addEventListener("keydown", onKeyDown); - return () => document.removeEventListener("keydown", onKeyDown); + // React Native TextInput stops bubbling key events, including from the composer. + document.addEventListener("keydown", onKeyDown, true); + return () => document.removeEventListener("keydown", onKeyDown, true); }, [active, isInteractive, model]); const close = useCallback(() => { model.close();