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:
@@ -94,7 +94,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
const fetched = await conversationService.getAllConversations();
|
const fetched = await conversationService.getAllConversations();
|
||||||
const parsed = fetched.map((c) => ({ id: c.id, title: c.name }));
|
const parsed = fetched.map((c) => ({ id: c.id, title: c.name }));
|
||||||
setConversations(parsed);
|
setConversations(parsed);
|
||||||
setSelectedConversation(parsed[0] ?? null);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load conversations:", err);
|
console.error("Failed to load conversations:", err);
|
||||||
}
|
}
|
||||||
@@ -132,6 +131,14 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
const handleQuestionSubmit = async () => {
|
const handleQuestionSubmit = async () => {
|
||||||
if (!query.trim() || isLoading) return;
|
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" }]);
|
const currMessages = messages.concat([{ text: query, speaker: "user" }]);
|
||||||
setMessages(currMessages);
|
setMessages(currMessages);
|
||||||
setQuery("");
|
setQuery("");
|
||||||
@@ -150,7 +157,7 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
try {
|
try {
|
||||||
await conversationService.streamQuery(
|
await conversationService.streamQuery(
|
||||||
query,
|
query,
|
||||||
selectedConversation!.id,
|
activeConversation.id,
|
||||||
(event) => {
|
(event) => {
|
||||||
if (!isMountedRef.current) return;
|
if (!isMountedRef.current) return;
|
||||||
if (event.type === "tool_start") {
|
if (event.type === "tool_start") {
|
||||||
@@ -309,16 +316,44 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Conversation title bar */}
|
{messages.length === 0 ? (
|
||||||
{selectedConversation && (
|
/* ── Empty / homepage state ── */
|
||||||
<div className="bg-warm-white/80 backdrop-blur-sm border-b border-sand-light/50 px-6 py-2.5">
|
<div className="flex-1 flex flex-col items-center justify-center px-4 gap-6">
|
||||||
<p className="text-xs font-semibold text-warm-gray truncate max-w-2xl mx-auto uppercase tracking-wider">
|
{/* Mobile conversation drawer */}
|
||||||
{selectedConversation.title || "Untitled Conversation"}
|
{showConversations && (
|
||||||
</p>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="relative">
|
||||||
{/* Messages */}
|
<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="flex-1 overflow-y-auto px-4 py-6">
|
||||||
<div className="max-w-2xl mx-auto flex flex-col gap-3">
|
<div className="max-w-2xl mx-auto flex flex-col gap-3">
|
||||||
{/* Mobile conversation drawer */}
|
{/* Mobile conversation drawer */}
|
||||||
@@ -333,23 +368,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
</div>
|
</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) => {
|
{messages.map((msg, index) => {
|
||||||
if (msg.speaker === "tool")
|
if (msg.speaker === "tool")
|
||||||
return <ToolBubble key={index} text={msg.text} />;
|
return <ToolBubble key={index} text={msg.text} />;
|
||||||
@@ -363,7 +381,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Input */}
|
|
||||||
<footer className="border-t border-sand-light/40 bg-cream/80 backdrop-blur-sm">
|
<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">
|
<div className="max-w-2xl mx-auto px-4 py-3">
|
||||||
<MessageInput
|
<MessageInput
|
||||||
@@ -376,6 +393,8 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user