nova / index.html
edwarddddr's picture
Add 2 files
9b5ddb3 verified
Raw
History Blame
25.5 kB
<!DOCTYPE html>
<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>