/** * MessagesBrowser — Sidebar list of recent assistant messages. * * Used by annotate-last. Lets the user pick which assistant message to * annotate when the newest transcript entry isn't the one they intended * (e.g., after `/rewind`). */ import React from "react"; export interface PickerMessage { messageId: string; text: string; timestamp?: string; } interface MessagesBrowserProps { messages: PickerMessage[]; selectedMessageId: string | null; onSelect: (messageId: string) => void; annotationCounts?: Map; } // Hard cap for browsers where line-clamp is unavailable, and to avoid huge sidebar text nodes. const PREVIEW_MAX_CHARS = 140; function previewText(text: string): string { const normalized = text.replace(/\s+/g, " ").trim(); return normalized.length > PREVIEW_MAX_CHARS ? normalized.slice(0, PREVIEW_MAX_CHARS).trimEnd() + "…" : normalized; } function formatTimestamp(ts?: string): string | null { if (!ts) return null; const d = new Date(ts); if (Number.isNaN(d.getTime())) return null; return d.toLocaleString([], { hour: "2-digit", minute: "2-digit", month: "short", day: "numeric", }); } export const MessagesBrowser: React.FC = ({ messages, selectedMessageId, onSelect, annotationCounts, }) => { if (messages.length === 0) { return (
No recent assistant messages found.
); } return (
Recent messages — newest first
{messages.map((msg, idx) => { const isSelected = msg.messageId === selectedMessageId; const isDefault = idx === 0; const ts = formatTimestamp(msg.timestamp); const annotationCount = annotationCounts?.get(msg.messageId) ?? 0; return ( ); })}
); };