import { useState } from 'react'; import { MessageSquare, Send, ChevronDown, ChevronRight } from 'lucide-react'; import type { Chat } from '@/api/types'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { formatTokens } from '@/lib/format'; interface Props { sessionId: string; projectId: string; chats: Chat[]; onOpenChat: (chatId: string) => void; onSend: (content: string) => void; onReopenChat: (chatId: string) => Promise; } function relTime(iso: string): string { const now = Date.now(); const t = Date.parse(iso); if (Number.isNaN(t)) return ''; const sec = Math.max(0, Math.floor((now - t) / 1000)); if (sec < 60) return `${sec}s ago`; const min = Math.floor(sec / 60); if (min < 60) return `${min}m ago`; const hr = Math.floor(min / 60); if (hr < 24) return `${hr}h ago`; const day = Math.floor(hr / 24); return `${day}d ago`; } function ChatRow({ chat, onClick, dimmed, trailing, }: { chat: Chat; onClick: () => void; dimmed?: boolean; trailing?: string; }) { const meta: string[] = [relTime(chat.updated_at)]; if (chat.message_count !== undefined && chat.message_count > 0) { meta.push(`${chat.message_count} msg`); } const tokens = formatTokens(chat.effective_context_tokens); if (tokens) meta.push(tokens); const preview = chat.last_message_preview; return ( ); } export function SessionLandingPage({ chats, onOpenChat, onSend, onReopenChat, }: Props) { const [composerValue, setComposerValue] = useState(''); const [showClosed, setShowClosed] = useState(false); const openChats = chats .filter((c) => c.status === 'open') .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()); const closedChats = chats .filter((c) => c.status === 'closed') .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()); function handleSend() { const text = composerValue.trim(); if (!text) return; onSend(text); setComposerValue(''); } // TODO: Landing page chat counts are a snapshot at mount. New messages in // visible chats won't update the per-row stats until next mount/navigation. // Wiring WS reactivity through here is deferred (rare use case: user is in // a pane when messages stream, not on the landing page). return (
{/* Open chats */} {openChats.length > 0 && (

Open chats

    {openChats.map((chat) => (
  • onOpenChat(chat.id)} />
  • ))}
)} {/* Closed chats */} {closedChats.length > 0 && (
{showClosed && (
    {closedChats.map((chat) => (
  • void onReopenChat(chat.id)} dimmed trailing="Reopen" />
  • ))}
)}
)} {openChats.length === 0 && closedChats.length === 0 && (
No chats yet. Type below to start a conversation.
)}
{/* Composer */}