Improve TUI timestamp consistency and formatting

This commit is contained in:
William Valentin
2026-02-21 09:25:14 -08:00
parent e582132ba8
commit c6731c8e20
3 changed files with 39 additions and 21 deletions
+14 -3
View File
@@ -78,10 +78,19 @@ export function App({
onTransfer, onTransfer,
onExit, onExit,
}: AppProps): React.ReactElement { }: 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 ctrlCExitWindowMs = 1_500;
const { exit } = useApp(); const { exit } = useApp();
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const [messages, setMessages] = useState<Message[]>(session.getHistory()); const [messages, setMessages] = useState<Message[]>(ensureTimestamps(session.getHistory()));
const [isStreaming, setIsStreaming] = useState(false); const [isStreaming, setIsStreaming] = useState(false);
const [streamingContent, setStreamingContent] = useState(''); const [streamingContent, setStreamingContent] = useState('');
const [scrollOffset, setScrollOffset] = useState(0); const [scrollOffset, setScrollOffset] = useState(0);
@@ -682,7 +691,7 @@ export function App({
const messageWithTimestamp = session.addMessage(userMessage); const messageWithTimestamp = session.addMessage(userMessage);
setMessages(prev => [...prev, messageWithTimestamp]); setMessages(prev => [...prev, messageWithTimestamp]);
} else { } else {
setMessages(prev => [...prev, { ...userMessage, timestamp: Date.now() }]); setMessages(prev => [...prev, ensureTimestamp(userMessage)]);
} }
setScrollOffset(0); setScrollOffset(0);
@@ -696,7 +705,7 @@ export function App({
await agent.process(command.content); await agent.process(command.content);
const usage = agent.getUsage(); const usage = agent.getUsage();
setTokenUsage({ inputTokens: usage.inputTokens, outputTokens: usage.outputTokens }); setTokenUsage({ inputTokens: usage.inputTokens, outputTokens: usage.outputTokens });
setMessages(session.getHistory()); setMessages(ensureTimestamps(session.getHistory()));
} else if (modelClient.chatStream) { } else if (modelClient.chatStream) {
let fullContent = ''; let fullContent = '';
@@ -757,6 +766,8 @@ export function App({
exit, exit,
onExit, onExit,
isStreaming, isStreaming,
ensureTimestamp,
ensureTimestamps,
messages.length, messages.length,
tokenUsage.inputTokens, tokenUsage.inputTokens,
tokenUsage.outputTokens, tokenUsage.outputTokens,
+7 -11
View File
@@ -13,16 +13,12 @@ export interface MessageListProps {
} }
// Helper to format timestamp in human-readable way // 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 date = new Date(timestamp);
const now = new Date(); return {
const isSameDay = date.toDateString() === now.toDateString(); date: date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' }),
time: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
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 // Individual message component
@@ -35,7 +31,7 @@ const MessageItem = memo(function MessageItem({
}): React.ReactElement { }): React.ReactElement {
const isUser = message.role === 'user'; const isUser = message.role === 'user';
const accentColor = isUser ? 'blue' : '#ff8c00'; const accentColor = isUser ? 'blue' : '#ff8c00';
const timestampText = message.timestamp ? formatTimestamp(message.timestamp) : 'unknown time'; const timestamp = formatTimestampParts(message.timestamp ?? Date.now());
return ( return (
<Box <Box
@@ -55,7 +51,7 @@ const MessageItem = memo(function MessageItem({
<Text color={accentColor} bold> <Text color={accentColor} bold>
{isUser ? 'You' : 'Flynn'} {isUser ? 'You' : 'Flynn'}
</Text> </Text>
<Text color="gray">| {timestampText}</Text> <Text color="gray">| {timestamp.date} | {timestamp.time}</Text>
</Box> </Box>
{/* Content */} {/* Content */}
+15 -4
View File
@@ -48,12 +48,16 @@ export function formatPrompt(state: 'default' | 'thinking'): string {
return `${colors.orange}${colors.bold}flynn>${colors.reset} `; return `${colors.orange}${colors.bold}flynn>${colors.reset} `;
} }
function formatMessageTime(timestamp: number): string { function formatMessageTimestampParts(timestamp: number): { date: string; time: string } {
return new Date(timestamp).toLocaleTimeString([], { const date = new Date(timestamp);
return {
date: date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' }),
time: date.toLocaleTimeString([], {
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
second: '2-digit', second: '2-digit',
}); }),
};
} }
export interface MinimalTuiConfig { export interface MinimalTuiConfig {
@@ -1237,9 +1241,16 @@ export class MinimalTui {
} }
private async handleMessage(content: string): Promise<void> { private async handleMessage(content: string): Promise<void> {
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 // Print Flynn label before response
process.stdout.write( 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(); this.startBusyIndicator();