import { IconPlus } from "@tabler/icons-react" import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { AssistantMessage } from "@/components/chat/assistant-message" import { ChatComposer } from "@/components/chat/chat-composer" import { ChatEmptyState } from "@/components/chat/chat-empty-state" import { ModelSelector } from "@/components/chat/model-selector" import { SessionHistoryMenu } from "@/components/chat/session-history-menu" import { TypingIndicator } from "@/components/chat/typing-indicator" import { UserMessage } from "@/components/chat/user-message" import { PageHeader } from "@/components/page-header" import { Button } from "@/components/ui/button" import { useChatModels } from "@/hooks/use-chat-models" import { useGateway } from "@/hooks/use-gateway" import { usePicoChat } from "@/hooks/use-pico-chat" import { useSessionHistory } from "@/hooks/use-session-history" export function ChatPage() { const { t } = useTranslation() const scrollRef = useRef(null) const [isAtBottom, setIsAtBottom] = useState(true) const [input, setInput] = useState("") const { messages, isTyping, activeSessionId, sendMessage, switchSession, newChat, } = usePicoChat() const { state: gwState } = useGateway() const isConnected = gwState === "running" const { defaultModelName, hasConfiguredModels, apiKeyModels, oauthModels, localModels, handleSetDefault, } = useChatModels({ isConnected }) const { sessions, hasMore, loadError, loadErrorMessage, observerRef, loadSessions, handleDeleteSession, } = useSessionHistory({ activeSessionId, onDeletedActiveSession: newChat, }) const handleScroll = (e: React.UIEvent) => { const { scrollTop, scrollHeight, clientHeight } = e.currentTarget setIsAtBottom(scrollHeight - scrollTop <= clientHeight + 10) } useEffect(() => { if (isAtBottom && scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight } }, [messages, isTyping, isAtBottom]) const handleSend = () => { if (!input.trim() || !isConnected) return sendMessage(input.trim()) setInput("") } return (
) } > { if (open) { void loadSessions(true) } }} onSwitchSession={switchSession} onDeleteSession={handleDeleteSession} />
{messages.length === 0 && !isTyping && ( )} {messages.map((msg) => (
{msg.role === "assistant" ? ( ) : ( )}
))} {isTyping && }
) }