import { MessageCircle, Clock, Sparkles, ChevronDown, ChevronRight, GitBranch, ArrowRight } from 'lucide-react'; import { useState } from 'react'; import { MessageFlow } from './MessageFlow' import { formatLargeText } from '../utils/formatters'; interface ConversationThreadProps { conversation: { sessionId: string; projectPath: string; projectName: string; messages: Array<{ parentUuid: string | null; isSidechain: boolean; userType: string; cwd: string; sessionId: string; version: string; type: string; message: any; uuid: string; timestamp: string; }>; startTime: string; endTime: string; messageCount: number; }; } interface ConversationMessage { role: 'user' | 'assistant' | 'system'; content: any; timestamp: string; turnNumber?: number; isNewInTurn?: boolean; isDuplicate?: boolean; } export function ConversationThread({ conversation }: ConversationThreadProps) { const [expandedSections, setExpandedSections] = useState>(new Set(['flow'])); const toggleSection = (section: string) => { const newExpanded = new Set(expandedSections); if (newExpanded.has(section)) { newExpanded.delete(section); } else { newExpanded.add(section); } setExpandedSections(newExpanded); }; // Extract all messages and analyze conversation flow from JSONL messages const analyzeConversationFlow = () => { const allMessages: ConversationMessage[] = []; // Check if messages exist if (!conversation.messages || !Array.isArray(conversation.messages)) { console.warn('No messages found in conversation:', conversation); return allMessages; } // Convert JSONL messages to conversation messages conversation.messages.forEach((msg) => { // Parse the message content let parsedMessage: any; try { parsedMessage = typeof msg.message === 'string' ? JSON.parse(msg.message) : msg.message; } catch (e) { parsedMessage = msg.message; } // Determine the role based on the type field let role: 'user' | 'assistant' | 'system' = 'user'; if (msg.type === 'assistant') { role = 'assistant'; } else if (msg.type === 'system') { role = 'system'; } // Extract content based on message structure let content = null; if (parsedMessage) { if (parsedMessage.content) { content = parsedMessage.content; } else if (parsedMessage.text) { content = parsedMessage.text; } else if (Array.isArray(parsedMessage)) { content = parsedMessage; } else if (typeof parsedMessage === 'string') { content = parsedMessage; } else { content = parsedMessage; } } if (content) { allMessages.push({ role, content, timestamp: msg.timestamp, turnNumber: undefined, // Not available in JSONL format isNewInTurn: true, }); } }); return allMessages; }; const messages = analyzeConversationFlow(); // Debug logging to identify assistant response issues console.log('Conversation Debug:', { messageCount: conversation.messageCount, totalMessages: messages.length, messages: messages.map(m => ({ role: m.role, contentPreview: JSON.stringify(m.content)?.substring(0, 50), turn: m.turnNumber, ts: m.timestamp, })), }); if (messages.length === 0) { return (

No messages found

This conversation appears to be empty

); } return (
{/* Conversation Flow Header */}
toggleSection('flow')} >

Conversation Flow
Conversation processed - {messages.length} messages

{messages.length} messages • {conversation.messageCount} total

{new Date(messages[messages.length - 1]?.timestamp).toLocaleTimeString()} {expandedSections.has('flow') ? ( ) : ( )}
{/* Conversation Messages */} {expandedSections.has('flow') && (
{messages.map((message, index) => ( ))} {/* Conversation Summary */}
Conversation Summary
{messages.length} messages • {conversation.messageCount} total messages
Latest: {new Date().toLocaleTimeString()}
)}
); }