134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import { motion } from "framer-motion"
|
|
import { ChatInterface } from "@/components/chat-interface"
|
|
import type { Agent } from "@/lib/types"
|
|
|
|
export default function Home() {
|
|
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null)
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [agents, setAgents] = useState<Agent[]>([])
|
|
const [agentsError, setAgentsError] = useState<string | null>(null)
|
|
const [isAgentsLoading, setIsAgentsLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
// Try to load previously selected agent from localStorage
|
|
const savedAgent = localStorage.getItem("selected-agent")
|
|
|
|
if (savedAgent) {
|
|
try {
|
|
const agent = JSON.parse(savedAgent)
|
|
setSelectedAgent(agent)
|
|
} catch (err) {
|
|
console.error("[home] Failed to load saved agent:", err)
|
|
}
|
|
}
|
|
|
|
setIsLoading(false)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
const fetchAgents = async () => {
|
|
try {
|
|
setIsAgentsLoading(true)
|
|
setAgentsError(null)
|
|
const response = await fetch("/api/agents")
|
|
const data = (await response.json()) as { agents?: unknown; error?: string }
|
|
|
|
if (!response.ok || !data.agents) {
|
|
throw new Error(data.error || "Failed to load agents")
|
|
}
|
|
|
|
setAgents(data.agents as typeof agents)
|
|
} catch (err) {
|
|
setAgents([])
|
|
setAgentsError(err instanceof Error ? err.message : "Failed to load agents")
|
|
} finally {
|
|
setIsAgentsLoading(false)
|
|
}
|
|
}
|
|
|
|
fetchAgents()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!selectedAgent || agents.length === 0) return
|
|
const match = agents.find((agent) => agent.id === selectedAgent.id)
|
|
if (!match) {
|
|
setSelectedAgent(null)
|
|
localStorage.removeItem("selected-agent")
|
|
localStorage.removeItem("selected-agent-id")
|
|
return
|
|
}
|
|
|
|
if (
|
|
match.name !== selectedAgent.name ||
|
|
match.description !== selectedAgent.description
|
|
) {
|
|
setSelectedAgent(match)
|
|
localStorage.setItem("selected-agent-id", match.id)
|
|
localStorage.setItem("selected-agent", JSON.stringify(match))
|
|
}
|
|
}, [agents, selectedAgent])
|
|
|
|
const handleAgentSelected = (agent: Agent) => {
|
|
setSelectedAgent(agent)
|
|
localStorage.setItem("selected-agent-id", agent.id)
|
|
localStorage.setItem("selected-agent", JSON.stringify(agent))
|
|
}
|
|
|
|
if (isLoading) {
|
|
return null // Avoid hydration mismatch
|
|
}
|
|
|
|
// If no agent is selected but we have agents loaded, select the first one
|
|
// This ensures we always show the ChatInterface with its beautiful selection UI
|
|
const activeAgent = selectedAgent || (agents.length > 0 ? agents[0] : null)
|
|
|
|
if (!activeAgent) {
|
|
return (
|
|
<motion.div
|
|
className="gallery-shell h-screen"
|
|
initial={{ opacity: 0, y: 25 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.9, ease: "easeOut" }}
|
|
>
|
|
<div className="flex h-full flex-col items-center justify-center gap-4 px-6 text-center">
|
|
{agentsError ? (
|
|
<p className="text-xs text-destructive">{agentsError}</p>
|
|
) : (
|
|
<p className="text-sm uppercase tracking-[0.2em] text-muted-foreground">
|
|
Loading agents...
|
|
</p>
|
|
)}
|
|
</div>
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
className="gallery-shell mobile-shell h-screen"
|
|
initial={{ opacity: 0, y: 25 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.9, ease: "easeOut" }}
|
|
>
|
|
<div className="flex h-full flex-col">
|
|
<main className="flex-1 overflow-hidden px-3 py-4 sm:px-6 sm:py-6">
|
|
<div className="mx-auto flex h-full max-w-5xl justify-center">
|
|
<div className="h-full w-full">
|
|
<ChatInterface
|
|
agent={activeAgent}
|
|
agents={agents}
|
|
onAgentSelected={handleAgentSelected}
|
|
isAgentsLoading={isAgentsLoading}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</motion.div>
|
|
)
|
|
}
|