build a fullstack website browser with all integrations and api's
Browse filesBuild an AI-powered web browser application with the following features:
1. CORE FEATURES
• AI search assistant that summarizes results, answers queries, and opens relevant websites.
• Built-in chatbot sidebar (like ChatGPT) that can analyze the currently opened webpage.
• Voice search + voice response.
• Multi-tab browsing UI.
• History, bookmarks, downloads page.
• Dark mode + light mode.
2. AI FUNCTIONALITY
• Use an LLM model for:
- Searching the web
- Summarizing webpages
- Explaining articles
- Performing tasks like “find cheapest flights”, “extract table data”, etc.
• Provide "Ask this page" button for content analysis.
• Allow user to send a screenshot of the page to the AI.
3. UI REQUIREMENTS
• Modern UI similar to Arc Browser / Opera / Edge.
• Left sidebar:
- Home
- Bookmarks
- History
- Downloads
- Settings
- AI Assistant
• Top address bar with:
- Back / forward
- Reload
- Search bar
- Microphone icon for voice input
• Smooth animations, rounded corners, neumorphic/glassmorphism style.
4. TECH STACK
• Frontend: React + Tailwind CSS
• Backend: Node.js / Express
• AI: OpenAI API or Gemini API
• Browser engine: Use Electron.js to create a desktop app.
5. ADDITIONAL REQUIREMENTS
• Save user data locally using IndexedDB or localStorage.
• Support PDF view, video playback, file downloads.
• Add loading shimmer whenever a page loads.
• Add keyboard shortcuts (Ctrl+T, Ctrl+W, Ctrl+Shift+T, Ctrl+L, etc.)
• Include a settings page for:
- API key input
- Clear cache/history
- Change theme
- Enable/disable AI features
6. OUTPUT EXPECTATION
• Generate full folder structure.
• Generate all React components.
• Generate backend API routes.
• Provide installation steps.
• Provide code in fully working forma
- README.md +7 -4
- components/footer.js +76 -0
- components/navbar.js +113 -0
- index.html +93 -19
- main.js +43 -0
- package.json +31 -0
- script.js +30 -0
- src/App.js +171 -0
- src/components/AddressBar.js +39 -0
- src/components/Sidebar.js +66 -0
- src/index.css +54 -0
- src/index.js +7 -0
- style.css +21 -19
- webpack.react.js +37 -0
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
colorFrom: indigo
|
| 5 |
colorTo: yellow
|
|
|
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: ShadowStack Browser 🌐
|
| 3 |
+
colorFrom: red
|
|
|
|
| 4 |
colorTo: yellow
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
.social-icon {
|
| 7 |
+
transition: all 0.3s ease;
|
| 8 |
+
}
|
| 9 |
+
.social-icon:hover {
|
| 10 |
+
transform: translateY(-3px);
|
| 11 |
+
}
|
| 12 |
+
</style>
|
| 13 |
+
<footer class="bg-gray-800 border-t border-gray-700 py-12">
|
| 14 |
+
<div class="container mx-auto px-4">
|
| 15 |
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
| 16 |
+
<div class="md:col-span-2">
|
| 17 |
+
<h3 class="text-xl font-bold mb-4 bg-gradient-to-r from-primary-500 to-secondary-500 bg-clip-text text-transparent">ShadowStack Noir</h3>
|
| 18 |
+
<p class="text-gray-400 mb-4">
|
| 19 |
+
Fullstack development solutions for modern businesses. We build scalable, secure, and performant web applications.
|
| 20 |
+
</p>
|
| 21 |
+
<div class="flex space-x-4">
|
| 22 |
+
<a href="#" class="social-icon text-gray-400 hover:text-primary-500">
|
| 23 |
+
<i data-feather="github"></i>
|
| 24 |
+
</a>
|
| 25 |
+
<a href="#" class="social-icon text-gray-400 hover:text-primary-500">
|
| 26 |
+
<i data-feather="twitter"></i>
|
| 27 |
+
</a>
|
| 28 |
+
<a href="#" class="social-icon text-gray-400 hover:text-primary-500">
|
| 29 |
+
<i data-feather="linkedin"></i>
|
| 30 |
+
</a>
|
| 31 |
+
<a href="#" class="social-icon text-gray-400 hover:text-primary-500">
|
| 32 |
+
<i data-feather="instagram"></i>
|
| 33 |
+
</a>
|
| 34 |
+
</div>
|
| 35 |
+
</div>
|
| 36 |
+
|
| 37 |
+
<div>
|
| 38 |
+
<h4 class="text-lg font-semibold mb-4">Quick Links</h4>
|
| 39 |
+
<ul class="space-y-2">
|
| 40 |
+
<li><a href="/" class="text-gray-400 hover:text-white">Home</a></li>
|
| 41 |
+
<li><a href="/services" class="text-gray-400 hover:text-white">Services</a></li>
|
| 42 |
+
<li><a href="/projects" class="text-gray-400 hover:text-white">Projects</a></li>
|
| 43 |
+
<li><a href="/about" class="text-gray-400 hover:text-white">About Us</a></li>
|
| 44 |
+
<li><a href="/contact" class="text-gray-400 hover:text-white">Contact</a></li>
|
| 45 |
+
</ul>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<div>
|
| 49 |
+
<h4 class="text-lg font-semibold mb-4">Contact</h4>
|
| 50 |
+
<ul class="space-y-2 text-gray-400">
|
| 51 |
+
<li class="flex items-center space-x-2">
|
| 52 |
+
<i data-feather="mail"></i>
|
| 53 |
+
<span>contact@shadowstack.dev</span>
|
| 54 |
+
</li>
|
| 55 |
+
<li class="flex items-center space-x-2">
|
| 56 |
+
<i data-feather="phone"></i>
|
| 57 |
+
<span>+1 (555) 123-4567</span>
|
| 58 |
+
</li>
|
| 59 |
+
<li class="flex items-center space-x-2">
|
| 60 |
+
<i data-feather="map-pin"></i>
|
| 61 |
+
<span>San Francisco, CA</span>
|
| 62 |
+
</li>
|
| 63 |
+
</ul>
|
| 64 |
+
</div>
|
| 65 |
+
</div>
|
| 66 |
+
|
| 67 |
+
<div class="border-t border-gray-700 mt-12 pt-8 text-center text-gray-500">
|
| 68 |
+
<p>© ${new Date().getFullYear()} ShadowStack Noir. All rights reserved.</p>
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
+
</footer>
|
| 72 |
+
`;
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
customElements.define('custom-footer', CustomFooter);
|
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomNavbar extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
.nav-link {
|
| 7 |
+
position: relative;
|
| 8 |
+
}
|
| 9 |
+
.nav-link::after {
|
| 10 |
+
content: '';
|
| 11 |
+
position: absolute;
|
| 12 |
+
bottom: -2px;
|
| 13 |
+
left: 0;
|
| 14 |
+
width: 0;
|
| 15 |
+
height: 2px;
|
| 16 |
+
background-color: #7c3aed;
|
| 17 |
+
transition: width 0.3s ease;
|
| 18 |
+
}
|
| 19 |
+
.nav-link:hover::after {
|
| 20 |
+
width: 100%;
|
| 21 |
+
}
|
| 22 |
+
</style>
|
| 23 |
+
<nav class="bg-gray-800 border-b border-gray-700">
|
| 24 |
+
<div class="container mx-auto px-4">
|
| 25 |
+
<div class="flex justify-between items-center py-4">
|
| 26 |
+
<a href="/" class="flex items-center space-x-2">
|
| 27 |
+
<span class="text-2xl font-bold bg-gradient-to-r from-primary-500 to-secondary-500 bg-clip-text text-transparent">ShadowStack</span>
|
| 28 |
+
</a>
|
| 29 |
+
|
| 30 |
+
<div class="hidden md:flex items-center space-x-8">
|
| 31 |
+
<a href="/" class="nav-link text-gray-300 hover:text-white">Home</a>
|
| 32 |
+
<a href="/services" class="nav-link text-gray-300 hover:text-white">Services</a>
|
| 33 |
+
<a href="/projects" class="nav-link text-gray-300 hover:text-white">Projects</a>
|
| 34 |
+
<a href="/about" class="nav-link text-gray-300 hover:text-white">About</a>
|
| 35 |
+
<a href="/contact" class="nav-link text-gray-300 hover:text-white">Contact</a>
|
| 36 |
+
|
| 37 |
+
<button id="themeToggle" class="p-2 rounded-full hover:bg-gray-700">
|
| 38 |
+
<i data-feather="moon" class="text-gray-300"></i>
|
| 39 |
+
</button>
|
| 40 |
+
</div>
|
| 41 |
+
|
| 42 |
+
<button class="md:hidden p-2 rounded-full hover:bg-gray-700" id="mobileMenuButton">
|
| 43 |
+
<i data-feather="menu" class="text-gray-300"></i>
|
| 44 |
+
</button>
|
| 45 |
+
</div>
|
| 46 |
+
|
| 47 |
+
<!-- Mobile menu -->
|
| 48 |
+
<div class="md:hidden hidden py-4 border-t border-gray-700" id="mobileMenu">
|
| 49 |
+
<div class="flex flex-col space-y-4">
|
| 50 |
+
<a href="/" class="text-gray-300 hover:text-white">Home</a>
|
| 51 |
+
<a href="/services" class="text-gray-300 hover:text-white">Services</a>
|
| 52 |
+
<a href="/projects" class="text-gray-300 hover:text-white">Projects</a>
|
| 53 |
+
<a href="/about" class="text-gray-300 hover:text-white">About</a>
|
| 54 |
+
<a href="/contact" class="text-gray-300 hover:text-white">Contact</a>
|
| 55 |
+
|
| 56 |
+
<div class="flex justify-between items-center pt-4 border-t border-gray-700">
|
| 57 |
+
<span>Dark Mode</span>
|
| 58 |
+
<button id="mobileThemeToggle" class="p-2 rounded-full hover:bg-gray-700">
|
| 59 |
+
<i data-feather="moon" class="text-gray-300"></i>
|
| 60 |
+
</button>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
</nav>
|
| 66 |
+
`;
|
| 67 |
+
|
| 68 |
+
// Add event listeners after rendering
|
| 69 |
+
setTimeout(() => {
|
| 70 |
+
const themeToggle = this.shadowRoot.getElementById('themeToggle');
|
| 71 |
+
const mobileThemeToggle = this.shadowRoot.getElementById('mobileThemeToggle');
|
| 72 |
+
const mobileMenuButton = this.shadowRoot.getElementById('mobileMenuButton');
|
| 73 |
+
const mobileMenu = this.shadowRoot.getElementById('mobileMenu');
|
| 74 |
+
|
| 75 |
+
if (themeToggle) {
|
| 76 |
+
themeToggle.addEventListener('click', this.toggleTheme);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
if (mobileThemeToggle) {
|
| 80 |
+
mobileThemeToggle.addEventListener('click', this.toggleTheme);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
if (mobileMenuButton) {
|
| 84 |
+
mobileMenuButton.addEventListener('click', () => {
|
| 85 |
+
mobileMenu.classList.toggle('hidden');
|
| 86 |
+
feather.replace();
|
| 87 |
+
});
|
| 88 |
+
}
|
| 89 |
+
}, 0);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
toggleTheme() {
|
| 93 |
+
const html = document.documentElement;
|
| 94 |
+
html.classList.toggle('dark');
|
| 95 |
+
|
| 96 |
+
// Save preference to localStorage
|
| 97 |
+
const isDark = html.classList.contains('dark');
|
| 98 |
+
localStorage.setItem('darkMode', isDark);
|
| 99 |
+
|
| 100 |
+
// Update icons
|
| 101 |
+
const icons = this.shadowRoot.querySelectorAll('[data-feather]');
|
| 102 |
+
icons.forEach(icon => {
|
| 103 |
+
if (icon.dataset.feather === 'moon' && !isDark) {
|
| 104 |
+
icon.dataset.feather = 'sun';
|
| 105 |
+
} else if (icon.dataset.feather === 'sun' && isDark) {
|
| 106 |
+
icon.dataset.feather = 'moon';
|
| 107 |
+
}
|
| 108 |
+
});
|
| 109 |
+
feather.replace();
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
customElements.define('custom-navbar', CustomNavbar);
|
|
@@ -1,19 +1,93 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" class="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>ShadowStack Noir | Fullstack Development</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css">
|
| 8 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
+
<script>
|
| 10 |
+
tailwind.config = {
|
| 11 |
+
darkMode: 'class',
|
| 12 |
+
theme: {
|
| 13 |
+
extend: {
|
| 14 |
+
colors: {
|
| 15 |
+
primary: {
|
| 16 |
+
500: '#7c3aed',
|
| 17 |
+
600: '#6d28d9',
|
| 18 |
+
},
|
| 19 |
+
secondary: {
|
| 20 |
+
500: '#14b8a6',
|
| 21 |
+
600: '#0d9488',
|
| 22 |
+
}
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
</script>
|
| 28 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 29 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 30 |
+
<script src="components/navbar.js"></script>
|
| 31 |
+
<script src="components/footer.js"></script>
|
| 32 |
+
</head>
|
| 33 |
+
<body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col">
|
| 34 |
+
<custom-navbar></custom-navbar>
|
| 35 |
+
|
| 36 |
+
<main class="flex-grow container mx-auto px-4 py-12">
|
| 37 |
+
<section class="hero mb-20">
|
| 38 |
+
<div class="max-w-4xl mx-auto text-center">
|
| 39 |
+
<h1 class="text-5xl md:text-6xl font-bold mb-6 bg-gradient-to-r from-primary-500 to-secondary-500 bg-clip-text text-transparent">
|
| 40 |
+
Fullstack Development <br> Made Simple
|
| 41 |
+
</h1>
|
| 42 |
+
<p class="text-xl text-gray-400 mb-10 max-w-2xl mx-auto">
|
| 43 |
+
Build modern web applications with our complete fullstack solutions. From frontend to backend, we've got you covered.
|
| 44 |
+
</p>
|
| 45 |
+
<div class="flex justify-center gap-4">
|
| 46 |
+
<a href="/services" class="px-8 py-3 bg-primary-600 hover:bg-primary-500 rounded-lg font-medium transition-colors">
|
| 47 |
+
Our Services
|
| 48 |
+
</a>
|
| 49 |
+
<a href="/contact" class="px-8 py-3 border border-gray-700 hover:border-gray-600 rounded-lg font-medium transition-colors">
|
| 50 |
+
Contact Us
|
| 51 |
+
</a>
|
| 52 |
+
</div>
|
| 53 |
+
</div>
|
| 54 |
+
</section>
|
| 55 |
+
|
| 56 |
+
<section class="features mb-20">
|
| 57 |
+
<h2 class="text-3xl font-bold mb-12 text-center">Why Choose Us</h2>
|
| 58 |
+
<div class="grid md:grid-cols-3 gap-8">
|
| 59 |
+
<div class="feature-card p-6 rounded-xl bg-gray-800 hover:bg-gray-700 transition-colors">
|
| 60 |
+
<div class="w-12 h-12 rounded-lg bg-primary-500/20 flex items-center justify-center mb-4">
|
| 61 |
+
<i data-feather="code" class="text-primary-500"></i>
|
| 62 |
+
</div>
|
| 63 |
+
<h3 class="text-xl font-semibold mb-3">Modern Tech Stack</h3>
|
| 64 |
+
<p class="text-gray-400">We use the latest technologies like React, Node.js, and modern databases to build fast, scalable applications.</p>
|
| 65 |
+
</div>
|
| 66 |
+
<div class="feature-card p-6 rounded-xl bg-gray-800 hover:bg-gray-700 transition-colors">
|
| 67 |
+
<div class="w-12 h-12 rounded-lg bg-secondary-500/20 flex items-center justify-center mb-4">
|
| 68 |
+
<i data-feather="layers" class="text-secondary-500"></i>
|
| 69 |
+
</div>
|
| 70 |
+
<h3 class="text-xl font-semibold mb-3">Fullstack Solutions</h3>
|
| 71 |
+
<p class="text-gray-400">From frontend UI to backend APIs and databases - we handle everything in one seamless package.</p>
|
| 72 |
+
</div>
|
| 73 |
+
<div class="feature-card p-6 rounded-xl bg-gray-800 hover:bg-gray-700 transition-colors">
|
| 74 |
+
<div class="w-12 h-12 rounded-lg bg-primary-500/20 flex items-center justify-center mb-4">
|
| 75 |
+
<i data-feather="shield" class="text-primary-500"></i>
|
| 76 |
+
</div>
|
| 77 |
+
<h3 class="text-xl font-semibold mb-3">Security Focused</h3>
|
| 78 |
+
<p class="text-gray-400">We implement best security practices to protect your data and users from modern threats.</p>
|
| 79 |
+
</div>
|
| 80 |
+
</div>
|
| 81 |
+
</section>
|
| 82 |
+
</main>
|
| 83 |
+
|
| 84 |
+
<custom-footer></custom-footer>
|
| 85 |
+
|
| 86 |
+
<script src="script.js"></script>
|
| 87 |
+
<script>
|
| 88 |
+
feather.replace();
|
| 89 |
+
</script>
|
| 90 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 91 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 92 |
+
</body>
|
| 93 |
+
</html>
|
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { app, BrowserWindow, ipcMain } = require('electron');
|
| 2 |
+
const path = require('path');
|
| 3 |
+
const isDev = require('electron-is-dev');
|
| 4 |
+
|
| 5 |
+
function createWindow() {
|
| 6 |
+
const win = new BrowserWindow({
|
| 7 |
+
width: 1200,
|
| 8 |
+
height: 800,
|
| 9 |
+
minWidth: 800,
|
| 10 |
+
minHeight: 600,
|
| 11 |
+
frame: false,
|
| 12 |
+
webPreferences: {
|
| 13 |
+
nodeIntegration: true,
|
| 14 |
+
contextIsolation: false,
|
| 15 |
+
enableRemoteModule: true,
|
| 16 |
+
webviewTag: true
|
| 17 |
+
}
|
| 18 |
+
});
|
| 19 |
+
|
| 20 |
+
win.loadURL(
|
| 21 |
+
isDev
|
| 22 |
+
? 'http://localhost:3000'
|
| 23 |
+
: `file://${path.join(__dirname, './dist/index.html')}`
|
| 24 |
+
);
|
| 25 |
+
|
| 26 |
+
if (isDev) {
|
| 27 |
+
win.webContents.openDevTools();
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
app.whenReady().then(createWindow);
|
| 32 |
+
|
| 33 |
+
app.on('window-all-closed', () => {
|
| 34 |
+
if (process.platform !== 'darwin') {
|
| 35 |
+
app.quit();
|
| 36 |
+
}
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
app.on('activate', () => {
|
| 40 |
+
if (BrowserWindow.getAllWindows().length === 0) {
|
| 41 |
+
createWindow();
|
| 42 |
+
}
|
| 43 |
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```json
|
| 2 |
+
{
|
| 3 |
+
"name": "shadowstack-browser",
|
| 4 |
+
"version": "1.0.0",
|
| 5 |
+
"description": "AI-powered web browser with integrated assistant",
|
| 6 |
+
"main": "main.js",
|
| 7 |
+
"scripts": {
|
| 8 |
+
"start": "electron .",
|
| 9 |
+
"dev": "concurrently \"npm run dev:react\" \"npm run dev:electron\"",
|
| 10 |
+
"dev:react": "webpack serve --config webpack.react.js --mode development",
|
| 11 |
+
"dev:electron": "electron .",
|
| 12 |
+
"build": "webpack --config webpack.react.js --mode production && electron-builder"
|
| 13 |
+
},
|
| 14 |
+
"dependencies": {
|
| 15 |
+
"@electron/remote": "^2.1.2",
|
| 16 |
+
"axios": "^1.6.0",
|
| 17 |
+
"electron-is-dev": "^2.0.0",
|
| 18 |
+
"express": "^4.18.2",
|
| 19 |
+
"openai": "^4.20.0",
|
| 20 |
+
"path": "^0.12.7"
|
| 21 |
+
},
|
| 22 |
+
"devDependencies": {
|
| 23 |
+
"concurrently": "^8.2.0",
|
| 24 |
+
"electron": "^28.0.0",
|
| 25 |
+
"electron-builder": "^24.9.0",
|
| 26 |
+
"webpack": "^5.89.0",
|
| 27 |
+
"webpack-cli": "^5.1.4",
|
| 28 |
+
"webpack-dev-server": "^4.15.1"
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Theme toggle functionality
|
| 2 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 3 |
+
// Check for saved theme preference
|
| 4 |
+
if (localStorage.getItem('darkMode') === 'false') {
|
| 5 |
+
document.documentElement.classList.remove('dark');
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
// Initialize tooltips
|
| 9 |
+
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
| 10 |
+
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
| 11 |
+
return new bootstrap.Tooltip(tooltipTriggerEl);
|
| 12 |
+
});
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
// Simple form validation example
|
| 16 |
+
function validateForm(form) {
|
| 17 |
+
const inputs = form.querySelectorAll('input[required], textarea[required]');
|
| 18 |
+
let isValid = true;
|
| 19 |
+
|
| 20 |
+
inputs.forEach(input => {
|
| 21 |
+
if (!input.value.trim()) {
|
| 22 |
+
input.classList.add('border-red-500');
|
| 23 |
+
isValid = false;
|
| 24 |
+
} else {
|
| 25 |
+
input.classList.remove('border-red-500');
|
| 26 |
+
}
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
return isValid;
|
| 30 |
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
+
import Sidebar from './components/Sidebar';
|
| 3 |
+
import AddressBar from './components/AddressBar';
|
| 4 |
+
import TabBar from './components/TabBar';
|
| 5 |
+
import WebView from './components/WebView';
|
| 6 |
+
import AIAssistant from './components/AIAssistant';
|
| 7 |
+
import SettingsModal from './components/SettingsModal';
|
| 8 |
+
import BookmarksPage from './pages/BookmarksPage';
|
| 9 |
+
import HistoryPage from './pages/HistoryPage';
|
| 10 |
+
import DownloadsPage from './pages/DownloadsPage';
|
| 11 |
+
|
| 12 |
+
const App = () => {
|
| 13 |
+
const [tabs, setTabs] = useState([
|
| 14 |
+
{ id: 1, title: 'New Tab', url: 'https://www.google.com', active: true }
|
| 15 |
+
]);
|
| 16 |
+
const [activeTabId, setActiveTabId] = useState(1);
|
| 17 |
+
const [showAIAssistant, setShowAIAssistant] = useState(false);
|
| 18 |
+
const [showSettings, setShowSettings] = useState(false);
|
| 19 |
+
const [currentPage, setCurrentPage] = useState('browser'); // browser, bookmarks, history, downloads
|
| 20 |
+
const [bookmarks, setBookmarks] = useState([]);
|
| 21 |
+
const [history, setHistory] = useState([]);
|
| 22 |
+
const [downloads, setDownloads] = useState([]);
|
| 23 |
+
|
| 24 |
+
// Load data from localStorage
|
| 25 |
+
useEffect(() => {
|
| 26 |
+
const savedBookmarks = JSON.parse(localStorage.getItem('bookmarks') || '[]');
|
| 27 |
+
const savedHistory = JSON.parse(localStorage.getItem('history') || '[]');
|
| 28 |
+
const savedDownloads = JSON.parse(localStorage.getItem('downloads') || '[]');
|
| 29 |
+
|
| 30 |
+
setBookmarks(savedBookmarks);
|
| 31 |
+
setHistory(savedHistory);
|
| 32 |
+
setDownloads(savedDownloads);
|
| 33 |
+
}, []);
|
| 34 |
+
|
| 35 |
+
// Save data to localStorage
|
| 36 |
+
useEffect(() => {
|
| 37 |
+
localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
|
| 38 |
+
}, [bookmarks]);
|
| 39 |
+
|
| 40 |
+
useEffect(() => {
|
| 41 |
+
localStorage.setItem('history', JSON.stringify(history));
|
| 42 |
+
}, [history]);
|
| 43 |
+
|
| 44 |
+
useEffect(() => {
|
| 45 |
+
localStorage.setItem('downloads', JSON.stringify(downloads));
|
| 46 |
+
}, [downloads]);
|
| 47 |
+
|
| 48 |
+
const addTab = () => {
|
| 49 |
+
const newTab = {
|
| 50 |
+
id: Date.now(),
|
| 51 |
+
title: 'New Tab',
|
| 52 |
+
url: 'https://www.google.com',
|
| 53 |
+
active: true
|
| 54 |
+
};
|
| 55 |
+
setTabs([...tabs.map(tab => ({ ...tab, active: false })), newTab]);
|
| 56 |
+
setActiveTabId(newTab.id);
|
| 57 |
+
setCurrentPage('browser');
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const closeTab = (id) => {
|
| 61 |
+
if (tabs.length <= 1) return;
|
| 62 |
+
const newTabs = tabs.filter(tab => tab.id !== id);
|
| 63 |
+
if (tabs.find(tab => tab.id === id)?.active) {
|
| 64 |
+
newTabs[0].active = true;
|
| 65 |
+
setActiveTabId(newTabs[0].id);
|
| 66 |
+
}
|
| 67 |
+
setTabs(newTabs);
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
+
const switchTab = (id) => {
|
| 71 |
+
setTabs(tabs.map(tab => ({
|
| 72 |
+
...tab,
|
| 73 |
+
active: tab.id === id
|
| 74 |
+
})));
|
| 75 |
+
setActiveTabId(id);
|
| 76 |
+
};
|
| 77 |
+
|
| 78 |
+
const updateTab = (id, updates) => {
|
| 79 |
+
setTabs(tabs.map(tab =>
|
| 80 |
+
tab.id === id ? { ...tab, ...updates } : tab
|
| 81 |
+
));
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
+
const addBookmark = (bookmark) => {
|
| 85 |
+
setBookmarks([...bookmarks, bookmark]);
|
| 86 |
+
};
|
| 87 |
+
|
| 88 |
+
const removeBookmark = (url) => {
|
| 89 |
+
setBookmarks(bookmarks.filter(b => b.url !== url));
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
const addToHistory = (entry) => {
|
| 93 |
+
setHistory([entry, ...history.slice(0, 99)]); // Keep last 100 entries
|
| 94 |
+
};
|
| 95 |
+
|
| 96 |
+
const addDownload = (download) => {
|
| 97 |
+
setDownloads([download, ...downloads]);
|
| 98 |
+
};
|
| 99 |
+
|
| 100 |
+
const activeTab = tabs.find(tab => tab.id === activeTabId) || tabs[0];
|
| 101 |
+
|
| 102 |
+
return (
|
| 103 |
+
<div className="flex h-screen bg-gray-900 text-white overflow-hidden">
|
| 104 |
+
<Sidebar
|
| 105 |
+
currentPage={currentPage}
|
| 106 |
+
setCurrentPage={setCurrentPage}
|
| 107 |
+
showAIAssistant={showAIAssistant}
|
| 108 |
+
setShowAIAssistant={setShowAIAssistant}
|
| 109 |
+
setShowSettings={setShowSettings}
|
| 110 |
+
addTab={addTab}
|
| 111 |
+
/>
|
| 112 |
+
|
| 113 |
+
<div className="flex flex-col flex-1">
|
| 114 |
+
<AddressBar
|
| 115 |
+
activeTab={activeTab}
|
| 116 |
+
updateTab={updateTab}
|
| 117 |
+
addTab={addTab}
|
| 118 |
+
setCurrentPage={setCurrentPage}
|
| 119 |
+
/>
|
| 120 |
+
|
| 121 |
+
<TabBar
|
| 122 |
+
tabs={tabs}
|
| 123 |
+
activeTabId={activeTabId}
|
| 124 |
+
switchTab={switchTab}
|
| 125 |
+
closeTab={closeTab}
|
| 126 |
+
addTab={addTab}
|
| 127 |
+
/>
|
| 128 |
+
|
| 129 |
+
{currentPage === 'browser' ? (
|
| 130 |
+
<WebView
|
| 131 |
+
activeTab={activeTab}
|
| 132 |
+
updateTab={updateTab}
|
| 133 |
+
addToHistory={addToHistory}
|
| 134 |
+
addDownload={addDownload}
|
| 135 |
+
/>
|
| 136 |
+
) : currentPage === 'bookmarks' ? (
|
| 137 |
+
<BookmarksPage
|
| 138 |
+
bookmarks={bookmarks}
|
| 139 |
+
removeBookmark={removeBookmark}
|
| 140 |
+
setCurrentPage={setCurrentPage}
|
| 141 |
+
/>
|
| 142 |
+
) : currentPage === 'history' ? (
|
| 143 |
+
<HistoryPage
|
| 144 |
+
history={history}
|
| 145 |
+
setCurrentPage={setCurrentPage}
|
| 146 |
+
/>
|
| 147 |
+
) : (
|
| 148 |
+
<DownloadsPage
|
| 149 |
+
downloads={downloads}
|
| 150 |
+
setCurrentPage={setCurrentPage}
|
| 151 |
+
/>
|
| 152 |
+
)}
|
| 153 |
+
</div>
|
| 154 |
+
|
| 155 |
+
{showAIAssistant && (
|
| 156 |
+
<AIAssistant
|
| 157 |
+
activeTab={activeTab}
|
| 158 |
+
onClose={() => setShowAIAssistant(false)}
|
| 159 |
+
/>
|
| 160 |
+
)}
|
| 161 |
+
|
| 162 |
+
{showSettings && (
|
| 163 |
+
<SettingsModal
|
| 164 |
+
onClose={() => setShowSettings(false)}
|
| 165 |
+
/>
|
| 166 |
+
)}
|
| 167 |
+
</div>
|
| 168 |
+
);
|
| 169 |
+
};
|
| 170 |
+
|
| 171 |
+
export default App;
|
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React, { useState } from 'react';
|
| 3 |
+
import {
|
| 4 |
+
ArrowLeftIcon,
|
| 5 |
+
ArrowRightIcon,
|
| 6 |
+
ArrowPathIcon,
|
| 7 |
+
MicrophoneIcon,
|
| 8 |
+
MagnifyingGlassIcon
|
| 9 |
+
} from '@heroicons/react/24/outline';
|
| 10 |
+
|
| 11 |
+
const AddressBar = ({ activeTab, updateTab, addTab, setCurrentPage }) => {
|
| 12 |
+
const [inputValue, setInputValue] = useState(activeTab?.url || '');
|
| 13 |
+
const [isListening, setIsListening] = useState(false);
|
| 14 |
+
|
| 15 |
+
const handleKeyDown = (e) => {
|
| 16 |
+
if (e.key === 'Enter') {
|
| 17 |
+
handleSubmit();
|
| 18 |
+
}
|
| 19 |
+
};
|
| 20 |
+
|
| 21 |
+
const handleSubmit = () => {
|
| 22 |
+
let url = inputValue.trim();
|
| 23 |
+
|
| 24 |
+
// If it looks like a search query, redirect to Google search
|
| 25 |
+
if (!url.includes('.') && url.split(' ').length > 0) {
|
| 26 |
+
url = `https://www.google.com/search?q=${encodeURIComponent(url)}`;
|
| 27 |
+
}
|
| 28 |
+
// If no protocol specified, add https://
|
| 29 |
+
else if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
| 30 |
+
url = `https://${url}`;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
updateTab(activeTab.id, { url });
|
| 34 |
+
setInputValue(url);
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
const handleVoiceSearch = () => {
|
| 38 |
+
if ('webkitSpeechRecognition' in window) {
|
| 39 |
+
const recognition = new window
|
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import {
|
| 3 |
+
HomeIcon,
|
| 4 |
+
BookmarkIcon,
|
| 5 |
+
ClockIcon,
|
| 6 |
+
ArrowDownTrayIcon,
|
| 7 |
+
Cog6ToothIcon,
|
| 8 |
+
ChatBubbleLeftRightIcon,
|
| 9 |
+
PlusIcon
|
| 10 |
+
} from '@heroicons/react/24/outline';
|
| 11 |
+
|
| 12 |
+
const Sidebar = ({
|
| 13 |
+
currentPage,
|
| 14 |
+
setCurrentPage,
|
| 15 |
+
showAIAssistant,
|
| 16 |
+
setShowAIAssistant,
|
| 17 |
+
setShowSettings,
|
| 18 |
+
addTab
|
| 19 |
+
}) => {
|
| 20 |
+
const menuItems = [
|
| 21 |
+
{ id: 'home', label: 'Home', icon: HomeIcon },
|
| 22 |
+
{ id: 'bookmarks', label: 'Bookmarks', icon: BookmarkIcon },
|
| 23 |
+
{ id: 'history', label: 'History', icon: ClockIcon },
|
| 24 |
+
{ id: 'downloads', label: 'Downloads', icon: ArrowDownTrayIcon },
|
| 25 |
+
{ id: 'settings', label: 'Settings', icon: Cog6ToothIcon }
|
| 26 |
+
];
|
| 27 |
+
|
| 28 |
+
return (
|
| 29 |
+
<div className="w-16 bg-gray-800 flex flex-col items-center py-4 space-y-6">
|
| 30 |
+
<div className="p-2 rounded-lg bg-indigo-600 mb-2">
|
| 31 |
+
<PlusIcon className="h-6 w-6 text-white" onClick={addTab} />
|
| 32 |
+
</div>
|
| 33 |
+
|
| 34 |
+
{menuItems.map((item) => (
|
| 35 |
+
<button
|
| 36 |
+
key={item.id}
|
| 37 |
+
onClick={() => {
|
| 38 |
+
if (item.id === 'settings') {
|
| 39 |
+
setShowSettings(true);
|
| 40 |
+
} else {
|
| 41 |
+
setCurrentPage(item.id);
|
| 42 |
+
}
|
| 43 |
+
}}
|
| 44 |
+
className={`p-2 rounded-lg transition-colors ${
|
| 45 |
+
currentPage === item.id ? 'bg-gray-700' : 'hover:bg-gray-700'
|
| 46 |
+
}`}
|
| 47 |
+
>
|
| 48 |
+
<item.icon className="h-6 w-6 text-gray-300" />
|
| 49 |
+
</button>
|
| 50 |
+
))}
|
| 51 |
+
|
| 52 |
+
<div className="mt-auto">
|
| 53 |
+
<button
|
| 54 |
+
onClick={() => setShowAIAssistant(!showAIAssistant)}
|
| 55 |
+
className={`p-2 rounded-lg transition-colors ${
|
| 56 |
+
showAIAssistant ? 'bg-indigo-600' : 'hover:bg-gray-700'
|
| 57 |
+
}`}
|
| 58 |
+
>
|
| 59 |
+
<ChatBubbleLeftRightIcon className="h-6 w-6 text-gray-300" />
|
| 60 |
+
</button>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
);
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
export default Sidebar;
|
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@tailwind base;
|
| 2 |
+
@tailwind components;
|
| 3 |
+
@tailwind utilities;
|
| 4 |
+
|
| 5 |
+
body {
|
| 6 |
+
margin: 0;
|
| 7 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
| 8 |
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
| 9 |
+
sans-serif;
|
| 10 |
+
-webkit-font-smoothing: antialiased;
|
| 11 |
+
-moz-osx-font-smoothing: grayscale;
|
| 12 |
+
overflow: hidden;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.webview-container {
|
| 16 |
+
flex: 1;
|
| 17 |
+
position: relative;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
webview {
|
| 21 |
+
width: 100%;
|
| 22 |
+
height: 100%;
|
| 23 |
+
position: absolute;
|
| 24 |
+
top: 0;
|
| 25 |
+
left: 0;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
.shimmer {
|
| 29 |
+
animation: shimmer 1.5s infinite linear;
|
| 30 |
+
background: linear-gradient(to right, #2d3748 4%, #4a5568 25%, #2d3748 36%);
|
| 31 |
+
background-size: 1000px 100%;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
@keyframes shimmer {
|
| 35 |
+
0% {
|
| 36 |
+
background-position: -1000px 0;
|
| 37 |
+
}
|
| 38 |
+
100% {
|
| 39 |
+
background-position: 1000px 0;
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.neumorphic {
|
| 44 |
+
background: #1a202c;
|
| 45 |
+
box-shadow: 5px 5px 10px #0d1017, -5px -5px 10px #272e45;
|
| 46 |
+
border-radius: 16px;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.glassmorphic {
|
| 50 |
+
background: rgba(26, 32, 44, 0.7);
|
| 51 |
+
backdrop-filter: blur(10px);
|
| 52 |
+
border-radius: 16px;
|
| 53 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 54 |
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import ReactDOM from 'react-dom/client';
|
| 3 |
+
import App from './App';
|
| 4 |
+
import './index.css';
|
| 5 |
+
|
| 6 |
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
| 7 |
+
root.render(<App />);
|
|
@@ -1,28 +1,30 @@
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
margin-bottom: 10px;
|
| 15 |
-
margin-top: 5px;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 2 |
+
|
| 3 |
body {
|
| 4 |
+
font-family: 'Inter', sans-serif;
|
|
|
|
| 5 |
}
|
| 6 |
|
| 7 |
+
.feature-card:hover i {
|
| 8 |
+
transform: scale(1.1);
|
| 9 |
+
transition: transform 0.2s ease;
|
| 10 |
}
|
| 11 |
|
| 12 |
+
/* Smooth scrolling */
|
| 13 |
+
html {
|
| 14 |
+
scroll-behavior: smooth;
|
|
|
|
|
|
|
| 15 |
}
|
| 16 |
|
| 17 |
+
/* Custom scrollbar */
|
| 18 |
+
::-webkit-scrollbar {
|
| 19 |
+
width: 8px;
|
|
|
|
|
|
|
|
|
|
| 20 |
}
|
| 21 |
+
::-webkit-scrollbar-track {
|
| 22 |
+
background: #1e1e1e;
|
| 23 |
+
}
|
| 24 |
+
::-webkit-scrollbar-thumb {
|
| 25 |
+
background: #7c3aed;
|
| 26 |
+
border-radius: 4px;
|
| 27 |
}
|
| 28 |
+
::-webkit-scrollbar-thumb:hover {
|
| 29 |
+
background: #6d28d9;
|
| 30 |
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const path = require('path');
|
| 2 |
+
|
| 3 |
+
module.exports = {
|
| 4 |
+
entry: './src/index.js',
|
| 5 |
+
output: {
|
| 6 |
+
path: path.resolve(__dirname, 'dist'),
|
| 7 |
+
filename: 'bundle.js'
|
| 8 |
+
},
|
| 9 |
+
module: {
|
| 10 |
+
rules: [
|
| 11 |
+
{
|
| 12 |
+
test: /\.(js|jsx)$/,
|
| 13 |
+
exclude: /node_modules/,
|
| 14 |
+
use: {
|
| 15 |
+
loader: 'babel-loader',
|
| 16 |
+
options: {
|
| 17 |
+
presets: ['@babel/preset-env', '@babel/preset-react']
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
test: /\.css$/,
|
| 23 |
+
use: ['style-loader', 'css-loader']
|
| 24 |
+
}
|
| 25 |
+
]
|
| 26 |
+
},
|
| 27 |
+
resolve: {
|
| 28 |
+
extensions: ['.js', '.jsx']
|
| 29 |
+
},
|
| 30 |
+
devServer: {
|
| 31 |
+
static: {
|
| 32 |
+
directory: path.join(__dirname, 'dist')
|
| 33 |
+
},
|
| 34 |
+
compress: true,
|
| 35 |
+
port: 3000
|
| 36 |
+
}
|
| 37 |
+
};
|