"use client"; import { useParams, usePathname, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; import { resolveThreadQueryIntent } from "@/core/threads/utils"; export function useThreadChat() { const pathname = usePathname(); const params = useParams<{ thread_id?: string }>(); // 兜底:当 params 还未就绪时,从 pathname 解析 thread_id。 const threadIdFromPathname = (() => { const parts = pathname.split("?")[0]?.split("/") ?? []; const idx = parts.lastIndexOf("chats"); if (idx >= 0 && parts.length > idx + 1) { return parts[idx + 1]; } return undefined; })(); const rawPathThreadId = params?.thread_id ?? threadIdFromPathname; const isNewRoute = rawPathThreadId === "new"; const threadIdFromPath = isNewRoute ? undefined : rawPathThreadId; // 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; } const stored = window.sessionStorage.getItem("workspace.thread_id"); return isValidThreadId(stored) ? stored : undefined; }; const searchParams = useSearchParams(); // 读取 query 的 thread_id(先用 hook,必要时用 window 兜底)。 const readQueryThreadId = () => { const fromHook = searchParams.get("thread_id")?.trim(); if (isValidThreadId(fromHook)) { return fromHook; } if (typeof window === "undefined") { return undefined; } const fromLocation = new URLSearchParams(window.location.search).get( "thread_id", ); if (isValidThreadId(fromLocation)) { return fromLocation.trim(); } return undefined; }; const queryThreadIdFromParams = readQueryThreadId(); // console.log("[useThreadChat] query.thread_id", queryThreadIdFromParams); // 归一化:当值为 "new" 时,替换为 query 中的 thread_id(如果存在)。 const normalizeThreadId = useCallback( (value?: string | null) => { if (!value) { return undefined; } return value === "new" ? queryThreadIdFromParams : value; }, [queryThreadIdFromParams], ); const intent = resolveThreadQueryIntent({ pathThreadId: threadIdFromPath, queryThreadId: queryThreadIdFromParams, isNewRoute, }); const { isNewThread: isNewRequested, showWelcomeStyle, invalidNewRoute } = intent; const effectiveThreadIdFromPath = invalidNewRoute ? undefined : normalizeThreadId(threadIdFromPath) ?? (isNewRoute ? undefined : readStoredThreadId()); // console.log("[useThreadChat] effectiveThreadIdFromPath", effectiveThreadIdFromPath); const [threadId, setThreadId] = useState(() => { return effectiveThreadIdFromPath ?? undefined; }); // New session is only controlled by `/workspace/chats/new`. const [isNewThread, setIsNewThread] = useState(() => isNewRequested); useEffect(() => { // 记住最近一次有效的 thread_id,供下次加载兜底使用。 if (threadId && threadId !== "new" && typeof window !== "undefined") { window.sessionStorage.setItem("workspace.thread_id", threadId); } setIsNewThread(isNewRoute); // Prefer path thread id, fall back to query thread_id when path is /new. setThreadId( invalidNewRoute ? undefined : normalizeThreadId(threadIdFromPath), ); }, [ invalidNewRoute, isNewRoute, normalizeThreadId, pathname, searchParams, threadId, threadIdFromPath, ]); const isMock = searchParams.get("mock") === "true"; return { threadId, isNewThread, setIsNewThread, isMock, showWelcomeStyle, invalidNewRoute, }; } function isValidThreadId(value?: string | null): value is string { if (!value) return false; const normalized = value.trim().toLowerCase(); return ( normalized.length > 0 && normalized !== "new" && normalized !== "undefined" && normalized !== "null" ); }