From 0aba5ef8c117ed6abfde446342950ccd561b1163 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:01:27 +0100 Subject: [PATCH] fix: retain textarea focus on send and auto-scroll on content expansion Remove disabled={isUploading} from the textarea so focus() isn't silently ignored on a disabled element. Fix ResizeObserver effect deps so the observer is actually created after the loading spinner transitions to message content. --- .../web/src/components/chat/MessageInput.tsx | 2 +- .../web/src/components/chat/MessageList.tsx | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/chat/MessageInput.tsx b/packages/web/src/components/chat/MessageInput.tsx index 7f20add9..cce54da4 100644 --- a/packages/web/src/components/chat/MessageInput.tsx +++ b/packages/web/src/components/chat/MessageInput.tsx @@ -329,7 +329,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { placeholder={`Message ${channelName.startsWith('@') ? channelName : `#${channelName}`}`} className="flex-1 py-[10px] px-1 bg-transparent text-txt-primary placeholder-txt-tertiary/60 outline-none resize-none text-[15px] leading-[1.375rem] max-h-[50vh] scrollbar-thin" rows={1} - disabled={isUploading} + /> {/* Send indicator */} diff --git a/packages/web/src/components/chat/MessageList.tsx b/packages/web/src/components/chat/MessageList.tsx index 3a5077d3..3deb5845 100644 --- a/packages/web/src/components/chat/MessageList.tsx +++ b/packages/web/src/components/chat/MessageList.tsx @@ -46,7 +46,9 @@ export function MessageList({ channelId }: MessageListProps) { const ackChannel = useChatStore((s) => s.ackChannel); const bottomRef = useRef(null); const containerRef = useRef(null); + const contentRef = useRef(null); const [isNearBottom, setIsNearBottom] = useState(true); + const isNearBottomRef = useRef(true); const [isLoadingMore, setIsLoadingMore] = useState(false); const prevMessagesLength = useRef(0); const ackTimerRef = useRef>(); @@ -68,6 +70,7 @@ export function MessageList({ channelId }: MessageListProps) { useEffect(() => { prevMessagesLength.current = 0; setIsNearBottom(true); + isNearBottomRef.current = true; }, [channelId]); // Handle scrolling: initial load snaps to bottom, new messages smooth-scroll if near bottom @@ -91,13 +94,31 @@ export function MessageList({ channelId }: MessageListProps) { } }, [messages.length, isNearBottom]); + // Auto-scroll when content height grows (embeds/images loading) while near bottom + const hasMessages = messages.length > 0; + useEffect(() => { + const content = contentRef.current; + const container = containerRef.current; + if (!content || !container) return; + + const observer = new ResizeObserver(() => { + if (isNearBottomRef.current && containerRef.current) { + containerRef.current.scrollTop = containerRef.current.scrollHeight; + } + }); + observer.observe(content); + return () => observer.disconnect(); + }, [hasMessages, channelId]); + const handleScroll = useCallback(async () => { const container = containerRef.current; if (!container) return; // Check if near bottom const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight; - setIsNearBottom(distanceFromBottom < 100); + const nearBottom = distanceFromBottom < 100; + setIsNearBottom(nearBottom); + isNearBottomRef.current = nearBottom; // Load more when scrolled to top if (container.scrollTop < 50 && hasMore && !isLoadingMore) { @@ -136,7 +157,7 @@ export function MessageList({ channelId }: MessageListProps) { {!hasMore && } -
+
{messages.map((msg, i) => { const prevMsg = messages[i - 1]; const showDate = shouldShowDateDivider(prevMsg, msg);