import React, { memo } from 'react'; import { Box, Text, Static } from 'ink'; import type { Message } from '../../../models/types.js'; import { getMessageText } from '../../../models/media.js'; import { renderMarkdown } from '../markdown.js'; import { getBannerLines } from '../banner.js'; export interface MessageListProps { messages: Message[]; scrollOffset?: number; streamingContent?: string; verbose?: boolean; } // Helper to format timestamp in human-readable way function formatTimestamp(timestamp: number): string { const date = new Date(timestamp); const now = new Date(); const isSameDay = date.toDateString() === now.toDateString(); if (isSameDay) { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } return date.toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // Individual message component const MessageItem = memo(function MessageItem({ message, index, }: { message: Message; index: number; }): React.ReactElement { const isUser = message.role === 'user'; const accentColor = isUser ? 'blue' : '#ff8c00'; const timestampText = message.timestamp ? formatTimestamp(message.timestamp) : 'unknown time'; return ( {/* Author line */} {isUser ? 'You' : 'Flynn'} | {timestampText} {/* Content */} {message.role === 'assistant' ? renderMarkdown(getMessageText(message)) : getMessageText(message)} ); }); export const MessageList = memo(function MessageList({ messages, scrollOffset = 0, streamingContent, verbose = false, }: MessageListProps): React.ReactElement { const visibleMessages = messages.slice(scrollOffset); return ( {visibleMessages.length === 0 && !streamingContent ? ( {getBannerLines().map((line, i) => ( {line} ))} {'\n'}Start typing to chat with Flynn. ) : ( <> {(message, index) => ( )} {streamingContent && ( Flynn {verbose ? streamingContent : renderMarkdown(streamingContent)} )} )} {messages.length > 0 && scrollOffset > 0 && ( {scrollOffset} more above )} ); });