diff --git a/core/http/react-ui/src/components/Sidebar.jsx b/core/http/react-ui/src/components/Sidebar.jsx index 1607c060c..58438fd51 100644 --- a/core/http/react-ui/src/components/Sidebar.jsx +++ b/core/http/react-ui/src/components/Sidebar.jsx @@ -130,12 +130,14 @@ export default function Sidebar({ isOpen, onClose }) { }, [location.pathname]) const toggleCollapse = () => { - setCollapsed(prev => { - const next = !prev - try { localStorage.setItem(COLLAPSED_KEY, String(next)) } catch (_) { /* ignore */ } - window.dispatchEvent(new CustomEvent('sidebar-collapse', { detail: { collapsed: next } })) - return next - }) + // Side effects (persist + broadcast) live in the handler body, never inside + // the setState updater: StrictMode double-invokes updaters in dev, and the + // synchronous sidebar-collapse dispatch re-entered setState from the + // listeners mid-update, so the toggle silently no-op'd in dev builds. + const next = !collapsed + try { localStorage.setItem(COLLAPSED_KEY, String(next)) } catch (_) { /* ignore */ } + setCollapsed(next) + window.dispatchEvent(new CustomEvent('sidebar-collapse', { detail: { collapsed: next } })) } const toggleSection = (id) => { diff --git a/core/http/react-ui/src/pages/Talk.jsx b/core/http/react-ui/src/pages/Talk.jsx index f8e49e102..d898bd67b 100644 --- a/core/http/react-ui/src/pages/Talk.jsx +++ b/core/http/react-ui/src/pages/Talk.jsx @@ -127,9 +127,12 @@ export default function Talk() { .finally(() => setModelsLoading(false)) }, []) - // Auto-scroll transcript + // Auto-scroll the transcript's own overflow container. scrollIntoView bubbles + // to every scrollable ancestor (incl. the window), which yanked the whole + // page down to the transcript box on mount; scoping to the box avoids it. useEffect(() => { - transcriptEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + const box = transcriptEndRef.current?.parentElement + box?.scrollTo({ top: box.scrollHeight, behavior: 'smooth' }) }, [transcript]) // Mirror Chat.jsx: connect / disconnect client MCP servers as the user toggles them.