import React, { useEffect, useState, useRef } from "react"; import { Terminal, ChevronDown, ChevronUp, AlertCircle, Info, Brain, Activity, } from "lucide-react"; import { Card } from "@/components/ds/ui/card"; import { useSDK } from "../root/SDKProvider"; import { supabase } from "../providers/supabase"; import { cn } from "@/lib/utils"; interface ProcessingEvent { id: string; activity_id: string; event_type: "thinking" | "acting" | "analyzing" | "success" | "error"; content: string; created_at: string; metadata?: any; } export const LiveTerminal = ({ activityId }: { activityId?: string }) => { const [events, setEvents] = useState([]); const [isExpanded, setIsExpanded] = useState(true); const scrollRef = useRef(null); const { isAvailable } = useSDK(); useEffect(() => { if (!isAvailable) return; // Fetch existing events const fetchEvents = async () => { let query = supabase .from("processing_events") .select("*") .order("created_at", { ascending: true }); if (activityId) { query = query.eq("activity_id", activityId); } else { query = query.limit(50); } const { data } = await query; if (data) setEvents(data); }; fetchEvents(); // Subscribe to new events const channel = supabase .channel("live-terminal") .on( "postgres_changes", { event: "INSERT", schema: "public", table: "processing_events", filter: activityId ? `activity_id=eq.${activityId}` : undefined, }, (payload) => { setEvents((prev) => [...prev, payload.new as ProcessingEvent].slice(-100), ); }, ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [activityId, isAvailable]); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [events]); const getEventIcon = (type: string) => { switch (type) { case "thinking": return ; case "acting": return ; case "analyzing": return ; case "success": return ; case "error": return ; default: return ; } }; return (
setIsExpanded(!isExpanded)} >
AI Live Terminal {events.length > 0 && ( LIVE )}
{isExpanded ? ( ) : ( )}
{isExpanded && (
{events.length === 0 ? (
Waiting for AI events...
) : ( events.map((event) => (
[ {new Date(event.created_at).toLocaleTimeString([], { hour12: false, })} ] {getEventIcon(event.event_type)} {event.content}
)) )}
)}
); };