codegalaxy / index.html
PrinceShadow003's picture
Manual changes saved
020e4a3 verified
import React, { useState, useEffect, useRef, useCallback } from 'react';
// --- Enhanced Icon Components with Modern Styling ---
const IconNova = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5" />
<path d="M2 12l10 5 10-5" />
</svg>
);
const IconHome = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
);
const IconTabs = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="18" height="18" x="3" y="3" rx="2" />
<path d="M21 12H3" />
<path d="M12 3v18" />
</svg>
);
const IconSparkles = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="m12 3-1.9 4.8-4.8 1.9 4.8 1.9L12 16l1.9-4.8 4.8-1.9-4.8-1.9L12 3z" />
<path d="M5 21v-3.8" />
<path d="M19 21v-3.8" />
<path d="M3 12H7.8" />
<path d="M16.2 12H21" />
</svg>
);
const IconSettings = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.38a2 2 0 0 0-.73-2.73l-.15-.1a2 2 0 0 1-1-1.72v-.51a2 2 0 0 1 1-1.74l-.15-.09a2 2 0 0 0 .73 2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
<circle cx="12" cy="12" r="3" />
</svg>
);
const IconPlus = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
);
const IconX = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
);
const IconSearch = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</svg>
);
const IconMic = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" x2="12" y1="19" y2="22" />
</svg>
);
const IconSend = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="m22 2-7 20-4-9-9-4Z" />
<path d="m22 2-11 11" />
</svg>
);
const IconGrid = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="7" height="7" x="3" y="3" rx="1" />
<rect width="7" height="7" x="14" y="3" rx="1" />
<rect width="7" height="7" x="14" y="14" rx="1" />
<rect width="7" height="7" x="3" y="14" rx="1" />
</svg>
);
const IconLayers = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="12 2 2 7 12 12 22 7 12 2" />
<polyline points="2 17 12 22 22 17" />
<polyline points="2 12 12 17 22 12" />
</svg>
);
const IconCheckSquare = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="9 11 12 14 22 4" />
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
</svg>
);
const IconCalendar = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="18" height="18" x="3" y="4" rx="2" ry="2" />
<line x1="16" x2="16" y1="2" y2="6" />
<line x1="8" x2="8" y1="2" y2="6" />
<line x1="3" x2="21" y1="10" y2="10" />
</svg>
);
const IconCloud = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z" />
</svg>
);
const IconSun = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="5" />
<line x1="12" x2="12" y1="1" y2="3" />
<line x1="12" x2="12" y1="21" y2="23" />
<line x1="4.22" x2="5.64" y1="4.22" y2="5.64" />
<line x1="18.36" x2="19.78" y1="18.36" y2="19.78" />
<line x1="1" x2="3" y1="12" y2="12" />
<line x1="21" x2="23" y1="12" y2="12" />
<line x1="4.22" x2="5.64" y1="19.78" y2="18.36" />
<line x1="18.36" x2="19.78" y1="5.64" y2="4.22" />
</svg>
);
const IconMoon = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
);
const IconCode = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="16 18 22 12 16 6" />
<polyline points="8 6 2 12 8 18" />
</svg>
);
const IconBrain = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 2.5 2.5 0 0 1-.34-5.99 2.5 2.5 0 0 1 5.76-1.24Z"/>
<path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 2.5 2.5 0 0 0 .34-5.99 2.5 2.5 0 0 0-5.76-1.24Z"/>
</svg>
);
const IconZap = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
</svg>
);
const IconShield = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
);
const IconDownload = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
);
// --- Search Engines Configuration ---
const SEARCH_ENGINES = {
google: {
name: "Google",
url: "https://www.google.com/search?q=",
icon: "🔍",
suggestionUrl: "https://suggestqueries.google.com/complete/search?client=firefox&q="
},
bing: {
name: "Bing",
url: "https://www.bing.com/search?q=",
icon: "🔎",
suggestionUrl: "https://api.bing.com/osjson.aspx?query="
},
duckduckgo: {
name: "DuckDuckGo",
url: "https://duckduckgo.com/?q=",
icon: "🦆",
suggestionUrl: "https://duckduckgo.com/ac/?q="
},
brave: {
name: "Brave",
url: "https://search.brave.com/search?q=",
icon: "🦁",
suggestionUrl: "https://search.brave.com/api/suggest?q="
},
youtube: {
name: "YouTube",
url: "https://www.youtube.com/results?search_query=",
icon: "📺",
suggestionUrl: "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&q="
}
};
// --- Enhanced Mock Data ---
const initialTabs = [
{
id: 1,
title: "NovaBrowse AI Dashboard",
url: "nova://dashboard",
topic: "home",
favicon: "🚀",
lastActive: "now",
isProtected: true
},
{
id: 2,
title: "Google",
url: "https://www.google.com",
topic: "search",
favicon: "🔍",
lastActive: "5m ago",
searchEngine: "google"
}
];
const topicColorsMap = {
home: { border: "border-blue-500", text: "text-blue-400", bg: "bg-blue-500", gradient: "from-blue-500 to-cyan-500" },
research: { border: "border-purple-500", text: "text-purple-400", bg: "bg-purple-500", gradient: "from-purple-500 to-pink-500" },
work: { border: "border-green-500", text: "text-green-400", bg: "bg-green-500", gradient: "from-green-500 to-emerald-500" },
entertainment: { border: "border-red-500", text: "text-red-400", bg: "bg-red-500", gradient: "from-red-500 to-orange-500" },
search: { border: "border-yellow-500", text: "text-yellow-400", bg: "bg-yellow-500", gradient: "from-yellow-500 to-orange-500" },
default: { border: "border-gray-500", text: "text-gray-400", bg: "bg-gray-500", gradient: "from-gray-500 to-slate-500" },
};
const mockWidgets = [
{ id: 1, icon: <IconCalendar className="w-6 h-6" />, title: "Calendar", content: "3 Meetings Today", metric: "+2 from yesterday", trend: "up" },
{ id: 2, icon: <IconCheckSquare className="w-6 h-6" />, title: "To-Do", content: "Finish browser prototype.", metric: "75% completed", trend: "up" },
{ id: 3, icon: <IconCloud className="w-6 h-6" />, title: "Weather", content: "24°C, Clear", metric: "Perfect for focus", trend: "stable" },
{ id: 4, icon: <IconSparkles className="w-6 h-6" />, title: "AI Tip", content: "Try 'Summarize this' on any article.", metric: "Saves 5min avg", trend: "up" },
];
const aiCommands = [
{ command: "Summarize this page", icon: "📄", description: "Get key points from current tab" },
{ command: "Organize my tabs", icon: "🗂️", description: "AI-powered tab grouping" },
{ command: "Find research papers", icon: "🔍", description: "Academic search optimization" },
{ command: "Translate to Spanish", icon: "🌎", description: "Real-time page translation" },
];
// --- Main Component ---
export default function AdvancedBrowser() {
const [tabs, setTabs] = useState(initialTabs);
const [activeTabId, setActiveTabId] = useState(1);
const [activePage, setActivePage] = useState('dashboard');
const [isCopilotOpen, setIsCopilotOpen] = useState(false);
const [copilotMessages, setCopilotMessages] = useState([
{ from: "ai", text: "Hello! I am Nova, your AI assistant. How can I help you browse today?" }
]);
const [copilotInput, setCopilotInput] = useState("");
const [isAiThinking, setIsAiThinking] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
const [theme, setTheme] = useState('dark');
const [isListening, setIsListening] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [showCommandPalette, setShowCommandPalette] = useState(false);
const [performanceMetrics, setPerformanceMetrics] = useState({
memory: "1.2GB", cpu: "15%", network: "45ms", tabs: "2 open"
});
const [currentUrl, setCurrentUrl] = useState("nova://dashboard");
const [isLoading, setIsLoading] = useState(false);
const [searchSuggestions, setSearchSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [currentSearchEngine, setCurrentSearchEngine] = useState("google");
const [downloads, setDownloads] = useState([]);
const [securityStatus, setSecurityStatus] = useState("secure");
const [bookmarks, setBookmarks] = useState([]);
const [history, setHistory] = useState([]);
const copilotMessagesEndRef = useRef(null);
const speechRecognitionRef = useRef(null);
const searchInputRef = useRef(null);
const iframeRef = useRef(null);
const activeTab = tabs.find(t => t.id === activeTabId);
// Enhanced theme simulation
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
setTheme(mediaQuery.matches ? 'dark' : 'light');
const handler = (e) => setTheme(e.matches ? 'dark' : 'light');
mediaQuery.addEventListener('change', handler);
return () => mediaQuery.removeEventListener('change', handler);
}, []);
// Enhanced scroll effect
useEffect(() => {
copilotMessagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [copilotMessages]);
// Enhanced speech recognition
useEffect(() => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';
recognition.onstart = () => setIsListening(true);
recognition.onend = () => setIsListening(false);
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
setSearchTerm(transcript);
handleSearch(transcript);
};
recognition.onerror = (event) => {
console.error("Speech recognition error:", event.error);
setIsListening(false);
};
speechRecognitionRef.current = recognition;
}
}, []);
// Command palette shortcut
useEffect(() => {
const handleKeyDown = (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
setShowCommandPalette(true);
setTimeout(() => searchInputRef.current?.focus(), 100);
}
if (e.key === 'Escape') {
setShowCommandPalette(false);
setShowSuggestions(false);
}
if ((e.metaKey || e.ctrlKey) && e.key === 't') {
e.preventDefault();
handleNewTab();
}
if ((e.metaKey || e.ctrlKey) && e.key === 'w') {
e.preventDefault();
if (tabs.length > 1) {
handleCloseTab(null, activeTabId);
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [tabs, activeTabId]);
// Search suggestions
useEffect(() => {
if (searchTerm.length > 2) {
fetchSearchSuggestions(searchTerm);
} else {
setSearchSuggestions([]);
}
}, [searchTerm]);
const fetchSearchSuggestions = async (query) => {
try {
const engine = SEARCH_ENGINES[currentSearchEngine];
const response = await fetch(`${engine.suggestionUrl}${encodeURIComponent(query)}`);
const data = await response.json();
setSearchSuggestions(data[1] || []);
setShowSuggestions(true);
} catch (error) {
console.error("Failed to fetch suggestions:", error);
}
};
const toggleTheme = () => {
setTheme(t => (t === 'dark' ? 'light' : 'dark'));
};
const getTopicColor = useCallback((topic, type = 'border') => {
const colors = topicColorsMap[topic] || topicColorsMap.default;
return colors[type];
}, []);
const handleTabClick = (id) => {
setActiveTabId(id);
const tab = tabs.find(t => t.id === id);
setCurrentUrl(tab.url);
setActivePage(tab.url === "nova://dashboard" ? 'dashboard' : 'web');
};
const handleNewTab = (url = "nova://dashboard", title = "New Tab") => {
const newId = Math.max(0, ...tabs.map(t => t.id)) + 1;
const newTab = {
id: newId,
title: title,
url: url,
topic: url === "nova://dashboard" ? "home" : "default",
favicon: "🌐",
lastActive: "now"
};
setTabs([...tabs, newTab]);
setActiveTabId(newId);
setCurrentUrl(url);
setActivePage(url === "nova://dashboard" ? 'dashboard' : 'web');
};
const handleCloseTab = (e, id) => {
e?.stopPropagation();
if (tabs.length === 1) {
handleNewTab();
setTabs(tabs.filter(t => t.id !== id));
return;
}
const newTabs = tabs.filter(t => t.id !== id);
setTabs(newTabs);
if (activeTabId === id) {
const newActiveId = newTabs[newTabs.length - 1].id;
setActiveTabId(newActiveId);
const newActiveTab = newTabs.find(t => t.id === newActiveId);
setCurrentUrl(newActiveTab.url);
setActivePage(newActiveTab.url === "nova://dashboard" ? 'dashboard' : 'web');
}
};
const organizeTabsByAi = () => {
const sortedTabs = [...tabs].sort((a, b) => a.topic.localeCompare(b.topic));
setTabs(sortedTabs);
};
const handleSearch = (query, engine = currentSearchEngine) => {
if (!query?.trim()) return;
setIsLoading(true);
setSearchTerm(query);
const searchUrl = SEARCH_ENGINES[engine].url + encodeURIComponent(query);
const searchTab = tabs.find(t => t.url.includes(SEARCH_ENGINES[engine].url));
if (searchTab) {
const updatedTabs = tabs.map(t =>
t.id === searchTab.id ? { ...t, url: searchUrl, title: `${SEARCH_ENGINES[engine].name} Search` } : t
);
setTabs(updatedTabs);
setActiveTabId(searchTab.id);
} else {
handleNewTab(searchUrl, `${SEARCH_ENGINES[engine].name} Search`);
}
setCurrentUrl(searchUrl);
setActivePage('web');
setShowSuggestions(false);
// Add to history
setHistory(prev => [...prev, {
url: searchUrl,
title: `${query} - ${SEARCH_ENGINES[engine].name} Search`,
timestamp: new Date().toISOString()
}]);
};
const handleUrlNavigation = (url) => {
if (!url) return;
let finalUrl = url;
if (!url.startsWith('http') && !url.startsWith('nova://')) {
finalUrl = SEARCH_ENGINES[currentSearchEngine].url + encodeURIComponent(url);
}
setIsLoading(true);
setCurrentUrl(finalUrl);
const updatedTabs = tabs.map(t =>
t.id === activeTabId ? { ...t, url: finalUrl, title: "Loading..." } : t
);
setTabs(updatedTabs);
if (finalUrl.startsWith('nova://')) {
setActivePage('dashboard');
} else {
setActivePage('web');
}
};
const handleVoiceSearch = () => {
if (speechRecognitionRef.current && !isListening) {
speechRecognitionRef.current.start();
} else if (isListening) {
speechRecognitionRef.current.stop();
}
};
const handleDownload = (url, filename) => {
const downloadId = Date.now();
const newDownload = {
id: downloadId,
url,
filename: filename || `download-${downloadId}`,
progress: 0,
status: 'downloading'
};
setDownloads(prev => [...prev, newDownload]);
// Simulate download progress
const interval = setInterval(() => {
setDownloads(prev => prev.map(d =>
d.id === downloadId
? { ...d, progress: Math.min(d.progress + 10, 100) }
: d
));
}, 200);
setTimeout(() => {
clearInterval(interval);
setDownloads(prev => prev.map(d =>
d.id === downloadId
? { ...d, progress: 100, status: 'completed' }
: d
));
}, 2000);
};
const addBookmark = (url, title) => {
const newBookmark = {
id: Date.now(),
url,
title,
favicon: "🔖",
dateAdded: new Date().toISOString()
};
setBookmarks(prev => [...prev, newBookmark]);
};
const removeBookmark = (id) => {
setBookmarks(prev => prev.filter(b => b.id !== id));
};
const handleIframeLoad = () => {
setIsLoading(false);
const updatedTabs = tabs.map(t =>
t.id === activeTabId ? { ...t, title: iframeRef.current?.contentDocument?.title || t.title } : t
);
setTabs(updatedTabs);
// Update security status based on URL
const isSecure = currentUrl.startsWith('https://');
setSecurityStatus(isSecure ? 'secure' : 'insecure');
};
const Sidebar = () => (
<nav className={`flex flex-col h-full py-4 glass-ui transition-all duration-300 ${
sidebarCollapsed ? 'w-16' : 'w-20'
} ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'} flex-shrink-0`}>
<div className={`p-2 mb-6 transition-all duration-300 ${
theme === 'dark' ? 'text-blue-400' : 'text-blue-600'
} hover:opacity-80 hover:scale-110 cursor-pointer`}>
<IconNova className="w-8 h-8" />
</div>
<div className="flex flex-col items-center space-y-4 flex-1">
{[
{ icon: <IconHome />, page: 'dashboard', label: 'Home' },
{ icon: <IconGrid />, page: 'tabs', label: 'Tabs' },
{ icon: <IconLayers />, page: 'bookmarks', label: 'Bookmarks' },
{ icon: <IconDownload />, page: 'downloads', label: 'Downloads' },
{ icon: <IconCode />, page: 'developer', label: 'Dev Tools' },
{ icon: <IconBrain />, page: 'ai', label: 'AI Studio' },
].map((item) => (
<button
key={item.page}
onClick={() => {
setActivePage(item.page);
if (item.page === 'dashboard') {
handleNewTab("nova://dashboard", "NovaBrowse Dashboard");
}
}}
className={`p-3 rounded-xl transition-all duration-300 group relative ${
activePage === item.page
? 'bg-gradient-to-br from-blue-500 to-purple-600 text-white shadow-lg'
: 'hover:bg-gray-500/20'
}`}
title={item.label}
>
{React.cloneElement(item.icon, { className: "w-6 h-6" })}
{!sidebarCollapsed && (
<span className="absolute left-14 bg-gray-900 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
{item.label}
</span>
)}
</button>
))}
</div>
<div className="space-y-4">
<button
onClick={toggleTheme}
className="p-3 rounded-xl transition-all duration-300 hover:bg-gray-500/20 group relative"
title="Toggle Theme"
>
{theme === 'dark' ? <IconSun className="w-6 h-6" /> : <IconMoon className="w-6 h-6" />}
{!sidebarCollapsed && (
<span className="absolute left-14 bg-gray-900 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
Toggle Theme
</span>
)}
</button>
<button
className="p-3 rounded-xl transition-all duration-300 hover:bg-gray-500/20 group relative"
title="Settings"
>
<IconSettings className="w-6 h-6" />
{!sidebarCollapsed && (
<span className="absolute left-14 bg-gray-900 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
Settings
</span>
)}
</button>
</div>
</nav>
);
const TabStrip = () => (
<div className={`flex items-center w-full h-14 overflow-x-auto glass-ui-top border-b ${
theme === 'dark' ? 'border-gray-500/20' : 'border-gray-300/30'
}`}>
<div className="flex h-full">
{tabs.map(tab => (
<div
key={tab.id}
onClick={() => handleTabClick(tab.id)}
className={`group relative flex items-center justify-between px-4 h-full min-w-[180px] max-w-[240px] cursor-pointer transition-all duration-300 border-r ${
activeTabId === tab.id
? 'bg-gradient-to-r from-blue-500/20 to-purple-500/10'
: (theme === 'dark' ? 'bg-gray-800/10 hover:bg-gray-500/20' : 'bg-white/10 hover:bg-gray-200/20')
} ${theme === 'dark' ? 'border-gray-700/30' : 'border-gray-300/30'}`}
>
<div className={`absolute inset-0 rounded-t-lg ${
activeTabId === tab.id ? getTopicColor(tab.topic, 'border') : 'border-transparent'
} border-t-2`}></div>
<div className="flex items-center space-x-3 flex-1 min-w-0">
<span className="text-lg">{tab.favicon}</span>
<span className={`text-sm truncate font-medium ${
theme === 'dark' ? 'text-gray-200' : 'text-gray-800'
}`}>{tab.title}</span>
</div>
<button
onClick={(e) => handleCloseTab(e, tab.id)}
className={`ml-2 p-1 rounded-full opacity-0 group-hover:opacity-100 transition-all ${
theme === 'dark' ? 'text-gray-400' : 'text-gray-600'
} hover:bg-red-500/50 hover:text-white`}
>
<IconX className="w-3 h-3" />
</button>
</div>
))}
</div>
<button
onClick={() => handleNewTab()}
className={`px-4 h-full transition-all duration-300 ${
theme === 'dark'
? 'bg-gray-800/20 text-gray-400 hover:bg-blue-500/20 hover:text-white'
: 'bg-gray-100/20 text-gray-600 hover:bg-blue-500/20 hover:text-white'
}`}
>
<IconPlus className="w-5 h-5" />
</button>
</div>
);
const AddressBar = () => (
<div className="flex items-center w-full h-16 p-3 glass-ui-top">
<div className="flex items-center space-x-2 mr-4">
<button
onClick={() => activeTabId && handleCloseTab(null, activeTabId)}
className={`p-2 rounded-xl transition-all ${
theme === 'dark' ? 'hover:bg-gray-700/50 text-gray-400' : 'hover:bg-gray-300/50 text-gray-600'
}`}
title="Close Tab (Ctrl+W)"
>
<IconX className="w-4 h-4" />
</button>
<button
onClick={() => handleNewTab()}
className={`p-2 rounded-xl transition-all ${
theme === 'dark' ? 'hover:bg-gray-700/50 text-gray-400' : 'hover:bg-gray-300/50 text-gray-600'
}`}
title="New Tab (Ctrl+T)"
>
<IconPlus className="w-4 h-4" />
</button>
</div>
<div className={`flex items-center w-full max-w-4xl rounded-2xl glass-ui-inset p-1 ${
theme === 'dark' ? 'shadow-inner-dark' : 'shadow-inner'
} relative`}>
{/* Search Engine Selector */}
<div className="relative group">
<button className="flex items-center px-3 py-2 rounded-xl mr-2 transition-all bg-gray-500/20 hover:bg-gray-500/30">
<span className="text-lg mr-2">{SEARCH_ENGINES[currentSearchEngine].icon}</span>
<span className="text-sm font-medium">{SEARCH_ENGINES[currentSearchEngine].name}</span>
</button>
<div className="absolute top-full left-0 mt-2 w-48 rounded-2xl glass-ui-copilot border border-gray-500/30 shadow-2xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-50">
{Object.entries(SEARCH_ENGINES).map(([key, engine]) => (
<button
key={key}
onClick={() => setCurrentSearchEngine(key)}
className={`w-full text-left p-3 flex items-center space-x-3 transition-all ${
currentSearchEngine === key
? 'bg-blue-500/20 text-blue-300'
: 'hover:bg-gray-500/20 text-gray-300'
}`}
>
<span className="text-lg">{engine.icon}</span>
<span>{engine.name}</span>
</button>
))}
</div>
</div>
<input
ref={searchInputRef}
type="text"
placeholder={`Search with ${SEARCH_ENGINES[currentSearchEngine].name} or enter address...`}
className={`flex-1 py-3 bg-transparent ${
theme === 'dark' ? 'text-white placeholder-gray-400' : 'text-black placeholder-gray-500'
} focus:outline-none font-body text-lg`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch(searchTerm)}
onFocus={() => searchTerm.length > 2 && setShowSuggestions(true)}
/>
{isLoading && (
<div className="absolute right-16 top-1/2 transform -translate-y-1/2">
<div className="w-5 h-5 border-2 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
</div>
)}
<button
onClick={handleVoiceSearch}
className={`p-3 mx-1 rounded-xl transition-all ${
theme === 'dark' ? 'text-gray-300' : 'text-gray-700'
} hover:bg-blue-500/30 hover:text-white ${isListening ? 'animate-pulse-glow bg-red-500/20' : ''}`}
title="Voice Command"
>
<IconMic className="w-5 h-5" />
</button>
{/* Security Indicator */}
<div className={`px-3 py-1 rounded-lg mr-2 ${
securityStatus === 'secure'
? 'bg-green-500/20 text-green-400'
: 'bg-red-500/20 text-red-400'
}`}>
<IconShield className="w-4 h-4" />
</div>
</div>
{/* Search Suggestions */}
{showSuggestions && searchSuggestions.length > 0 && (
<div className="absolute top-full left-1/2 transform -translate-x-1/2 mt-2 w-full max-w-2xl rounded-2xl glass-ui-copilot border border-gray-500/30 shadow-2xl z-40">
{searchSuggestions.slice(0, 8).map((suggestion, index) => (
<button
key={index}
onClick={() => {
setSearchTerm(suggestion);
handleSearch(suggestion);
}}
className="w-full text-left p-4 hover:bg-gray-500/20 transition-all border-b border-gray-500/10 last:border-b-0"
>
<div className="flex items-center space-x-3">
<IconSearch className="w-4 h-4 text-gray-400" />
<span className="text-gray-200">{suggestion}</span>
</div>
</button>
))}
</div>
)}
<div className="flex items-center space-x-2 ml-4">
<button
onClick={() => currentUrl && addBookmark(currentUrl, activeTab?.title)}
className={`p-3 rounded-xl transition-all duration-300 ${
theme === 'dark' ? 'text-yellow-400 hover:bg-yellow-500/20' : 'text-yellow-600 hover:bg-yellow-500/10'
}`}
title="Bookmark this page"
>
🔖
</button>
<button
onClick={() => setIsCopilotOpen(true)}
className="relative p-3 text-blue-300 rounded-xl transition-all duration-300 hover:bg-blue-500/30 hover:text-blue-100 neon-button-glow group"
title="AI Copilot"
>
<IconSparkles className="w-6 h-6" />
<span className="absolute -top-1 -right-1 w-3 h-3 bg-blue-400 rounded-full animate-pulse"></span>
<span className="absolute -bottom-8 left-1/2 transform -translate-x-1/2 bg-gray-900 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
AI Assistant
</span>
</button>
</div>
</div>
);
const WebView = () => (
<div className="w-full h-full relative">
{isLoading && (
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 to-purple-500 z-10">
<div className="h-full bg-blue-400 animate-pulse" style={{ width: '80%' }}></div>
</div>
)}
{currentUrl.startsWith('nova://') ? (
<EnhancedDashboard />
) : (
<iframe
ref={iframeRef}
src={currentUrl}
className="w-full h-full border-0"
onLoad={handleIframeLoad}
title="Web Content"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
/>
)}
</div>
);
const EnhancedDashboard = () => (
<div className="h-full overflow-y-auto">
<div className="absolute inset-0 -z-10">
<div className={`absolute inset-0 opacity-20 ${
theme === 'dark'
? 'bg-gradient-to-br from-blue-900/30 via-purple-900/20 to-cyan-900/30'
: 'bg-gradient-to-br from-blue-100/50 via-purple-100/30 to-cyan-100/50'
}`}></div>
</div>
<div className="container mx-auto px-8 py-12">
<div className="text-center mb-16">
<h1 className="text-7xl font-black bg-gradient-to-r from-blue-400 via-purple-500 to-cyan-400 bg-clip-text text-transparent mb-6 animate-gradient">
NovaBrowse AI
</h1>
<p className={`text-2xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-600'} font-light mb-8`}>
The Future of Intelligent Browsing
</p>
<div className="w-full max-w-3xl mx-auto mb-16">
<div className={`relative rounded-3xl glass-ui p-2 shadow-2xl neon-focus-glow ${
theme === 'light' ? 'border-gray-300/50' : ''
}`}>
<IconSearch className={`absolute left-6 top-1/2 transform -translate-y-1/2 w-6 h-6 ${
theme === 'dark' ? 'text-gray-300' : 'text-gray-700'
}`} />
<input
type="text"
placeholder="Ask Nova anything or search the web..."
className={`w-full p-4 pl-16 text-xl bg-transparent ${
theme === 'dark' ? 'text-white placeholder-gray-400' : 'text-black placeholder-gray-500'
} focus:outline-none font-body`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch(searchTerm)}
/>
<button
onClick={handleVoiceSearch}
className={`absolute right-4 top-1/2 transform -translate-y-1/2 p-3 rounded-2xl transition-all ${
theme === 'dark' ? 'text-gray-300' : 'text-gray-700'
} hover:bg-blue-500/30 hover:text-white ${isListening ? 'animate-pulse-glow bg-red-500/20' : ''}`}
>
<IconMic className="w-6 h-6" />
</button>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
{mockWidgets.map((widget, i) => (
<div
key={widget.id}
className="group p-6 rounded-2xl glass-ui hover-3d-tilt transition-all duration-500 animate-fade-in-up cursor-pointer border border-gray-500/10 hover:border-blue-500/30"
style={{ animationDelay: `${i * 100}ms` }}
>
<div className="flex items-center justify-between mb-4">
<div className={`p-3 rounded-xl bg-gradient-to-br ${getTopicColor('home', 'gradient')}`}>
{widget.icon}
</div>
<div className={`text-sm px-2 py-1 rounded-lg ${
widget.trend === 'up' ? 'bg-green-500/20 text-green-400' :
widget.trend === 'down' ? 'bg-red-500/20 text-red-400' :
'bg-blue-500/20 text-blue-400'
}`}>
{widget.metric}
</div>
</div>
<h3 className={`text-lg font-bold mb-2 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
{widget.title}
</h3>
<p className={`text-sm ${theme === 'dark' ? 'text-gray-300' : 'text-gray-600'} mb-4`}>
{widget.content}
</p>
<div className="w-full bg-gray-500/20 rounded-full h-1">
<div className={`h-1 rounded-full bg-gradient-to-r ${getTopicColor('home', 'gradient')} ${
widget.trend === 'up' ? 'w-3/4' : 'w-1/2'
}`}></div>
</div>
</div>
))}
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{aiCommands.slice(0, 4).map((cmd, i) => (
<button
key={i}
onClick={() => {
setCopilotInput(cmd.command);
setIsCopilotOpen(true);
}}
className={`p-4 rounded-2xl glass-ui transition-all duration-300 hover:scale-105 group ${
theme === 'dark' ? 'hover:bg-blue-500/20' : 'hover:bg-blue-500/10'
}`}
>
<div className="text-2xl mb-2">{cmd.icon}</div>
<div className={`text-sm font-medium text-left ${
theme === 'dark' ? 'text-gray-200' : 'text-gray-800'
}`}>{cmd.command}</div>
<div className={`text-xs text-left ${
theme === 'dark' ? 'text-gray-400' : 'text-gray-600'
}`}>{cmd.description}</div>
</button>
))}
</div>
{/* Quick Access Sites */}
<div className="mt-16">
<h2 className={`text-3xl font-bold mb-8 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
Quick Access
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
{Object.entries(SEARCH_ENGINES).map(([key, engine]) => (
<button
key={key}
onClick={() => handleNewTab(engine.url, `${engine.name} Home`)}
className="p-6 rounded-2xl glass-ui transition-all duration-300 hover:scale-105 group text-center"
>
<div className="text-3xl mb-3">{engine.icon}</div>
<div className={`text-sm font-medium ${
theme === 'dark' ? 'text-gray-200' : 'text-gray-800'
}`}>{engine.name}</div>
</button>
))}
</div>
</div>
</div>
</div>
);
const BookmarksView = () => (
<div className="p-8">
<div className="flex items-center justify-between mb-8">
<h1 className="text-5xl font-black bg-gradient-to-r from-yellow-400 to-orange-400 bg-clip-text text-transparent">
Bookmarks
</h1>
<button
onClick={() => currentUrl && addBookmark(currentUrl, activeTab?.title)}
className="px-6 py-3 rounded-2xl bg-gradient-to-r from-yellow-500 to-orange-500 text-white font-semibold hover:scale-105 transition-transform shadow-lg"
>
+ Add Current Page
</button>
</div>
{bookmarks.length === 0 ? (
<div className="text-center py-16">
<div className="text-6xl mb-4">🔖</div>
<h3 className={`text-2xl font-bold mb-2 ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
No bookmarks yet
</h3>
<p className={`${theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}`}>
Bookmark your favorite sites for quick access
</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{bookmarks.map(bookmark => (
<div
key={bookmark.id}
className="group p-6 rounded-2xl glass-ui h-32 flex flex-col justify-between cursor-pointer transition-all duration-300 hover-3d-tilt border-2 border-transparent hover:border-yellow-500/50"
onClick={() => handleNewTab(bookmark.url, bookmark.title)}
>
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<div className="text-2xl mb-2">{bookmark.favicon}</div>
<p className={`font-semibold text-lg truncate ${
theme === 'dark' ? 'text-white' : 'text-gray-900'
}`}>{bookmark.title}</p>
<p className={`text-sm mt-1 truncate ${
theme === 'dark' ? 'text-gray-400' : 'text-gray-600'
}`}>{new URL(bookmark.url).hostname}</p>
</div>
<button
onClick={(e) => {
e.stopPropagation();
removeBookmark(bookmark.id);
}}
className={`p-2 rounded-xl opacity-0 group-hover:opacity-100 transition-all ${
theme === 'dark' ? 'bg-gray-700 text-gray-400' : 'bg-gray-300 text-gray-600'
} hover:bg-red-500 hover:text-white`}
>
<IconX className="w-4 h-4" />
</button>
</div>
</div>
))}
</div>
)}
</div>
);
const DownloadsView = () => (
<div className="p-8">
<h1 className="text-5xl font-black bg-gradient-to-r from-green-400 to-cyan-400 bg-clip-text text-transparent mb-8">
Downloads
</h1>
{downloads.length === 0 ? (
<div className="text-center py-16">
<IconDownload className="w-16 h-16 mx-auto mb-4 text-gray-400" />
<h3 className={`text-2xl font-bold mb-2 ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
No downloads yet
</h3>
<p className={`${theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}`}>
Your downloaded files will appear here
</p>
</div>
) : (
<div className="space-y-4">
{downloads.map(download => (
<div key={download.id} className="p-6 rounded-2xl glass-ui">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<IconDownload className="w-8 h-8 text-green-400" />
<div>
<h3 className={`font-semibold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
{download.filename}
</h3>
<p className={`text-sm ${theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}`}>
{download.status === 'completed' ? 'Completed' : 'Downloading...'}
</p>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="w-32 bg-gray-500/20 rounded-full h-2">
<div
className="h-2 rounded-full bg-gradient-to-r from-green-500 to-cyan-500 transition-all duration-300"
style={{ width: `${download.progress}%` }}
></div>
</div>
<span className={`text-sm font-mono ${
theme === 'dark' ? 'text-gray-400' : 'text-gray-600'
}`}>
{download.progress}%
</span>
</div>
</div>
</div>
))}
</div>
)}
</div>
);
const MainContent = () => (
<div className="flex-1 w-full h-full overflow-y-auto">
{activePage === 'dashboard' && <EnhancedDashboard />}
{activePage === 'web' && <WebView />}
{activePage === 'tabs' && <EnhancedTabGridView />}
{activePage === 'bookmarks' && <BookmarksView />}
{activePage === 'downloads' && <DownloadsView />}
{activePage === 'developer' && <EnhancedDeveloperView />}
{activePage === 'ai' && <AIStudioView />}
</div>
);
// ... (Other view components like EnhancedTabGridView, EnhancedDeveloperView, AIStudioView remain similar but updated)
const Copilot = () => (
<div
className={`fixed top-0 right-0 h-full w-full max-w-md z-50 transform transition-all duration-500 ease-out ${
isCopilotOpen ? 'translate-x-0' : 'translate-x-full'
}`}
>
<div className="flex flex-col h-full glass-ui-copilot border-l border-blue-500/30 shadow-2xl shadow-black/50 backdrop-blur-xl">
<div className={`flex items-center justify-between p-6 border-b ${
theme === 'dark' ? 'border-gray-500/20' : 'border-gray-300/30'
} flex-shrink-0`}>
<div className="flex items-center space-x-3">
<div className="p-2 rounded-xl bg-gradient-to-br from-blue-500 to-purple-500">
<IconSparkles className="w-6 h-6 text-white" />
</div>
<div>
<h2 className={`text-xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
Nova Copilot
</h2>
<p className={`text-sm ${theme === 'dark' ? 'text-blue-300' : 'text-blue-600'}`}>
{isAiThinking ? 'Thinking...' : 'Always learning'}
</p>
</div>
</div>
<button
onClick={() => setIsCopilotOpen(false)}
className={`p-2 rounded-xl transition-all ${
theme === 'dark'
? 'text-gray-400 hover:bg-gray-700/50 hover:text-white'
: 'text-gray-600 hover:bg-gray-300/50 hover:text-gray-900'
}`}
>
<IconX className="w-5 h-5" />
</button>
</div>
<div className="flex-1 p-6 space-y-6 overflow-y-auto">
{copilotMessages.map((msg, i) => (
<div key={i} className={`flex ${msg.from === 'user' ? 'justify-end' : 'justify-start'}`}>
<div
className={`max-w-xs px-6 py-4 rounded-3xl transition-all duration-300 ${
msg.from === 'user'
? 'bg-gradient-to-br from-blue-500 to-cyan-500 text-white rounded-br-lg shadow-lg'
: (theme === 'dark'
? 'bg-gray-700/70 text-gray-200 rounded-bl-lg border border-gray-600/50'
: 'bg-gray-200/70 text-gray-800 rounded-bl-lg border border-gray-300/50')
} group hover:scale-105 cursor-pointer`}
>
<div className="flex items-center space-x-2 mb-2">
{msg.from === 'ai' && (
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center">
<IconSparkles className="w-3 h-3 text-white" />
</div>
)}
<span className={`text-xs font-semibold ${
msg.from === 'user' ? 'text-blue-100' : (theme === 'dark' ? 'text-purple-300' : 'text-purple-600')
}`}>
{msg.from === 'user' ? 'You' : 'Nova AI'}
</span>
</div>
<p className="text-sm leading-relaxed whitespace-pre-wrap">{msg.text}</p>
</div>
</div>
))}
{isAiThinking && (
<div className="flex justify-start">
<div className={`max-w-xs px-6 py-4 rounded-3xl rounded-bl-lg ${
theme === 'dark' ? 'bg-gray-700/70' : 'bg-gray-200/70'
} border ${theme === 'dark' ? 'border-gray-600/50' : 'border-gray-300/50'}`}>
<div className="flex items-center space-x-2">
<div className="flex space-x-1">
<div className="w-2 h-2 bg-blue-400 rounded-full animate-bounce"></div>
<div className="w-2 h-2 bg-blue-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }}></div>
<div className="w-2 h-2 bg-blue-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }}></div>
</div>
<span className="text-sm text-blue-400">Nova is thinking...</span>
</div>
</div>
</div>
)}
<div ref={copilotMessagesEndRef} />
</div>
<form onSubmit={(e) => {
e.preventDefault();
if (copilotInput.trim()) {
setCopilotMessages(prev => [...prev, { from: 'user', text: copilotInput }]);
setCopilotInput('');
setIsAiThinking(true);
setTimeout(() => {
setCopilotMessages(prev => [...prev, {
from: 'ai',
text: "I'm an AI assistant in NovaBrowse. In a real implementation, I would connect to services like Gemini API to provide intelligent responses to your queries about browsing, searching, and productivity."
}]);
setIsAiThinking(false);
}, 2000);
}
}} className={`p-6 border-t ${
theme === 'dark' ? 'border-gray-500/20' : 'border-gray-300/30'
} flex-shrink-0`}>
<div className="flex items-center space-x-3">
<div className="flex-1 rounded-2xl glass-ui-inset p-1">
<input
type="text"
value={copilotInput}
onChange={(e) => setCopilotInput(e.target.value)}
placeholder="Ask Nova anything..."
className={`w-full px-4 py-3 bg-transparent ${
theme === 'dark' ? 'text-white placeholder-gray-400' : 'text-black placeholder-gray-500'
} focus:outline-none text-lg`}
disabled={isAiThinking}
/>
</div>
<button
type="submit"
className={`p-4 rounded-2xl transition-all duration-300 ${
isAiThinking || !copilotInput.trim()
? 'bg-gray-500/20 text-gray-500 cursor-not-allowed'
: 'bg-gradient-to-br from-blue-500 to-purple-500 text-white hover:scale-105 shadow-lg'
}`}
disabled={isAiThinking || !copilotInput.trim()}
>
<IconSend className="w-5 h-5" />
</button>
</div>
</form>
</div>
</div>
);
return (
<>
<style>{`
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Nunito+Sans:wght@400;700&family=Orbitron:wght@500;700;900&family=Poppins:wght@500;700;900&display=swap');
body {
font-family: 'Inter', sans-serif;
margin: 0;
padding: 0;
overflow: hidden;
}
.font-heading {
font-family: 'Orbitron', sans-serif;
}
.font-body {
font-family: 'Nunito Sans', sans-serif;
}
.font-mono {
font-family: 'Orbitron', sans-serif;
}
/* Enhanced Glassmorphism */
.glass-ui {
background: ${theme === 'dark' ? 'rgba(30, 30, 40, 0.4)' : 'rgba(255, 255, 255, 0.3)'};
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid ${theme === 'dark' ? 'rgba(255, 255, 255, 0.125)' : 'rgba(255, 255, 255, 0.3)'};
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.glass-ui-light {
background: ${theme === 'dark' ? 'rgba(40, 40, 50, 0.3)' : 'rgba(255, 255, 255, 0.4)'};
backdrop-filter: blur(12px) saturate(160%);
-webkit-backdrop-filter: blur(12px) saturate(160%);
border: 1px solid ${theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(255, 255, 255, 0.2)'};
}
.glass-ui-top {
background: ${theme === 'dark' ? 'rgba(23, 23, 33, 0.6)' : 'rgba(240, 240, 255, 0.5)'};
backdrop-filter: blur(24px) saturate(200%);
-webkit-backdrop-filter: blur(24px) saturate(200%);
}
.glass-ui-copilot {
background: ${theme === 'dark' ? 'rgba(15, 15, 25, 0.85)' : 'rgba(230, 230, 245, 0.9)'};
backdrop-filter: blur(32px) saturate(200%);
-webkit-backdrop-filter: blur(32px) saturate(200%);
}
.glass-ui-inset {
background: ${theme === 'dark' ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.4)'};
box-shadow: inset 0 2px 8px rgba(0,0,0,0.1);
}
/* Enhanced Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fadeInUp 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
}
@keyframes gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.animate-gradient {
background-size: 200% 200%;
animation: gradient 6s ease infinite;
}
@keyframes pulseGlow {
0%, 100% {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.4);
}
50% {
box-shadow: 0 0 40px rgba(59, 130, 246, 0.8);
}
}
.animate-pulse-glow {
animation: pulseGlow 2s infinite;
}
.hover-3d-tilt {
transform-style: preserve-3d;
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.hover-3d-tilt:hover {
transform: perspective(1000px) rotateX(5deg) rotateY(5deg) scale(1.02);
}
.neon-button-glow {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.6), 0 0 40px rgba(59, 130, 246, 0.3);
}
.neon-focus-glow:focus-within {
box-shadow: 0 0 30px rgba(59, 130, 246, 0.8), 0 0 60px rgba(59, 130, 246, 0.4);
border-color: rgba(59, 130, 246, 0.8);
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: ${theme === 'dark' ? '#1a1a2e' : '#f1f1f1'};
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(to bottom, #3b82f6, #8b5cf6);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(to bottom, #2563eb, #7c3aed);
}
::selection {
background: rgba(59, 130, 246, 0.3);
}
`}</style>
<div className={`flex w-full h-screen overflow-hidden transition-colors duration-500 ${
theme === 'dark' ? 'text-white bg-gray-900' : 'text-gray-900 bg-gray-100'
}`}>
<div className="fixed inset-0 -z-10">
<div className={`absolute inset-0 transition-all duration-1000 ${
theme === 'dark'
? 'bg-gradient-to-br from-gray-900 via-[#0a0a2a] to-[#1a0033]'
: 'bg-gradient-to-br from-blue-50 via-purple-50 to-cyan-50'
}`}></div>
<div className={`absolute inset-0 opacity-30 ${
theme === 'dark'
? 'bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-blue-900/20 via-transparent to-transparent'
: 'bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-blue-200/30 via-transparent to-transparent'
}`}></div>
</div>
<Sidebar />
<main className="relative flex flex-col flex-1 h-full overflow-hidden">
<TabStrip />
<AddressBar />
<div className="relative flex-1 w-full h-full overflow-hidden">
<MainContent />
</div>
<Copilot />
</main>
</div>
</>
);
}