Add Claude.ai-style homepage with centered empty state

Show centered cat icon + "Ask me anything" + input when no messages
exist. Transition to scrollable messages + bottom input once chat
starts. Auto-create a conversation on first message if none selected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ryan
2026-03-11 09:47:37 -04:00
parent d1cb55ff1a
commit da9b52dda1

View File

@@ -94,7 +94,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
const fetched = await conversationService.getAllConversations();
const parsed = fetched.map((c) => ({ id: c.id, title: c.name }));
setConversations(parsed);
setSelectedConversation(parsed[0] ?? null);
} catch (err) {
console.error("Failed to load conversations:", err);
}
@@ -132,6 +131,14 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
const handleQuestionSubmit = async () => {
if (!query.trim() || isLoading) return;
let activeConversation = selectedConversation;
if (!activeConversation) {
const newConv = await conversationService.createConversation();
activeConversation = { title: newConv.name, id: newConv.id };
setSelectedConversation(activeConversation);
setConversations((prev) => [activeConversation!, ...prev]);
}
const currMessages = messages.concat([{ text: query, speaker: "user" }]);
setMessages(currMessages);
setQuery("");
@@ -150,7 +157,7 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
try {
await conversationService.streamQuery(
query,
selectedConversation!.id,
activeConversation.id,
(event) => {
if (!isMountedRef.current) return;
if (event.type === "tool_start") {
@@ -309,16 +316,44 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
</div>
</header>
{/* Conversation title bar */}
{selectedConversation && (
<div className="bg-warm-white/80 backdrop-blur-sm border-b border-sand-light/50 px-6 py-2.5">
<p className="text-xs font-semibold text-warm-gray truncate max-w-2xl mx-auto uppercase tracking-wider">
{selectedConversation.title || "Untitled Conversation"}
</p>
{messages.length === 0 ? (
/* ── Empty / homepage state ── */
<div className="flex-1 flex flex-col items-center justify-center px-4 gap-6">
{/* Mobile conversation drawer */}
{showConversations && (
<div className="md:hidden w-full max-w-2xl bg-warm-white rounded-2xl border border-sand-light p-3 shadow-sm">
<ConversationList
conversations={conversations}
onCreateNewConversation={handleCreateNewConversation}
onSelectConversation={handleSelectConversation}
selectedId={selectedConversation?.id}
/>
</div>
)}
{/* Messages */}
<div className="relative">
<div className="absolute -inset-6 bg-amber-soft/20 rounded-full blur-3xl" />
<img src={catIcon} alt="Simba" className="relative w-20 h-20" />
</div>
<h1
className="text-2xl font-bold text-charcoal"
style={{ fontFamily: "var(--font-display)" }}
>
Ask me anything
</h1>
<div className="w-full max-w-2xl">
<MessageInput
query={query}
handleQueryChange={handleQueryChange}
handleKeyDown={handleKeyDown}
handleQuestionSubmit={handleQuestionSubmit}
setSimbaMode={setSimbaMode}
isLoading={isLoading}
/>
</div>
</div>
) : (
/* ── Active chat state ── */
<>
<div className="flex-1 overflow-y-auto px-4 py-6">
<div className="max-w-2xl mx-auto flex flex-col gap-3">
{/* Mobile conversation drawer */}
@@ -333,23 +368,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
</div>
)}
{/* Empty state */}
{messages.length === 0 && !isLoading && (
<div className="flex flex-col items-center justify-center py-24 gap-5">
<div className="relative">
<div className="absolute -inset-6 bg-amber-soft/20 rounded-full blur-3xl" />
<img
src={catIcon}
alt="Simba"
className="relative w-16 h-16 opacity-50"
/>
</div>
<p className="text-warm-gray/60 text-sm">
Ask Simba anything
</p>
</div>
)}
{messages.map((msg, index) => {
if (msg.speaker === "tool")
return <ToolBubble key={index} text={msg.text} />;
@@ -363,7 +381,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
</div>
</div>
{/* Input */}
<footer className="border-t border-sand-light/40 bg-cream/80 backdrop-blur-sm">
<div className="max-w-2xl mx-auto px-4 py-3">
<MessageInput
@@ -376,6 +393,8 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
/>
</div>
</footer>
</>
)}
</div>
</div>
);