From 159f13c471f3d2a3d21be0a90c9ae3d08c022562 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:10:19 +0000 Subject: [PATCH 1/7] Optimize composer menu renders Co-authored-by: Julius Marminge --- apps/web/src/components/chat/ChatComposer.tsx | 91 ++++++++----------- 1 file changed, 37 insertions(+), 54 deletions(-) diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 2c4743de3c6..fd2e43cd664 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -598,6 +598,16 @@ export const ChatComposer = memo( () => sortProviderInstanceEntries(deriveProviderInstanceEntries(providerStatuses)), [providerStatuses], ); + const providerInstanceEntryById = useMemo>( + () => { + const entriesById = new Map(); + for (const entry of providerInstanceEntries) { + entriesById.set(entry.instanceId, entry); + } + return entriesById; + }, + [providerInstanceEntries], + ); const selectedProviderByThreadId = composerDraft.activeProvider ?? null; const threadProvider = activeThread?.session?.providerInstanceId ?? @@ -618,15 +628,12 @@ export const ChatComposer = memo( const lockedInstanceId = activeThread.session?.providerInstanceId ?? activeThreadModelSelection?.instanceId; if (!lockedInstanceId) return null; - return ( - providerInstanceEntries.find((entry) => entry.instanceId === lockedInstanceId) - ?.continuationGroupKey ?? null - ); + return providerInstanceEntryById.get(lockedInstanceId)?.continuationGroupKey ?? null; }, [ activeThread, activeThreadModelSelection?.instanceId, lockedProvider, - providerInstanceEntries, + providerInstanceEntryById, ]); // Resolve which configured instance the composer is currently targeting. @@ -648,10 +655,9 @@ export const ChatComposer = memo( ]; for (const candidate of candidates) { if (!candidate) continue; - const match = providerInstanceEntries.find( - (entry) => entry.instanceId === candidate && entry.enabled, - ); + const match = providerInstanceEntryById.get(ProviderInstanceId.make(candidate)); if (match) { + if (!match.enabled) continue; // When locked to a specific driver kind, ignore persisted instance // ids from a different kind or continuation group. if (lockedProvider && match.driverKind !== lockedProvider) continue; @@ -691,6 +697,7 @@ export const ChatComposer = memo( explicitSelectedInstanceId, lockedContinuationGroupKey, lockedProvider, + providerInstanceEntryById, providerInstanceEntries, selectedProvider, ]); @@ -709,8 +716,8 @@ export const ChatComposer = memo( // instance gets its own slash commands, skills, and model list — not // the first snapshot for the same driver kind. const selectedProviderEntry = useMemo( - () => providerInstanceEntries.find((entry) => entry.instanceId === selectedInstanceId), - [providerInstanceEntries, selectedInstanceId], + () => providerInstanceEntryById.get(selectedInstanceId), + [providerInstanceEntryById, selectedInstanceId], ); const selectedProviderStatus = useMemo( () => selectedProviderEntry?.snapshot ?? null, @@ -928,20 +935,24 @@ export const ChatComposer = memo( const composerMenuSearchKey = composerTrigger ? `${composerTrigger.kind}:${composerTrigger.query.trim().toLowerCase()}` : null; + const activeComposerMenuItemId = useMemo( + () => + resolveComposerMenuActiveItemId({ + items: composerMenuItems, + highlightedItemId: composerHighlightedItemId, + currentSearchKey: composerMenuSearchKey, + highlightedSearchKey: composerHighlightedSearchKey, + }), + [ + composerHighlightedItemId, + composerHighlightedSearchKey, + composerMenuItems, + composerMenuSearchKey, + ], + ); const activeComposerMenuItem = useMemo(() => { - const activeItemId = resolveComposerMenuActiveItemId({ - items: composerMenuItems, - highlightedItemId: composerHighlightedItemId, - currentSearchKey: composerMenuSearchKey, - highlightedSearchKey: composerHighlightedSearchKey, - }); - return composerMenuItems.find((item) => item.id === activeItemId) ?? null; - }, [ - composerHighlightedItemId, - composerHighlightedSearchKey, - composerMenuItems, - composerMenuSearchKey, - ]); + return composerMenuItems.find((item) => item.id === activeComposerMenuItemId) ?? null; + }, [activeComposerMenuItemId, composerMenuItems]); composerMenuOpenRef.current = composerMenuOpen; composerMenuItemsRef.current = composerMenuItems; @@ -1128,35 +1139,6 @@ export const ChatComposer = memo( composerTerminalContextsRef.current = composerTerminalContexts; }, [composerTerminalContexts, composerTerminalContextsRef]); - // ------------------------------------------------------------------ - // Composer menu highlight sync - // ------------------------------------------------------------------ - useEffect(() => { - if (!composerMenuOpen) { - setComposerHighlightedItemId(null); - setComposerHighlightedSearchKey(null); - return; - } - const nextActiveItemId = resolveComposerMenuActiveItemId({ - items: composerMenuItems, - highlightedItemId: composerHighlightedItemId, - currentSearchKey: composerMenuSearchKey, - highlightedSearchKey: composerHighlightedSearchKey, - }); - setComposerHighlightedItemId((existing) => - existing === nextActiveItemId ? existing : nextActiveItemId, - ); - setComposerHighlightedSearchKey((existing) => - existing === composerMenuSearchKey ? existing : composerMenuSearchKey, - ); - }, [ - composerHighlightedItemId, - composerHighlightedSearchKey, - composerMenuItems, - composerMenuOpen, - composerMenuSearchKey, - ]); - const lastSyncedPendingInputRef = useRef<{ requestId: string | null; questionId: string | null; @@ -1573,7 +1555,7 @@ export const ChatComposer = memo( (key: "ArrowDown" | "ArrowUp") => { if (composerMenuItems.length === 0) return; const highlightedIndex = composerMenuItems.findIndex( - (item) => item.id === composerHighlightedItemId, + (item) => item.id === activeComposerMenuItemId, ); const normalizedIndex = highlightedIndex >= 0 ? highlightedIndex : key === "ArrowDown" ? -1 : 0; @@ -1582,8 +1564,9 @@ export const ChatComposer = memo( (normalizedIndex + offset + composerMenuItems.length) % composerMenuItems.length; const nextItem = composerMenuItems[nextIndex]; setComposerHighlightedItemId(nextItem?.id ?? null); + setComposerHighlightedSearchKey(composerMenuSearchKey); }, - [composerHighlightedItemId, composerMenuItems], + [activeComposerMenuItemId, composerMenuItems, composerMenuSearchKey], ); const blurMobileComposerAfterSend = useCallback(() => { From 14ff38cdf896042277006b8a4bc4f2228037b63f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:11:42 +0000 Subject: [PATCH 2/7] Stabilize composer editor callbacks Co-authored-by: Julius Marminge --- apps/web/src/components/chat/ChatComposer.tsx | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index fd2e43cd664..f1a2f0ca5fc 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -1635,43 +1635,49 @@ export const ChatComposer = memo( // ------------------------------------------------------------------ // Callbacks: command key // ------------------------------------------------------------------ - const onComposerCommandKey = ( - key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab", - event: KeyboardEvent, - ) => { - if (key === "Tab" && event.shiftKey) { - toggleInteractionMode(); - return true; - } - const { trigger } = resolveActiveComposerTrigger(); - const menuIsActive = composerMenuOpenRef.current || trigger !== null; - if (menuIsActive) { - const currentItems = composerMenuItemsRef.current; - const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0]; - if (key === "ArrowDown" && currentItems.length > 0) { - nudgeComposerMenuHighlight("ArrowDown"); + const onComposerCommandKey = useCallback( + (key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab", event: KeyboardEvent) => { + if (key === "Tab" && event.shiftKey) { + toggleInteractionMode(); return true; } - if (key === "ArrowUp" && currentItems.length > 0) { - nudgeComposerMenuHighlight("ArrowUp"); - return true; + const { trigger } = resolveActiveComposerTrigger(); + const menuIsActive = composerMenuOpenRef.current || trigger !== null; + if (menuIsActive) { + const currentItems = composerMenuItemsRef.current; + const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0]; + if (key === "ArrowDown" && currentItems.length > 0) { + nudgeComposerMenuHighlight("ArrowDown"); + return true; + } + if (key === "ArrowUp" && currentItems.length > 0) { + nudgeComposerMenuHighlight("ArrowUp"); + return true; + } + if ((key === "Enter" || key === "Tab") && selectedItem) { + onSelectComposerItem(selectedItem); + return true; + } } - if ((key === "Enter" || key === "Tab") && selectedItem) { - onSelectComposerItem(selectedItem); + if (key === "Enter" && !event.shiftKey) { + submitComposer(); return true; } - } - if (key === "Enter" && !event.shiftKey) { - submitComposer(); - return true; - } - return false; - }; + return false; + }, + [ + nudgeComposerMenuHighlight, + onSelectComposerItem, + resolveActiveComposerTrigger, + submitComposer, + toggleInteractionMode, + ], + ); // ------------------------------------------------------------------ // Callbacks: images // ------------------------------------------------------------------ - const addComposerImages = (files: File[]) => { + const addComposerImages = useCallback((files: File[]) => { if (!activeThreadId || files.length === 0) return; if (pendingUserInputs.length > 0) { toastManager.add({ @@ -1714,7 +1720,13 @@ export const ChatComposer = memo( addComposerImagesToDraft(nextImages); } setThreadError(activeThreadId, error); - }; + }, [ + activeThreadId, + addComposerImage, + addComposerImagesToDraft, + pendingUserInputs.length, + setThreadError, + ]); const removeComposerImage = (imageId: string) => { removeComposerImageFromDraft(imageId); @@ -1723,14 +1735,14 @@ export const ChatComposer = memo( // ------------------------------------------------------------------ // Callbacks: paste / drag // ------------------------------------------------------------------ - const onComposerPaste = (event: React.ClipboardEvent) => { + const onComposerPaste = useCallback((event: React.ClipboardEvent) => { const files = Array.from(event.clipboardData.files); if (files.length === 0) return; const imageFiles = files.filter((file) => file.type.startsWith("image/")); if (imageFiles.length === 0) return; event.preventDefault(); addComposerImages(imageFiles); - }; + }, [addComposerImages]); const onComposerDragEnter = (event: React.DragEvent) => { if (!event.dataTransfer.types.includes("Files")) return; From 74a4e73f3b81b84227d04f6e76d93151490a3901 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:13:51 +0000 Subject: [PATCH 3/7] Keep composer editor handlers stable Co-authored-by: Julius Marminge --- apps/web/src/components/chat/ChatComposer.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index f1a2f0ca5fc..547171a23ce 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -147,6 +147,14 @@ const COMPOSER_FLOATING_LAYER_SELECTOR = [ '[data-slot="autocomplete-popup"]', ].join(","); +function useStableCallback( + callback: (...args: Args) => Return, +): (...args: Args) => Return { + const callbackRef = useRef(callback); + callbackRef.current = callback; + return useCallback((...args: Args) => callbackRef.current(...args), []); +} + const extendReplacementRangeForTrailingSpace = ( text: string, rangeEnd: number, @@ -1635,7 +1643,7 @@ export const ChatComposer = memo( // ------------------------------------------------------------------ // Callbacks: command key // ------------------------------------------------------------------ - const onComposerCommandKey = useCallback( + const onComposerCommandKey = useStableCallback( (key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab", event: KeyboardEvent) => { if (key === "Tab" && event.shiftKey) { toggleInteractionMode(); @@ -1665,13 +1673,6 @@ export const ChatComposer = memo( } return false; }, - [ - nudgeComposerMenuHighlight, - onSelectComposerItem, - resolveActiveComposerTrigger, - submitComposer, - toggleInteractionMode, - ], ); // ------------------------------------------------------------------ @@ -1735,14 +1736,14 @@ export const ChatComposer = memo( // ------------------------------------------------------------------ // Callbacks: paste / drag // ------------------------------------------------------------------ - const onComposerPaste = useCallback((event: React.ClipboardEvent) => { + const onComposerPaste = useStableCallback((event: React.ClipboardEvent) => { const files = Array.from(event.clipboardData.files); if (files.length === 0) return; const imageFiles = files.filter((file) => file.type.startsWith("image/")); if (imageFiles.length === 0) return; event.preventDefault(); addComposerImages(imageFiles); - }, [addComposerImages]); + }); const onComposerDragEnter = (event: React.DragEvent) => { if (!event.dataTransfer.types.includes("Files")) return; From b355e12c612b9485fa908a89bd3962d904aae3ca Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:15:01 +0000 Subject: [PATCH 4/7] Memoize composer prompt editor Co-authored-by: Julius Marminge --- .../src/components/ComposerPromptEditor.tsx | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/web/src/components/ComposerPromptEditor.tsx b/apps/web/src/components/ComposerPromptEditor.tsx index 453be25a93f..3f3ecee9f3e 100644 --- a/apps/web/src/components/ComposerPromptEditor.tsx +++ b/apps/web/src/components/ComposerPromptEditor.tsx @@ -39,6 +39,7 @@ import { import { createContext, forwardRef, + memo, useCallback, useContext, useEffect, @@ -1650,63 +1651,62 @@ function ComposerPromptEditorInner({ ); } -export const ComposerPromptEditor = forwardRef< - ComposerPromptEditorHandle, - ComposerPromptEditorProps ->(function ComposerPromptEditor( - { - value, - cursor, - terminalContexts, - skills, - disabled, - placeholder, - className, - onRemoveTerminalContext, - onChange, - onCommandKeyDown, - onPaste, - }, - ref, -) { - const initialValueRef = useRef(value); - const initialTerminalContextsRef = useRef(terminalContexts); - const initialSkillMetadataRef = useRef(skillMetadataByName(skills)); - const initialConfig = useMemo( - () => ({ - namespace: "t3tools-composer-editor", - editable: true, - nodes: [ComposerMentionNode, ComposerSkillNode, ComposerTerminalContextNode], - editorState: () => { - $setComposerEditorPrompt( - initialValueRef.current, - initialTerminalContextsRef.current, - initialSkillMetadataRef.current, - ); - }, - onError: (error) => { - throw error; - }, - }), - [], - ); +export const ComposerPromptEditor = memo( + forwardRef(function ComposerPromptEditor( + { + value, + cursor, + terminalContexts, + skills, + disabled, + placeholder, + className, + onRemoveTerminalContext, + onChange, + onCommandKeyDown, + onPaste, + }, + ref, + ) { + const initialValueRef = useRef(value); + const initialTerminalContextsRef = useRef(terminalContexts); + const initialSkillMetadataRef = useRef(skillMetadataByName(skills)); + const initialConfig = useMemo( + () => ({ + namespace: "t3tools-composer-editor", + editable: true, + nodes: [ComposerMentionNode, ComposerSkillNode, ComposerTerminalContextNode], + editorState: () => { + $setComposerEditorPrompt( + initialValueRef.current, + initialTerminalContextsRef.current, + initialSkillMetadataRef.current, + ); + }, + onError: (error) => { + throw error; + }, + }), + [], + ); - return ( - - - - ); -}); + return ( + + + + ); + }), +); From f6b293d7e060c7db390e324f2a3480f6f98e12b6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:15:33 +0000 Subject: [PATCH 5/7] Revert "Memoize composer prompt editor" This reverts commit b355e12c612b9485fa908a89bd3962d904aae3ca. --- .../src/components/ComposerPromptEditor.tsx | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/web/src/components/ComposerPromptEditor.tsx b/apps/web/src/components/ComposerPromptEditor.tsx index 3f3ecee9f3e..453be25a93f 100644 --- a/apps/web/src/components/ComposerPromptEditor.tsx +++ b/apps/web/src/components/ComposerPromptEditor.tsx @@ -39,7 +39,6 @@ import { import { createContext, forwardRef, - memo, useCallback, useContext, useEffect, @@ -1651,62 +1650,63 @@ function ComposerPromptEditorInner({ ); } -export const ComposerPromptEditor = memo( - forwardRef(function ComposerPromptEditor( - { - value, - cursor, - terminalContexts, - skills, - disabled, - placeholder, - className, - onRemoveTerminalContext, - onChange, - onCommandKeyDown, - onPaste, - }, - ref, - ) { - const initialValueRef = useRef(value); - const initialTerminalContextsRef = useRef(terminalContexts); - const initialSkillMetadataRef = useRef(skillMetadataByName(skills)); - const initialConfig = useMemo( - () => ({ - namespace: "t3tools-composer-editor", - editable: true, - nodes: [ComposerMentionNode, ComposerSkillNode, ComposerTerminalContextNode], - editorState: () => { - $setComposerEditorPrompt( - initialValueRef.current, - initialTerminalContextsRef.current, - initialSkillMetadataRef.current, - ); - }, - onError: (error) => { - throw error; - }, - }), - [], - ); +export const ComposerPromptEditor = forwardRef< + ComposerPromptEditorHandle, + ComposerPromptEditorProps +>(function ComposerPromptEditor( + { + value, + cursor, + terminalContexts, + skills, + disabled, + placeholder, + className, + onRemoveTerminalContext, + onChange, + onCommandKeyDown, + onPaste, + }, + ref, +) { + const initialValueRef = useRef(value); + const initialTerminalContextsRef = useRef(terminalContexts); + const initialSkillMetadataRef = useRef(skillMetadataByName(skills)); + const initialConfig = useMemo( + () => ({ + namespace: "t3tools-composer-editor", + editable: true, + nodes: [ComposerMentionNode, ComposerSkillNode, ComposerTerminalContextNode], + editorState: () => { + $setComposerEditorPrompt( + initialValueRef.current, + initialTerminalContextsRef.current, + initialSkillMetadataRef.current, + ); + }, + onError: (error) => { + throw error; + }, + }), + [], + ); - return ( - - - - ); - }), -); + return ( + + + + ); +}); From 56024f4ef82e291209884d7fd796256a5a20d8e6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:16:46 +0000 Subject: [PATCH 6/7] Format composer optimization Co-authored-by: Julius Marminge --- apps/web/src/components/chat/ChatComposer.tsx | 116 +++++++++--------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 547171a23ce..1fa58094b28 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -606,16 +606,15 @@ export const ChatComposer = memo( () => sortProviderInstanceEntries(deriveProviderInstanceEntries(providerStatuses)), [providerStatuses], ); - const providerInstanceEntryById = useMemo>( - () => { - const entriesById = new Map(); - for (const entry of providerInstanceEntries) { - entriesById.set(entry.instanceId, entry); - } - return entriesById; - }, - [providerInstanceEntries], - ); + const providerInstanceEntryById = useMemo< + ReadonlyMap + >(() => { + const entriesById = new Map(); + for (const entry of providerInstanceEntries) { + entriesById.set(entry.instanceId, entry); + } + return entriesById; + }, [providerInstanceEntries]); const selectedProviderByThreadId = composerDraft.activeProvider ?? null; const threadProvider = activeThread?.session?.providerInstanceId ?? @@ -1678,56 +1677,59 @@ export const ChatComposer = memo( // ------------------------------------------------------------------ // Callbacks: images // ------------------------------------------------------------------ - const addComposerImages = useCallback((files: File[]) => { - if (!activeThreadId || files.length === 0) return; - if (pendingUserInputs.length > 0) { - toastManager.add({ - type: "error", - title: "Attach images after answering plan questions.", - }); - return; - } - const nextImages: ComposerImageAttachment[] = []; - let nextImageCount = composerImagesRef.current.length; - let error: string | null = null; - for (const file of files) { - if (!file.type.startsWith("image/")) { - error = `Unsupported file type for '${file.name}'. Please attach image files only.`; - continue; + const addComposerImages = useCallback( + (files: File[]) => { + if (!activeThreadId || files.length === 0) return; + if (pendingUserInputs.length > 0) { + toastManager.add({ + type: "error", + title: "Attach images after answering plan questions.", + }); + return; } - if (file.size > PROVIDER_SEND_TURN_MAX_IMAGE_BYTES) { - error = `'${file.name}' exceeds the ${IMAGE_SIZE_LIMIT_LABEL} attachment limit.`; - continue; + const nextImages: ComposerImageAttachment[] = []; + let nextImageCount = composerImagesRef.current.length; + let error: string | null = null; + for (const file of files) { + if (!file.type.startsWith("image/")) { + error = `Unsupported file type for '${file.name}'. Please attach image files only.`; + continue; + } + if (file.size > PROVIDER_SEND_TURN_MAX_IMAGE_BYTES) { + error = `'${file.name}' exceeds the ${IMAGE_SIZE_LIMIT_LABEL} attachment limit.`; + continue; + } + if (nextImageCount >= PROVIDER_SEND_TURN_MAX_ATTACHMENTS) { + error = `You can attach up to ${PROVIDER_SEND_TURN_MAX_ATTACHMENTS} images per message.`; + break; + } + const previewUrl = URL.createObjectURL(file); + nextImages.push({ + type: "image", + id: randomUUID(), + name: file.name || "image", + mimeType: file.type, + sizeBytes: file.size, + previewUrl, + file, + }); + nextImageCount += 1; } - if (nextImageCount >= PROVIDER_SEND_TURN_MAX_ATTACHMENTS) { - error = `You can attach up to ${PROVIDER_SEND_TURN_MAX_ATTACHMENTS} images per message.`; - break; + if (nextImages.length === 1 && nextImages[0]) { + addComposerImage(nextImages[0]); + } else if (nextImages.length > 1) { + addComposerImagesToDraft(nextImages); } - const previewUrl = URL.createObjectURL(file); - nextImages.push({ - type: "image", - id: randomUUID(), - name: file.name || "image", - mimeType: file.type, - sizeBytes: file.size, - previewUrl, - file, - }); - nextImageCount += 1; - } - if (nextImages.length === 1 && nextImages[0]) { - addComposerImage(nextImages[0]); - } else if (nextImages.length > 1) { - addComposerImagesToDraft(nextImages); - } - setThreadError(activeThreadId, error); - }, [ - activeThreadId, - addComposerImage, - addComposerImagesToDraft, - pendingUserInputs.length, - setThreadError, - ]); + setThreadError(activeThreadId, error); + }, + [ + activeThreadId, + addComposerImage, + addComposerImagesToDraft, + pendingUserInputs.length, + setThreadError, + ], + ); const removeComposerImage = (imageId: string) => { removeComposerImageFromDraft(imageId); From da1c2152a6ed567a2aa79a4a54b68efcc4283ab7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 16:19:06 +0000 Subject: [PATCH 7/7] Fix composer image callback deps Co-authored-by: Julius Marminge --- apps/web/src/components/chat/ChatComposer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 1fa58094b28..9ddf82da633 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -1726,6 +1726,7 @@ export const ChatComposer = memo( activeThreadId, addComposerImage, addComposerImagesToDraft, + composerImagesRef, pendingUserInputs.length, setThreadError, ],