diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 96c5aa93..adfc778c 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from 'react'; +import { useState, useMemo, useRef, useEffect } from 'react'; import { useParams, Link } from 'react-router'; import { StatCard, StatusDot, Badge, MonoText, ProgressBar, @@ -224,6 +224,18 @@ export default function AgentHealth() { const { appId } = useParams(); const { data: agents } = useAgents(undefined, appId); const { data: events } = useAgentEvents(appId); + const timelineRef = useRef(null); + + // Override EventFeed's auto-scroll-to-bottom so newest (DESC) events stay visible at top + useEffect(() => { + const el = timelineRef.current; + if (!el) return; + const timer = requestAnimationFrame(() => { + const list = el.querySelector('[aria-label="Event feed"]') as HTMLElement | null; + if (list) list.scrollTop = 0; + }); + return () => cancelAnimationFrame(timer); + }, [events]); const [selectedInstance, setSelectedInstance] = useState(null); const [panelOpen, setPanelOpen] = useState(false); @@ -256,7 +268,7 @@ export default function AgentHealth() { : ('running' as const), message: `${e.agentId}: ${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`, timestamp: new Date(e.timestamp), - })).toReversed(), + })), [events], ); @@ -500,7 +512,7 @@ export default function AgentHealth() { {/* EventFeed */} {feedEvents.length > 0 && ( -
+
Timeline {feedEvents.length} events diff --git a/ui/src/pages/AgentInstance/AgentInstance.module.css b/ui/src/pages/AgentInstance/AgentInstance.module.css index f1ec0d5b..2027ec17 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.module.css +++ b/ui/src/pages/AgentInstance/AgentInstance.module.css @@ -132,6 +132,9 @@ border-radius: var(--radius-lg); box-shadow: var(--shadow-card); overflow: hidden; + display: flex; + flex-direction: column; + max-height: 420px; } .logHeader { diff --git a/ui/src/pages/AgentInstance/AgentInstance.tsx b/ui/src/pages/AgentInstance/AgentInstance.tsx index 2004ec5f..338edbaa 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.tsx +++ b/ui/src/pages/AgentInstance/AgentInstance.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useMemo, useState, useRef, useEffect } from 'react'; import { useParams, Link } from 'react-router'; import { StatCard, StatusDot, Badge, LineChart, AreaChart, BarChart, @@ -36,10 +36,23 @@ export default function AgentInstance() { const timeFrom = timeRange.start.toISOString(); const timeTo = timeRange.end.toISOString(); + const timelineRef = useRef(null); + const { data: agents, isLoading } = useAgents(undefined, appId); const { data: events } = useAgentEvents(appId, instanceId); const { data: timeseries } = useStatsTimeseries(timeFrom, timeTo, undefined, appId); + // Override EventFeed's auto-scroll-to-bottom so newest (DESC) events stay visible at top + useEffect(() => { + const el = timelineRef.current; + if (!el) return; + const timer = requestAnimationFrame(() => { + const list = el.querySelector('[aria-label="Event feed"]') as HTMLElement | null; + if (list) list.scrollTop = 0; + }); + return () => cancelAnimationFrame(timer); + }, [events]); + const agent = useMemo( () => (agents || []).find((a: any) => a.id === instanceId) as any, [agents, instanceId], @@ -90,8 +103,7 @@ export default function AgentInstance() { : ('running' as const), message: `${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`, timestamp: new Date(e.timestamp), - })) - .toReversed(), + })), [events, instanceId], ); @@ -436,7 +448,7 @@ export default function AgentInstance() { )}
-
+
Timeline {feedEvents.length} events