From 87b73e2b0834bf5b1196d988cc3f73a387610fae Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Thu, 9 Apr 2026 13:29:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20=E8=BF=9B=E5=85=A5/new?= =?UTF-8?q?=E9=A2=84=E5=88=9B=E5=BB=BA=E4=BC=9A=E8=AF=9D=E5=B9=B6=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E8=B7=B3=E8=BD=AC=E8=81=8A=E5=A4=A9=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/workspace/chats/[thread_id]/page.tsx | 59 +++++++++++++++++-- .../components/workspace/workspace-header.tsx | 2 +- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index a538fb90..c87b55c2 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -2,7 +2,8 @@ import { FilesIcon, ListTodoIcon, XIcon } from "lucide-react"; import { useRouter } from "next/navigation"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { toast } from "sonner"; import { ConversationEmptyState } from "@/components/ai-elements/conversation"; import { Button } from "@/components/ui/button"; @@ -28,6 +29,7 @@ import { ThreadTitle } from "@/components/workspace/thread-title"; import { Tooltip } from "@/components/workspace/tooltip"; import { useSpecificChatMode } from "@/components/workspace/use-chat-mode"; import { Welcome } from "@/components/workspace/welcome"; +import { getAPIClient } from "@/core/api"; import { useI18n } from "@/core/i18n/hooks"; import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages"; import { useNotification } from "@/core/notification/hooks"; @@ -66,13 +68,18 @@ export default function ChatPage() { // 新逻辑:历史渲染和新会话仅由路由 /chats/new 控制,不再读取 isnew/is_chatting 参数。 const shouldRenderHistory = !showWelcomeStyle; - const createNewSession = useMemo(() => isNewThread, [isNewThread]); const safeThreadId = useMemo(() => { if (!threadId || threadId === "new") { return undefined; } return threadId; }, [threadId]); + // `/new` + `thread_id` now reuses the pre-created thread, instead of creating + // a new session on first submit. + const createNewSession = useMemo( + () => isNewThread && !safeThreadId, + [isNewThread, safeThreadId], + ); const streamThreadId = useMemo(() => { if (isNewThread && createNewSession) { @@ -80,9 +87,38 @@ export default function ChatPage() { } return safeThreadId; }, [createNewSession, isNewThread, safeThreadId]); + const apiClient = useMemo(() => getAPIClient(isMock), [isMock]); + const warnedMissingThreadIdRef = useRef(false); + const initializedThreadRef = useRef(null); const { showNotification } = useNotification(); + useEffect(() => { + if (!isNewThread) { + warnedMissingThreadIdRef.current = false; + return; + } + if (!safeThreadId) { + if (!warnedMissingThreadIdRef.current) { + warnedMissingThreadIdRef.current = true; + toast.error("缺少 thread_id,无法创建会话"); + } + return; + } + warnedMissingThreadIdRef.current = false; + if (initializedThreadRef.current === safeThreadId) return; + initializedThreadRef.current = safeThreadId; + void apiClient.threads + .create({ + threadId: safeThreadId, + ifExists: "do_nothing", + }) + .catch(() => { + initializedThreadRef.current = null; + toast.error("会话创建失败,请稍后重试"); + }); + }, [apiClient, isNewThread, safeThreadId]); + // 监听宿主页 selectedSkill 消息 const { skillError: selectedSkillError, @@ -205,10 +241,24 @@ export default function ChatPage() { if (isSelectedSkillBootstrapping) { return; } + if (isNewThread && !safeThreadId) { + toast.error("缺少 thread_id,无法发送消息"); + return; + } setHasSubmitted(true); + if (safeThreadId && (isNewThread || showWelcomeStyle)) { + router.replace(`/workspace/chats/${safeThreadId}?is_chatting=true`); + } void sendMessage(safeThreadId, message); }, - [isSelectedSkillBootstrapping, safeThreadId, sendMessage], + [ + isNewThread, + isSelectedSkillBootstrapping, + router, + safeThreadId, + sendMessage, + showWelcomeStyle, + ], ); const handleStop = useCallback(async () => { await thread.stop(); @@ -456,7 +506,8 @@ export default function ChatPage() { disabled={ env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" || isSelectedSkillBootstrapping || - isUploading + isUploading || + (isNewThread && !safeThreadId) } onContextChange={(context) => setSettings("context", context)} onSubmit={handleSubmit} diff --git a/frontend/src/components/workspace/workspace-header.tsx b/frontend/src/components/workspace/workspace-header.tsx index 546c1fa5..25bb408c 100644 --- a/frontend/src/components/workspace/workspace-header.tsx +++ b/frontend/src/components/workspace/workspace-header.tsx @@ -43,7 +43,7 @@ export function WorkspaceHeader({ className }: { className?: string }) { ) : (
{/* TODO: 测试标识 */} - XClaw v3.2.1 dev: 给通信面板加收起按钮 + XClaw v3.2.1 fix(frontend): 进入/new预创建会话并强制跳转聊天态
)}