fix: persist canvas chat session selection
This commit is contained in:
parent
8a2483ad7d
commit
f2ed508cb4
@ -4,6 +4,7 @@ import { useChat } from "@ai-sdk/react";
|
||||
import { DefaultChatTransport } from "ai";
|
||||
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { createPortal } from "react-dom";
|
||||
import type { DetailUiProtocol } from "@/lib/detail-ui-protocol";
|
||||
import type { DetailUiScreenReviewPatch } from "@/lib/detail-ui-protocol";
|
||||
@ -1209,6 +1210,16 @@ interface CanvasChatSession {
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
const CANVAS_CHAT_QUERY_PARAM = "chat";
|
||||
|
||||
function normalizeCanvasChatSessionId(value?: string | null) {
|
||||
const raw = String(value || "").trim();
|
||||
if (!raw) return null;
|
||||
|
||||
const rawUuid = raw.startsWith("canvas-") ? raw.slice("canvas-".length) : raw;
|
||||
return isValidChatUuid(rawUuid) ? `canvas-${rawUuid}` : null;
|
||||
}
|
||||
|
||||
export function CanvasChatPanel({
|
||||
chatId,
|
||||
initialMessages = [],
|
||||
@ -1234,6 +1245,13 @@ export function CanvasChatPanel({
|
||||
);
|
||||
const effectiveAgentEnabled = agentEnabled && !isVideoWorkspace;
|
||||
const { data: session } = useSession();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const requestedChatSessionId = useMemo(
|
||||
() => normalizeCanvasChatSessionId(searchParams.get(CANVAS_CHAT_QUERY_PARAM)),
|
||||
[searchParams]
|
||||
);
|
||||
const [input, setInput] = useState("");
|
||||
const [attachments, setAttachments] = useState<PickedObjectAttachment[]>([]);
|
||||
const [selectedModelId, setSelectedModelId] = useState<string>(
|
||||
@ -1299,8 +1317,10 @@ export function CanvasChatPanel({
|
||||
});
|
||||
const [detailModeMessageStartIndex, setDetailModeMessageStartIndex] = useState(0);
|
||||
const canvasId = useMemo(() => toRawCanvasChatId(chatId), [chatId]);
|
||||
const [activeChatId, setActiveChatId] = useState<string>(chatId);
|
||||
const activeChatIdRef = useRef<string>(chatId);
|
||||
const [activeChatId, setActiveChatId] = useState<string>(
|
||||
() => requestedChatSessionId || chatId
|
||||
);
|
||||
const activeChatIdRef = useRef<string>(requestedChatSessionId || chatId);
|
||||
const inFlightChatIdRef = useRef<string | null>(null);
|
||||
const inFlightRawChatIdRef = useRef<string | null>(null);
|
||||
const [chatSessions, setChatSessions] = useState<CanvasChatSession[]>([]);
|
||||
@ -1334,10 +1354,29 @@ export function CanvasChatPanel({
|
||||
const pendingLaunchRef = useRef<CanvasLaunchPayload | null>(null);
|
||||
const [launchAutoSendTick, setLaunchAutoSendTick] = useState(0);
|
||||
const hasInitializedChatStateRef = useRef(false);
|
||||
const [loadedChatSessionStorageKey, setLoadedChatSessionStorageKey] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const hasInitializedVideoWorkspaceRef = useRef(false);
|
||||
const sessionMenuRef = useRef<HTMLDivElement>(null);
|
||||
const historyScrollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const forceHistoryHydrationRef = useRef<string | null>(null);
|
||||
|
||||
const replaceChatSessionQuery = useCallback(
|
||||
(sessionId: string) => {
|
||||
const normalized = normalizeCanvasChatSessionId(sessionId);
|
||||
if (!normalized || typeof window === "undefined") return;
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set(CANVAS_CHAT_QUERY_PARAM, normalized);
|
||||
const query = params.toString();
|
||||
const hash = window.location.hash || "";
|
||||
router.replace(`${pathname}${query ? `?${query}` : ""}${hash}`, {
|
||||
scroll: false,
|
||||
});
|
||||
},
|
||||
[pathname, router]
|
||||
);
|
||||
|
||||
// 🆕 标签悬停预览状态
|
||||
const [tagPreview, setTagPreview] = useState<TagPreviewInfo | null>(null);
|
||||
@ -1657,10 +1696,20 @@ export function CanvasChatPanel({
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
const requestedSession: CanvasChatSession | null =
|
||||
requestedChatSessionId && requestedChatSessionId !== rootChatId
|
||||
? {
|
||||
id: requestedChatSessionId,
|
||||
title: "新对话",
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
: null;
|
||||
const initialChatId = requestedChatSessionId || rootChatId;
|
||||
const sessionStorageKey = getSessionStorageKey(canvasId, chatScope);
|
||||
|
||||
try {
|
||||
const key = getSessionStorageKey(canvasId, chatScope);
|
||||
const saved = localStorage.getItem(key);
|
||||
const saved = localStorage.getItem(sessionStorageKey);
|
||||
const parsed = saved ? (JSON.parse(saved) as CanvasChatSession[]) : [];
|
||||
|
||||
const sanitized = parsed
|
||||
@ -1672,21 +1721,33 @@ export function CanvasChatPanel({
|
||||
updatedAt: Number(session.updatedAt) || now,
|
||||
}));
|
||||
|
||||
const hasCurrent = sanitized.some((session) => session.id === rootChatId);
|
||||
const nextSessions = hasCurrent
|
||||
? sanitized
|
||||
: [fallbackSession, ...sanitized];
|
||||
let nextSessions = sanitized;
|
||||
const requiredSessions = requestedSession
|
||||
? [fallbackSession, requestedSession]
|
||||
: [fallbackSession];
|
||||
for (const requiredSession of requiredSessions) {
|
||||
const hasSession = nextSessions.some(
|
||||
(session) => session.id === requiredSession.id
|
||||
);
|
||||
if (!hasSession) {
|
||||
nextSessions = [requiredSession, ...nextSessions];
|
||||
}
|
||||
}
|
||||
|
||||
const sorted = [...nextSessions].sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
setChatSessions(sorted);
|
||||
setActiveChatId(rootChatId);
|
||||
localStorage.setItem(key, JSON.stringify(sorted));
|
||||
setActiveChatId(initialChatId);
|
||||
localStorage.setItem(sessionStorageKey, JSON.stringify(sorted));
|
||||
setLoadedChatSessionStorageKey(sessionStorageKey);
|
||||
} catch (error) {
|
||||
console.error("[CanvasChat] Failed to load chat sessions:", error);
|
||||
setChatSessions([fallbackSession]);
|
||||
setActiveChatId(rootChatId);
|
||||
setChatSessions(
|
||||
requestedSession ? [requestedSession, fallbackSession] : [fallbackSession]
|
||||
);
|
||||
setActiveChatId(initialChatId);
|
||||
setLoadedChatSessionStorageKey(sessionStorageKey);
|
||||
}
|
||||
}, [canvasId, chatId, chatScope]);
|
||||
}, [canvasId, chatId, chatScope, requestedChatSessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSessionMenuOpen) return;
|
||||
@ -1700,15 +1761,18 @@ export function CanvasChatPanel({
|
||||
}, [isSessionMenuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const sessionStorageKey = getSessionStorageKey(canvasId, chatScope);
|
||||
if (loadedChatSessionStorageKey !== sessionStorageKey) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(
|
||||
getSessionStorageKey(canvasId, chatScope),
|
||||
sessionStorageKey,
|
||||
JSON.stringify(chatSessions)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[CanvasChat] Failed to save chat sessions:", error);
|
||||
}
|
||||
}, [canvasId, chatScope, chatSessions]);
|
||||
}, [canvasId, chatScope, chatSessions, loadedChatSessionStorageKey]);
|
||||
|
||||
// Canvas store
|
||||
const addNode = useCanvasStore((state) => state.addNode);
|
||||
@ -10075,8 +10139,9 @@ export function CanvasChatPanel({
|
||||
releaseSendGuard(activeChatId);
|
||||
setChatSessions((prevSessions) => [newSession, ...prevSessions]);
|
||||
setActiveChatId(newChatSessionId);
|
||||
replaceChatSessionQuery(newChatSessionId);
|
||||
toast.success("已新建聊天窗口");
|
||||
}, [activeChatId, chatSessions.length, releaseSendGuard]);
|
||||
}, [activeChatId, chatSessions.length, releaseSendGuard, replaceChatSessionQuery]);
|
||||
|
||||
const switchToSession = useCallback((sessionId: string) => {
|
||||
if (!sessionId || sessionId === activeChatId) {
|
||||
@ -10086,9 +10151,10 @@ export function CanvasChatPanel({
|
||||
forceHistoryHydrationRef.current = toRawCanvasChatId(sessionId);
|
||||
releaseSendGuard(activeChatId);
|
||||
setActiveChatId(sessionId);
|
||||
replaceChatSessionQuery(sessionId);
|
||||
setIsSessionMenuOpen(false);
|
||||
toast.success("已切换聊天窗口");
|
||||
}, [activeChatId, releaseSendGuard]);
|
||||
}, [activeChatId, releaseSendGuard, replaceChatSessionQuery]);
|
||||
|
||||
const activeSessionTitle = useMemo(() => {
|
||||
const current = chatSessions.find((session) => session.id === activeChatId);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user