From fa29e76656f97872b6ac5d756e31c2331978e2b1 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 1 May 2026 20:39:27 -0700 Subject: [PATCH 1/3] fix(loading): fix spans and draft chat state --- .../mothership-chat/mothership-chat.tsx | 19 ++++++++---------- .../home/components/user-input/user-input.tsx | 20 +++++++++++++------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx index c4b8910b127..4693b19de4a 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx @@ -1,6 +1,6 @@ 'use client' -import { useLayoutEffect, useMemo, useRef } from 'react' +import { useLayoutEffect, useRef } from 'react' import { cn } from '@/lib/core/utils/cn' import { MessageActions } from '@/app/workspace/[workspaceId]/components' import { ChatMessageAttachments } from '@/app/workspace/[workspaceId]/home/components/chat-message-attachments' @@ -111,17 +111,14 @@ export function MothershipChat({ const { staged: stagedMessages, isStaging } = useProgressiveList(messages, stagingKey) const stagedMessageCount = stagedMessages.length const stagedOffset = messages.length - stagedMessages.length - const precedingUserContentByIndex = useMemo(() => { - const contentByIndex: Array = [] - let lastUserContent: string | undefined - for (const [index, message] of messages.entries()) { - contentByIndex[index] = lastUserContent - if (message.role === 'user') { - lastUserContent = message.content - } + const precedingUserContentByIndex: Array = [] + let lastUserContent: string | undefined + for (const [index, message] of messages.entries()) { + precedingUserContentByIndex[index] = lastUserContent + if (message.role === 'user') { + lastUserContent = message.content } - return contentByIndex - }, [messages]) + } const initialScrollDoneRef = useRef(false) const userInputRef = useRef(null) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index 2f17d62486c..e2530821646 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -153,12 +153,13 @@ export const UserInput = forwardRef(function Us const overlayRef = useRef(null) const plusMenuRef = useRef(null) - const prevDefaultValueRef = useRef(defaultValue) - useEffect(() => { - if (defaultValue === prevDefaultValueRef.current) return - prevDefaultValueRef.current = defaultValue - if (defaultValue) setValue(defaultValue) - }, [defaultValue]) + const [prevDefaultValue, setPrevDefaultValue] = useState(defaultValue) + if (defaultValue && defaultValue !== prevDefaultValue) { + setPrevDefaultValue(defaultValue) + setValue(defaultValue) + } else if (!defaultValue && prevDefaultValue) { + setPrevDefaultValue(defaultValue) + } const files = useFileAttachments({ userId: userId || session?.user?.id, @@ -206,6 +207,13 @@ export const UserInput = forwardRef(function Us })) ) } + if (draft.text) { + const textarea = textareaRef.current + if (textarea) { + textarea.focus() + textarea.setSelectionRange(draft.text.length, draft.text.length) + } + } }, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentional mount-only restore // Skip the initial save — restore's setState calls haven't propagated yet, so From a495d3a2097b9209a39d52d682ed3ea05cc5f076 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 1 May 2026 20:48:25 -0700 Subject: [PATCH 2/3] fix(user-input): document auto-focus RAF ordering relative to draft restore --- .../[workspaceId]/home/components/user-input/user-input.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index e2530821646..45a5bee38b9 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -536,6 +536,9 @@ export const UserInput = forwardRef(function Us wasSendingRef.current = isSending }, [isSending, textareaRef]) + // Auto-focus on mount. The RAF runs after all synchronous mount effects, so if + // the draft-restore effect already focused the textarea the isEditingElsewhere + // guard below will be true and we skip the re-focus, preserving cursor position. useEffect(() => { const raf = window.requestAnimationFrame(() => { const active = document.activeElement From 3cb4a15d4368afd7f87f168a6aec6c30b3609e57 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 1 May 2026 20:49:55 -0700 Subject: [PATCH 3/3] chore(user-input): remove extraneous comments --- .../[workspaceId]/home/components/user-input/user-input.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index 45a5bee38b9..6a83a037c1b 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -216,8 +216,6 @@ export const UserInput = forwardRef(function Us } }, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentional mount-only restore - // Skip the initial save — restore's setState calls haven't propagated yet, so - // files/contexts are still empty and would transiently wipe a file-only draft. const isFirstSaveRef = useRef(true) useEffect(() => { if (isFirstSaveRef.current) { @@ -426,7 +424,6 @@ export const UserInput = forwardRef(function Us const newValue = `${before}${insertText}${after}` pendingCursorRef.current = newPos - // Eagerly sync refs so successive drop-handler iterations see the updated position valueRef.current = newValue atInsertPosRef.current = newPos mentionRangeRef.current = null @@ -536,9 +533,6 @@ export const UserInput = forwardRef(function Us wasSendingRef.current = isSending }, [isSending, textareaRef]) - // Auto-focus on mount. The RAF runs after all synchronous mount effects, so if - // the draft-restore effect already focused the textarea the isEditingElsewhere - // guard below will be true and we skip the re-focus, preserving cursor position. useEffect(() => { const raf = window.requestAnimationFrame(() => { const active = document.activeElement