diff --git a/src/frontends/tui/components/App.tsx b/src/frontends/tui/components/App.tsx index 5e67298..9b8068e 100644 --- a/src/frontends/tui/components/App.tsx +++ b/src/frontends/tui/components/App.tsx @@ -78,10 +78,19 @@ export function App({ onTransfer, onExit, }: AppProps): React.ReactElement { + const ensureTimestamp = useCallback((message: Message): Message => ({ + ...message, + timestamp: message.timestamp ?? Date.now(), + }), []); + + const ensureTimestamps = useCallback((history: Message[]): Message[] => ( + history.map(ensureTimestamp) + ), [ensureTimestamp]); + const ctrlCExitWindowMs = 1_500; const { exit } = useApp(); const [input, setInput] = useState(''); - const [messages, setMessages] = useState(session.getHistory()); + const [messages, setMessages] = useState(ensureTimestamps(session.getHistory())); const [isStreaming, setIsStreaming] = useState(false); const [streamingContent, setStreamingContent] = useState(''); const [scrollOffset, setScrollOffset] = useState(0); @@ -682,7 +691,7 @@ export function App({ const messageWithTimestamp = session.addMessage(userMessage); setMessages(prev => [...prev, messageWithTimestamp]); } else { - setMessages(prev => [...prev, { ...userMessage, timestamp: Date.now() }]); + setMessages(prev => [...prev, ensureTimestamp(userMessage)]); } setScrollOffset(0); @@ -696,7 +705,7 @@ export function App({ await agent.process(command.content); const usage = agent.getUsage(); setTokenUsage({ inputTokens: usage.inputTokens, outputTokens: usage.outputTokens }); - setMessages(session.getHistory()); + setMessages(ensureTimestamps(session.getHistory())); } else if (modelClient.chatStream) { let fullContent = ''; @@ -757,6 +766,8 @@ export function App({ exit, onExit, isStreaming, + ensureTimestamp, + ensureTimestamps, messages.length, tokenUsage.inputTokens, tokenUsage.outputTokens, diff --git a/src/frontends/tui/components/MessageList.tsx b/src/frontends/tui/components/MessageList.tsx index b7d311f..66fa002 100644 --- a/src/frontends/tui/components/MessageList.tsx +++ b/src/frontends/tui/components/MessageList.tsx @@ -13,16 +13,12 @@ export interface MessageListProps { } // Helper to format timestamp in human-readable way -function formatTimestamp(timestamp: number): string { +function formatTimestampParts(timestamp: number): { date: string; time: 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' }); + return { + date: date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' }), + time: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + }; } // Individual message component @@ -35,7 +31,7 @@ const MessageItem = memo(function MessageItem({ }): React.ReactElement { const isUser = message.role === 'user'; const accentColor = isUser ? 'blue' : '#ff8c00'; - const timestampText = message.timestamp ? formatTimestamp(message.timestamp) : 'unknown time'; + const timestamp = formatTimestampParts(message.timestamp ?? Date.now()); return ( {isUser ? 'You' : 'Flynn'} - | {timestampText} + | {timestamp.date} | {timestamp.time} {/* Content */} diff --git a/src/frontends/tui/minimal.ts b/src/frontends/tui/minimal.ts index 855c9e9..df8a472 100644 --- a/src/frontends/tui/minimal.ts +++ b/src/frontends/tui/minimal.ts @@ -48,12 +48,16 @@ export function formatPrompt(state: 'default' | 'thinking'): string { return `${colors.orange}${colors.bold}flynn>${colors.reset} `; } -function formatMessageTime(timestamp: number): string { - return new Date(timestamp).toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); +function formatMessageTimestampParts(timestamp: number): { date: string; time: string } { + const date = new Date(timestamp); + return { + date: date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' }), + time: date.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }), + }; } export interface MinimalTuiConfig { @@ -1237,9 +1241,16 @@ export class MinimalTui { } private async handleMessage(content: string): Promise { + const userTimestamp = formatMessageTimestampParts(Date.now()); + process.stdout.write( + `\n${colors.blue}${colors.bold}You${colors.reset} ${colors.gray}[${userTimestamp.date} | ${userTimestamp.time}]${colors.reset}\n`, + ); + process.stdout.write(`${content}\n`); + + const assistantTimestamp = formatMessageTimestampParts(Date.now()); // Print Flynn label before response process.stdout.write( - `\n${colors.orange}${colors.bold}Flynn${colors.reset} ${colors.gray}[${formatMessageTime(Date.now())}]${colors.reset}\n`, + `\n${colors.orange}${colors.bold}Flynn${colors.reset} ${colors.gray}[${assistantTimestamp.date} | ${assistantTimestamp.time}]${colors.reset}\n`, ); this.startBusyIndicator();