diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 8e8c62365..c3bf1eee6 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -364,6 +364,8 @@ function normalizeClawdComposerText(value: string) { type ClawdComposerSubmissionSnapshot = { chatId: string; + canvasId?: string; + chatScope?: "canvas" | "agent"; expiresAt: number; startedAt: number; submittedTexts: string[]; @@ -387,6 +389,11 @@ function parseClawdComposerSubmissionSnapshot( if (!value || typeof value !== "object") return null; const snapshot = value as Partial; const chatId = String(snapshot.chatId || "").trim(); + const canvasId = String(snapshot.canvasId || "").trim(); + const chatScope = + snapshot.chatScope === "canvas" || snapshot.chatScope === "agent" + ? snapshot.chatScope + : undefined; const startedAt = Number(snapshot.startedAt); const expiresAt = Number(snapshot.expiresAt); const submittedTexts = Array.isArray(snapshot.submittedTexts) @@ -404,6 +411,8 @@ function parseClawdComposerSubmissionSnapshot( return { chatId, + canvasId: canvasId || undefined, + chatScope, expiresAt, startedAt, submittedTexts, @@ -2192,6 +2201,7 @@ export function CanvasChatPanel({ }, [input]); const clearComposerEditor = useCallback(() => { + inputRef.current = ""; setInput(""); const editableDiv = textareaRef.current as HTMLDivElement | null; if (editableDiv) { @@ -2223,11 +2233,27 @@ export function CanvasChatPanel({ return recentSubmission; }, []); + const isClawdSubmissionForCurrentComposer = useCallback( + (submissionSnapshot: ClawdComposerSubmissionSnapshot) => { + if (submissionSnapshot.chatId === activeChatIdRef.current) return true; + + if (submissionSnapshot.canvasId && submissionSnapshot.canvasId !== canvasId) { + return false; + } + if (submissionSnapshot.chatScope && submissionSnapshot.chatScope !== chatScope) { + return false; + } + + return Boolean(submissionSnapshot.canvasId || submissionSnapshot.chatScope); + }, + [canvasId, chatScope] + ); + const clearRestoredClawdComposerText = useCallback(() => { const submissionSnapshot = getActiveClawdSubmissionSnapshot(); const editableDiv = textareaRef.current as HTMLDivElement | null; if (!submissionSnapshot || !editableDiv) return; - if (submissionSnapshot.chatId !== activeChatIdRef.current) return; + if (!isClawdSubmissionForCurrentComposer(submissionSnapshot)) return; if (composerUserEditedAtRef.current > submissionSnapshot.startedAt) return; const editorText = normalizeClawdComposerText( @@ -2252,7 +2278,11 @@ export function CanvasChatPanel({ if (!editorWasRestored && !stateWasRestored) return; clearComposerEditor(); - }, [clearComposerEditor, getActiveClawdSubmissionSnapshot]); + }, [ + clearComposerEditor, + getActiveClawdSubmissionSnapshot, + isClawdSubmissionForCurrentComposer, + ]); useEffect(() => { if (typeof window === "undefined") return; @@ -6157,7 +6187,12 @@ export function CanvasChatPanel({ if (!historyData || !rawChatId) return; const shouldForceHydrate = forceHistoryHydrationRef.current === rawChatId; - if ((status === "streaming" || status === "submitted") && !shouldForceHydrate) return; + if ( + (status === "streaming" || status === "submitted" || isClawdSubmitting) && + !shouldForceHydrate + ) { + return; + } const historyMessages = historyData.messages || []; const signature = buildCanvasHistoryMessagesSignature(rawChatId, historyMessages); @@ -6187,7 +6222,7 @@ export function CanvasChatPanel({ } }; } - }, [historyData, rawChatId, setMessages, status]); + }, [historyData, isClawdSubmitting, rawChatId, setMessages, status]); useEffect(() => { const streamState = streamImageGenerationStateByChatId[activeChatId]; @@ -9573,6 +9608,8 @@ export function CanvasChatPanel({ const clawdSubmissionStartedAt = Date.now(); const clawdSubmissionSnapshot: ClawdComposerSubmissionSnapshot = { chatId: activeChatId, + canvasId, + chatScope, expiresAt: clawdSubmissionStartedAt + CLAWD_SUBMITTED_COMPOSER_RECOVERY_TTL_MS, startedAt: clawdSubmissionStartedAt, @@ -10001,6 +10038,8 @@ export function CanvasChatPanel({ activeComposerMode, agentEntryMode, agentRequiredUploadCount, + canvasId, + chatScope, rawChatId, input, attachments, @@ -13454,7 +13493,7 @@ export function CanvasChatPanel({ getActiveClawdSubmissionSnapshot(); const isRestoredSubmittedClawdText = activeClawdSubmission !== null && - activeClawdSubmission.chatId === activeChatIdRef.current && + isClawdSubmissionForCurrentComposer(activeClawdSubmission) && isClawdSubmittedComposerText( activeClawdSubmission, textContent