Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>NOVA | AI Companion</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/react@18/umd/react.development.js"></script> | |
| <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary: #6366f1; | |
| --secondary: #8b5cf6; | |
| } | |
| @keyframes float { | |
| 0% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| 100% { transform: translateY(0px); } | |
| } | |
| .animate-float { | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| .gradient-text { | |
| background: linear-gradient(90deg, var(--primary), var(--secondary)); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .message-enter { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| .message-enter-active { | |
| opacity: 1; | |
| transform: translateY(0); | |
| transition: all 300ms ease-out; | |
| } | |
| .sidebar-transition { | |
| transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .custom-scrollbar::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.05); | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb { | |
| background: rgba(0, 0, 0, 0.2); | |
| border-radius: 3px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 text-gray-800 font-sans"> | |
| <div id="root"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect, useRef } = React; | |
| const Navbar = ({ toggleSidebar }) => { | |
| return ( | |
| <header className="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between sticky top-0 z-10"> | |
| <div className="flex items-center space-x-4"> | |
| <button | |
| onClick={toggleSidebar} | |
| className="md:hidden text-gray-500 hover:text-gray-700 focus:outline-none" | |
| > | |
| <i className="fas fa-bars text-xl"></i> | |
| </button> | |
| <div className="flex items-center"> | |
| <div className="w-8 h-8 rounded-full bg-gradient-to-r from-indigo-500 to-purple-500 flex items-center justify-center text-white"> | |
| <i className="fas fa-robot"></i> | |
| </div> | |
| <h1 className="ml-3 text-xl font-bold gradient-text">NOVA</h1> | |
| </div> | |
| </div> | |
| <div className="flex items-center space-x-4"> | |
| <button className="text-gray-500 hover:text-gray-700 focus:outline-none"> | |
| <i className="fas fa-search"></i> | |
| </button> | |
| <button className="text-gray-500 hover:text-gray-700 focus:outline-none"> | |
| <i className="fas fa-cog"></i> | |
| </button> | |
| </div> | |
| </header> | |
| ); | |
| }; | |
| const Sidebar = ({ isOpen, closeSidebar }) => { | |
| const [activeTab, setActiveTab] = useState('chat'); | |
| const tabs = [ | |
| { id: 'chat', icon: 'fa-comment-dots', label: 'Chat' }, | |
| { id: 'journal', icon: 'fa-book', label: 'Journal' }, | |
| { id: 'resources', icon: 'fa-box-open', label: 'Resources' }, | |
| { id: 'automation', icon: 'fa-magic', label: 'Automation' }, | |
| { id: 'insights', icon: 'fa-chart-line', label: 'Insights' } | |
| ]; | |
| return ( | |
| <aside className={`fixed md:relative inset-y-0 left-0 w-64 bg-white border-r border-gray-200 flex flex-col z-20 sidebar-transition ${isOpen ? 'translate-x-0' : '-translate-x-full'} md:translate-x-0`}> | |
| <div className="p-4 border-b border-gray-200 flex items-center justify-between"> | |
| <h2 className="text-lg font-semibold text-gray-800">Navigation</h2> | |
| <button | |
| onClick={closeSidebar} | |
| className="md:hidden text-gray-500 hover:text-gray-700 focus:outline-none" | |
| > | |
| <i className="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div className="flex-1 overflow-y-auto custom-scrollbar"> | |
| <nav className="p-4"> | |
| <ul className="space-y-1"> | |
| {tabs.map(tab => ( | |
| <li key={tab.id}> | |
| <button | |
| onClick={() => setActiveTab(tab.id)} | |
| className={`w-full text-left px-4 py-3 rounded-lg flex items-center transition-colors ${activeTab === tab.id ? 'bg-indigo-50 text-indigo-600' : 'hover:bg-gray-50 text-gray-700'}`} | |
| > | |
| <i className={`fas ${tab.icon} mr-3 ${activeTab === tab.id ? 'text-indigo-500' : 'text-gray-500'}`}></i> | |
| <span className="font-medium">{tab.label}</span> | |
| </button> | |
| </li> | |
| ))} | |
| </ul> | |
| </nav> | |
| <div className="px-4 pb-4"> | |
| <div className="bg-indigo-50 rounded-xl p-4"> | |
| <h3 className="text-sm font-medium text-indigo-800 mb-2">Daily Challenge</h3> | |
| <p className="text-xs text-indigo-600 mb-3">Complete 3 journal entries to unlock premium features</p> | |
| <div className="w-full bg-indigo-100 rounded-full h-2"> | |
| <div className="bg-indigo-500 h-2 rounded-full" style={{ width: '33%' }}></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="p-4 border-t border-gray-200"> | |
| <div className="flex items-center"> | |
| <div className="w-10 h-10 rounded-full bg-gradient-to-r from-indigo-400 to-purple-400 flex items-center justify-center text-white"> | |
| <i className="fas fa-user"></i> | |
| </div> | |
| <div className="ml-3"> | |
| <p className="text-sm font-medium text-gray-800">Alex Johnson</p> | |
| <p className="text-xs text-gray-500">Premium Member</p> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| ); | |
| }; | |
| const ActionPanel = ({ isOpen, closePanel }) => { | |
| const quickActions = [ | |
| { icon: 'fa-plus', label: 'New Task', color: 'text-purple-500' }, | |
| { icon: 'fa-calendar', label: 'Schedule', color: 'text-blue-500' }, | |
| { icon: 'fa-lightbulb', label: 'Ideas', color: 'text-yellow-500' }, | |
| { icon: 'fa-chart-pie', label: 'Analyze', color: 'text-green-500' } | |
| ]; | |
| const recentActivities = [ | |
| { icon: 'fa-book', label: 'Journal entry completed', time: '2h ago' }, | |
| { icon: 'fa-tasks', label: '3 tasks automated', time: 'Yesterday' }, | |
| { icon: 'fa-bolt', label: 'Energy optimized', time: '2 days ago' } | |
| ]; | |
| return ( | |
| <aside className={`fixed md:relative inset-y-0 right-0 w-72 bg-white border-l border-gray-200 flex flex-col z-20 sidebar-transition ${isOpen ? 'translate-x-0' : 'translate-x-full'} md:translate-x-0`}> | |
| <div className="p-4 border-b border-gray-200 flex items-center justify-between"> | |
| <h2 className="text-lg font-semibold text-gray-800">Action Panel</h2> | |
| <button | |
| onClick={closePanel} | |
| className="md:hidden text-gray-500 hover:text-gray-700 focus:outline-none" | |
| > | |
| <i className="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div className="flex-1 overflow-y-auto custom-scrollbar p-4"> | |
| <div className="mb-8"> | |
| <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Quick Actions</h3> | |
| <div className="grid grid-cols-2 gap-3"> | |
| {quickActions.map((action, index) => ( | |
| <button | |
| key={index} | |
| className="bg-gray-50 hover:bg-gray-100 rounded-xl p-3 flex flex-col items-center transition-colors" | |
| > | |
| <div className={`w-10 h-10 rounded-full ${action.color.replace('text', 'bg')} bg-opacity-10 flex items-center justify-center mb-2`}> | |
| <i className={`fas ${action.icon} ${action.color}`}></i> | |
| </div> | |
| <span className="text-xs font-medium text-gray-700">{action.label}</span> | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="mb-8"> | |
| <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Recent Activities</h3> | |
| <div className="space-y-3"> | |
| {recentActivities.map((activity, index) => ( | |
| <div key={index} className="flex items-start"> | |
| <div className="mt-1 mr-3"> | |
| <div className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center text-gray-500"> | |
| <i className={`fas ${activity.icon} text-xs`}></i> | |
| </div> | |
| </div> | |
| <div className="flex-1"> | |
| <p className="text-sm font-medium text-gray-800">{activity.label}</p> | |
| <p className="text-xs text-gray-500">{activity.time}</p> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div> | |
| <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Personalized Suggestions</h3> | |
| <div className="bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl p-4"> | |
| <p className="text-sm font-medium text-gray-800 mb-2">Based on your patterns:</p> | |
| <ul className="text-xs text-gray-600 space-y-2"> | |
| <li className="flex items-start"> | |
| <i className="fas fa-check-circle text-indigo-400 mr-2 mt-0.5"></i> | |
| <span>Schedule a weekly review at 4pm Fridays</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <i className="fas fa-check-circle text-indigo-400 mr-2 mt-0.5"></i> | |
| <span>Automate your morning routine</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <i className="fas fa-check-circle text-indigo-400 mr-2 mt-0.5"></i> | |
| <span>Try the 5-minute journal prompt</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| ); | |
| }; | |
| const Message = ({ message, isUser }) => { | |
| return ( | |
| <div className={`flex ${isUser ? 'justify-end' : 'justify-start'} mb-4`}> | |
| <div className={`max-w-xs md:max-w-md lg:max-w-lg rounded-2xl p-4 ${isUser ? 'bg-indigo-500 text-white rounded-br-none' : 'bg-white border border-gray-200 rounded-bl-none'}`}> | |
| <div className="flex items-start"> | |
| {!isUser && ( | |
| <div className="mr-3"> | |
| <div className="w-8 h-8 rounded-full bg-gradient-to-r from-indigo-400 to-purple-400 flex items-center justify-center text-white"> | |
| <i className="fas fa-robot text-sm"></i> | |
| </div> | |
| </div> | |
| )} | |
| <div className="flex-1"> | |
| <p className={isUser ? 'text-white' : 'text-gray-800'}>{message.text}</p> | |
| {message.actions && ( | |
| <div className="mt-3 flex flex-wrap gap-2"> | |
| {message.actions.map((action, index) => ( | |
| <button | |
| key={index} | |
| className={`text-xs px-3 py-1 rounded-full ${isUser ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700'} hover:opacity-90 transition-opacity`} | |
| > | |
| {action} | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| <p className={`text-xs mt-2 ${isUser ? 'text-indigo-200' : 'text-gray-500'}`}> | |
| {new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} | |
| </p> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const ChatInput = ({ onSendMessage }) => { | |
| const [input, setInput] = useState(''); | |
| const inputRef = useRef(null); | |
| const handleSubmit = (e) => { | |
| e.preventDefault(); | |
| if (input.trim()) { | |
| onSendMessage(input); | |
| setInput(''); | |
| } | |
| }; | |
| const handleKeyDown = (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| handleSubmit(e); | |
| } | |
| }; | |
| useEffect(() => { | |
| inputRef.current.focus(); | |
| }, []); | |
| return ( | |
| <form onSubmit={handleSubmit} className="bg-white border-t border-gray-200 p-4"> | |
| <div className="max-w-3xl mx-auto"> | |
| <div className="relative"> | |
| <textarea | |
| ref={inputRef} | |
| value={input} | |
| onChange={(e) => setInput(e.target.value)} | |
| onKeyDown={handleKeyDown} | |
| placeholder="Message NOVA..." | |
| className="w-full border border-gray-300 rounded-xl py-3 px-4 pr-12 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent resize-none" | |
| rows="1" | |
| /> | |
| <button | |
| type="submit" | |
| disabled={!input.trim()} | |
| className={`absolute right-3 bottom-3 w-8 h-8 rounded-full flex items-center justify-center ${input.trim() ? 'bg-indigo-500 text-white' : 'bg-gray-200 text-gray-400'}`} | |
| > | |
| <i className="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| <div className="flex items-center justify-between mt-2 px-1"> | |
| <div className="flex space-x-2"> | |
| <button type="button" className="text-gray-500 hover:text-gray-700"> | |
| <i className="fas fa-microphone"></i> | |
| </button> | |
| <button type="button" className="text-gray-500 hover:text-gray-700"> | |
| <i className="fas fa-image"></i> | |
| </button> | |
| <button type="button" className="text-gray-500 hover:text-gray-700"> | |
| <i className="fas fa-paperclip"></i> | |
| </button> | |
| </div> | |
| <p className="text-xs text-gray-500"> | |
| NOVA v2.1 · <span className="text-indigo-500">Learning active</span> | |
| </p> | |
| </div> | |
| </div> | |
| </form> | |
| ); | |
| }; | |
| const ChatWindow = ({ messages, onSendMessage }) => { | |
| const messagesEndRef = useRef(null); | |
| useEffect(() => { | |
| messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); | |
| }, [messages]); | |
| return ( | |
| <div className="flex-1 overflow-y-auto custom-scrollbar p-4 bg-gray-50"> | |
| <div className="max-w-3xl mx-auto"> | |
| {/* Welcome message */} | |
| <div className="flex justify-center mb-8"> | |
| <div className="bg-white border border-gray-200 rounded-2xl p-6 text-center max-w-md"> | |
| <div className="w-16 h-16 rounded-full bg-gradient-to-r from-indigo-400 to-purple-400 flex items-center justify-center text-white mx-auto mb-4 animate-float"> | |
| <i className="fas fa-robot text-2xl"></i> | |
| </div> | |
| <h3 className="text-xl font-bold text-gray-800 mb-2">Hello, I'm NOVA</h3> | |
| <p className="text-gray-600 mb-4">Your AI companion for productivity, wellness, and growth. How can I assist you today?</p> | |
| <div className="grid grid-cols-2 gap-2"> | |
| <button className="bg-indigo-50 hover:bg-indigo-100 text-indigo-600 text-xs px-3 py-2 rounded-lg"> | |
| Journal prompt | |
| </button> | |
| <button className="bg-indigo-50 hover:bg-indigo-100 text-indigo-600 text-xs px-3 py-2 rounded-lg"> | |
| Task automation | |
| </button> | |
| <button className="bg-indigo-50 hover:bg-indigo-100 text-indigo-600 text-xs px-3 py-2 rounded-lg"> | |
| Resource tips | |
| </button> | |
| <button className="bg-indigo-50 hover:bg-indigo-100 text-indigo-600 text-xs px-3 py-2 rounded-lg"> | |
| Energy check | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Chat messages */} | |
| {messages.map((message, index) => ( | |
| <Message key={index} message={message} isUser={message.isUser} /> | |
| ))} | |
| <div ref={messagesEndRef} /> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const App = () => { | |
| const [sidebarOpen, setSidebarOpen] = useState(false); | |
| const [panelOpen, setPanelOpen] = useState(false); | |
| const [messages, setMessages] = useState([ | |
| { | |
| text: "Welcome back! I've analyzed your patterns and have some personalized suggestions for you today.", | |
| timestamp: new Date(), | |
| isUser: false, | |
| actions: ["View suggestions", "Dismiss"] | |
| } | |
| ]); | |
| const toggleSidebar = () => setSidebarOpen(!sidebarOpen); | |
| const togglePanel = () => setPanelOpen(!panelOpen); | |
| const handleSendMessage = (text) => { | |
| const userMessage = { | |
| text, | |
| timestamp: new Date(), | |
| isUser: true | |
| }; | |
| setMessages(prev => [...prev, userMessage]); | |
| // Simulate bot response | |
| setTimeout(() => { | |
| const responses = [ | |
| "I understand. Would you like me to help break this down into actionable steps?", | |
| "That's an interesting point. Based on your previous activities, I can suggest some resources.", | |
| "I've logged this in your journal. Would you like to reflect on similar past experiences?", | |
| "I can automate parts of this process for you. Shall I prepare some options?" | |
| ]; | |
| const botMessage = { | |
| text: responses[Math.floor(Math.random() * responses.length)], | |
| timestamp: new Date(), | |
| isUser: false, | |
| actions: ["Yes please", "Not now", "More options"] | |
| }; | |
| setMessages(prev => [...prev, botMessage]); | |
| }, 1000 + Math.random() * 2000); | |
| }; | |
| return ( | |
| <div className="flex h-screen overflow-hidden bg-gray-50"> | |
| <Sidebar isOpen={sidebarOpen} closeSidebar={() => setSidebarOpen(false)} /> | |
| <div className="flex-1 flex flex-col overflow-hidden"> | |
| <Navbar toggleSidebar={toggleSidebar} /> | |
| <div className="flex flex-1 overflow-hidden"> | |
| <main className="flex-1 flex flex-col overflow-hidden"> | |
| <ChatWindow messages={messages} onSendMessage={handleSendMessage} /> | |
| <ChatInput onSendMessage={handleSendMessage} /> | |
| </main> | |
| <ActionPanel isOpen={panelOpen} closePanel={() => setPanelOpen(false)} /> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const root = ReactDOM.createRoot(document.getElementById('root')); | |
| root.render(<App />); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=edwarddddr/nova" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |