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
)}
);
});