diff --git a/e2e/src/steps/home/starter.steps.ts b/e2e/src/steps/home/starter.steps.ts index 01971680eb..f7a234e5aa 100644 --- a/e2e/src/steps/home/starter.steps.ts +++ b/e2e/src/steps/home/starter.steps.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ /** * Home Starter Steps * @@ -61,7 +62,9 @@ When('用户点击创建 Agent 按钮', async function (this: CustomWorld) { await expect(createAgentButton).toBeVisible({ timeout: WAIT_TIMEOUT }); await createAgentButton.click(); - await this.page.waitForTimeout(500); + + // Wait for mode switch animation and ChatInput scroll-into-view to settle + await this.page.waitForTimeout(800); console.log(' ✅ 已点击创建 Agent 按钮'); }); @@ -76,7 +79,9 @@ When('用户点击创建 Group 按钮', async function (this: CustomWorld) { await expect(createGroupButton).toBeVisible({ timeout: WAIT_TIMEOUT }); await createGroupButton.click(); - await this.page.waitForTimeout(500); + + // Wait for mode switch animation and ChatInput scroll-into-view to settle + await this.page.waitForTimeout(800); console.log(' ✅ 已点击创建 Group 按钮'); }); @@ -89,7 +94,9 @@ When('用户点击写作按钮', async function (this: CustomWorld) { await expect(writeButton).toBeVisible({ timeout: WAIT_TIMEOUT }); await writeButton.click(); - await this.page.waitForTimeout(500); + + // Wait for mode switch animation and ChatInput scroll-into-view to settle + await this.page.waitForTimeout(800); console.log(' ✅ 已点击写作按钮'); }); @@ -97,27 +104,14 @@ When('用户点击写作按钮', async function (this: CustomWorld) { When('用户在输入框中输入 {string}', async function (this: CustomWorld, message: string) { console.log(` 📍 Step: 在输入框中输入 "${message}"...`); - // The chat input is a contenteditable editor, need to click first then type + // The chat input is a contenteditable editor, need to click first then type. + // Target the contenteditable element INSIDE the ChatInput container directly, + // since clicking the container might hit the action bar/footer area instead. const chatInputContainer = this.page.locator('[data-testid="chat-input"]').first(); + await expect(chatInputContainer).toBeVisible({ timeout: WAIT_TIMEOUT }); - // If data-testid not found, try alternative selectors - let inputFound = false; - if ((await chatInputContainer.count()) > 0) { - await chatInputContainer.click(); - inputFound = true; - } else { - // Try to find the editor by its contenteditable attribute - const editor = this.page.locator('[contenteditable="true"]').first(); - if ((await editor.count()) > 0) { - await editor.click(); - inputFound = true; - } - } - - if (!inputFound) { - throw new Error('Could not find chat input'); - } - + const editor = chatInputContainer.locator('[contenteditable="true"]').first(); + await editor.click(); await this.page.waitForTimeout(300); await this.page.keyboard.type(message, { delay: 30 }); @@ -127,8 +121,9 @@ When('用户在输入框中输入 {string}', async function (this: CustomWorld, When('用户按 Enter 发送', { timeout: 30_000 }, async function (this: CustomWorld) { console.log(' 📍 Step: 按 Enter 发送...'); - // Wait for editor's debounced onChange (100ms default) to sync inputMessage to store - // Without this, inputMessage is empty and send() silently returns + // Wait for editor's debounced onChange (100ms default) to sync inputMessage to store. + // The send() function reads directly from the editor as a fallback, but this wait + // ensures maximum reliability. await this.page.waitForTimeout(200); // Listen for navigation to capture the agent/group ID diff --git a/src/app/[variants]/(main)/home/features/InputArea/index.tsx b/src/app/[variants]/(main)/home/features/InputArea/index.tsx index 590b9ad06e..cb7d232b9f 100644 --- a/src/app/[variants]/(main)/home/features/InputArea/index.tsx +++ b/src/app/[variants]/(main)/home/features/InputArea/index.tsx @@ -1,6 +1,6 @@ import { Flexbox } from '@lobehub/ui'; import { AnimatePresence, m as motion } from 'motion/react'; -import { useMemo } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import DragUploadZone, { useUploadFiles } from '@/components/DragUploadZone'; import { type ActionKeys } from '@/features/ChatInput'; @@ -26,6 +26,20 @@ const InputArea = () => { const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill); const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis); const showSkillBanner = isLobehubSkillEnabled || isKlavisEnabled; + const chatInputRef = useRef(null); + + // When a starter mode is activated (e.g. Create Agent / Create Group / Write), + // the SuggestQuestions panel renders below the ChatInput and may push the total + // content height beyond the viewport, causing the ChatInput to scroll out of view. + // Re-focus the editor and scroll it into view so the user can type immediately. + useEffect(() => { + if (!inputActiveMode) return; + + requestAnimationFrame(() => { + chatInputRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + useChatStore.getState().mainInputEditor?.focus(); + }); + }, [inputActiveMode]); // Get agent's model info for vision support check const model = useAgentStore((s) => agentByIdSelectors.getAgentModelById(inboxAgentId)(s)); @@ -66,7 +80,10 @@ const InputArea = () => { return ( - + {showSkillBanner && }