diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 674d375b..f2b82a0f 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -283,9 +283,7 @@ export default function AgentHealth() { }); }, [appConfig, configDraft, updateConfig, toast, appId]); const [eventSortAsc, setEventSortAsc] = useState(false); - const [isTimelineAtTop, setIsTimelineAtTop] = useState(true); - const timelineScrollRef = useRef(null); - const eventStream = useInfiniteAgentEvents({ appId, isAtTop: isTimelineAtTop }); + const eventStream = useInfiniteAgentEvents({ appId, isAtTop: true }); const [appFilter, setAppFilter] = useState(''); type AppSortKey = 'status' | 'name' | 'tps' | 'cpu' | 'heartbeat'; @@ -985,32 +983,29 @@ export default function AgentHealth() { - - 0} - maxHeight={360} - > +
{feedEvents.length > 0 ? ( - + ) : (
{eventStream.isLoading ? 'Loading events\u2026' : 'No events in the selected time range.'}
)} - +
+ {eventStream.hasNextPage && ( + + )} diff --git a/ui/src/pages/AgentInstance/AgentInstance.tsx b/ui/src/pages/AgentInstance/AgentInstance.tsx index ca53c900..59d1a872 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.tsx +++ b/ui/src/pages/AgentInstance/AgentInstance.tsx @@ -42,15 +42,13 @@ export default function AgentInstance() { const [logSortAsc, setLogSortAsc] = useState(false); const [eventSortAsc, setEventSortAsc] = useState(false); const [isLogAtTop, setIsLogAtTop] = useState(true); - const [isTimelineAtTop, setIsTimelineAtTop] = useState(true); const logScrollRef = useRef(null); - const timelineScrollRef = useRef(null); const timeFrom = timeRange.start.toISOString(); const timeTo = timeRange.end.toISOString(); const selectedEnv = useEnvironmentStore((s) => s.environment); const { data: agents, isLoading } = useAgents(undefined, appId); - const eventStream = useInfiniteAgentEvents({ appId, agentId: instanceId, isAtTop: isTimelineAtTop }); + const eventStream = useInfiniteAgentEvents({ appId, agentId: instanceId, isAtTop: true }); const agent = useMemo( () => (agents || []).find((a: any) => a.instanceId === instanceId) as any, @@ -491,32 +489,29 @@ export default function AgentInstance() { - - 0} - maxHeight={360} - > +
{feedEvents.length > 0 ? ( - + ) : (
{eventStream.isLoading ? 'Loading events…' : 'No events in the selected time range.'}
)} - +
+ {eventStream.hasNextPage && ( + + )} diff --git a/ui/src/styles/log-panel.module.css b/ui/src/styles/log-panel.module.css index fd9f4951..b0e8bd0c 100644 --- a/ui/src/styles/log-panel.module.css +++ b/ui/src/styles/log-panel.module.css @@ -129,3 +129,36 @@ align-items: center; gap: 6px; } + +/* Timeline: EventFeed owns its own scroll (so its internal search/filter + toolbar stays pinned). Give it a bounded height and let its .list scroll. + Pagination via the explicit "Load older" button below, not scroll. */ +.eventFeedContainer { + display: flex; + flex-direction: column; + max-height: 360px; + min-height: 0; + overflow: hidden; +} + +.loadOlderBtn { + padding: 8px 12px; + border: none; + border-top: 1px solid var(--border-subtle); + background: var(--bg-surface); + color: var(--text-muted); + font-size: 12px; + cursor: pointer; + text-align: center; + font-family: var(--font-body); +} + +.loadOlderBtn:hover:not(:disabled) { + color: var(--text-primary); + background: var(--bg-hover); +} + +.loadOlderBtn:disabled { + opacity: 0.5; + cursor: not-allowed; +}