import React from 'react'; import { MessageSquare, AlertCircle } from 'lucide-react'; import { Card } from '../ui/card'; import { Button } from '../ui/button'; import BackButton from '../ui/BackButton'; import { ScrollArea } from '../ui/scroll-area'; import MarkdownContent from '../MarkdownContent'; import ToolCallWithResponse from '../ToolCallWithResponse'; import ImagePreview from '../ImagePreview'; import { ToolRequestMessageContent, ToolResponseMessageContent, TextContent, } from '../../types/message'; import { type Message } from '../../types/message'; import { formatMessageTimestamp } from '../../utils/timeUtils'; import { extractImagePaths, removeImagePathsFromText } from '../../utils/imageUtils'; /** * Get tool responses map from messages */ export const getToolResponsesMap = ( messages: Message[], messageIndex: number, toolRequests: ToolRequestMessageContent[] ) => { const responseMap = new Map(); if (messageIndex >= 0) { for (let i = messageIndex + 1; i < messages.length; i++) { const responses = messages[i].content .filter((c) => c.type === 'toolResponse') .map((c) => c as ToolResponseMessageContent); for (const response of responses) { const matchingRequest = toolRequests.find((req) => req.id === response.id); if (matchingRequest) { responseMap.set(response.id, response); } } } } return responseMap; }; /** * Props for the SessionHeaderCard component */ export interface SessionHeaderCardProps { onBack: () => void; children: React.ReactNode; } /** * Common header card for session views */ export const SessionHeaderCard: React.FC = ({ onBack, children }) => { return ( {children} ); }; /** * Props for the SessionMessages component */ export interface SessionMessagesProps { messages: Message[]; isLoading: boolean; error: string | null; onRetry: () => void; } /** * Common component for displaying session messages */ export const SessionMessages: React.FC = ({ messages, isLoading, error, onRetry, }) => { return (
{isLoading ? (
) : error ? (

Error Loading Session Details

{error}

) : messages?.length > 0 ? ( messages .map((message, index) => { // Extract text content from the message let textContent = message.content .filter((c): c is TextContent => c.type === 'text') .map((c) => c.text) .join('\n'); // Extract image paths from the message const imagePaths = extractImagePaths(textContent); // Remove image paths from text for display const displayText = imagePaths.length > 0 ? removeImagePathsFromText(textContent, imagePaths) : textContent; // Get tool requests from the message const toolRequests = message.content .filter((c) => c.type === 'toolRequest') .map((c) => c as ToolRequestMessageContent); // Get tool responses map using the helper function const toolResponsesMap = getToolResponsesMap(messages, index, toolRequests); // Skip pure tool response messages for cleaner display const isOnlyToolResponse = message.content.length > 0 && message.content.every((c) => c.type === 'toolResponse'); if (message.role === 'user' && isOnlyToolResponse) { return null; } return (
{message.role === 'user' ? 'You' : 'Goose'} {formatMessageTimestamp(message.created)}
{/* Text content */} {displayText && (
0 || imagePaths.length > 0 ? 'mb-4' : ''}`} >
)} {/* Render images if any */} {imagePaths.length > 0 && (
{imagePaths.map((imagePath, imageIndex) => ( ))}
)} {/* Tool requests and responses */} {toolRequests.length > 0 && (
{toolRequests.map((toolRequest) => ( ))}
)}
); }) .filter(Boolean) // Filter out null entries ) : (

No messages found

This session doesn't contain any messages

)}
); };