hide streamed logs as they arrive, but show a button to load previous (#257)

This commit is contained in:
Anastasiia Solop
2025-07-19 16:04:37 +02:00
committed by GitHub
parent 0e91ecd016
commit ee77d999bd
3 changed files with 29 additions and 9 deletions

View File

@@ -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<HTMLDivElement>(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}
/>
</div>
);
@@ -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 && (
<div className="flex justify-center mb-4">
<Button
variant="outline"
className="w-full"
onClick={() =>
setVisibleCount((c) => Math.min(c + 100, allEntries.length))
}
onClick={() => setVisibleCount((c) => c + 100)}
>
Load previous logs
</Button>

View File

@@ -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) {

View File

@@ -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,