From c013befa940270547f34c560f46eec85da76d9c4 Mon Sep 17 00:00:00 2001 From: Douglas Date: Tue, 24 Mar 2026 11:30:32 +0000 Subject: [PATCH] add auto scroll animation for the worker log --- src/components/WorkFlow/node.tsx | 106 +++++++++++++++++-------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/src/components/WorkFlow/node.tsx b/src/components/WorkFlow/node.tsx index c787944fb..cd3208952 100644 --- a/src/components/WorkFlow/node.tsx +++ b/src/components/WorkFlow/node.tsx @@ -280,18 +280,30 @@ export function Node({ id, data }: NodeProps) { const toolkits = selectedTask?.toolkits; const lastToolkit = toolkits?.[toolkits.length - 1]; - const toolkitChangeKey = `${selectedTask?.id ?? ''}:${toolkits?.length ?? 0}:${lastToolkit?.toolkitId ?? ''}:${lastToolkit?.toolkitStatus ?? ''}`; + const toolkitChangeKey = `${selectedTask?.id ?? ''}:${toolkits?.length ?? 0}:${lastToolkit?.toolkitId ?? ''}:${lastToolkit?.toolkitStatus ?? ''}:${lastToolkit?.message?.slice(-30) ?? ''}`; useEffect(() => { if (!isExpanded || !toolkits?.length) return; scrollLogToBottom(); }, [isExpanded, toolkits?.length, toolkitChangeKey, scrollLogToBottom]); - // Reset scroll-to-bottom flag when switching tasks so new task always starts at bottom + // Scroll to bottom when a report appears + useEffect(() => { + if (!isExpanded || !selectedTask?.report) return; + scrollLogToBottom(); + }, [isExpanded, selectedTask?.report, scrollLogToBottom]); + + // Reset scroll-to-bottom flag when switching tasks or when panel opens useEffect(() => { wasAtBottomRef.current = true; }, [selectedTask?.id]); + useEffect(() => { + if (isExpanded) { + wasAtBottomRef.current = true; + } + }, [isExpanded]); + // Track whether user has scrolled up so we don't override manual reading useEffect(() => { const el = logRef.current; @@ -413,17 +425,17 @@ export function Node({ id, data }: NodeProps) { : 'w-[342px]' } ${ data.isEditMode ? 'h-full' : 'max-h-[calc(100vh-200px)]' - } flex overflow-hidden rounded-xl border border-solid border-worker-border-default bg-worker-surface-primary ${ + } rounded-xl border-worker-border-default bg-worker-surface-primary flex overflow-hidden border border-solid ${ getCurrentTask()?.activeAgent === id ? `${agentMap[data.type]?.borderColor} z-50` - : 'z-10 border-worker-border-default' - } transition-all duration-300 ease-in-out ${ + : 'border-worker-border-default z-10' + } ease-in-out transition-all duration-300 ${ (data.agent?.tasks?.length ?? 0) === 0 && 'opacity-30' }`} > -
-
-
+
+
+
-
+
@@ -448,7 +460,7 @@ export function Node({ id, data }: NodeProps) { - +
{ e.stopPropagation(); const newWorkerList = workerList.filter( @@ -484,7 +496,7 @@ export function Node({ id, data }: NodeProps) {
{/* {JSON.stringify(data.agent)} */} {toolkitLabels.map((toolkit, index) => ( @@ -494,7 +506,7 @@ export function Node({ id, data }: NodeProps) { ))}
{ chatStore.setActiveWorkspace( chatStore.activeTaskId as string, @@ -506,15 +518,15 @@ export function Node({ id, data }: NodeProps) { > {browserImages.length > 0 && (
{browserImages.map((img, index) => (
{data.type} @@ -524,7 +536,7 @@ export function Node({ id, data }: NodeProps) { (_, index) => (
) )} @@ -533,8 +545,8 @@ export function Node({ id, data }: NodeProps) { {data.type === 'document_agent' && data?.agent?.tasks && data.agent.tasks.length > 0 && ( -
-
+
+
@@ -542,14 +554,14 @@ export function Node({ id, data }: NodeProps) { {data.type === 'developer_agent' && terminalTasks.length > 0 && (
{terminalTasks.map((task) => (
-
+
@@ -558,7 +570,7 @@ export function Node({ id, data }: NodeProps) { (_, index) => (
) )} @@ -566,7 +578,7 @@ export function Node({ id, data }: NodeProps) { )}
{data.agent?.tasks && data.agent?.tasks.length > 0 && ( -
+
{/*
Subtasks
*/}
{ e.stopPropagation(); }} - className="scrollbar scrollbar-always-visible flex flex-col gap-2 overflow-y-auto px-3 pb-2 duration-500 ease-out animate-in fade-in-0 slide-in-from-bottom-4" + className="scrollbar scrollbar-always-visible gap-2 px-3 pb-2 ease-out animate-in fade-in-0 slide-in-from-bottom-4 flex flex-col overflow-y-auto duration-500" style={{ maxHeight: data.img && data.img.length > 0 @@ -650,7 +662,7 @@ export function Node({ id, data }: NodeProps) { } }} key={`taskList-${task.id}-${task.failure_count}`} - className={`flex gap-2 rounded-xl px-sm py-sm transition-all duration-300 ease-in-out animate-in fade-in-0 slide-in-from-left-2 ${ + className={`gap-2 rounded-xl px-sm py-sm ease-in-out animate-in fade-in-0 slide-in-from-left-2 flex transition-all duration-300 ${ task.reAssignTo ? 'bg-task-fill-warning' : task.status === TaskStatus.COMPLETED @@ -743,14 +755,14 @@ export function Node({ id, data }: NodeProps) { : task.status === TaskStatus.BLOCKED ? 'text-text-body' : 'text-text-primary' - } pointer-events-auto select-text whitespace-pre-line text-wrap break-all text-xs font-medium leading-13`} + } text-xs font-medium leading-13 pointer-events-auto text-wrap break-all whitespace-pre-line select-text`} > -
+
No. {getTaskId(task.id)}
{task.reAssignTo ? ( -
+
Reassigned to {task.reAssignTo}
) : ( @@ -772,11 +784,11 @@ export function Node({ id, data }: NodeProps) {
{task.content}
{task?.status === TaskStatus.RUNNING && ( -
+
{/* active toolkit */} {lastActiveToolkit?.toolkitStatus === AgentStatusValue.RUNNING && ( -
+
{getToolkitIcon( lastActiveToolkit.toolkitName ?? '' )} @@ -787,11 +799,11 @@ export function Node({ id, data }: NodeProps) { ].activeWorkspace ? '!w-[100px]' : '!w-[500px]' - } min-w-0 flex-shrink-0 flex-grow-0 overflow-hidden text-ellipsis whitespace-nowrap pt-1 text-xs leading-17 text-text-primary`} + } min-w-0 pt-1 text-xs leading-17 text-text-primary flex-shrink-0 flex-grow-0 overflow-hidden text-ellipsis whitespace-nowrap`} >
@@ -812,14 +824,14 @@ export function Node({ id, data }: NodeProps) { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: 24 }} transition={{ duration: 0.3, ease: 'easeIn' }} - className="flex w-[342px] shrink-0 flex-col gap-sm overflow-hidden rounded-r-xl bg-worker-surface-secondary py-2 pl-sm" + className="gap-sm rounded-r-xl bg-worker-surface-secondary py-2 pl-sm flex w-[342px] shrink-0 flex-col overflow-hidden" >
{ e.stopPropagation(); }} - className="scrollbar scrollbar-always-visible max-h-[calc(100vh-200px)] overflow-y-scroll pr-sm" + className="scrollbar scrollbar-always-visible pr-sm max-h-[calc(100vh-200px)] overflow-y-scroll" > {selectedTask && ( @@ -829,7 +841,7 @@ export function Node({ id, data }: NodeProps) { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -16 }} transition={{ duration: 0.25, ease: 'easeIn' }} - className="flex w-full flex-col gap-sm" + className="gap-sm flex w-full flex-col" > {selectedTask.toolkits && selectedTask.toolkits.length > 0 && @@ -839,7 +851,7 @@ export function Node({ id, data }: NodeProps) { {toolkit.toolkitName === 'notice' ? (
{/* first row: icon + toolkit name */} -
+
{toolkit.toolkitStatus === AgentStatusValue.RUNNING ? ( + {toolkit.toolkitName}
{/* second row: method + message */} -
-
+
+
{toolkit.toolkitMethods ? toolkit.toolkitMethods .charAt(0) @@ -911,10 +923,10 @@ export function Node({ id, data }: NodeProps) { : ''}
{toolkit.message} @@ -925,7 +937,7 @@ export function Node({ id, data }: NodeProps) { {toolkit.message && ( @@ -948,9 +960,9 @@ export function Node({ id, data }: NodeProps) { onWheel={(e) => { e.stopPropagation(); }} - className="group relative my-2 flex w-full flex-col rounded-lg bg-surface-primary" + className="group my-2 rounded-lg bg-surface-primary relative flex w-full flex-col" > -
+
Completion Report