From cd2a41b8a69278d74a5e1823f4c1b62745af1bab Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Fri, 10 Apr 2026 17:19:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E5=8C=BA=E8=BE=93=E5=85=A5=E6=A1=86=E4=B8=8E=20artifa?= =?UTF-8?q?cts=20=E5=B1=95=E7=A4=BA=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改进工作区核心交互,提升输入与结果查看的一致性和可用性。 调整 prompt 输入相关组件逻辑,优化输入行为与状态反馈 更新 workspace input-box 交互细节,改善可用性与稳定性 优化 message-group 展示逻辑,增强消息区域可读性 调整 artifact-file-detail 预览相关实现,为后续 Office 文件展示做准备 补充并更新 thread-routing e2e 用例,覆盖关键路由与交互回归场景 --- .../components/ai-elements/prompt-input.tsx | 12 ++++++----- .../artifacts/artifact-file-detail.tsx | 4 ++-- .../src/components/workspace/input-box.tsx | 1 - .../workspace/messages/message-group.tsx | 17 +++++++++++---- frontend/tests/e2e/thread-routing.spec.ts | 21 +++++++++++++++++++ 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/ai-elements/prompt-input.tsx b/frontend/src/components/ai-elements/prompt-input.tsx index ddfe967e..682b804e 100644 --- a/frontend/src/components/ai-elements/prompt-input.tsx +++ b/frontend/src/components/ai-elements/prompt-input.tsx @@ -883,7 +883,7 @@ export const PromptInputTextarea = ({ if (!submitOnEnter) { return; } - if (e.shiftKey) { + if (e.shiftKey || e.ctrlKey || e.metaKey) { return; } e.preventDefault(); @@ -1089,10 +1089,12 @@ export const PromptInputSubmit = ({ controller.attachments.files.length > 0 : false; - // 正在 streaming 时不允许发送 - const isStreaming = status === "streaming" || status === "submitted"; - - const isDisabled = disabled || !hasContent || isStreaming; + const isStreaming = status === "streaming"; + const isSubmitted = status === "submitted"; + // Streaming 时按钮用于停止,不受输入内容是否为空限制 + const isDisabled = isStreaming + ? !!disabled + : disabled || !hasContent || isSubmitted; let Icon = ; diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index db59d746..388e32cc 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -95,8 +95,8 @@ export function ArtifactFileDetail({ return checkCodeFile(filepath); }, [filepath, isWriteFile, isSkillFile]); const previewable = useMemo(() => { - return (language === "html" && !isWriteFile) || language === "markdown"; - }, [isWriteFile, language]); + return language === "html" || language === "markdown"; + }, [language]); const artifactUrl = useMemo(() => { if (!threadId) { return ""; diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx index df181b56..fe69aaf1 100644 --- a/frontend/src/components/workspace/input-box.tsx +++ b/frontend/src/components/workspace/input-box.tsx @@ -342,7 +342,6 @@ export function InputBox({ "size-full", !effectiveIsFocused && "h-[80px] py-0 leading-20", )} - submitOnEnter={false} disabled={isInputDisabled} placeholder={t.inputBox.placeholder} autoFocus={autoFocus} diff --git a/frontend/src/components/workspace/messages/message-group.tsx b/frontend/src/components/workspace/messages/message-group.tsx index 45c57211..53a4a101 100644 --- a/frontend/src/components/workspace/messages/message-group.tsx +++ b/frontend/src/components/workspace/messages/message-group.tsx @@ -76,6 +76,9 @@ export function MessageGroup({ return filteredSteps[filteredSteps.length - 1]; } }, [lastToolCallStep, steps]); + const totalToolStepCount = aboveLastToolCallSteps.length + (lastToolCallStep ? 1 : 0); + const shouldShowToolSteps = !!lastToolCallStep && + (showAbove || aboveLastToolCallSteps.length === 0); const rehypePlugins = useRehypeSplitWordsIntoSpans(isLoading); return ( setShowAbove(!showAbove)} + onClick={(event) => { + event.stopPropagation(); + setShowAbove((prev) => !prev); + }} > {showAbove ? t.toolCalls.lessSteps - : t.toolCalls.moreSteps(aboveLastToolCallSteps.length)} + : t.toolCalls.moreSteps(totalToolStepCount)} } icon={ @@ -108,7 +114,7 @@ export function MessageGroup({ > )} - {lastToolCallStep && ( + {shouldShowToolSteps && ( {showAbove && aboveLastToolCallSteps.map((step) => @@ -145,7 +151,10 @@ export function MessageGroup({ key={lastReasoningStep.id} className="w-full items-start justify-start text-left" variant="ghost" - onClick={() => setShowLastThinking(!showLastThinking)} + onClick={(event) => { + event.stopPropagation(); + setShowLastThinking((prev) => !prev); + }} >
{ { timeout: 30_000 }, ); }); + + test("streaming 中点击停止可中断输出", async ({ page }) => { + const threadId = uuid(); + const text = + "请逐行输出 1 到 500 的数字,并在每一行前面加上“第N行:”前缀,不要省略。"; + + await openChat(page, newChatEntry(threadId)); + await expect(page.getByTestId("welcome-suggestions")).toBeVisible(); + + await sendMessage(page, text); + + const submitButton = page.locator("button[aria-label='Submit']"); + + await expect(submitButton).toHaveText("停止", { timeout: 30_000 }); + await expect(submitButton).toBeEnabled(); + + await submitButton.click(); + + // 点击停止后应退出 streaming 态,按钮文本不再是“停止” + await expect(submitButton).toHaveText("发送", { timeout: 30_000 }); + }); });