feat: enhance TUI with colors, command hints, and improved rendering
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import React, { memo } from 'react';
|
||||
import { Box, Text, Static } from 'ink';
|
||||
import type { Message } from '../../../models/types.js';
|
||||
import { renderMarkdown } from '../markdown.js';
|
||||
|
||||
@@ -9,12 +9,71 @@ export interface MessageListProps {
|
||||
streamingContent?: string;
|
||||
}
|
||||
|
||||
export function MessageList({
|
||||
// Helper to format timestamp in human-readable way
|
||||
function formatTimestamp(timestamp: number): string {
|
||||
const now = Date.now();
|
||||
const diff = now - timestamp;
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (seconds < 60) return 'just now';
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
if (days < 7) return `${days}d ago`;
|
||||
return new Date(timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
// 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) : '';
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={index}
|
||||
marginBottom={1}
|
||||
flexDirection="row"
|
||||
>
|
||||
<Box minWidth={1} backgroundColor={accentColor} />
|
||||
<Box
|
||||
flexDirection="column"
|
||||
flexGrow={1}
|
||||
paddingX={2}
|
||||
paddingY={1}
|
||||
>
|
||||
{/* Author line */}
|
||||
<Box marginBottom={1} justifyContent="space-between" flexDirection="row">
|
||||
<Text color={accentColor} bold>
|
||||
{isUser ? 'You' : 'Flynn'}
|
||||
</Text>
|
||||
<Text color="gray">| {timestampText}</Text>
|
||||
</Box>
|
||||
|
||||
{/* Content */}
|
||||
<Text wrap="wrap">
|
||||
{message.role === 'assistant'
|
||||
? renderMarkdown(message.content)
|
||||
: message.content}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
export const MessageList = memo(function MessageList({
|
||||
messages,
|
||||
scrollOffset = 0,
|
||||
streamingContent,
|
||||
}: MessageListProps): React.ReactElement {
|
||||
// Calculate visible area (approximate, Ink handles overflow)
|
||||
const visibleMessages = messages.slice(scrollOffset);
|
||||
|
||||
return (
|
||||
@@ -23,26 +82,25 @@ export function MessageList({
|
||||
<Text color="gray">No messages yet. Start typing to chat with Flynn.</Text>
|
||||
) : (
|
||||
<>
|
||||
{visibleMessages.map((message, index) => (
|
||||
<Box key={`${scrollOffset + index}-${message.role}`} marginBottom={1} flexDirection="column">
|
||||
<Text color={message.role === 'user' ? 'blue' : 'green'} bold>
|
||||
{message.role === 'user' ? 'You:' : 'Flynn:'}
|
||||
</Text>
|
||||
<Box marginLeft={1}>
|
||||
<Text wrap="wrap">
|
||||
{message.role === 'assistant'
|
||||
? renderMarkdown(message.content)
|
||||
: message.content}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
<Static items={visibleMessages}>
|
||||
{(message, index) => (
|
||||
<MessageItem key={index} message={message} index={index} />
|
||||
)}
|
||||
</Static>
|
||||
{streamingContent && (
|
||||
<Box marginBottom={1} flexDirection="column">
|
||||
<Text color="green" bold>Flynn:</Text>
|
||||
<Box marginLeft={1}>
|
||||
<Box marginBottom={1} flexDirection="row">
|
||||
<Box minWidth={1} backgroundColor="#ff8c00" />
|
||||
<Box
|
||||
flexDirection="column"
|
||||
flexGrow={1}
|
||||
paddingX={2}
|
||||
paddingY={1}
|
||||
>
|
||||
<Box marginBottom={1}>
|
||||
<Text color="#ff8c00" bold>Flynn</Text>
|
||||
</Box>
|
||||
<Text wrap="wrap">{streamingContent}</Text>
|
||||
<Text color="yellow">|</Text>
|
||||
<Text color="yellow">▌</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
@@ -55,4 +113,4 @@ export function MessageList({
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user