/** * Chat Page * Native React implementation communicating with OpenClaw Gateway * via gateway:rpc IPC. Session selector, thinking toggle, and refresh * are in the toolbar; messages render with markdown + streaming. */ import { useEffect, useRef } from 'react'; import { AlertCircle, Bot, MessageSquare, Sparkles } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { useChatStore } from '@/stores/chat'; import { useGatewayStore } from '@/stores/gateway'; import { LoadingSpinner } from '@/components/common/LoadingSpinner'; import { ChatToolbar } from './ChatToolbar'; import { ChatMessage } from './ChatMessage'; import { ChatInput } from './ChatInput'; import { extractText } from './message-utils'; export function Chat() { const gatewayStatus = useGatewayStore((s) => s.status); const isGatewayRunning = gatewayStatus.state === 'running'; const messages = useChatStore((s) => s.messages); const loading = useChatStore((s) => s.loading); const sending = useChatStore((s) => s.sending); const error = useChatStore((s) => s.error); const showThinking = useChatStore((s) => s.showThinking); const streamingMessage = useChatStore((s) => s.streamingMessage); const loadHistory = useChatStore((s) => s.loadHistory); const loadSessions = useChatStore((s) => s.loadSessions); const sendMessage = useChatStore((s) => s.sendMessage); const clearError = useChatStore((s) => s.clearError); const messagesEndRef = useRef(null); // Load data when gateway is running useEffect(() => { if (isGatewayRunning) { loadHistory(); loadSessions(); } }, [isGatewayRunning, loadHistory, loadSessions]); // Auto-scroll on new messages or streaming useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, streamingMessage, sending]); // Gateway not running if (!isGatewayRunning) { return (

Gateway Not Running

The OpenClaw Gateway needs to be running to use chat. It will start automatically, or you can start it from Settings.

); } // Extract streaming text for display const streamText = streamingMessage ? extractText(streamingMessage) : ''; return (
{/* Toolbar: session selector, refresh, thinking toggle */}
{/* spacer */}
{/* Messages Area */}
{loading ? (
) : messages.length === 0 && !sending ? ( ) : ( <> {messages.map((msg, idx) => ( ))} {/* Streaming message */} {sending && streamText && ( )} {/* Typing indicator when sending but no stream yet */} {sending && !streamText && ( )} )} {/* Scroll anchor */}
{/* Error bar */} {error && (

{error}

)} {/* Input Area */}
); } // ── Welcome Screen ────────────────────────────────────────────── function WelcomeScreen() { return (

ClawX Chat

Your AI assistant is ready. Start a conversation below.

{[ { icon: MessageSquare, title: 'Ask Questions', desc: 'Get answers on any topic' }, { icon: Sparkles, title: 'Creative Tasks', desc: 'Writing, brainstorming, ideas' }, ].map((item, i) => (

{item.title}

{item.desc}

))}
); } // ── Typing Indicator ──────────────────────────────────────────── function TypingIndicator() { return (
); } export default Chat;