feat(tui): add markdown rendering and scroll support to MessageList
This commit is contained in:
@@ -1,29 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import type { Message } from '../../../models/types.js';
|
import type { Message } from '../../../models/types.js';
|
||||||
|
import { renderMarkdown } from '../markdown.js';
|
||||||
|
|
||||||
export interface MessageListProps {
|
export interface MessageListProps {
|
||||||
messages: Message[];
|
messages: Message[];
|
||||||
maxHeight?: number;
|
scrollOffset?: number;
|
||||||
|
streamingContent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MessageList({ messages, maxHeight = 20 }: MessageListProps): React.ReactElement {
|
export function MessageList({
|
||||||
// Show only recent messages that fit
|
messages,
|
||||||
const visibleMessages = messages.slice(-maxHeight);
|
scrollOffset = 0,
|
||||||
|
streamingContent,
|
||||||
|
}: MessageListProps): React.ReactElement {
|
||||||
|
// Calculate visible area (approximate, Ink handles overflow)
|
||||||
|
const visibleMessages = messages.slice(scrollOffset);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" flexGrow={1} paddingX={1}>
|
<Box flexDirection="column" flexGrow={1} paddingX={1} overflowY="hidden">
|
||||||
{visibleMessages.length === 0 ? (
|
{visibleMessages.length === 0 && !streamingContent ? (
|
||||||
<Text color="gray">No messages yet. Start typing to chat with Flynn.</Text>
|
<Text color="gray">No messages yet. Start typing to chat with Flynn.</Text>
|
||||||
) : (
|
) : (
|
||||||
visibleMessages.map((message, index) => (
|
<>
|
||||||
<Box key={index} marginBottom={1}>
|
{visibleMessages.map((message, index) => (
|
||||||
<Text color={message.role === 'user' ? 'blue' : 'green'}>
|
<Box key={`${scrollOffset + index}-${message.role}`} marginBottom={1} flexDirection="column">
|
||||||
{message.role === 'user' ? 'You: ' : 'Flynn: '}
|
<Text color={message.role === 'user' ? 'blue' : 'green'} bold>
|
||||||
</Text>
|
{message.role === 'user' ? 'You:' : 'Flynn:'}
|
||||||
<Text wrap="wrap">{message.content}</Text>
|
</Text>
|
||||||
</Box>
|
<Box marginLeft={1}>
|
||||||
))
|
<Text wrap="wrap">
|
||||||
|
{message.role === 'assistant'
|
||||||
|
? renderMarkdown(message.content)
|
||||||
|
: message.content}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
{streamingContent && (
|
||||||
|
<Box marginBottom={1} flexDirection="column">
|
||||||
|
<Text color="green" bold>Flynn:</Text>
|
||||||
|
<Box marginLeft={1}>
|
||||||
|
<Text wrap="wrap">{streamingContent}</Text>
|
||||||
|
<Text color="yellow">|</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{messages.length > 0 && scrollOffset > 0 && (
|
||||||
|
<Box position="absolute" marginTop={-1}>
|
||||||
|
<Text color="gray">{scrollOffset} more above</Text>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user