diff --git a/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx b/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx index 3ffc28a7..f94091c7 100644 --- a/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +++ b/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx @@ -19,6 +19,8 @@ function Conversation() { const [shouldAutoScrollLogs, setShouldAutoScrollLogs] = useState(true); const [conversationUpdateTrigger, setConversationUpdateTrigger] = useState(0); const [visibleCount, setVisibleCount] = useState(100); + const [visibleRunningEntriesCount, setVisibleRunningEntriesCount] = + useState(0); const scrollContainerRef = useRef(null); @@ -120,8 +122,8 @@ function Conversation() { // Paginate: show only the last visibleCount entries const visibleEntries = useMemo( - () => allEntries.slice(-visibleCount), - [allEntries, visibleCount] + () => allEntries.slice(-(visibleCount - visibleRunningEntriesCount)), + [allEntries, visibleCount, visibleRunningEntriesCount] ); const renderedVisibleEntries = useMemo( @@ -160,6 +162,8 @@ function Conversation() { executionProcess={runningProcess} onConversationUpdate={handleConversationUpdate} diffDeletable + visibleEntriesNum={visibleCount} + onDisplayEntriesChange={setVisibleRunningEntriesCount} /> ); @@ -169,6 +173,7 @@ function Conversation() { attemptData.runningProcessDetails, handleConversationUpdate, allEntries, + visibleCount, ]); // Check if we should show the status banner - only if the most recent process failed/stopped @@ -192,14 +197,12 @@ function Conversation() { onScroll={handleLogsScroll} className="h-full overflow-y-auto" > - {visibleCount < allEntries.length && ( + {visibleCount - visibleRunningEntriesCount < allEntries.length && (
diff --git a/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx b/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx index d749829b..1ffcebaf 100644 --- a/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +++ b/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx @@ -8,6 +8,8 @@ import useNormalizedConversation from '@/hooks/useNormalizedConversation'; interface NormalizedConversationViewerProps { executionProcess: ExecutionProcess; onConversationUpdate?: () => void; + onDisplayEntriesChange?: (num: number) => void; + visibleEntriesNum?: number; diff?: WorktreeDiff | null; isBackgroundRefreshing?: boolean; diffDeletable?: boolean; @@ -17,11 +19,15 @@ export function NormalizedConversationViewer({ executionProcess, diffDeletable, onConversationUpdate, + visibleEntriesNum, + onDisplayEntriesChange, }: NormalizedConversationViewerProps) { const { loading, error, conversation, displayEntries } = useNormalizedConversation({ executionProcess, onConversationUpdate, + onDisplayEntriesChange, + visibleEntriesNum, }); if (loading) { diff --git a/frontend/src/hooks/useNormalizedConversation.ts b/frontend/src/hooks/useNormalizedConversation.ts index 4cd1a5af..4506d1e1 100644 --- a/frontend/src/hooks/useNormalizedConversation.ts +++ b/frontend/src/hooks/useNormalizedConversation.ts @@ -21,9 +21,13 @@ import { const useNormalizedConversation = ({ executionProcess, onConversationUpdate, + onDisplayEntriesChange, + visibleEntriesNum, }: { executionProcess?: ExecutionProcess; onConversationUpdate?: () => void; + onDisplayEntriesChange?: (num: number) => void; + visibleEntriesNum?: number; }) => { const { projectId } = useContext(TaskDetailsContext); const { attemptData } = useContext(TaskAttemptDataContext); @@ -413,10 +417,17 @@ const useNormalizedConversation = ({ if (!conversation?.entries) return []; // Filter out any null entries that may have been created by duplicate patch application - return conversation.entries.filter((entry): entry is NormalizedEntry => - Boolean(entry && (entry as NormalizedEntry).entry_type) + const displayEntries = conversation.entries.filter( + (entry): entry is NormalizedEntry => + Boolean(entry && (entry as NormalizedEntry).entry_type) ); - }, [conversation?.entries]); + onDisplayEntriesChange?.(displayEntries.length); + if (visibleEntriesNum && displayEntries.length > visibleEntriesNum) { + return displayEntries.slice(-visibleEntriesNum); + } + + return displayEntries; + }, [conversation?.entries, onDisplayEntriesChange, visibleEntriesNum]); return { displayEntries,