diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 96e36c683..bb3c104b3 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -2432,6 +2432,33 @@ export function CanvasChatPanel({ ); }, [getSendGuard]); + const persistChatSessions = useCallback( + (sessions: CanvasChatSession[]) => { + const sessionStorageKey = getSessionStorageKey(canvasId, chatScope); + try { + localStorage.setItem(sessionStorageKey, JSON.stringify(sessions)); + setLoadedChatSessionStorageKey(sessionStorageKey); + } catch (error) { + console.error("[CanvasChat] Failed to save chat sessions:", error); + } + }, + [canvasId, chatScope] + ); + + const updateChatSessions = useCallback( + (updater: (previousSessions: CanvasChatSession[]) => CanvasChatSession[]) => { + setChatSessions((previousSessions) => { + const nextSessions = updater(previousSessions); + if (nextSessions === previousSessions) { + return previousSessions; + } + persistChatSessions(nextSessions); + return nextSessions; + }); + }, + [persistChatSessions] + ); + const startNewChatSession = useCallback( (options?: { mode?: CanvasComposerMode; @@ -2462,13 +2489,19 @@ export function CanvasChatPanel({ forceHistoryHydrationRef.current = null; releaseSendGuard(activeChatId); - setChatSessions((prevSessions) => [newSession, ...prevSessions]); + updateChatSessions((prevSessions) => [newSession, ...prevSessions]); setActiveChatId(newChatSessionId); replaceChatSessionQuery(newChatSessionId); toast.success(options?.toastMessage || "已新建聊天窗口"); return newChatSessionId; }, - [activeChatId, chatSessions, releaseSendGuard, replaceChatSessionQuery] + [ + activeChatId, + chatSessions, + releaseSendGuard, + replaceChatSessionQuery, + updateChatSessions, + ] ); const setSessionClawdSkillTag = useCallback( @@ -2479,11 +2512,12 @@ export function CanvasChatPanel({ ) => { if (!chatSessionId) return; const normalizedTagId = String(tagId || "").trim() || null; - setChatSessions((prevSessions) => + updateChatSessions((prevSessions) => prevSessions.map((session) => session.id === chatSessionId ? { ...session, + mode: "clawd", clawdSkillTagId: normalizedTagId, clawdSkillTagPromptApplied: normalizedTagId === null @@ -2495,17 +2529,18 @@ export function CanvasChatPanel({ ) ); }, - [] + [updateChatSessions] ); const markSessionClawdSkillTagPromptApplied = useCallback( (chatSessionId: string, tagId: string) => { if (!chatSessionId || !tagId) return; - setChatSessions((prevSessions) => + updateChatSessions((prevSessions) => prevSessions.map((session) => session.id === chatSessionId && session.clawdSkillTagId === tagId ? { ...session, + mode: "clawd", clawdSkillTagPromptApplied: true, updatedAt: Date.now(), } @@ -2513,7 +2548,7 @@ export function CanvasChatPanel({ ) ); }, - [] + [updateChatSessions] ); const handleTypewriterActiveChange = useCallback((typingKey: string, active: boolean) => { @@ -4510,12 +4545,84 @@ export function CanvasChatPanel({ activeChatSession, activeComposerMode ); + useEffect(() => { + if (activeChatSessionMode !== "clawd") return; + if (!isClawdModeEnabled) return; + if (activeComposerMode !== "clawd") { + setComposerModeOverride("clawd"); + } + if (isDetailGenerationMode) { + setIsDetailGenerationMode(false); + } + }, [ + activeChatSessionMode, + activeComposerMode, + isClawdModeEnabled, + isDetailGenerationMode, + ]); + + useEffect(() => { + if (!activeChatId) return; + const historyMessages = historyData?.messages || []; + if (historyMessages.length === 0) return; + if (!historyMessages.some(isClawdChatMessage)) return; + + updateChatSessions((prevSessions) => { + const currentIndex = prevSessions.findIndex( + (session) => session.id === activeChatId + ); + const now = Date.now(); + + if (currentIndex < 0) { + const title = + deriveSessionTitleFromMessages(historyMessages) || `${CLAW_MODE_LABEL} 对话`; + return [ + { + id: activeChatId, + title, + mode: "clawd", + createdAt: now, + updatedAt: now, + }, + ...prevSessions, + ]; + } + + const current = prevSessions[currentIndex]; + if (getCanvasChatSessionMode(current) === "clawd") { + return prevSessions; + } + + const nextSessions = [...prevSessions]; + nextSessions[currentIndex] = { + ...current, + mode: "clawd", + updatedAt: Math.max(Number(current.updatedAt) || 0, now), + }; + nextSessions.sort((a, b) => b.updatedAt - a.updatedAt); + return nextSessions; + }); + + if (isClawdModeEnabled && activeComposerMode !== "clawd") { + setComposerModeOverride("clawd"); + if (isDetailGenerationMode) { + setIsDetailGenerationMode(false); + } + } + }, [ + activeChatId, + activeComposerMode, + historyData?.messages, + isClawdModeEnabled, + isDetailGenerationMode, + updateChatSessions, + ]); const currentModeChatSessions = useMemo( () => chatSessions.filter( - (session) => getCanvasChatSessionMode(session) === activeComposerMode + (session) => getCanvasChatSessionMode(session) === activeChatSessionMode ), - [activeComposerMode, chatSessions] + [activeChatSessionMode, chatSessions] ); const agentSelectableModels = useMemo( () => @@ -5319,7 +5426,7 @@ export function CanvasChatPanel({ if (canReuseActiveEmptySession) { forceHistoryHydrationRef.current = null; targetChatSessionId = activeChatId; - setChatSessions((prevSessions) => + updateChatSessions((prevSessions) => prevSessions.map((session) => session.id === activeChatId ? { @@ -5404,6 +5511,7 @@ export function CanvasChatPanel({ releaseSendGuard, replaceChatSessionQuery, startNewChatSession, + updateChatSessions, ] ); @@ -7216,7 +7324,7 @@ export function CanvasChatPanel({ const derivedTitle = deriveSessionTitleFromMessages(messages); const now = Date.now(); - setChatSessions((prevSessions) => { + updateChatSessions((prevSessions) => { const currentIndex = prevSessions.findIndex( (session) => session.id === activeChatId ); @@ -7252,7 +7360,7 @@ export function CanvasChatPanel({ nextSessions.sort((a, b) => b.updatedAt - a.updatedAt); return nextSessions; }); - }, [activeChatId, messages]); + }, [activeChatId, messages, updateChatSessions]); useEffect(() => { if (activeChatSessionMode === "clawd") return; @@ -10550,7 +10658,7 @@ export function CanvasChatPanel({ }, } as ChatMessage; - setChatSessions((prevSessions) => + updateChatSessions((prevSessions) => prevSessions.map((session) => session.id === activeChatId ? { @@ -11096,6 +11204,7 @@ export function CanvasChatPanel({ status, mutate, markSessionClawdSkillTagPromptApplied, + updateChatSessions, buildHistoryMessagesUrl, workflowContext, messages, @@ -12473,8 +12582,8 @@ export function CanvasChatPanel({ ); const createNewChatSession = useCallback(() => { - startNewChatSession({ mode: activeComposerMode }); - }, [activeComposerMode, startNewChatSession]); + startNewChatSession({ mode: activeChatSessionMode }); + }, [activeChatSessionMode, startNewChatSession]); const switchToSession = useCallback((sessionId: string) => { if (!sessionId || sessionId === activeChatId) {