From a08944835d9bf442ae3c70bf92e3030634f0e5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dimitri=20Lavren=C3=BCk?= <20122620+dlavrenuek@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:32:41 +0100 Subject: [PATCH] feat(ai-builder): Add autofocus for input field (#22919) --- .../AskAssistantChat/AskAssistantChat.vue | 2 ++ .../AskAssistantChat.test.ts.snap | 7 ++++++ .../N8nPromptInput/N8nPromptInput.test.ts | 24 +++++++++++++++++++ .../N8nPromptInput/N8nPromptInput.vue | 6 +++++ 4 files changed, 39 insertions(+) diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue index 5300615fb15..b2d35f4c157 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue @@ -499,6 +499,7 @@ defineExpose({ :max-length="maxCharacterLength" :min-lines="2" data-test-id="chat-suggestions-input" + autofocus @upgrade-click="emit('upgrade-click')" @submit="onSendMessage" @stop="emit('stop')" @@ -549,6 +550,7 @@ defineExpose({ :max-length="maxCharacterLength" :refocus-after-send="true" data-test-id="chat-input" + autofocus @upgrade-click="emit('upgrade-click')" @submit="onSendMessage" @stop="emit('stop')" diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap index 8e65281bc53..08f8d6fb67a 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap @@ -86,6 +86,7 @@ exports[`AskAssistantChat > limits maximum input length when maxCharacterLength data-test-id="chat-input-wrapper" > renders chat with messages correctly 1`] = ` data-test-id="chat-input-wrapper" > renders default placeholder chat correctly 1`] = ` data-test-id="chat-input-wrapper" > renders end of session chat correctly 1`] = ` data-test-id="chat-input-wrapper" > renders error message correctly with retry button 1` data-test-id="chat-input-wrapper" > renders message with code snippet 1`] = ` data-test-id="chat-input-wrapper" > renders streaming chat correctly 1`] = ` data-test-id="chat-input-wrapper" > { wrapper.unmount(); }); }); + + describe('autofocus', () => { + it('should be focused if enabled', async () => { + const { emitted } = renderComponent({ + props: { + autofocus: true, + }, + global: { + stubs: ['N8nCallout', 'N8nScrollArea', 'N8nSendStopButton'], + }, + }); + + expect(emitted('focus')).toBeTruthy(); + }); + + it('should not be focused if disabled', () => { + const { emitted } = renderComponent({ + global: { + stubs: ['N8nCallout', 'N8nScrollArea', 'N8nSendStopButton'], + }, + }); + expect(emitted('focus')).toBeFalsy(); + }); + }); }); diff --git a/packages/frontend/@n8n/design-system/src/components/N8nPromptInput/N8nPromptInput.vue b/packages/frontend/@n8n/design-system/src/components/N8nPromptInput/N8nPromptInput.vue index d965aef99f6..f5562d5a07f 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nPromptInput/N8nPromptInput.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nPromptInput/N8nPromptInput.vue @@ -22,6 +22,7 @@ export interface N8nPromptInputProps { creditsRemaining?: number; showAskOwnerTooltip?: boolean; refocusAfterSend?: boolean; + autofocus?: boolean; } const INFINITE_CREDITS = -1; @@ -38,6 +39,7 @@ const props = withDefaults(defineProps(), { creditsRemaining: undefined, showAskOwnerTooltip: false, refocusAfterSend: false, + autofocus: false, }); const emit = defineEmits<{ @@ -314,6 +316,10 @@ function focusInput() { onMounted(() => { // Adjust height on mount to respect minLines or initial content void nextTick(() => adjustHeight()); + + if (props.autofocus) { + focusInput(); + } }); defineExpose({