From 875cfa7e7b52806d338d8f736b131c44658ce7a9 Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Wed, 1 Apr 2026 15:10:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A8=B3=E5=AE=9A=20/new=20=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E5=A4=8D=E7=94=A8=E9=80=BB=E8=BE=91=E5=B9=B6=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E4=BC=9A=E8=AF=9D=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/workspace/chats/[thread_id]/page.tsx | 14 +++++++--- .../workspace/chats/use-thread-chat.ts | 27 ++++++++++++------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index e8be7565..c812ffb1 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -66,7 +66,10 @@ export default function ChatPage() { !isNewThread || searchParams.get("xclaw_used")?.trim().toLowerCase() === "true"; - // Submission strategy: + // Submission strategy: always reuse the thread id from query; never create new. + const createNewSession = false; + /* + // Original strategy: // - isnew=false + thread_id: reuse existing thread (explicit request from URL) // - xclaw_used=true: follow `isnew` (isnew=false => reuse existing thread) // - otherwise: create/start a new session (no history) @@ -86,6 +89,7 @@ export default function ChatPage() { } return searchParams.get("isnew")?.trim().toLowerCase() !== "false"; }, [isNewThread, searchParams]); + */ const streamThreadId = useMemo(() => { return isNewThread && createNewSession ? undefined : threadId; }, [createNewSession, isNewThread, threadId]); @@ -105,7 +109,8 @@ export default function ChatPage() { onStart: (currentThreadId) => { setIsNewThread(false); // Keep /new in history so router.back() can return to it. - history.pushState(null, "", pathOfThread(currentThreadId)); + router.replace(`/workspace/chats/${currentThreadId}`); + // history.pushState(null, "", pathOfThread(currentThreadId)); }, onFinish: (state) => { if (document.hidden || !document.hasFocus()) { @@ -226,6 +231,9 @@ export default function ChatPage() { setArtifactsOpen, setIsNewThread, ]); + // shouldRenderHistory || historyCutoff === null + // console.log('shouldRenderHistory', shouldRenderHistory, 'historyCutoff', historyCutoff); + return ( @@ -497,7 +505,7 @@ export default function ChatPage() { XClawUsed: false, }); resetNewSessionState(); - // 因为threadId可能为undefined,所以这里不直接导航到 /workspace/chats/new,而是通过 replace 的方式更新 URL 参数,保持在当前页面,触发 useThreadChat 重新计算状态。 + // 始终复用 query 中的 thread_id。 const nextQuery = new URLSearchParams(); nextQuery.set("isnew", "false"); nextQuery.set("xclaw_used", "false"); diff --git a/frontend/src/components/workspace/chats/use-thread-chat.ts b/frontend/src/components/workspace/chats/use-thread-chat.ts index d5fc4024..c4f68868 100644 --- a/frontend/src/components/workspace/chats/use-thread-chat.ts +++ b/frontend/src/components/workspace/chats/use-thread-chat.ts @@ -7,6 +7,7 @@ export function useThreadChat() { const pathname = usePathname(); const router = useRouter(); const params = useParams<{ thread_id?: string }>(); + // 兜底:当 params 还未就绪时,从 pathname 解析 thread_id。 const threadIdFromPathname = (() => { const parts = pathname.split("?")[0]?.split("/") ?? []; const idx = parts.lastIndexOf("chats"); @@ -15,11 +16,14 @@ export function useThreadChat() { } return undefined; })(); - const threadIdFromPath = params?.thread_id !== 'new' ? params?.thread_id : threadIdFromPathname; - console.log("[useThreadChat] pathname", pathname); - console.log("[useThreadChat] params.thread_id", params?.thread_id); - console.log("[useThreadChat] threadIdFromPathname", threadIdFromPathname); - console.log("[useThreadChat] threadIdFromPath", threadIdFromPath); + // 优先使用 params;如果是 "new",则回退到 pathname 解析出的 id。 + const threadIdFromPath = + params?.thread_id !== "new" ? params?.thread_id : threadIdFromPathname; + // console.log("[useThreadChat] pathname", pathname); + // console.log("[useThreadChat] params.thread_id", params?.thread_id); + // console.log("[useThreadChat] threadIdFromPathname", threadIdFromPathname); + // console.log("[useThreadChat] threadIdFromPath", threadIdFromPath); + // 持久化兜底:用于处理首屏水合或 params 时序问题。 const readStoredThreadId = () => { if (typeof window === "undefined") { return undefined; @@ -29,6 +33,7 @@ export function useThreadChat() { }; const searchParams = useSearchParams(); + // 读取 query 的 thread_id(先用 hook,必要时用 window 兜底)。 const readQueryThreadId = () => { const fromHook = searchParams.get("thread_id")?.trim(); if (fromHook && fromHook !== "new") { @@ -45,8 +50,10 @@ export function useThreadChat() { } return undefined; }; + const queryThreadIdFromParams = readQueryThreadId(); - console.log("[useThreadChat] query.thread_id", queryThreadIdFromParams); + // console.log("[useThreadChat] query.thread_id", queryThreadIdFromParams); + // 归一化:当值为 "new" 时,替换为 query 中的 thread_id(如果存在)。 const normalizeThreadId = (value?: string | null) => { if (!value) { return undefined; @@ -58,17 +65,19 @@ export function useThreadChat() { searchParams.get("isnew")?.trim().toLowerCase() === "false"; const effectiveThreadIdFromPath = normalizeThreadId(threadIdFromPath) ?? readStoredThreadId(); - console.log("[useThreadChat] effectiveThreadIdFromPath", effectiveThreadIdFromPath); + // console.log("[useThreadChat] effectiveThreadIdFromPath", effectiveThreadIdFromPath); const [threadId, setThreadId] = useState(() => { return effectiveThreadIdFromPath ?? undefined; }); + // /new 或缺少 query 的 thread_id 时,视为新会话状态。 但是这个并不是新会话的意思,而是说当前处在对话状态。 const [isNewThread, setIsNewThread] = useState( - () => threadIdFromPath === "new", + () => threadIdFromPath === "new" || !queryThreadIdFromParams, ); useEffect(() => { + // 记住最近一次有效的 thread_id,供下次加载兜底使用。 if (threadId && threadId !== "new" && typeof window !== "undefined") { window.sessionStorage.setItem("workspace.thread_id", threadId); } @@ -82,7 +91,7 @@ export function useThreadChat() { return; } setIsNewThread(false); - console.log("threadIdFromPath", threadIdFromPath, "normalized", normalizeThreadId(threadIdFromPath)); + // console.log("threadIdFromPath", threadIdFromPath, "normalized", normalizeThreadId(threadIdFromPath)); setThreadId(normalizeThreadId(threadIdFromPath)); }, [pathname, router, searchParams, threadIdFromPath]);