| ... | @@ -13,6 +13,7 @@ import { | ... | @@ -13,6 +13,7 @@ import { |
| 13 | type KeyboardEvent, | 13 | type KeyboardEvent, |
| 14 | useCallback, | 14 | useCallback, |
| 15 | useEffect, | 15 | useEffect, |
| | 16 | useLayoutEffect, |
| 16 | useMemo, | 17 | useMemo, |
| 17 | useRef, | 18 | useRef, |
| 18 | useState, | 19 | useState, |
| ... | @@ -131,6 +132,7 @@ export default function ChatView() { | ... | @@ -131,6 +132,7 @@ export default function ChatView() { |
| 131 | const [expandedWorkGroups, setExpandedWorkGroups] = useState<Record<string, boolean>>({}); | 132 | const [expandedWorkGroups, setExpandedWorkGroups] = useState<Record<string, boolean>>({}); |
| 132 | const [nowTick, setNowTick] = useState(() => Date.now()); | 133 | const [nowTick, setNowTick] = useState(() => Date.now()); |
| 133 | const [terminalFocusRequestId, setTerminalFocusRequestId] = useState(0); | 134 | const [terminalFocusRequestId, setTerminalFocusRequestId] = useState(0); |
| | 135 | const messagesScrollRef = useRef<HTMLDivElement>(null); |
| 134 | const messagesEndRef = useRef<HTMLDivElement>(null); | 136 | const messagesEndRef = useRef<HTMLDivElement>(null); |
| 135 | const textareaRef = useRef<HTMLTextAreaElement>(null); | 137 | const textareaRef = useRef<HTMLTextAreaElement>(null); |
| 136 | const composerImagesRef = useRef<ComposerImageAttachment[]>([]); | 138 | const composerImagesRef = useRef<ComposerImageAttachment[]>([]); |
| ... | @@ -345,6 +347,12 @@ export default function ChatView() { | ... | @@ -345,6 +347,12 @@ export default function ChatView() { |
| 345 | // Auto-scroll on new messages | 347 | // Auto-scroll on new messages |
| 346 | const messageCount = activeThread?.messages.length ?? 0; | 348 | const messageCount = activeThread?.messages.length ?? 0; |
| 347 | const workLogCount = workLogEntries.length; | 349 | const workLogCount = workLogEntries.length; |
| | 350 | useLayoutEffect(() => { |
| | 351 | if (!activeThread?.id) return; |
| | 352 | const scrollContainer = messagesScrollRef.current; |
| | 353 | if (!scrollContainer) return; |
| | 354 | scrollContainer.scrollTop = scrollContainer.scrollHeight; |
| | 355 | }, [activeThread?.id]); |
| 348 | useEffect(() => { | 356 | useEffect(() => { |
| 349 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | 357 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| 350 | }, [messageCount]); | 358 | }, [messageCount]); |
| ... | @@ -1014,7 +1022,7 @@ export default function ChatView() { | ... | @@ -1014,7 +1022,7 @@ export default function ChatView() { |
| 1014 | )} | 1022 | )} |
| 1015 | | 1023 | |
| 1016 | {/* Messages */} | 1024 | {/* Messages */} |
| 1017 | <div className="min-h-0 flex-1 overflow-y-auto px-5 py-4"> | 1025 | <div ref={messagesScrollRef} className="min-h-0 flex-1 overflow-y-auto px-5 py-4"> |
| 1018 | {activeThread.messages.length === 0 && !isWorking ? ( | 1026 | {activeThread.messages.length === 0 && !isWorking ? ( |
| 1019 | <div className="flex h-full items-center justify-center"> | 1027 | <div className="flex h-full items-center justify-center"> |
| 1020 | <p className="text-sm text-muted-foreground/30"> | 1028 | <p className="text-sm text-muted-foreground/30"> |