feat(ui): Add React based UI for the vibes at /app

This adds a completely separate frontend based on React because I
found that code gen works better with React once the application gets
bigger. In particular it was getting very hard to move past add
connectors and actions.

The idea is to replace the standard UI with this once it has been
tested. But for now it is available at /app in addition to the
original at /

Signed-off-by: Richard Palethorpe <io@richiejp.com>
This commit is contained in:
Richard Palethorpe
2025-03-24 14:36:18 +00:00
parent 438a65caf6
commit 71e66c651c
61 changed files with 6452 additions and 2 deletions

View File

@@ -0,0 +1,106 @@
import { useState, useRef, useEffect } from 'react';
import { useParams, useOutletContext } from 'react-router-dom';
import { useChat } from '../hooks/useChat';
function Chat() {
const { name } = useParams();
const { showToast } = useOutletContext();
const [message, setMessage] = useState('');
const messagesEndRef = useRef(null);
// Use our custom chat hook
const {
messages,
sending,
error,
isConnected,
sendMessage,
clearChat
} = useChat(name);
// Scroll to bottom when messages change
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// Show error toast if there's an error
useEffect(() => {
if (error) {
showToast(error, 'error');
}
}, [error, showToast]);
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
if (!message.trim()) return;
const success = await sendMessage(message.trim());
if (success) {
setMessage('');
}
};
return (
<div className="chat-container">
<header className="chat-header">
<h1>Chat with {name}</h1>
<div className="connection-status">
<span className={`status-indicator ${isConnected ? 'connected' : 'disconnected'}`}>
{isConnected ? 'Connected' : 'Disconnected'}
</span>
</div>
<button
className="clear-chat-btn"
onClick={clearChat}
disabled={messages.length === 0}
>
Clear Chat
</button>
</header>
<div className="messages-container">
{messages.length === 0 ? (
<div className="empty-chat">
<p>No messages yet. Start a conversation with {name}!</p>
</div>
) : (
messages.map((msg) => (
<div
key={msg.id}
className={`message ${msg.sender === 'user' ? 'user-message' : 'agent-message'}`}
>
<div className="message-content">
{msg.content}
</div>
<div className="message-timestamp">
{new Date(msg.timestamp).toLocaleTimeString()}
</div>
</div>
))
)}
<div ref={messagesEndRef} />
</div>
<form className="message-form" onSubmit={handleSubmit}>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Type your message..."
disabled={sending || !isConnected}
className="message-input"
/>
<button
type="submit"
disabled={sending || !message.trim() || !isConnected}
className="send-button"
>
{sending ? 'Sending...' : 'Send'}
</button>
</form>
</div>
);
}
export default Chat;