From f45c396991a2888e419b8dd171a5dd7f1e11e650 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 13 Aug 2026 16:15:00 -0700 Subject: [PATCH] fix(chat): focus the composer when opening a new or existing chat --- .../home/components/user-input/user-input.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 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 d8bf4142639..1b2c6467340 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 @@ -46,6 +46,16 @@ export type { FileAttachmentForApi } from '@/app/workspace/[workspaceId]/home/ty const logger = createLogger('UserInput') +/** + * Whether the element is somewhere the user could be typing. Focusing the composer on mount + * must not steal focus from another field, but may take it from a link or button — opening a + * chat leaves the sidebar link focused, and the composer should win. + */ +function isTextEntry(element: HTMLElement): boolean { + const tag = element.tagName + return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || element.isContentEditable +} + interface UserInputProps { defaultValue?: string draftScopeKey?: string @@ -453,12 +463,10 @@ const UserInputImpl = forwardRef(function UserI useEffect(() => { const raf = window.requestAnimationFrame(() => { + if (!document.hasFocus()) return const active = document.activeElement - const pageHasFocus = document.hasFocus() - const hasNeutralFocus = active === document.body || active === document.documentElement - if (pageHasFocus && hasNeutralFocus) { - textareaRef.current?.focus() - } + if (active instanceof HTMLElement && isTextEntry(active)) return + textareaRef.current?.focus() }) return () => window.cancelAnimationFrame(raf) }, [textareaRef])