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 6a83a037c1b..a60c8ebff23 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 @@ -11,6 +11,7 @@ import { useRef, useState, } from 'react' +import { createLogger } from '@sim/logger' import { Paperclip } from 'lucide-react' import { useParams } from 'next/navigation' import { Button, Tooltip } from '@/components/emcn' @@ -58,6 +59,8 @@ import type { ChatContext } from '@/stores/panel' export type { FileAttachmentForApi } from '@/app/workspace/[workspaceId]/home/types' +const logger = createLogger('UserInput') + function getCaretAnchor( textarea: HTMLTextAreaElement, caretPos: number @@ -148,7 +151,8 @@ export const UserInput = forwardRef(function Us const [value, setValue] = useState(() => { if (defaultValue) return defaultValue if (!draftScopeKey) return '' - return useMothershipDraftsStore.getState().drafts[draftScopeKey]?.text ?? '' + const text = useMothershipDraftsStore.getState().drafts[draftScopeKey]?.text + return typeof text === 'string' ? text : '' }) const overlayRef = useRef(null) const plusMenuRef = useRef(null) @@ -189,14 +193,17 @@ export const UserInput = forwardRef(function Us useEffect(() => { if (hasRestoredDraftRef.current || !draftScopeKey) return hasRestoredDraftRef.current = true - const draft = useMothershipDraftsStore.getState().drafts[draftScopeKey] - if (!draft) return - if (draft.contexts?.length) { - contextManagement.setSelectedContexts(draft.contexts) - } - if (draft.fileAttachments?.length) { - files.restoreAttachedFiles( - draft.fileAttachments.map((a) => ({ + let restoredContexts: ChatContext[] | null = null + let restoredFiles: AttachedFile[] | null = null + let caretText: string | null = null + try { + const draft = useMothershipDraftsStore.getState().drafts[draftScopeKey] + if (!draft) return + if (draft.contexts?.length) { + restoredContexts = draft.contexts + } + if (draft.fileAttachments?.length) { + restoredFiles = draft.fileAttachments.map((a) => ({ id: a.id, name: a.filename, size: a.size, @@ -205,13 +212,22 @@ export const UserInput = forwardRef(function Us key: a.key, uploading: false, })) - ) + } + if (typeof draft.text === 'string' && draft.text.length > 0) { + caretText = draft.text + } + } catch (err) { + logger.error('Failed to read draft, clearing', { err }) + useMothershipDraftsStore.getState().clearDraft(draftScopeKey) + return } - if (draft.text) { + if (restoredContexts) contextManagement.setSelectedContexts(restoredContexts) + if (restoredFiles) files.restoreAttachedFiles(restoredFiles) + if (caretText !== null) { const textarea = textareaRef.current if (textarea) { textarea.focus() - textarea.setSelectionRange(draft.text.length, draft.text.length) + textarea.setSelectionRange(caretText.length, caretText.length) } } }, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentional mount-only restore