Fundamental_Analysis/frontend/src/components/workflow/RealtimeLogs.tsx
Lv, Qi e9e4d0c1b3 chore: massive update covering recent refactoring and bug fixes
- fix: infinite message loop in workflow orchestrator
- feat: restore realtime LLM streaming from report generator to frontend
- refactor: major update to provider services (generic workers, workflow adapters)
- refactor: common contracts and message definitions updated
- feat: enhanced logging and observability in orchestrator
- docs: update project management tasks and status
- chore: dependency updates and config adjustments
2025-11-30 19:17:02 +08:00

77 lines
3.6 KiB
TypeScript

import { useState } from 'react';
import { Terminal, ChevronUp, ChevronDown } from 'lucide-react';
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useAutoScroll } from '@/hooks/useAutoScroll';
import { cn } from "@/lib/utils";
export interface LogEntry {
log: string;
timestamp?: number; // Added optional timestamp for potential sorting if needed
}
interface RealtimeLogsProps {
logs: string[];
className?: string;
}
export function RealtimeLogs({ logs, className }: RealtimeLogsProps) {
// Default to expanded if there are logs, or maybe collapsed to be unintrusive?
// Original code: const [isExpanded, setIsExpanded] = useState(false);
// Let's keep it collapsed by default as per original code to avoid clutter.
const [isExpanded, setIsExpanded] = useState(false);
const logsViewportRef = useAutoScroll(logs.length);
const toggleExpand = () => {
setIsExpanded(!isExpanded);
};
return (
<Card className={cn("flex flex-col shadow-sm transition-all duration-300 ease-in-out border-l-4 border-l-primary py-0 gap-0 bg-background overflow-hidden", className, isExpanded ? "h-[300px]" : "h-8")}>
<div
className="flex items-center justify-between px-2 py-1 cursor-pointer hover:bg-muted/50 transition-colors h-8 shrink-0"
onClick={toggleExpand}
>
<div className="flex items-center gap-2 overflow-hidden flex-1">
<Terminal className="h-3 w-3 text-muted-foreground shrink-0" />
<span className="text-[10px] font-medium text-muted-foreground whitespace-nowrap shrink-0 mr-2">Real-time Logs</span>
{/* Preview last log when collapsed */}
{!isExpanded && logs.length > 0 && (
<div className="flex-1 flex items-center gap-2 overflow-hidden text-[10px] font-mono text-muted-foreground/80">
<span className="truncate">{logs[logs.length - 1]}</span>
</div>
)}
{!isExpanded && logs.length === 0 && (
<span className="text-[10px] italic text-muted-foreground/50">Waiting for logs...</span>
)}
</div>
<Button variant="ghost" size="icon" className="h-4 w-4 text-muted-foreground hover:text-foreground shrink-0 ml-2">
{isExpanded ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
</Button>
</div>
{/* Expanded Content */}
<div
className={cn(
"flex-1 bg-muted/10 border-t transition-all duration-300 min-h-0",
isExpanded ? "opacity-100 visible" : "opacity-0 invisible h-0 overflow-hidden"
)}
>
<div ref={logsViewportRef} className="h-full overflow-y-auto p-3 font-mono text-[10px] leading-relaxed scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent">
<div className="space-y-1">
{logs.length === 0 && <span className="text-muted-foreground italic">Waiting for logs...</span>}
{logs.map((entry, i) => (
<div key={i} className="break-all flex gap-2">
<span className="text-foreground/90">{entry}</span>
</div>
))}
</div>
</div>
</div>
</Card>
);
}