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 { ToolRequestMessageContent, ToolResponseMessageContent } from '../../types/message'; import { type Message } from '../../types/message'; /** * Format a timestamp into a human-readable date string */ export const formatDate = (timestamp: number) => { const date = new Date(timestamp * 1000); const getOrdinal = (n: number) => { const s = ['th', 'st', 'nd', 'rd']; const v = n % 100; return n + (s[(v - 20) % 10] || s[v] || s[0]); }; const hours = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, }); const month = date.toLocaleString('en-US', { month: 'short' }); const day = getOrdinal(date.getDate()); const year = date.getFullYear(); return `${hours}, ${month} ${day}, ${year}`; }; /** * 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 const textContent = message.content .filter((c) => c.type === 'text') .map((c) => c.text) .join('\n'); // 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'} {new Date(message.created * 1000).toLocaleTimeString()}
{/* Text content */} {textContent && (
0 ? 'mb-4' : ''}`}>
)} {/* 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

)}
); };