first commit

This commit is contained in:
2026-03-10 20:28:48 +01:00
commit e56a7687bf
208 changed files with 40140 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,500 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Microsoft Azure</title>
<!-- Use Inter to simulate Segoe UI / premium modern font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="/src/style.css" rel="stylesheet" />
<!-- Material Symbols Outlined for icons -->
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
rel="stylesheet" />
</head>
<body>
<div id="azure-portal">
<!-- TOP NAVIGATION -->
<header class="top-nav">
<div class="nav-left">
<button id="sidebar-toggle" class="icon-btn tooltip-container" aria-label="Toggle menu">
<span class="material-symbols-outlined nav-icon">menu</span>
</button>
<div class="brand">
<span class="material-symbols-outlined brand-icon">cloud</span>
<span class="brand-text">Microsoft Azure</span>
</div>
</div>
<div class="nav-middle">
<div class="search-bar">
<span class="material-symbols-outlined search-icon">search</span>
<input type="text" placeholder="Search resources, services, and docs (G+/)" />
</div>
</div>
<div class="nav-right">
<button class="icon-btn tooltip-container"><span
class="material-symbols-outlined nav-icon">terminal</span></button>
<button class="icon-btn tooltip-container"><span
class="material-symbols-outlined nav-icon">filter_alt</span></button>
<button class="icon-btn tooltip-container"><span
class="material-symbols-outlined nav-icon">notifications</span></button>
<button class="icon-btn tooltip-container"><span
class="material-symbols-outlined nav-icon">settings</span></button>
<button class="icon-btn tooltip-container" style="margin-right: 12px;"><span
class="material-symbols-outlined nav-icon">help</span></button>
<div class="user-profile">
<div class="avatar">EP</div>
<div class="tenant-info">
<span class="email">eliott@example.com</span>
<span class="tenant">Default Directory</span>
</div>
</div>
</div>
</header>
<div class="portal-body">
<!-- LEFT SIDEBAR -->
<aside class="sidebar expanded" id="sidebar">
<div class="sidebar-menu">
<a href="#" class="sidebar-item highlight" data-page="create-resource">
<span class="material-symbols-outlined">add</span>
<span class="sidebar-text">Create a resource</span>
</a>
<div class="sidebar-divider"></div>
<a href="#" class="sidebar-item" data-page="home">
<span class="material-symbols-outlined">home</span>
<span class="sidebar-text">Home</span>
</a>
<a href="#" class="sidebar-item active" data-page="dashboard">
<span class="material-symbols-outlined">dashboard</span>
<span class="sidebar-text">Dashboard</span>
</a>
<a href="#" class="sidebar-item" data-page="all-services">
<span class="material-symbols-outlined">menu_open</span>
<span class="sidebar-text">All services</span>
</a>
<div class="sidebar-group-title">FAVORITES</div>
<a href="#" class="sidebar-item" data-page="resource-groups">
<span class="material-symbols-outlined">folder</span>
<span class="sidebar-text">Resource groups</span>
</a>
<a href="#" class="sidebar-item" data-page="app-services">
<span class="material-symbols-outlined">apps</span>
<span class="sidebar-text">App Services</span>
</a>
<a href="#" class="sidebar-item" data-page="virtual-machines">
<span class="material-symbols-outlined">memory</span>
<span class="sidebar-text">Virtual machines</span>
</a>
<a href="#" class="sidebar-item" data-page="storage-accounts">
<span class="material-symbols-outlined">storage</span>
<span class="sidebar-text">Storage accounts</span>
</a>
</div>
</aside>
<!-- MAIN CONTENT AREA -->
<main class="main-content">
<!-- DASHBOARD PAGE (Default Active) -->
<div id="page-dashboard" class="page-view active">
<div class="page-header">
<h1 class="page-title">Dashboard</h1>
<div class="page-actions">
<button class="action-btn"><span class="material-symbols-outlined">edit</span> Edit</button>
<button class="action-btn"><span class="material-symbols-outlined">share</span> Share</button>
<button class="action-btn"><span class="material-symbols-outlined">fullscreen</span> Full screen</button>
<button class="action-btn"><span class="material-symbols-outlined">content_copy</span> Clone</button>
<button class="action-btn text-danger"><span class="material-symbols-outlined">delete</span>
Delete</button>
</div>
</div>
<div class="dashboard-grid">
<!-- Widget 1: Getting Started -->
<div class="widget span-2-col widget-border-top">
<div class="widget-header">
<h2>Quick starts + tutorials</h2>
<button class="widget-menu-btn"><span class="material-symbols-outlined">more_horiz</span></button>
</div>
<div class="widget-content">
<div class="empty-state">
<span class="material-symbols-outlined empty-icon" style="color:#0078d4">emoji_objects</span>
<p>Learn how to deploy your first app</p>
<a href="#" class="azure-link" style="margin-top:8px; display:inline-block">View tutorials</a>
</div>
</div>
</div>
<!-- Widget 2: Resources -->
<div class="widget span-2-col">
<div class="widget-header">
<h2>All resources</h2>
<button class="widget-menu-btn"><span class="material-symbols-outlined">more_horiz</span></button>
</div>
<div class="widget-content p-0">
<div class="table-container">
<table class="azure-table">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="material-symbols-outlined type-icon text-blue">dvr</span> prod-web-server</td>
<td>Virtual machine</td>
<td>East US</td>
</tr>
<tr>
<td><span class="material-symbols-outlined type-icon text-orange">storage</span> main-db-cluster
</td>
<td>SQL database</td>
<td>East US 2</td>
</tr>
<tr>
<td><span class="material-symbols-outlined type-icon text-green">folder</span> logs-storage-acc
</td>
<td>Storage account</td>
<td>West Europe</td>
</tr>
<tr>
<td><span class="material-symbols-outlined type-icon text-purple">dns</span> my-app-service</td>
<td>App Service</td>
<td>Central US</td>
</tr>
</tbody>
</table>
<div class="widget-footer-link">
<a href="#" class="azure-link">View all</a>
</div>
</div>
</div>
</div>
<!-- Widget 3: Service Health -->
<div class="widget widget-border-top-green">
<div class="widget-header">
<h2>Service Health</h2>
<button class="widget-menu-btn"><span class="material-symbols-outlined">more_horiz</span></button>
</div>
<div class="widget-content">
<div class="health-summary">
<div class="health-status">
<span class="material-symbols-outlined health-icon">check_circle</span>
<div class="health-text">
<span class="health-title">No issues</span>
<span class="health-sub">Last 24 hours</span>
</div>
</div>
<div class="health-metrics">
<div class="metric"><span class="val">0</span> Service issues</div>
<div class="metric"><span class="val">0</span> Planned maintenance</div>
<div class="metric"><span class="val">0</span> Health advisories</div>
</div>
</div>
</div>
</div>
<!-- Widget 4: Cost Management -->
<div class="widget">
<div class="widget-header">
<h2>Cost Management</h2>
<button class="widget-menu-btn"><span class="material-symbols-outlined">more_horiz</span></button>
</div>
<div class="widget-content">
<div class="cost-summary">
<div class="cost-amount">$342.50</div>
<div class="cost-subtitle">Accumulated cost (Current billing period)</div>
<div class="cost-chart-mock">
<div class="bar" style="height: 20%;" title="Resource Groups: $60"></div>
<div class="bar" style="height: 50%;" title="Virtual Machines: $150"></div>
<div class="bar" style="height: 30%;" title="App Services: $90"></div>
<div class="bar" style="height: 15%;" title="Storage: $40"></div>
</div>
<a href="#" class="azure-link">Go to Cost Management</a>
</div>
</div>
</div>
<!-- Widget 5: Recent -->
<div class="widget span-2-col">
<div class="widget-header">
<h2>Recent resources</h2>
<button class="widget-menu-btn"><span class="material-symbols-outlined">more_horiz</span></button>
</div>
<div class="widget-content p-0">
<ul class="resource-list">
<li class="resource-list-item">
<span class="material-symbols-outlined icon text-purple">dns</span>
<div class="details">
<span class="name">my-app-service</span>
<span class="sub">App Service</span>
</div>
</li>
<li class="resource-list-item">
<span class="material-symbols-outlined icon text-orange">storage</span>
<div class="details">
<span class="name">main-db-cluster</span>
<span class="sub">SQL database</span>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- HOME PAGE -->
<div id="page-home" class="page-view">
<div class="home-banner">
<h2>Welcome to Azure</h2>
<p>Build, manage, and monitor all your applications from a single, unified console.</p>
<button class="action-btn"
style="background-color: var(--azure-blue); color: white; border:none; padding: 8px 16px;">
Start with an Azure free account
</button>
</div>
<div class="page-header">
<h1 class="page-title">Azure services</h1>
</div>
<div class="services-grid">
<div class="service-card" data-link="virtual-machines">
<span class="material-symbols-outlined icon text-blue">dvr</span>
<span class="title">Virtual machines</span>
</div>
<div class="service-card" data-link="app-services">
<span class="material-symbols-outlined icon text-purple">dns</span>
<span class="title">App Services</span>
</div>
<div class="service-card" data-link="storage-accounts">
<span class="material-symbols-outlined icon text-green">folder</span>
<span class="title">Storage accounts</span>
</div>
<div class="service-card" data-link="resource-groups">
<span class="material-symbols-outlined icon text-orange">folder_special</span>
<span class="title">Resource groups</span>
</div>
</div>
</div>
<!-- ALL SERVICES PAGE -->
<div id="page-all-services" class="page-view">
<div class="page-header">
<h1 class="page-title">All services</h1>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Filter services..." />
</div>
<div class="services-grid" style="margin-top: 0;">
<div class="service-card">
<span class="material-symbols-outlined icon text-blue">language</span>
<span class="title">CDN Profiles</span>
</div>
<div class="service-card">
<span class="material-symbols-outlined icon text-purple">cloud</span>
<span class="title">Cloud Services</span>
</div>
<div class="service-card">
<span class="material-symbols-outlined icon text-orange">database</span>
<span class="title">SQL databases</span>
</div>
<div class="service-card">
<span class="material-symbols-outlined icon text-green">hub</span>
<span class="title">Virtual networks</span>
</div>
<div class="service-card">
<span class="material-symbols-outlined icon text-blue">shield</span>
<span class="title">Key Vaults</span>
</div>
</div>
</div>
<!-- CREATE A RESOURCE PAGE -->
<div id="page-create-resource" class="page-view">
<div class="page-header">
<h1 class="page-title">Create a resource</h1>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Search the Marketplace" style="width: 400px;" />
</div>
<div class="dashboard-grid">
<div class="widget">
<div class="widget-header">
<h2>Popular</h2>
</div>
<div class="widget-content p-0">
<ul class="resource-list">
<li class="resource-list-item">
<span class="material-symbols-outlined icon text-blue">dvr</span>
<div class="details"><span class="name">Windows Server 2022</span><span class="sub">Microsoft</span>
</div>
</li>
<li class="resource-list-item">
<span class="material-symbols-outlined icon text-purple">dns</span>
<div class="details"><span class="name">Web App</span><span class="sub">Microsoft</span></div>
</li>
<li class="resource-list-item">
<span class="material-symbols-outlined icon text-orange">database</span>
<div class="details"><span class="name">SQL Database</span><span class="sub">Microsoft</span></div>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- GENERIC LIST PAGES (Resource groups, VMs, etc.) -->
<div id="page-resource-groups" class="page-view resource-list-page">
<div class="page-header">
<h1 class="page-title">Resource groups</h1>
<button class="action-btn" style="background-color: var(--azure-blue); color: white; border:none;"><span
class="material-symbols-outlined" style="color:white; font-size:16px;">add</span> Create</button>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Filter for any field..." />
<button class="action-btn"><span class="material-symbols-outlined">refresh</span> Refresh</button>
</div>
<div class="table-container">
<table class="azure-table">
<thead>
<tr>
<th>Name</th>
<th>Subscription</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="material-symbols-outlined type-icon text-orange">folder_special</span> core-infra-rg
</td>
<td>Pay-As-You-Go</td>
<td>East US</td>
</tr>
<tr>
<td><span class="material-symbols-outlined type-icon text-orange">folder_special</span> app-prod-rg
</td>
<td>Pay-As-You-Go</td>
<td>West Europe</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="page-app-services" class="page-view resource-list-page">
<div class="page-header">
<h1 class="page-title">App Services</h1>
<button class="action-btn" style="background-color: var(--azure-blue); color: white; border:none;"><span
class="material-symbols-outlined" style="color:white; font-size:16px;">add</span> Create</button>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Filter for any field..." />
<button class="action-btn"><span class="material-symbols-outlined">refresh</span> Refresh</button>
</div>
<div class="table-container">
<table class="azure-table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="material-symbols-outlined type-icon text-purple">dns</span> my-app-service</td>
<td>Running</td>
<td>Central US</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="page-virtual-machines" class="page-view resource-list-page">
<div class="page-header">
<h1 class="page-title">Virtual machines</h1>
<button class="action-btn" style="background-color: var(--azure-blue); color: white; border:none;"><span
class="material-symbols-outlined" style="color:white; font-size:16px;">add</span> Create</button>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Filter for any field..." />
<button class="action-btn"><span class="material-symbols-outlined">refresh</span> Refresh</button>
<button class="action-btn"><span class="material-symbols-outlined">play_arrow</span> Start</button>
<button class="action-btn"><span class="material-symbols-outlined">stop</span> Stop</button>
</div>
<div class="table-container">
<table class="azure-table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Size</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="material-symbols-outlined type-icon text-blue">dvr</span> prod-web-server</td>
<td>Running</td>
<td>Standard D2s v3</td>
<td>East US</td>
</tr>
<tr>
<td><span class="material-symbols-outlined type-icon text-blue">dvr</span> dev-test-vm</td>
<td>Stopped (deallocated)</td>
<td>Standard B1s</td>
<td>East US</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="page-storage-accounts" class="page-view resource-list-page">
<div class="page-header">
<h1 class="page-title">Storage accounts</h1>
<button class="action-btn" style="background-color: var(--azure-blue); color: white; border:none;"><span
class="material-symbols-outlined" style="color:white; font-size:16px;">add</span> Create</button>
</div>
<div class="list-header-controls">
<input type="text" class="filter-input" placeholder="Filter for any field..." />
<button class="action-btn"><span class="material-symbols-outlined">refresh</span> Refresh</button>
</div>
<div class="table-container">
<table class="azure-table">
<thead>
<tr>
<th>Name</th>
<th>Kind</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="material-symbols-outlined type-icon text-green">folder</span> logs-storage-acc</td>
<td>StorageV2 (general purpose v2)</td>
<td>West Europe</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1104
Azure Portal (Gemini 3.1)/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"name": "azureportal",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^7.3.1"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,102 @@
import './style.css'
document.addEventListener('DOMContentLoaded', () => {
// --- Sidebar Toggle functionality ---
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebar-toggle');
if (sidebar && sidebarToggle) {
sidebarToggle.addEventListener('click', () => {
sidebar.classList.toggle('collapsed');
});
}
// --- Client-side Routing Structure ---
const sidebarLinks = document.querySelectorAll('.sidebar-item[data-page]');
const pageViews = document.querySelectorAll('.page-view');
sidebarLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
// Remove active from all links
document.querySelectorAll('.sidebar-item').forEach(item => {
item.classList.remove('active');
});
// Add active to clicked link
link.classList.add('active');
// Hide all pages
pageViews.forEach(page => {
page.classList.remove('active');
});
// Show target page
const targetPageId = link.getAttribute('data-page');
const targetPage = document.getElementById(`page-${targetPageId}`);
if (targetPage) {
targetPage.classList.add('active');
}
// If on mobile/small screen, clicking a link could optionally auto-collapse sidebar
if (window.innerWidth <= 768) {
sidebar.classList.add('collapsed');
}
});
});
// --- Initial Page Setup based on hash (Optional enhancement) ---
const hash = window.location.hash.replace('#', '');
if (hash) {
const matchingLink = document.querySelector(`.sidebar-item[data-page="${hash}"]`);
if (matchingLink) {
matchingLink.click();
}
}
// --- Interactive element mockups ---
// --- In-page Card Navigation ---
const serviceCards = document.querySelectorAll('.service-card[data-link]');
serviceCards.forEach(card => {
card.addEventListener('click', () => {
const targetHash = card.getAttribute('data-link');
const matchingSidebarLink = document.querySelector(`.sidebar-item[data-page="${targetHash}"]`);
if (matchingSidebarLink) {
matchingSidebarLink.click();
}
});
});
// Cost Chart mock animation
const bars = document.querySelectorAll('.cost-chart-mock .bar');
bars.forEach(bar => {
// Store original height
const h = bar.style.height;
// Set to 0 initially
bar.style.height = '0%';
// Animate to original height
setTimeout(() => {
bar.style.height = h;
}, 100);
});
// Table row click simulation
const tableRows = document.querySelectorAll('.azure-table tbody tr');
tableRows.forEach(row => {
row.addEventListener('click', () => {
// In a real app this would navigate to the resource blade
const resourceName = row.cells[0].innerText.trim();
console.log(`Navigating to ${resourceName}...`);
// Add a subtle flash effect
const originalBg = row.style.backgroundColor;
row.style.backgroundColor = 'var(--border-color)';
setTimeout(() => {
row.style.backgroundColor = originalBg;
}, 150);
});
});
});

View File

@@ -0,0 +1,768 @@
:root {
/* Azure Portal Dark Mode Tokens */
--bg-main: #111111;
/* #1b1a19 or purely #000 depending on exact theme, let's use #111 */
--bg-nav: #1b1a19;
--bg-sidebar: #252423;
--bg-card: #323130;
--bg-card-hover: #3b3a39;
--text-primary: #ffffff;
--text-secondary: #c8c6c4;
--text-placeholder: #a19f9d;
--azure-blue: #0078d4;
--azure-blue-hover: #2b88d8;
--azure-blue-focus: #4da6ff;
--border-color: #484644;
--border-light: #605e5c;
/* Status Colors */
--status-green: #107c10;
--status-green-light: #6BB700;
--status-orange: #d83b01;
--status-purple: #8764b8;
--status-red: #a4262c;
--sidebar-width: 250px;
--sidebar-collapsed-width: 48px;
--nav-height: 48px;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
color: var(--text-primary);
background-color: var(--bg-main);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
height: 100vh;
overflow: hidden;
}
#azure-portal {
display: flex;
flex-direction: column;
height: 100vh;
}
/* Material Icons Baseline Alignment */
.material-symbols-outlined {
font-variation-settings:
'FILL' 0,
'wght' 300,
'GRAD' 0,
'opsz' 24;
vertical-align: middle;
}
/* ================== TOP NAVIGATION ================== */
.top-nav {
height: var(--nav-height);
background-color: var(--bg-nav);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border-bottom: 2px solid var(--azure-blue);
/* Signature top border/accent */
flex-shrink: 0;
}
.nav-left,
.nav-middle,
.nav-right {
display: flex;
align-items: center;
}
.nav-left {
gap: 16px;
}
.brand {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: opacity 0.2s ease;
}
.brand:hover {
opacity: 0.8;
}
.brand-icon {
color: var(--azure-blue);
font-size: 20px;
}
.icon-btn {
background: transparent;
border: none;
color: var(--text-primary);
height: 32px;
width: 32px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s;
}
.icon-btn:hover {
background-color: var(--bg-card);
}
.icon-btn:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: -2px;
}
.nav-icon {
font-size: 20px;
}
.search-bar {
display: flex;
align-items: center;
background-color: var(--bg-main);
border: 1px solid var(--border-color);
border-radius: 4px;
width: 400px;
height: 32px;
padding: 0 8px;
gap: 8px;
transition: border-color 0.2s, background-color 0.2s;
}
.search-bar:focus-within {
border-color: var(--azure-blue);
background-color: var(--bg-nav);
}
.search-icon {
color: var(--text-secondary);
font-size: 18px;
}
.search-bar input {
background: transparent;
border: none;
color: var(--text-primary);
width: 100%;
font-size: 14px;
font-family: inherit;
outline: none;
}
.search-bar input::placeholder {
color: var(--text-placeholder);
}
.user-profile {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.user-profile:hover {
background-color: var(--bg-card);
}
.avatar {
background-color: var(--status-purple);
color: white;
height: 32px;
width: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 12px;
}
.tenant-info {
display: flex;
flex-direction: column;
font-size: 12px;
}
.tenant-info .email {
font-weight: 500;
}
.tenant-info .tenant {
color: var(--text-secondary);
}
/* ================== PORTAL BODY ================== */
.portal-body {
display: flex;
flex: 1;
overflow: hidden;
}
/* ================== LEFT SIDEBAR ================== */
.sidebar {
background-color: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
width: var(--sidebar-width);
transition: width 0.3s cubic-bezier(0.2, 0, 0, 1);
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sidebar.collapsed {
width: var(--sidebar-collapsed-width);
}
.sidebar.collapsed .sidebar-text,
.sidebar.collapsed .sidebar-group-title {
opacity: 0;
pointer-events: none;
}
.sidebar-menu {
padding: 8px 0;
display: flex;
flex-direction: column;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 16px;
padding: 8px 12px;
color: var(--text-primary);
text-decoration: none;
font-size: 14px;
transition: background-color 0.2s;
white-space: nowrap;
}
.sidebar-item:hover {
background-color: var(--bg-card);
}
.sidebar-item.active {
background-color: var(--bg-card);
border-left: 3px solid var(--azure-blue);
padding-left: 9px;
}
.sidebar-item.highlight .material-symbols-outlined {
color: var(--status-green-light);
}
.sidebar-item.highlight:hover {
background-color: var(--border-color);
}
.sidebar-item .material-symbols-outlined {
font-size: 20px;
color: var(--text-secondary);
flex-shrink: 0;
}
.sidebar-text {
transition: opacity 0.2s;
}
.sidebar-divider {
height: 1px;
background-color: var(--border-color);
margin: 8px 16px;
}
.sidebar-group-title {
font-size: 11px;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 16px 16px 8px 16px;
white-space: nowrap;
transition: opacity 0.2s;
}
/* ================== PAGE VIEWS ================== */
.page-view {
display: none;
animation: fadeIn 0.2s ease-in-out;
}
.page-view.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ================== GENERIC PAGE LAYOUTS ================== */
.resource-list-page {
display: flex;
flex-direction: column;
height: 100%;
}
.list-header-controls {
display: flex;
gap: 16px;
align-items: center;
padding: 12px 0 24px 0;
}
.filter-input {
background-color: var(--bg-main);
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 6px 12px;
border-radius: 2px;
min-width: 250px;
font-family: inherit;
font-size: 13px;
}
.filter-input:focus {
outline: none;
border-color: var(--azure-blue);
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-top: 24px;
}
.service-card {
background-color: var(--bg-card);
border: 1px solid var(--border-color);
padding: 16px;
border-radius: 2px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 12px;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s;
}
.service-card:hover {
background-color: var(--bg-card-hover);
transform: translateY(-2px);
}
.service-card .icon {
font-size: 32px;
}
.service-card .title {
font-size: 13px;
font-weight: 500;
}
/* Home Page specific */
.home-banner {
background: linear-gradient(135deg, rgba(0, 120, 212, 0.1) 0%, rgba(0, 0, 0, 0) 100%);
padding: 32px;
border-radius: 4px;
border: 1px solid var(--border-color);
margin-bottom: 24px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.home-banner h2 {
font-size: 24px;
font-weight: 400;
margin-bottom: 8px;
}
.home-banner p {
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 16px;
}
/* ================== DASHBOARD MAIN ================== */
.main-content {
flex: 1;
overflow-y: auto;
padding: 24px;
background-color: var(--bg-main);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title {
font-size: 20px;
font-weight: 600;
}
.page-actions {
display: flex;
gap: 8px;
}
.action-btn {
background: transparent;
color: var(--text-primary);
border: 1px solid transparent;
padding: 6px 12px;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: background-color 0.2s, border-color 0.2s;
}
.action-btn:hover {
background-color: var(--bg-card);
border-color: var(--border-color);
}
.action-btn .material-symbols-outlined {
font-size: 16px;
color: var(--azure-blue);
}
.action-btn.text-danger .material-symbols-outlined {
color: var(--status-red);
}
/* ================== GRID & WIDGETS ================== */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
grid-auto-rows: minmax(180px, auto);
}
.span-2-col {
grid-column: span 2;
}
@media (max-width: 1000px) {
.span-2-col {
grid-column: span 1;
}
}
.widget {
background-color: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 2px;
display: flex;
flex-direction: column;
box-shadow: 0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108);
transition: box-shadow 0.2s, transform 0.1s ease-out;
}
.widget:hover {
box-shadow: 0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108);
}
.widget-border-top {
border-top: 4px solid var(--azure-blue);
}
.widget-border-top-green {
border-top: 4px solid var(--status-green);
}
.widget-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px 8px;
}
.widget-header h2 {
font-size: 14px;
font-weight: 600;
margin: 0;
}
.widget-menu-btn {
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
height: 24px;
width: 24px;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
}
.widget-menu-btn:hover {
background-color: var(--border-color);
color: var(--text-primary);
}
.widget-content {
padding: 0 16px 16px;
flex: 1;
display: flex;
flex-direction: column;
}
.widget-content.p-0 {
padding: 0;
}
.azure-link {
color: var(--azure-blue);
text-decoration: none;
font-size: 13px;
font-weight: 500;
}
.azure-link:hover {
text-decoration: underline;
}
/* Empty State Widget */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
color: var(--text-secondary);
font-size: 13px;
padding: 16px;
}
.empty-icon {
font-size: 32px;
margin-bottom: 8px;
}
/* Table Widget */
.table-container {
overflow-x: auto;
}
.azure-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
text-align: left;
}
.azure-table th {
color: var(--text-secondary);
font-weight: 500;
padding: 8px 16px;
border-bottom: 1px solid var(--border-color);
}
.azure-table td {
padding: 8px 16px;
border-bottom: 1px solid var(--border-color);
}
.azure-table tr:hover td {
background-color: var(--bg-card-hover);
cursor: pointer;
}
.azure-table tr:last-child td {
border-bottom: none;
}
.type-icon {
font-size: 16px;
margin-right: 8px;
vertical-align: sub;
}
.text-blue {
color: #5bc0de;
}
.text-orange {
color: var(--status-orange);
}
.text-green {
color: var(--status-green-light);
}
.text-purple {
color: #d49aff;
}
.widget-footer-link {
padding: 12px 16px;
border-top: 1px solid var(--border-color);
background-color: rgba(255, 255, 255, 0.02);
}
/* Health Widget */
.health-summary {
display: flex;
flex-direction: column;
gap: 16px;
}
.health-status {
display: flex;
align-items: center;
gap: 12px;
}
.health-icon {
color: var(--status-green-light);
font-size: 32px;
}
.health-text {
display: flex;
flex-direction: column;
}
.health-title {
font-size: 16px;
font-weight: 600;
}
.health-sub {
font-size: 12px;
color: var(--text-secondary);
}
.health-metrics {
display: flex;
flex-direction: column;
gap: 8px;
font-size: 13px;
}
.metric .val {
font-weight: 600;
display: inline-block;
width: 16px;
}
/* Cost Widget */
.cost-summary {
display: flex;
flex-direction: column;
gap: 8px;
}
.cost-amount {
font-size: 28px;
font-weight: 300;
}
.cost-subtitle {
font-size: 12px;
color: var(--text-secondary);
}
.cost-chart-mock {
display: flex;
align-items: flex-end;
height: 60px;
gap: 4px;
margin: 12px 0;
border-bottom: 1px solid var(--border-color);
}
.cost-chart-mock .bar {
flex: 1;
background-color: var(--azure-blue);
border-radius: 2px 2px 0 0;
transition: height 1s ease-out, background-color 0.2s;
}
.cost-chart-mock .bar:hover {
background-color: var(--azure-blue-hover);
}
/* List Widget */
.resource-list {
list-style: none;
margin: 0;
padding: 0;
}
.resource-list-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
cursor: pointer;
transition: background-color 0.2s;
}
.resource-list-item:hover {
background-color: var(--bg-card-hover);
}
.resource-list-item:last-child {
border-bottom: none;
}
.resource-list-item .icon {
font-size: 24px;
}
.resource-list-item .details {
display: flex;
flex-direction: column;
}
.resource-list-item .name {
font-size: 13px;
font-weight: 500;
color: var(--azure-blue);
}
.resource-list-item .sub {
font-size: 12px;
color: var(--text-secondary);
}

24
Claude (Gemini 3.1)/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,16 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

View File

@@ -0,0 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
])

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Claude - Clone</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

3240
Claude (Gemini 3.1)/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
{
"name": "claude-clone",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"lucide-react": "^0.576.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.13.1"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"@vitejs/plugin-react-swc": "^4.2.3",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"vite": "^7.3.1"
},
"description": "This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.",
"main": "eslint.config.js",
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@@ -0,0 +1,28 @@
import { Routes, Route } from 'react-router-dom'
import Layout from './components/Layout'
import Home from './pages/Home'
import Thread from './pages/Thread'
import Projects from './pages/Projects'
import Profile from './pages/Profile'
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
{/* Default route is the new chat view */}
<Route index element={<Home />} />
{/* Active chat thread */}
<Route path="chat/:id" element={<Thread />} />
{/* Projects page */}
<Route path="projects" element={<Projects />} />
{/* Profile settings / workspace */}
<Route path="profile" element={<Profile />} />
</Route>
</Routes>
)
}
export default App

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,88 @@
.chat-input-wrapper {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 0 16px;
position: relative;
}
.chat-input-container {
display: flex;
align-items: flex-end;
background-color: var(--bg-primary);
/* matches background usually, or slightly offset depending on mode */
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 8px 12px 8px 8px;
box-shadow: var(--shadow-sm);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
background-color: #FFFFFF;
/* Often a pure white distinct from the F3F1EB bg */
}
.chat-input-container:focus-within {
border-color: var(--border-focus);
box-shadow: 0 0 0 1px var(--border-focus);
}
.attach-button {
margin-right: 8px;
margin-bottom: 2px;
color: var(--text-tertiary);
}
.attach-button:hover {
color: var(--text-primary);
background-color: var(--bg-button);
}
.chat-textarea {
flex: 1;
border: none;
background: transparent;
resize: none;
outline: none;
font-family: var(--font-family-base);
font-size: 16px;
line-height: 1.5;
color: var(--text-primary);
padding: 8px 0;
max-height: 200px;
overflow-y: auto;
}
.chat-textarea::placeholder {
color: var(--text-tertiary);
}
.send-button {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 8px;
background-color: var(--bg-button);
color: var(--text-tertiary);
margin-left: 8px;
margin-bottom: 4px;
transition: all var(--transition-fast);
}
.send-button.active {
background-color: var(--accent-color);
color: #FFFFFF;
cursor: pointer;
}
.send-button.active:hover {
background-color: var(--accent-color-hover);
}
.chat-disclaimer {
text-align: center;
font-size: 12px;
color: var(--text-tertiary);
margin-top: 12px;
margin-bottom: 12px;
}

View File

@@ -0,0 +1,63 @@
import { useState, useRef, useEffect } from 'react'
import { ArrowUp, Paperclip } from 'lucide-react'
import './ChatInput.css'
export default function ChatInput({ onSend, placeholder = "Message Claude..." }) {
const [text, setText] = useState('')
const textareaRef = useRef(null)
// Auto-resize the textarea
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = 'auto'
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`
}
}, [text])
const handleKeyDown = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
if (text.trim()) {
onSend(text)
setText('')
}
}
}
return (
<div className="chat-input-wrapper">
<div className="chat-input-container">
<button className="icon-button attach-button tooltip-container">
<Paperclip size={20} />
<span className="tooltip-text">Add attachment</span>
</button>
<textarea
ref={textareaRef}
className="chat-textarea"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder}
rows={1}
/>
<button
className={`send-button ${text.trim() ? 'active' : ''}`}
onClick={() => {
if (text.trim()) {
onSend(text)
setText('')
}
}}
disabled={!text.trim()}
>
<ArrowUp size={18} />
</button>
</div>
<div className="chat-disclaimer">
Claude can make mistakes. Please verify important information.
</div>
</div>
)
}

View File

@@ -0,0 +1,55 @@
.main-header {
height: var(--header-height);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
background-color: transparent;
/* Claude's header blends with bg */
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
}
.header-left,
.header-right {
display: flex;
align-items: center;
min-width: 48px;
/* ensures center element stays perfectly centered */
}
.header-center {
flex: 1;
display: flex;
justify-content: center;
}
.model-selector {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 8px;
font-family: var(--font-family-base);
font-weight: 500;
font-size: 15px;
color: var(--text-secondary);
transition: background-color var(--transition-fast), color var(--transition-fast);
}
.model-selector:hover {
background-color: var(--bg-button-hover);
color: var(--text-primary);
}
.model-chevron {
color: var(--text-tertiary);
margin-top: 2px;
}
.new-chat-action {
color: var(--text-secondary);
}

View File

@@ -0,0 +1,42 @@
import { PanelLeft, ChevronDown } from 'lucide-react'
import './Header.css'
export default function Header({ toggleSidebar, sidebarOpen }) {
return (
<header className="main-header">
<div className="header-left">
{/* Only show the open toggle if the sidebar is closed */}
{!sidebarOpen && (
<button
className="icon-button tooltip-container"
onClick={toggleSidebar}
aria-label="Open sidebar"
>
<PanelLeft size={20} />
<span className="tooltip-text">Open sidebar</span>
</button>
)}
</div>
<div className="header-center">
<button className="model-selector">
<span className="model-name">Claude 3.5 Sonnet</span>
<ChevronDown size={14} className="model-chevron" />
</button>
</div>
<div className="header-right">
{/* Placeholder for right-side actions if any (like new chat button in top right when sidebar collapsed) */}
{!sidebarOpen && (
<button className="icon-button tooltip-container new-chat-action">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
</svg>
<span className="tooltip-text">New chat</span>
</button>
)}
</div>
</header>
)
}

View File

@@ -0,0 +1,43 @@
.layout {
display: flex;
height: 100vh;
width: 100vw;
overflow: hidden;
background-color: var(--bg-primary);
}
.layout-sidebar-container {
height: 100%;
flex-shrink: 0;
transition: width var(--transition-sidebar), padding var(--transition-sidebar);
background-color: var(--bg-secondary);
border-right: 1px solid var(--border-color);
overflow: hidden;
}
.layout-sidebar-container.open {
width: var(--sidebar-width);
}
.layout-sidebar-container.closed {
width: var(--sidebar-width-collapsed);
border-right: none;
}
.layout-main {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
position: relative;
}
.layout-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
position: relative;
display: flex;
flex-direction: column;
}

View File

@@ -0,0 +1,31 @@
import { useState } from 'react'
import { Outlet } from 'react-router-dom'
import Sidebar from './Sidebar'
import Header from './Header'
import './Layout.css'
export default function Layout() {
const [sidebarOpen, setSidebarOpen] = useState(true)
const toggleSidebar = () => {
setSidebarOpen(!sidebarOpen)
}
return (
<div className="layout">
{/* Sidebar acts like a drawer but pushes content naturally when open */}
<div className={`layout-sidebar-container ${sidebarOpen ? 'open' : 'closed'}`}>
<Sidebar isOpen={sidebarOpen} toggleSidebar={toggleSidebar} />
</div>
{/* Main content area */}
<div className="layout-main">
<Header toggleSidebar={toggleSidebar} sidebarOpen={sidebarOpen} />
<div className="layout-content">
{/* Where the nested routes will render */}
<Outlet />
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,163 @@
.sidebar-inner {
height: 100%;
width: var(--sidebar-width);
display: flex;
flex-direction: column;
background-color: var(--bg-secondary);
transition: opacity 200ms ease;
padding: 12px;
}
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.new-chat-btn {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 8px 12px;
border-radius: 8px;
margin-right: 8px;
transition: background-color var(--transition-fast);
}
.new-chat-btn:hover {
background-color: var(--bg-button-hover);
}
.new-chat-content {
display: flex;
align-items: center;
gap: 8px;
}
.logo-icon {
width: 20px;
height: 20px;
background-color: var(--text-primary);
color: var(--bg-primary);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-family-serif);
font-weight: bold;
font-size: 14px;
}
.new-chat-text {
font-weight: 500;
font-size: 14px;
}
.new-chat-icon {
color: var(--text-secondary);
}
.sidebar-nav {
margin-bottom: 24px;
}
.sidebar-nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
transition: background-color var(--transition-fast);
}
.sidebar-nav-item:hover {
background-color: var(--bg-button-hover);
}
.nav-icon {
font-size: 16px;
}
.sidebar-history-wrapper {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
margin-right: -4px;
/* offset scrollbar */
padding-right: 4px;
}
.sidebar-history-section {
margin-bottom: 24px;
}
.history-group {
font-size: 12px;
font-weight: 600;
color: var(--text-tertiary);
padding: 0 12px;
margin-bottom: 8px;
}
.history-item {
display: block;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
color: var(--text-secondary);
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
transition: background-color var(--transition-fast), color var(--transition-fast);
}
.history-item:hover {
background-color: var(--bg-button-hover);
color: var(--text-primary);
}
.sidebar-footer {
margin-top: auto;
padding-top: 12px;
}
.user-profile {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 12px;
border-radius: 8px;
cursor: pointer;
transition: background-color var(--transition-fast);
}
.user-profile:hover {
background-color: var(--bg-button-hover);
}
.avatar {
width: 28px;
height: 28px;
border-radius: 4px;
background-color: #D36C4F;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
.username {
font-size: 14px;
font-weight: 500;
flex: 1;
}
.profile-chevron {
color: var(--text-tertiary);
}

View File

@@ -0,0 +1,77 @@
import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { PenSquare, PanelLeftClose, ChevronDown } from 'lucide-react'
import './Sidebar.css'
export default function Sidebar({ isOpen, toggleSidebar }) {
const navigate = useNavigate()
// Mock chat history
const recentChats = [
{ id: '1', title: 'React Performance Tips', time: 'Today' },
{ id: '2', title: 'Python Web Scraping', time: 'Yesterday' },
{ id: '3', title: 'Explain Quantum Computing', time: 'Previous 7 Days' },
]
return (
<div className="sidebar-inner" style={{ opacity: isOpen ? 1 : 0 }}>
<div className="sidebar-header">
<button className="new-chat-btn" onClick={() => navigate('/')}>
<div className="new-chat-content">
<span className="logo-icon">C</span>
<span className="new-chat-text">New chat</span>
</div>
<PenSquare size={16} className="new-chat-icon" />
</button>
<button className="icon-button tooltip-container" onClick={toggleSidebar} aria-label="Close sidebar">
<PanelLeftClose size={20} />
<span className="tooltip-text">Close sidebar</span>
</button>
</div>
<div className="sidebar-nav">
<Link to="/projects" className="sidebar-nav-item">
<span className="nav-icon">📁</span>
Projects
</Link>
</div>
<div className="sidebar-history-wrapper">
<div className="sidebar-history-section">
<div className="history-group">Today</div>
{recentChats.filter(c => c.time === 'Today').map(chat => (
<Link key={chat.id} to={`/chat/${chat.id}`} className="history-item">
{chat.title}
</Link>
))}
</div>
<div className="sidebar-history-section">
<div className="history-group">Yesterday</div>
{recentChats.filter(c => c.time === 'Yesterday').map(chat => (
<Link key={chat.id} to={`/chat/${chat.id}`} className="history-item">
{chat.title}
</Link>
))}
</div>
<div className="sidebar-history-section">
<div className="history-group">Previous 7 Days</div>
{recentChats.filter(c => c.time === 'Previous 7 Days').map(chat => (
<Link key={chat.id} to={`/chat/${chat.id}`} className="history-item">
{chat.title}
</Link>
))}
</div>
</div>
<div className="sidebar-footer">
<div className="user-profile">
<div className="avatar">A</div>
<span className="username">Antigravity</span>
<ChevronDown size={14} className="profile-chevron" />
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,141 @@
:root {
/* Colors - Light Theme (Claude Default) */
--bg-primary: #F3F1EB;
--bg-secondary: #EBE8E1;
--bg-tertiary: #E3E0DA;
--bg-button: #EFECE5;
--bg-button-hover: #E5E1D8;
--text-primary: #2C2C2C;
--text-secondary: #5A5A5A;
--text-tertiary: #8A8A8A;
--border-color: rgba(0, 0, 0, 0.08);
--border-focus: #5A5A5A;
--accent-color: #D97757;
/* Claude orange-ish accent */
--accent-color-hover: #C56648;
--claude-bubble-bg: #FFFFFF;
--user-bubble-bg: #E3E0DA;
/* or slightly darker depending on reference */
/* Typography */
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
--font-family-serif: "Tiempos Text", "Georgia", serif;
/* Claude's distinctive serif */
--font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* Layout */
--sidebar-width: 280px;
--sidebar-width-collapsed: 0px;
--header-height: 56px;
--input-area-height-min: 52px;
/* Transitions */
--transition-fast: 200ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-sidebar: 300ms cubic-bezier(0.4, 0, 0.2, 1);
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family-base);
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#root {
height: 100vh;
display: flex;
overflow: hidden;
}
a {
color: inherit;
text-decoration: none;
}
button {
background: none;
border: none;
font-family: inherit;
color: inherit;
cursor: pointer;
}
.icon-button {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 6px;
transition: background-color var(--transition-fast);
color: var(--text-secondary);
}
.icon-button:hover {
background-color: var(--bg-button-hover);
color: var(--text-primary);
}
/* Tooltip styles */
.tooltip-container {
position: relative;
display: inline-flex;
}
.tooltip-text {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
margin-top: 8px;
padding: 6px 10px;
background-color: #2C2C2C;
color: #FFFFFF;
font-size: 12px;
border-radius: 4px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity var(--transition-fast);
z-index: 50;
}
.tooltip-container:hover .tooltip-text {
opacity: 1;
}
/* Scrollbar styling for that sleek look */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--text-tertiary);
border-radius: 4px;
opacity: 0.5;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}

View File

@@ -0,0 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)

View File

@@ -0,0 +1,76 @@
.home-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 24px;
}
.home-content {
width: 100%;
max-width: 800px;
display: flex;
flex-direction: column;
align-items: center;
gap: 32px;
animation: fade-in 0.5s ease-out;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.greeting {
font-family: var(--font-family-serif);
font-size: 32px;
font-weight: 500;
color: var(--text-primary);
text-align: center;
margin-bottom: 8px;
}
.input-section {
width: 100%;
}
.suggestions-grid {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
width: 100%;
max-width: 600px;
}
.suggestion-card {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background-color: transparent;
border: 1px solid var(--border-color);
border-radius: 999px;
/* Pill shape */
font-size: 14px;
color: var(--text-secondary);
transition: all var(--transition-fast);
cursor: pointer;
}
.suggestion-card:hover {
background-color: #FFFFFF;
color: var(--text-primary);
border-color: rgba(0, 0, 0, 0.15);
/* Slightly darker border on hover */
box-shadow: var(--shadow-sm);
}

View File

@@ -0,0 +1,41 @@
import { useNavigate } from 'react-router-dom'
import { FileText, Terminal, Lightbulb } from 'lucide-react'
import ChatInput from '../components/ChatInput'
import './Home.css'
export default function Home() {
const navigate = useNavigate()
const handleSend = (text) => {
// In a real app we'd create the thread then navigate. Let's mock a thread ID.
const mockThreadId = Math.random().toString(36).substring(7)
navigate(`/chat/${mockThreadId}`)
}
const suggestions = [
{ title: "Analyze a complex document", icon: <FileText size={18} /> },
{ title: "Help me write a Python script", icon: <Terminal size={18} /> },
{ title: "Explain a concept simply", icon: <Lightbulb size={18} /> },
]
return (
<div className="home-container">
<div className="home-content">
<h1 className="greeting">Good evening, Antigravity</h1>
<div className="input-section">
<ChatInput onSend={handleSend} placeholder="How can Claude help you today?" />
</div>
<div className="suggestions-grid">
{suggestions.map((s, idx) => (
<button key={idx} className="suggestion-card">
<span className="suggestion-icon">{s.icon}</span>
<span className="suggestion-title">{s.title}</span>
</button>
))}
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,8 @@
export default function Profile() {
return (
<div style={{ padding: '64px', maxWidth: '800px', margin: '0 auto' }}>
<h1 style={{ fontFamily: 'var(--font-family-serif)', fontSize: '32px', marginBottom: '24px' }}>Settings</h1>
<p style={{ color: 'var(--text-secondary)' }}>Profile and settings placeholder view.</p>
</div>
)
}

View File

@@ -0,0 +1,96 @@
.projects-container {
padding: 48px;
max-width: 1024px;
margin: 0 auto;
width: 100%;
}
.projects-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.projects-header h1 {
font-family: var(--font-family-serif);
font-size: 32px;
font-weight: 500;
color: var(--text-primary);
}
.create-project-btn {
background-color: var(--accent-color);
color: white;
padding: 8px 16px;
border-radius: 8px;
font-weight: 500;
transition: background-color var(--transition-fast);
}
.create-project-btn:hover {
background-color: var(--accent-color-hover);
}
.projects-subtitle {
color: var(--text-secondary);
font-size: 16px;
margin-bottom: 48px;
max-width: 600px;
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
.project-card {
background-color: #FFFFFF;
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 24px;
cursor: pointer;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.project-card:hover {
border-color: var(--border-focus);
box-shadow: var(--shadow-sm);
}
.project-card-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.project-icon {
font-size: 20px;
}
.project-title {
font-weight: 600;
font-size: 16px;
color: var(--text-primary);
}
.project-desc {
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 24px;
line-height: 1.4;
height: 40px;
/* fixed height for alignment */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.project-meta {
font-size: 12px;
color: var(--text-tertiary);
}

View File

@@ -0,0 +1,36 @@
import { FolderDot } from 'lucide-react'
import './Projects.css'
export default function Projects() {
const projects = [
{ title: 'Website Redesign', description: 'Assets and specifications for the Q3 redesign', date: 'Oct 12' },
{ title: 'React Performance', description: 'Notes on profiling and optimizing renders', date: 'Sep 28' },
{ title: 'API Documentation', description: 'Drafts for the new v2 endpoints', date: 'Sep 15' },
]
return (
<div className="projects-container">
<div className="projects-header">
<h1>Projects</h1>
<button className="create-project-btn">Create Project</button>
</div>
<p className="projects-subtitle">
Projects bring together your chats, documents, and code into one dedicated workspace.
</p>
<div className="projects-grid">
{projects.map((proj, idx) => (
<div key={idx} className="project-card">
<div className="project-card-header">
<div className="project-icon"><FolderDot size={20} /></div>
<h3 className="project-title">{proj.title}</h3>
</div>
<p className="project-desc">{proj.description}</p>
<div className="project-meta">{proj.date}</div>
</div>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,98 @@
.thread-container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
position: relative;
}
.messages-area {
flex: 1;
overflow-y: auto;
padding: 64px 24px 120px 24px;
/* top padding for header, bottom padding for sticky input */
}
.messages-list {
max-width: 768px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 32px;
}
.message-row {
display: flex;
gap: 16px;
animation: msg-fade-in 0.3s ease-out;
}
@keyframes msg-fade-in {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message-row.user {
justify-content: flex-end;
}
.message-avatar {
width: 28px;
height: 28px;
border-radius: 4px;
background-color: var(--text-primary);
color: var(--bg-primary);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-family-serif);
font-weight: bold;
font-size: 14px;
flex-shrink: 0;
margin-top: 4px;
/* align with first line of text */
}
.message-content {
font-size: 16px;
line-height: 1.6;
color: var(--text-primary);
min-width: 0;
/* allows text wrapping */
}
.message-row.user .message-content {
background-color: var(--user-bubble-bg);
padding: 12px 16px;
border-radius: 12px;
max-width: 85%;
}
.message-row.assistant .message-content {
max-width: 100%;
}
.message-content p {
margin-bottom: 1em;
}
.message-content p:last-child {
margin-bottom: 0;
}
/* Linear gradient fade out effect before the sticky input */
.thread-input-sticky {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, var(--bg-primary) 80%, transparent);
padding: 24px 0 32px 0;
}

View File

@@ -0,0 +1,64 @@
import { useEffect, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
import ChatInput from '../components/ChatInput'
import './Thread.css'
export default function Thread() {
const { id } = useParams()
const scrollRef = useRef(null)
// Mock initial state based on "just sent a message"
const [messages, setMessages] = useState([
{ role: 'user', content: 'Can you help me build a perfect clone of your interface?' }
])
// Fake Claude responding
useEffect(() => {
if (messages[messages.length - 1]?.role === 'user') {
const timer = setTimeout(() => {
setMessages(prev => [...prev, {
role: 'assistant',
content: 'I\'d be happy to help you build a clone of this interface! To achieve the look and feel, you\'ll want to focus on:\n\n1. The typography (mixing a sharp serif for headings with a clean sans-serif for body text).\n2. The muted color palette, particularly the off-white backgrounds (#F3F1EB).\n3. The smooth drawer interactions and input area auto-resizing.\n\nLet\'s get started. Which part would you like to build first?'
}])
}, 1000)
return () => clearTimeout(timer)
}
}, [messages])
// Auto-scroll to bottom
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight
}
}, [messages])
const handleSend = (text) => {
setMessages(prev => [...prev, { role: 'user', content: text }])
}
return (
<div className="thread-container">
<div className="messages-area" ref={scrollRef}>
<div className="messages-list">
{messages.map((msg, index) => (
<div key={index} className={`message-row ${msg.role}`}>
{msg.role === 'assistant' && (
<div className="message-avatar assistant">C</div>
)}
<div className="message-content">
{msg.content.split('\n').map((line, i) => (
<p key={i}>{line}</p>
))}
</div>
</div>
))}
</div>
</div>
<div className="thread-input-sticky">
<ChatInput onSend={handleSend} placeholder="Reply to Claude..." />
</div>
</div>
)
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})

24
CrowdStrike (Gemini 3.1)/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,16 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

View File

@@ -0,0 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
])

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>crowdstrike--gemini-3-1-</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

3902
CrowdStrike (Gemini 3.1)/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
{
"name": "crowdstrike--gemini-3-1-",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.2.1",
"lucide-react": "^0.576.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"recharts": "^3.7.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.4.27",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"postcss": "^8.5.8",
"tailwindcss": "^4.2.1",
"vite": "^7.3.1"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@@ -0,0 +1,464 @@
import React, { useState } from 'react';
import {
AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer,
PieChart, Pie, Cell, BarChart, Bar,
} from 'recharts';
import {
Activity, Search, HardDrive, ShieldCheck, Globe, ShieldAlert, Database,
Sparkles, Settings, HelpCircle, Bell, User, MoreHorizontal, ArrowUpRight,
Menu, ChevronRight
} from 'lucide-react';
// --- MOCK DATA ---
// Detection Summary Data (30 days)
const detectionData = Array.from({ length: 30 }, (_, i) => ({
date: `Oct ${i + 1}`,
count: Math.floor(Math.random() * 50) + (i > 25 ? 150 : 20), // spike at the end
}));
// Severity Breakdown Data
const severityData = [
{ name: 'Critical', value: 34, color: '#E7343A' },
{ name: 'High', value: 89, color: '#FF8C00' },
{ name: 'Medium', value: 214, color: '#F5C542' },
{ name: 'Low', value: 452, color: '#4ECDC4' },
{ name: 'Info', value: 890, color: '#6B7280' },
];
// Active Incidents Bar Data
const incidentsBarData = [
{ name: 'New', value: 12, fill: '#E7343A' },
{ name: 'In Progress', value: 8, fill: '#F59E0B' },
{ name: 'Reopened', value: 3, fill: '#FF8C00' },
{ name: 'Closed', value: 45, fill: '#10B981' }
];
// Top Threat Actors
const threatActors = [
{ name: 'FANCY BEAR', country: '🇷🇺', level: 'critical' },
{ name: 'SCATTERED SPIDER', country: '🌎', level: 'critical' },
{ name: 'LABYRINTH CHOLLIMA', country: '🇰🇵', level: 'high' },
{ name: 'VELVET TEMPEST', country: '🇨🇳', level: 'high' },
{ name: 'SLIPPY SPIDER', country: '🌎', level: 'medium' },
];
const levelColors = {
critical: 'bg-cs-red',
high: 'bg-orange-500',
medium: 'bg-yellow-500',
low: 'bg-teal-400',
};
// MITRE Tactics for Heatmap
const mitreTactics = [
"Init Acc", "Execution", "Persistence", "Priv Esc", "Def Evasion", "Cred Acc",
"Discovery", "Lat Mvt", "Collection", "C2", "Exfil", "Impact"
];
const mitreCoverage = mitreTactics.map(t => ({
tactic: t,
// Generate 3 sub-techniques coverage visual per tactic
cells: Array.from({ length: 3 }, () => {
const rand = Math.random();
if (rand > 0.7) return 'bg-cs-success/80';
if (rand > 0.4) return 'bg-cs-warning/80';
return 'bg-white/5';
})
}));
// Recent Alerts
const recentAlerts = [
{ id: 1, severity: 'critical', name: 'Suspicious PowerShell Execution', host: 'WIN-SRV-012', tactic: 'Execution', tech: 'T1059.001', time: '10 mins ago', status: 'New' },
{ id: 2, severity: 'high', name: 'Credential Dumping Attempt', host: 'DEV-LT-449', tactic: 'Credential Access', tech: 'T1003', time: '24 mins ago', status: 'In Progress' },
{ id: 3, severity: 'critical', name: 'Lateral Movement via RDP', host: 'WIN-SRV-008', tactic: 'Lateral Movement', tech: 'T1021.001', time: '1 hr ago', status: 'New' },
{ id: 4, severity: 'medium', name: 'Malicious DLL Side-Loading', host: 'HR-LT-102', tactic: 'Defense Evasion', tech: 'T1574.002', time: '3 hrs ago', status: 'In Progress' },
{ id: 5, severity: 'low', name: 'Anomalous DNS Queries', host: 'DC-01', tactic: 'C2', tech: 'T1071.004', time: '5 hrs ago', status: 'Closed' },
];
const statusStyles = {
'New': 'bg-cs-red/20 text-cs-red border-cs-red/30 animate-pulse',
'In Progress': 'bg-cs-warning/20 text-cs-warning border-cs-warning/30',
'Closed': 'bg-cs-success/20 text-cs-success border-cs-success/30'
};
// Nav Items
const navSections = [
{ title: "Activity", icon: <Activity size={18} />, active: true },
{ title: "Investigate", icon: <Search size={18} /> },
{ title: "Hosts", icon: <HardDrive size={18} /> },
{ title: "Configuration", icon: <ShieldCheck size={18} /> },
{ title: "Intelligence", icon: <Globe size={18} /> },
{ title: "Exposure Mgmt", icon: <ShieldAlert size={18} /> },
{ title: "Next-Gen SIEM", icon: <Database size={18} /> },
];
// --- COMPONENTS ---
const WidgetCard = ({ title, children, className = "" }) => (
<div className={`bg-cs-card border border-cs-card-border rounded-lg p-5 flex flex-col transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/50 ${className}`}>
<div className="flex justify-between items-center mb-4">
<h3 className="text-xs font-semibold tracking-widest text-cs-text-secondary uppercase">{title}</h3>
<button className="text-cs-text-muted hover:text-white transition-colors">
<MoreHorizontal size={16} />
</button>
</div>
<div className="flex-1 w-full relative">
{children}
</div>
</div>
);
export default function FalconDashboard() {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [searchFocused, setSearchFocused] = useState(false);
return (
<div className="min-h-screen bg-cs-bg text-cs-text flex overflow-hidden font-sans">
{/* SIDEBAR */}
<aside className={`${sidebarOpen ? 'w-60' : 'w-16'} bg-cs-sidebar border-r border-cs-card-border flex flex-col transition-all duration-300 z-20`}>
{/* Logo Area */}
<div className="h-16 flex items-center px-4 border-b border-cs-card-border cursor-pointer" onClick={() => setSidebarOpen(!sidebarOpen)}>
<div className="w-8 h-8 rounded shrink-0 flex items-center justify-center bg-cs-red/10 text-cs-red">
{/* Simple Falcon representative SVG shape */}
<svg viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6">
<path d="M12 2L2 22h20L12 2zm0 4.5l6.5 13.5h-13L12 6.5z" />
</svg>
</div>
{sidebarOpen && (
<span className="ml-3 font-bold text-lg tracking-wider text-white">FALCON</span>
)}
</div>
{/* Nav Links */}
<nav className="flex-1 overflow-y-auto py-4 no-scrollbar">
<ul className="space-y-1 px-2">
{navSections.map((nav, i) => (
<li key={i}>
<a href="#" className={`flex items-center px-2 py-2.5 rounded-md transition-all group relative ${nav.active ? 'bg-white/5 text-white' : 'text-cs-text-secondary hover:bg-white/5 hover:text-white'}`}>
{nav.active && <div className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-cs-red rounded-r-md"></div>}
<span className={`${nav.active ? 'text-cs-red' : 'group-hover:text-white'}`}>{nav.icon}</span>
{sidebarOpen && <span className="ml-3 text-sm font-medium">{nav.title}</span>}
</a>
</li>
))}
{/* AI Divider */}
<li className="mt-6 mb-2 px-4">
{sidebarOpen ? <div className="text-[10px] font-bold text-cs-info uppercase tracking-widest border-b border-cs-info/20 pb-1">Charlotte AI</div> : <div className="h-px bg-cs-info/20 my-2"></div>}
</li>
<li>
<a href="#" className="flex items-center px-2 py-2.5 rounded-md transition-all text-cs-text-secondary hover:bg-white/5 hover:text-white group">
<span className="text-cs-info group-hover:text-cs-info drop-shadow-[0_0_8px_rgba(99,102,241,0.5)]"><Sparkles size={18} /></span>
{sidebarOpen && <span className="ml-3 text-sm font-medium text-indigo-200">Ask Charlotte</span>}
</a>
</li>
</ul>
</nav>
{/* Bottom Actions */}
<div className="p-4 border-t border-cs-card-border space-y-4">
<button className="flex items-center text-cs-text-secondary hover:text-white w-full">
<User size={18} />
{sidebarOpen && <span className="ml-3 text-sm">E. Alderson</span>}
</button>
<button className="flex items-center text-cs-text-secondary hover:text-white w-full">
<Settings size={18} />
{sidebarOpen && <span className="ml-3 text-sm">Settings</span>}
</button>
</div>
</aside>
{/* MAIN CONTENT AREA */}
<main className="flex-1 flex flex-col h-screen overflow-hidden">
{/* Top Header */}
<header className="h-16 flex items-center justify-between px-6 border-b border-cs-card-border bg-cs-bg z-10 shrink-0">
<div className="flex items-center">
<h1 className="text-lg font-medium text-white flex items-center">
Activity <ChevronRight size={16} className="mx-2 text-cs-text-muted" /> Dashboard
</h1>
</div>
{/* Charlotte AI Search Bar */}
<div className={`flex-1 max-w-xl mx-8 relative transition-all duration-300 ${searchFocused ? 'scale-[1.02]' : ''}`}>
<div className={`absolute inset-0 rounded-full bg-gradient-to-r from-cs-info/20 to-cs-red/20 opacity-0 transition-opacity duration-300 blur-md ${searchFocused ? 'opacity-100' : ''}`}></div>
<div className={`relative flex items-center bg-[#181c27] border ${searchFocused ? 'border-cs-info/50 shadow-[0_0_15px_rgba(99,102,241,0.15)]' : 'border-cs-card-border'} rounded-full px-4 py-2`}>
<Sparkles size={18} className={`${searchFocused ? 'text-cs-info animate-pulse' : 'text-cs-text-muted'}`} />
<input
type="text"
placeholder="Ask Charlotte AI to investigate an IP, summarize alerts, or build a dashboard..."
className="bg-transparent border-none outline-none text-sm text-white ml-3 w-full placeholder:text-cs-text-muted"
onFocus={() => setSearchFocused(true)}
onBlur={() => setSearchFocused(false)}
/>
<div className="hidden sm:flex px-2 py-0.5 bg-white/5 rounded border border-white/10 text-[10px] text-cs-text-muted ml-2">K</div>
</div>
</div>
<div className="flex items-center space-x-5">
<button className="relative text-cs-text-secondary hover:text-white transition-colors">
<Bell size={20} />
<span className="absolute -top-1.5 -right-1.5 bg-cs-red text-[10px] font-bold text-white w-4 h-4 flex items-center justify-center rounded-full border-2 border-cs-bg">7</span>
</button>
<div className="w-px h-6 bg-cs-card-border"></div>
<div className="flex items-center text-sm cursor-pointer hover:text-white text-cs-text-secondary">
Acme Corp
<ChevronRight size={14} className="ml-1 rotate-90" />
</div>
</div>
</header>
{/* Dashboard Grid */}
<div className="flex-1 overflow-y-auto p-6 scroll-smooth">
<div className="max-w-[1600px] mx-auto grid grid-cols-1 md:grid-cols-12 gap-6">
{/* Widget 1: Detection Summary (8 cols) */}
<WidgetCard title="Detections Over Time" className="md:col-span-8 min-h-[300px]">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={detectionData} margin={{ top: 10, right: 0, left: -20, bottom: 0 }}>
<defs>
<linearGradient id="colorCount" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#E7343A" stopOpacity={0.3} />
<stop offset="95%" stopColor="#E7343A" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" vertical={false} />
<XAxis dataKey="date" stroke="#4B5563" fontSize={11} tickLine={false} axisLine={false} minTickGap={30} />
<YAxis stroke="#4B5563" fontSize={11} tickLine={false} axisLine={false} />
<RechartsTooltip
contentStyle={{ backgroundColor: '#151923', borderColor: 'rgba(255,255,255,0.1)', color: '#fff' }}
itemStyle={{ color: '#E7343A' }}
/>
<Area type="monotone" dataKey="count" stroke="#E7343A" strokeWidth={2} fillOpacity={1} fill="url(#colorCount)" />
</AreaChart>
</ResponsiveContainer>
</WidgetCard>
{/* Widget 2: Severity Breakdown (4 cols) */}
<WidgetCard title="Detections by Severity" className="md:col-span-4 min-h-[300px] flex flex-col">
<div className="flex-1 relative">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={severityData}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={85}
paddingAngle={2}
dataKey="value"
stroke="none"
>
{severityData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<RechartsTooltip
contentStyle={{ backgroundColor: '#151923', borderColor: 'rgba(255,255,255,0.1)', color: '#fff', borderRadius: '4px' }}
itemStyle={{ color: '#fff' }}
/>
</PieChart>
</ResponsiveContainer>
<div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<span className="text-3xl font-mono font-light tracking-tight">{severityData.reduce((a, b) => a + b.value, 0)}</span>
<span className="text-[10px] text-cs-text-secondary uppercase">Total</span>
</div>
</div>
<div className="mt-2 grid grid-cols-3 gap-y-2 gap-x-1">
{severityData.slice(0, 3).map(s => (
<div key={s.name} className="flex items-center text-xs">
<span className="w-2 h-2 rounded-full mr-1.5 shrink-0" style={{ backgroundColor: s.color }}></span>
<span className="text-cs-text-secondary truncate">{s.name}</span>
</div>
))}
{severityData.slice(3).map(s => (
<div key={s.name} className="flex items-center text-xs">
<span className="w-2 h-2 rounded-full mr-1.5 shrink-0" style={{ backgroundColor: s.color }}></span>
<span className="text-cs-text-secondary truncate">{s.name}</span>
</div>
))}
</div>
</WidgetCard>
{/* Widget 3: Active Incidents (4 cols) */}
<WidgetCard title="Active Incidents" className="md:col-span-4 min-h-[220px]">
<div className="flex flex-col h-full justify-between">
<div>
<div className="flex items-end justify-between">
<div className="text-5xl font-mono font-light text-white tracking-tight">23</div>
<div className="flex items-center text-cs-red text-sm font-medium mb-1">
<ArrowUpRight size={16} className="mr-0.5" />
+3 in 24h
</div>
</div>
<p className="text-xs text-cs-text-secondary mt-2">Requires immediate attention</p>
</div>
<div className="mt-6">
<div className="flex justify-between text-[10px] text-cs-text-muted mb-1 uppercase tracking-wider">
<span>New (12)</span>
<span>In Prog (8)</span>
</div>
<div className="w-full h-2 rounded-full bg-white/5 flex overflow-hidden">
<div className="h-full bg-cs-red" style={{ width: '52%' }}></div>
<div className="h-full bg-cs-warning" style={{ width: '35%' }}></div>
<div className="h-full bg-orange-500" style={{ width: '13%' }}></div>
</div>
</div>
</div>
</WidgetCard>
{/* Widget 4: Endpoint Status (4 cols) */}
<WidgetCard title="Endpoint Health" className="md:col-span-4 min-h-[220px]">
<div className="flex flex-col h-full justify-between">
<div className="flex items-center mt-2">
<div className="relative w-16 h-16 shrink-0 mr-4">
<svg viewBox="0 0 36 36" className="w-16 h-16 transform -rotate-90">
<path strokeDasharray="100, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke="rgba(255,255,255,0.05)" strokeWidth="4" />
<path strokeDasharray="94, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke="#10B981" strokeWidth="4" className="drop-shadow-[0_0_4px_rgba(16,185,129,0.5)]" />
</svg>
<div className="absolute inset-0 flex items-center justify-center text-xs font-mono font-bold">94%</div>
</div>
<div>
<div className="text-3xl font-mono font-light tracking-tight">12,847</div>
<div className="text-xs text-cs-text-secondary uppercase tracking-widest mt-1">Total Agents</div>
</div>
</div>
<div className="grid grid-cols-2 gap-2 mt-6">
<div className="bg-white/5 px-3 py-2 rounded-md flex justify-between items-center text-sm">
<div className="flex items-center"><span className="w-2 h-2 rounded-full bg-cs-success mr-2"></span>Online</div>
<span className="font-mono">12,104</span>
</div>
<div className="bg-white/5 px-3 py-2 rounded-md flex justify-between items-center text-sm">
<div className="flex items-center"><span className="w-2 h-2 rounded-full bg-cs-danger mr-2"></span>Offline</div>
<span className="font-mono">612</span>
</div>
</div>
</div>
</WidgetCard>
{/* Widget 5: Top Threat Actors (4 cols) */}
<WidgetCard title="Trending Adversaries" className="md:col-span-4 min-h-[220px]">
<div className="flex flex-col h-full -mt-2">
<div className="space-y-3 flex-1">
{threatActors.map((actor, i) => (
<div key={i} className="flex items-center justify-between group">
<div className="flex items-center">
<span className="text-lg mr-2 leading-none">{actor.country}</span>
<span className="text-sm font-medium tracking-wide font-mono hover:text-white transition-colors cursor-pointer text-gray-200">{actor.name}</span>
</div>
<div className="flex items-center">
<span className={`w-2 h-2 rounded-full ${levelColors[actor.level]} shadow-[0_0_6px_currentColor]`}></span>
</div>
</div>
))}
</div>
<div className="text-right mt-2">
<a href="#" className="text-xs text-cs-info hover:text-indigo-300 transition-colors inline-flex items-center">
View Intel Database <ChevronRight size={14} className="ml-1" />
</a>
</div>
</div>
</WidgetCard>
{/* Widget 6: MITRE Heatmap (8 cols) */}
<WidgetCard title="MITRE ATT&CK Coverage" className="md:col-span-8 min-h-[220px] overflow-x-auto no-scrollbar">
<div className="min-w-[700px] flex flex-col h-full">
<div className="flex space-x-1 mb-2">
{mitreTactics.map(t => (
<div key={t} className="flex-1 text-[10px] text-cs-text-secondary text-center truncate px-1 uppercase">{t}</div>
))}
</div>
<div className="flex-1 flex space-x-1">
{mitreCoverage.map((col, i) => (
<div key={i} className="flex-1 space-y-1">
{col.cells.map((color, j) => (
<div key={j} className={`h-8 w-full rounded-sm ${color} transition-colors hover:brightness-125 cursor-pointer`}></div>
))}
</div>
))}
</div>
<div className="mt-4 flex items-center justify-end space-x-4 text-[10px] text-cs-text-muted uppercase tracking-wider">
<div className="flex items-center"><div className="w-3 h-3 bg-cs-success/80 rounded-sm mr-1.5"></div> High</div>
<div className="flex items-center"><div className="w-3 h-3 bg-cs-warning/80 rounded-sm mr-1.5"></div> Medium</div>
<div className="flex items-center"><div className="w-3 h-3 bg-white/5 rounded-sm mr-1.5"></div> None</div>
</div>
</div>
</WidgetCard>
{/* Widget 8: Charlotte AI Insights (4 cols) */}
<WidgetCard title="Charlotte AI Insights" className="md:col-span-4 min-h-[220px] border-cs-info/30 relative overflow-hidden group">
{/* Subtle animated gradient background for AI feel */}
<div className="absolute inset-0 bg-gradient-to-br from-cs-info/5 to-transparent opacity-50 group-hover:opacity-100 transition-opacity"></div>
<div className="relative z-10 flex flex-col h-full gap-3 -mt-2">
<div className="bg-white/5 border border-white/5 rounded-md p-3 relative overflow-hidden">
<div className="absolute left-0 top-0 bottom-0 w-1 bg-cs-warning"></div>
<p className="text-xs leading-relaxed">
<span className="inline-block mr-1">🔍</span> Unusual spike in <span className="text-blue-300 font-mono">PowerShell</span> activity detected across 3 host groups in the last 6 hours. Recommend investigation.
</p>
</div>
<div className="bg-white/5 border border-white/5 rounded-md p-3 relative overflow-hidden">
<div className="absolute left-0 top-0 bottom-0 w-1 bg-cs-red"></div>
<p className="text-xs leading-relaxed">
<span className="inline-block mr-1"></span> <span className="text-red-300 font-mono">CVE-2025-21298</span> exploitation attempts detected from 2 external IPs. 47 endpoints remain unpatched.
</p>
</div>
<div className="bg-white/5 border border-white/5 rounded-md p-3 relative overflow-hidden">
<div className="absolute left-0 top-0 bottom-0 w-1 bg-cs-success"></div>
<p className="text-xs leading-relaxed">
<span className="inline-block mr-1">📊</span> Detection efficacy improved 12% this month due to updated behavioral IOAs.
</p>
</div>
</div>
</WidgetCard>
{/* Widget 7: Recent Alerts Table (12 cols) */}
<WidgetCard title="Recent Alerts" className="md:col-span-12">
<div className="overflow-x-auto">
<table className="w-full text-left text-sm whitespace-nowrap">
<thead>
<tr className="text-cs-text-secondary border-b border-cs-card-border">
<th className="pb-3 px-2 font-medium w-6">Sev</th>
<th className="pb-3 px-2 font-medium">Alert Name</th>
<th className="pb-3 px-2 font-medium">Hostname</th>
<th className="pb-3 px-2 font-medium">Tactic</th>
<th className="pb-3 px-2 font-medium">Time (Local)</th>
<th className="pb-3 px-2 font-medium text-right">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-cs-card-border/50">
{recentAlerts.map(alert => (
<tr key={alert.id} className="hover:bg-white/[0.02] transition-colors group cursor-pointer">
<td className="py-3 px-2">
<div className={`w-2.5 h-2.5 rounded-full ${levelColors[alert.severity]} shadow-[0_0_5px_currentColor]`}></div>
</td>
<td className="py-3 px-2 font-medium text-white group-hover:text-cs-info transition-colors">{alert.name}</td>
<td className="py-3 px-2 font-mono text-xs text-gray-300">{alert.host}</td>
<td className="py-3 px-2">
<span className="text-xs text-cs-text-secondary block">{alert.tactic}</span>
<span className="text-[10px] bg-white/10 px-1.5 py-0.5 rounded text-gray-300 font-mono mt-0.5 inline-block">{alert.tech}</span>
</td>
<td className="py-3 px-2 text-xs text-cs-text-muted">{alert.time}</td>
<td className="py-3 px-2 text-right">
<span className={`inline-block px-2.5 py-1 rounded text-[10px] font-bold uppercase tracking-wide border ${statusStyles[alert.status]}`}>
{alert.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</WidgetCard>
</div>
{/* Footer spacer */}
<div className="h-10"></div>
</div>
</main>
</div>
);
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,40 @@
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap');
@import "tailwindcss";
@theme {
--color-cs-bg: #0B0E14;
--color-cs-sidebar: #12161F;
--color-cs-card: #151923;
--color-cs-card-border: rgba(255, 255, 255, 0.06);
--color-cs-red: #E7343A;
--color-cs-red-hover: #FF4654;
--color-cs-text: #FFFFFF;
--color-cs-text-secondary: #8B919A;
--color-cs-text-muted: #4B5563;
--color-cs-success: #10B981;
--color-cs-warning: #F59E0B;
--color-cs-danger: #EF4444;
--color-cs-info: #6366F1;
}
body {
background-color: var(--color-cs-bg);
color: var(--color-cs-text);
font-family: 'DM Sans', sans-serif;
}
/* Custom scrollbar for dark theme */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-cs-bg);
}
::-webkit-scrollbar-thumb {
background: var(--color-cs-text-muted);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-cs-text-secondary);
}

View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
})

24
Facebook (Gemini 3.1)/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

View File

@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>facebook--gemini-3-1-</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

3354
Facebook (Gemini 3.1)/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
{
"name": "facebook--gemini-3-1-",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"lucide-react": "^0.576.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

View File

@@ -0,0 +1,40 @@
import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom';
import Navbar from './components/Navbar';
import Home from './pages/Home';
import Login from './pages/Login';
import Friends from './pages/Friends';
import Watch from './pages/Watch';
import Marketplace from './pages/Marketplace';
import Groups from './pages/Groups';
import Profile from './pages/Profile';
const AppContent = () => {
const location = useLocation();
const isLoginPage = location.pathname === '/login';
return (
<>
{!isLoginPage && <Navbar />}
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/" element={<Home />} />
<Route path="/friends" element={<Friends />} />
<Route path="/watch" element={<Watch />} />
<Route path="/marketplace" element={<Marketplace />} />
<Route path="/groups" element={<Groups />} />
<Route path="/profile" element={<Profile />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</>
);
};
function App() {
return (
<Router>
<AppContent />
</Router>
);
}
export default App;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,123 @@
.navbar {
height: 56px;
background-color: var(--fb-card-bg);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
position: sticky;
top: 0;
z-index: 1000;
}
.navbar-left {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
}
.fb-logo-link {
text-decoration: none;
}
.fb-logo {
background-color: var(--fb-blue);
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
}
.search-bar {
background-color: var(--fb-bg);
border-radius: 20px;
padding: 8px 12px;
display: flex;
align-items: center;
gap: 8px;
color: var(--fb-text-secondary);
}
.search-bar input {
background: none;
border: none;
outline: none;
font-size: 15px;
}
.navbar-middle {
display: flex;
align-items: center;
gap: 4px;
height: 100%;
}
.nav-link {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 0 32px;
color: var(--fb-text-secondary);
border-bottom: 3px solid transparent;
transition: all 0.2s;
}
.nav-link:hover {
background-color: var(--fb-hover);
border-radius: 8px;
}
.nav-link.active {
color: var(--fb-blue);
border-bottom-color: var(--fb-blue);
border-radius: 0;
}
.nav-link.active:hover {
background: none;
}
.navbar-right {
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-end;
flex: 1;
}
.nav-item-round {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--fb-hover);
display: flex;
align-items: center;
justify-content: center;
}
.nav-item-profile {
display: flex;
align-items: center;
gap: 4px;
padding: 4px;
cursor: pointer;
}
.avatar-placeholder {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { Search, Home, Play, Store, Users, Bell, MessageCircle, Grid, ChevronDown } from 'lucide-react';
import { NavLink } from 'react-router-dom';
import './Navbar.css';
const Navbar: React.FC = () => {
return (
<nav className="navbar">
<div className="navbar-left">
<NavLink to="/" className="fb-logo-link">
<div className="fb-logo">f</div>
</NavLink>
<div className="search-bar">
<Search size={18} />
<input type="text" placeholder="Search Facebook" />
</div>
</div>
<div className="navbar-middle">
<NavLink to="/" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
<Home size={28} />
</NavLink>
<NavLink to="/friends" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
<Users size={28} />
</NavLink>
<NavLink to="/watch" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
<Play size={28} />
</NavLink>
<NavLink to="/marketplace" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
<Store size={28} />
</NavLink>
<NavLink to="/groups" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}>
<Users size={28} />
</NavLink>
</div>
<div className="navbar-right">
<div className="nav-item-round"><Grid size={20} /></div>
<div className="nav-item-round"><MessageCircle size={20} /></div>
<div className="nav-item-round"><Bell size={20} /></div>
<NavLink to="/profile" className="nav-item-profile">
<div className="avatar-placeholder">U</div>
<ChevronDown size={16} />
</NavLink>
</div>
</nav>
);
};
export default Navbar;

View File

@@ -0,0 +1,72 @@
.sidebar {
width: 360px;
height: calc(100vh - 56px);
overflow-y: auto;
padding: 8px;
position: sticky;
top: 56px;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
}
.sidebar-item:hover {
background-color: var(--fb-hover);
}
.sidebar-item span {
font-size: 15px;
font-weight: 500;
}
.avatar-placeholder-small {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.icon-circle {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
}
.sidebar-divider {
border: none;
border-top: 1px solid var(--fb-divider);
margin: 8px;
}
.sidebar-title {
margin: 16px 8px 8px;
color: var(--fb-text-secondary);
font-size: 17px;
}
.shortcut-icon {
width: 36px;
height: 36px;
border-radius: 8px;
background-color: var(--fb-blue);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { Users, Bookmark, Clock, Play, Store, Calendar, ChevronDown } from 'lucide-react';
import './Sidebar.css';
const SidebarItem = ({ icon: Icon, label, color }: { icon: any, label: string, color?: string }) => (
<div className="sidebar-item">
<Icon size={28} color={color || 'var(--fb-blue)'} />
<span>{label}</span>
</div>
);
const Sidebar: React.FC = () => {
return (
<div className="sidebar">
<div className="sidebar-item">
<div className="avatar-placeholder-small">U</div>
<span style={{ fontWeight: 500 }}>User Name</span>
</div>
<SidebarItem icon={Users} label="Friends" color="#1877F2" />
<SidebarItem icon={Bookmark} label="Saved" color="#B249C0" />
<SidebarItem icon={Clock} label="Memories" color="#1877F2" />
<SidebarItem icon={Play} label="Video" color="#1877F2" />
<SidebarItem icon={Store} label="Marketplace" color="#1877F2" />
<SidebarItem icon={Calendar} label="Events" color="#F35E59" />
<div className="sidebar-item">
<div className="icon-circle"><ChevronDown size={20} /></div>
<span>See more</span>
</div>
<hr className="sidebar-divider" />
<h4 className="sidebar-title">Your shortcuts</h4>
<div className="sidebar-item">
<div className="shortcut-icon">G</div>
<span>My Favorite Group</span>
</div>
</div>
);
};
export default Sidebar;

View File

@@ -0,0 +1,36 @@
:root {
--fb-blue: #0866FF;
--fb-bg: #F0F2F5;
--fb-card-bg: #FFFFFF;
--fb-hover: #E4E6EB;
--fb-text: #050505;
--fb-text-secondary: #65676B;
--fb-divider: #CED0D4;
--fb-radius: 8px;
--fb-shadow: 0 12px 28px 0 rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.1);
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: var(--fb-bg);
color: var(--fb-text);
}
* {
box-sizing: border-box;
}
a {
text-decoration: none;
color: inherit;
}
button {
border: none;
background: none;
cursor: pointer;
padding: 0;
}

View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@@ -0,0 +1,119 @@
.friends-page {
display: flex;
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.friends-sidebar {
width: 360px;
background-color: var(--fb-card-bg);
padding: 16px;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
position: sticky;
top: 56px;
height: calc(100vh - 56px);
}
.friends-sidebar h2 {
margin-bottom: 20px;
}
.friends-menu-item {
padding: 12px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
}
.friends-menu-item:hover {
background-color: var(--fb-hover);
}
.friends-menu-item.active {
background-color: var(--fb-hover);
color: var(--fb-blue);
}
.friends-content {
flex: 1;
padding: 24px 32px;
overflow-y: auto;
}
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.content-header a {
color: var(--fb-blue);
font-size: 14px;
}
.requests-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 32px;
}
.friend-request-card {
background-color: var(--fb-card-bg);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
.friend-avatar-large {
height: 200px;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
font-weight: bold;
}
.friend-info {
padding: 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.friend-name {
font-weight: 600;
font-size: 17px;
}
.mutual-friends {
font-size: 13px;
color: var(--fb-text-secondary);
}
.confirm-btn {
background-color: var(--fb-blue);
color: white;
padding: 8px;
border-radius: 6px;
font-weight: 600;
}
.delete-btn {
background-color: var(--fb-hover);
color: var(--fb-text);
padding: 8px;
border-radius: 6px;
font-weight: 600;
}
.divider {
border: none;
border-top: 1px solid var(--fb-divider);
margin-bottom: 24px;
}

View File

@@ -0,0 +1,53 @@
import React from 'react';
import './Friends.css';
const FriendRequest = ({ name }: { name: string }) => (
<div className="friend-request-card">
<div className="friend-avatar-large">{name[0]}</div>
<div className="friend-info">
<div className="friend-name">{name}</div>
<div className="mutual-friends">5 mutual friends</div>
<button className="confirm-btn">Confirm</button>
<button className="delete-btn">Delete</button>
</div>
</div>
);
const Friends: React.FC = () => {
return (
<div className="friends-page">
<div className="friends-sidebar">
<h2>Friends</h2>
<div className="friends-menu-item active">Home</div>
<div className="friends-menu-item">Friend Requests</div>
<div className="friends-menu-item">Suggestions</div>
<div className="friends-menu-item">All Friends</div>
<div className="friends-menu-item">Birthdays</div>
<div className="friends-menu-item">Custom Lists</div>
</div>
<div className="friends-content">
<div className="content-header">
<h3>Friend Requests</h3>
<a href="#">See all</a>
</div>
<div className="requests-grid">
<FriendRequest name="Alice Walker" />
<FriendRequest name="Charlie Brown" />
<FriendRequest name="David Miller" />
</div>
<hr className="divider" />
<div className="content-header">
<h3>People You May Know</h3>
<a href="#">See all</a>
</div>
<div className="requests-grid">
<FriendRequest name="Eve Adams" />
<FriendRequest name="Frank Wright" />
<FriendRequest name="Grace Hopper" />
</div>
</div>
</div>
);
};
export default Friends;

View File

@@ -0,0 +1,88 @@
.groups-page {
display: flex;
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.groups-sidebar {
width: 360px;
background-color: var(--fb-card-bg);
padding: 16px;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
position: sticky;
top: 56px;
height: calc(100vh - 56px);
}
.create-group-btn {
width: 100%;
background-color: #E7F3FF;
color: var(--fb-blue);
padding: 10px;
border-radius: 6px;
font-weight: 600;
margin-top: 16px;
}
.groups-content {
flex: 1;
padding: 24px 32px;
overflow-y: auto;
}
.groups-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
margin-top: 16px;
}
.group-card {
background-color: var(--fb-card-bg);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
.group-banner-placeholder {
height: 120px;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
color: var(--fb-text-secondary);
}
.group-info {
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
}
.group-name {
font-weight: 700;
font-size: 17px;
}
.group-members {
font-size: 13px;
color: var(--fb-text-secondary);
}
.join-btn {
width: 100%;
background-color: var(--fb-hover);
color: var(--fb-text);
padding: 8px;
border-radius: 6px;
font-weight: 600;
margin-top: 8px;
}
.join-btn:hover {
background-color: var(--fb-divider);
}

View File

@@ -0,0 +1,38 @@
import React from 'react';
import './Groups.css';
const GroupCard = ({ name, members }: { name: string, members: string }) => (
<div className="group-card">
<div className="group-banner-placeholder">Banner</div>
<div className="group-info">
<div className="group-name">{name}</div>
<div className="group-members">{members} members</div>
<button className="join-btn">Join Group</button>
</div>
</div>
);
const Groups: React.FC = () => {
return (
<div className="groups-page">
<div className="groups-sidebar">
<h2>Groups</h2>
<div className="friends-menu-item active">Your feed</div>
<div className="friends-menu-item">Discover</div>
<div className="friends-menu-item">Your groups</div>
<button className="create-group-btn">+ Create new group</button>
</div>
<div className="groups-content">
<h3>Suggested for you</h3>
<div className="groups-grid">
<GroupCard name="React Developers" members="150k" />
<GroupCard name="Cooking Enthusiasts" members="45k" />
<GroupCard name="Photography Lovers" members="80k" />
<GroupCard name="Local Yard Sale" members="12k" />
</div>
</div>
</div>
);
};
export default Groups;

View File

@@ -0,0 +1,234 @@
.home-container {
display: flex;
justify-content: space-between;
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.feed-container {
flex: 1;
max-width: 680px;
padding: 16px 32px;
display: flex;
flex-direction: column;
gap: 16px;
}
.stories-container {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 8px;
}
.story-card {
width: 112px;
height: 200px;
border-radius: 10px;
background-color: var(--fb-divider);
position: relative;
flex-shrink: 0;
cursor: pointer;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.story-avatar {
position: absolute;
top: 12px;
left: 12px;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--fb-blue);
color: white;
display: flex;
align-items: center;
justify-content: center;
border: 4px solid var(--fb-blue);
z-index: 2;
font-weight: bold;
}
.story-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(transparent, rgba(0,0,0,0.4));
}
.story-name {
position: absolute;
bottom: 8px;
left: 12px;
color: white;
font-size: 13px;
font-weight: 500;
z-index: 2;
}
.create-post-card {
background-color: var(--fb-card-bg);
border-radius: var(--fb-radius);
padding: 12px 16px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.create-post-top {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.create-post-top input {
flex: 1;
background-color: var(--fb-bg);
border: none;
border-radius: 20px;
padding: 0 16px;
font-size: 17px;
outline: none;
}
.create-post-top input:hover {
background-color: var(--fb-hover);
}
.create-post-bottom {
display: flex;
justify-content: space-around;
padding-top: 8px;
}
.create-action {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
color: var(--fb-text-secondary);
font-weight: 500;
}
.create-action:hover {
background-color: var(--fb-hover);
}
.post-card {
background-color: var(--fb-card-bg);
border-radius: var(--fb-radius);
padding: 12px 0;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.post-header {
display: flex;
justify-content: space-between;
padding: 0 16px 12px;
}
.post-user-info {
display: flex;
gap: 8px;
}
.post-user-name {
font-weight: 600;
font-size: 15px;
}
.post-time {
font-size: 13px;
color: var(--fb-text-secondary);
}
.post-content {
padding: 4px 16px 12px;
font-size: 15px;
}
.post-image-placeholder {
background-color: var(--fb-bg);
height: 400px;
display: flex;
align-items: center;
justify-content: center;
color: var(--fb-text-secondary);
font-weight: 500;
}
.post-stats {
padding: 10px 16px;
display: flex;
justify-content: space-between;
color: var(--fb-text-secondary);
font-size: 15px;
}
.post-divider {
border: none;
border-top: 1px solid var(--fb-divider);
margin: 0 16px;
}
.post-actions {
display: flex;
justify-content: space-around;
padding: 4px 16px;
}
.post-action {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 8px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
color: var(--fb-text-secondary);
font-weight: 500;
}
.post-action:hover {
background-color: var(--fb-hover);
}
.right-sidebar {
width: 360px;
padding: 16px;
position: sticky;
top: 56px;
height: calc(100vh - 56px);
}
.contacts-header {
display: flex;
justify-content: space-between;
color: var(--fb-text-secondary);
font-weight: 500;
margin-bottom: 12px;
}
.contacts-icons {
display: flex;
gap: 12px;
}
.contact-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
border-radius: 8px;
cursor: pointer;
}
.contact-item:hover {
background-color: var(--fb-hover);
}

View File

@@ -0,0 +1,105 @@
import React from 'react';
import Sidebar from '../components/Sidebar';
import './Home.css';
import { Video, Image, Smile, MoreHorizontal, ThumbsUp, MessageSquare, Share2 } from 'lucide-react';
const StoryCard = ({ name, avatar }: { name: string, avatar: string }) => (
<div className="story-card">
<div className="story-avatar">{avatar}</div>
<div className="story-overlay"></div>
<span className="story-name">{name}</span>
</div>
);
const PostCard = ({ user, time, content, image }: { user: string, time: string, content: string, image?: string }) => (
<div className="post-card">
<div className="post-header">
<div className="post-user-info">
<div className="avatar-placeholder-small">{user[0]}</div>
<div>
<div className="post-user-name">{user}</div>
<div className="post-time">{time}</div>
</div>
</div>
<MoreHorizontal size={20} color="var(--fb-text-secondary)" />
</div>
<div className="post-content">{content}</div>
{image && <div className="post-image-placeholder">Image Content</div>}
<div className="post-stats">
<div className="likes">👍 12</div>
<div className="comments-shares">4 comments 2 shares</div>
</div>
<hr className="post-divider" />
<div className="post-actions">
<div className="post-action"><ThumbsUp size={20} /> Like</div>
<div className="post-action"><MessageSquare size={20} /> Comment</div>
<div className="post-action"><Share2 size={20} /> Share</div>
</div>
</div>
);
const Home: React.FC = () => {
return (
<div className="home-container">
<Sidebar />
<div className="feed-container">
<div className="stories-container">
<StoryCard name="Your Story" avatar="+" />
<StoryCard name="John Doe" avatar="JD" />
<StoryCard name="Jane Smith" avatar="JS" />
<StoryCard name="Bob Wilson" avatar="BW" />
</div>
<div className="create-post-card">
<div className="create-post-top">
<div className="avatar-placeholder-small">U</div>
<input type="text" placeholder="What's on your mind, User?" />
</div>
<hr className="post-divider" />
<div className="create-post-bottom">
<div className="create-action"><Video color="#F3425E" /> Live Video</div>
<div className="create-action"><Image color="#45BD62" /> Photo/video</div>
<div className="create-action"><Smile color="#F7B928" /> Feeling/activity</div>
</div>
</div>
<PostCard
user="Jane Smith"
time="2 hours ago"
content="Just had an amazing lunch! #food #sunny"
image="true"
/>
<PostCard
user="Tech News"
time="5 hours ago"
content="Vite is taking over the world of frontend development! Have you tried it yet?"
/>
<PostCard
user="Adventure Seekers"
time="1 day ago"
content="Check out this breathtaking view from our hike yesterday."
image="true"
/>
</div>
<div className="right-sidebar">
<div className="contacts-header">
<span>Contacts</span>
<div className="contacts-icons">
<Video size={16} />
<MoreHorizontal size={16} />
</div>
</div>
<div className="contact-item">
<div className="avatar-placeholder-small">JD</div>
<span>John Doe</span>
</div>
<div className="contact-item">
<div className="avatar-placeholder-small">JS</div>
<span>Jane Smith</span>
</div>
</div>
</div>
);
};
export default Home;

View File

@@ -0,0 +1,102 @@
.login-container {
background-color: #f0f2f5;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-content {
display: flex;
max-width: 980px;
width: 100%;
padding: 20px;
justify-content: space-between;
align-items: center;
}
.login-left {
max-width: 500px;
margin-bottom: 80px;
}
.fb-text-logo {
color: #1877f2;
font-size: 60px;
margin-bottom: 0;
letter-spacing: -2px;
}
.login-left p {
font-size: 28px;
line-height: 32px;
font-weight: 500;
margin-top: -10px;
}
.login-right {
width: 396px;
display: flex;
flex-direction: column;
align-items: center;
}
.login-card {
background: white;
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.1);
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
}
.login-card input {
height: 52px;
padding: 14px 16px;
font-size: 17px;
border: 1px solid #dddfe2;
border-radius: 6px;
outline-color: #1877f2;
}
.login-btn {
background-color: #1877f2;
color: white;
font-size: 20px;
font-weight: 700;
padding: 10px;
border-radius: 6px;
cursor: pointer;
}
.forgot-password {
color: #1877f2;
font-size: 14px;
text-align: center;
}
.login-card hr {
border: none;
border-top: 1px solid #dadde1;
margin: 8px 0;
}
.create-account-btn {
background-color: #42b72a;
color: white;
font-size: 17px;
font-weight: 700;
padding: 0 16px;
height: 48px;
border-radius: 6px;
width: fit-content;
margin: 6px auto;
cursor: pointer;
}
.create-page-text {
margin-top: 28px;
font-size: 14px;
}

View File

@@ -0,0 +1,35 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import './Login.css';
const Login: React.FC = () => {
const navigate = useNavigate();
const handleLogin = () => {
navigate('/');
};
return (
<div className="login-container">
<div className="login-content">
<div className="login-left">
<h1 className="fb-text-logo">facebook</h1>
<p>Facebook helps you connect and share with the people in your life.</p>
</div>
<div className="login-right">
<div className="login-card">
<input type="text" placeholder="Email address or phone number" />
<input type="password" placeholder="Password" />
<button className="login-btn" onClick={handleLogin}>Log In</button>
<a href="#" className="forgot-password">Forgotten password?</a>
<hr />
<button className="create-account-btn">Create new account</button>
</div>
<p className="create-page-text"><b>Create a Page</b> for a celebrity, brand or business.</p>
</div>
</div>
</div>
);
};
export default Login;

View File

@@ -0,0 +1,74 @@
.market-page {
display: flex;
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.market-sidebar {
width: 360px;
background-color: var(--fb-card-bg);
padding: 16px;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
position: sticky;
top: 56px;
height: calc(100vh - 56px);
}
.create-listing-btn {
width: 100%;
background-color: #E7F3FF;
color: var(--fb-blue);
padding: 10px;
border-radius: 6px;
font-weight: 600;
margin-top: 16px;
}
.market-content {
flex: 1;
padding: 24px 32px;
overflow-y: auto;
}
.market-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-top: 16px;
}
.market-item-card {
background-color: var(--fb-card-bg);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
cursor: pointer;
}
.item-image-placeholder {
height: 200px;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
color: var(--fb-text-secondary);
}
.item-info {
padding: 12px;
}
.item-price {
font-weight: 700;
font-size: 17px;
}
.item-title {
font-size: 15px;
margin: 4px 0;
}
.item-location {
font-size: 13px;
color: var(--fb-text-secondary);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import './Marketplace.css';
const MarketplaceItem = ({ title, price, location }: { title: string, price: string, location: string }) => (
<div className="market-item-card">
<div className="item-image-placeholder">Item Image</div>
<div className="item-info">
<div className="item-price">{price}</div>
<div className="item-title">{title}</div>
<div className="item-location">{location}</div>
</div>
</div>
);
const Marketplace: React.FC = () => {
return (
<div className="market-page">
<div className="market-sidebar">
<h2>Marketplace</h2>
<div className="friends-menu-item active">Browse all</div>
<div className="friends-menu-item">Notifications</div>
<div className="friends-menu-item">Inbox</div>
<div className="friends-menu-item">Buying</div>
<div className="friends-menu-item">Selling</div>
<button className="create-listing-btn">+ Create new listing</button>
</div>
<div className="market-content">
<h3>Today's picks</h3>
<div className="market-grid">
<MarketplaceItem title="2018 Mountain Bike" price="$450" location="San Francisco, CA" />
<MarketplaceItem title="Cozy Sofa" price="$200" location="Oakland, CA" />
<MarketplaceItem title="iPhone 13 Pro" price="$600" location="San Jose, CA" />
<MarketplaceItem title="Vintage Camera" price="$85" location="Berkeley, CA" />
<MarketplaceItem title="Modern Desk" price="$120" location="Palo Alto, CA" />
<MarketplaceItem title="Gaming Monitor" price="$250" location="San Mateo, CA" />
</div>
</div>
</div>
);
};
export default Marketplace;

View File

@@ -0,0 +1,165 @@
.profile-page {
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.profile-top {
background-color: var(--fb-card-bg);
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.cover-photo-placeholder {
height: 350px;
background-color: var(--fb-divider);
display: flex;
align-items: center;
justify-content: center;
color: var(--fb-text-secondary);
}
.profile-header-info {
max-width: 1000px;
margin: 0 auto;
padding: 0 32px 16px;
display: flex;
align-items: flex-end;
gap: 16px;
position: relative;
margin-top: -32px;
}
.profile-avatar-large {
width: 168px;
height: 168px;
border-radius: 50%;
background-color: var(--fb-divider);
border: 4px solid var(--fb-card-bg);
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
font-weight: bold;
}
.profile-name-section {
margin-bottom: 24px;
}
.profile-name-section h1 {
margin: 0;
font-size: 32px;
}
.friends-count {
color: var(--fb-text-secondary);
font-weight: 600;
}
.profile-actions {
margin-left: auto;
margin-bottom: 24px;
display: flex;
gap: 8px;
}
.add-story-btn {
background-color: var(--fb-blue);
color: white;
padding: 10px 16px;
border-radius: 6px;
font-weight: 600;
}
.edit-profile-btn {
background-color: var(--fb-hover);
padding: 10px 16px;
border-radius: 6px;
font-weight: 600;
}
.profile-divider {
border: none;
border-top: 1px solid var(--fb-divider);
max-width: 1000px;
margin: 0 auto;
}
.profile-nav {
max-width: 1000px;
margin: 0 auto;
display: flex;
padding: 0 32px;
}
.profile-nav-item {
padding: 16px;
color: var(--fb-text-secondary);
font-weight: 600;
cursor: pointer;
border-bottom: 3px solid transparent;
}
.profile-nav-item.active {
color: var(--fb-blue);
border-bottom-color: var(--fb-blue);
}
.profile-bottom {
max-width: 1000px;
margin: 16px auto;
padding: 0 32px;
display: grid;
grid-template-columns: 360px 1fr;
gap: 16px;
}
.profile-card {
background-color: var(--fb-card-bg);
padding: 16px;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
margin-bottom: 16px;
}
.profile-card h3 {
margin-top: 0;
}
.edit-details-btn {
width: 100%;
background-color: var(--fb-hover);
padding: 8px;
border-radius: 6px;
font-weight: 600;
margin-top: 16px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header a {
color: var(--fb-blue);
}
.photos-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin-top: 16px;
}
.photo-square {
background-color: var(--fb-divider);
aspect-ratio: 1;
border-radius: 8px;
}
.post-card-simple {
background-color: var(--fb-card-bg);
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
margin-top: 16px;
}

View File

@@ -0,0 +1,68 @@
import React from 'react';
import './Profile.css';
const Profile: React.FC = () => {
return (
<div className="profile-page">
<div className="profile-top">
<div className="cover-photo-placeholder">Cover Photo</div>
<div className="profile-header-info">
<div className="profile-avatar-large">U</div>
<div className="profile-name-section">
<h1>User Name</h1>
<div className="friends-count">1.2k friends</div>
</div>
<div className="profile-actions">
<button className="add-story-btn">+ Add to story</button>
<button className="edit-profile-btn">Edit profile</button>
</div>
</div>
<hr className="profile-divider" />
<div className="profile-nav">
<div className="profile-nav-item active">Posts</div>
<div className="profile-nav-item">About</div>
<div className="profile-nav-item">Friends</div>
<div className="profile-nav-item">Photos</div>
<div className="profile-nav-item">Videos</div>
<div className="profile-nav-item">Check-ins</div>
</div>
</div>
<div className="profile-bottom">
<div className="profile-left-col">
<div className="profile-card">
<h3>Intro</h3>
<p>Software Engineer at Tech Corp</p>
<p>Lives in San Francisco, CA</p>
<p>From New York, NY</p>
<button className="edit-details-btn">Edit details</button>
</div>
<div className="profile-card">
<div className="card-header">
<h3>Photos</h3>
<a href="#">See all photos</a>
</div>
<div className="photos-grid">
<div className="photo-square"></div>
<div className="photo-square"></div>
<div className="photo-square"></div>
<div className="photo-square"></div>
</div>
</div>
</div>
<div className="profile-right-col">
<div className="create-post-card">
<div className="create-post-top">
<div className="avatar-placeholder-small">U</div>
<input type="text" placeholder="What's on your mind?" />
</div>
</div>
<div className="post-card-simple">
<p style={{ padding: '16px' }}>No posts yet.</p>
</div>
</div>
</div>
</div>
);
};
export default Profile;

View File

@@ -0,0 +1,53 @@
.watch-page {
display: flex;
background-color: var(--fb-bg);
min-height: calc(100vh - 56px);
}
.watch-sidebar {
width: 360px;
background-color: var(--fb-card-bg);
padding: 16px;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
position: sticky;
top: 56px;
height: calc(100vh - 56px);
}
.watch-content {
flex: 1;
padding: 24px 32px;
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 16px;
}
.video-post-card {
background-color: var(--fb-card-bg);
border-radius: var(--fb-radius);
padding: 12px 0;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.video-content {
padding: 4px 16px 12px;
font-size: 15px;
}
.video-placeholder {
background-color: black;
height: 450px;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.play-icon-large {
background: rgba(0,0,0,0.4);
padding: 20px;
border-radius: 50%;
border: 2px solid white;
}

View File

@@ -0,0 +1,53 @@
import React from 'react';
import './Watch.css';
import { Play, MoreHorizontal, ThumbsUp, MessageSquare, Share2 } from 'lucide-react';
const VideoPost = ({ title, channel }: { title: string, channel: string }) => (
<div className="video-post-card">
<div className="post-header">
<div className="post-user-info">
<div className="avatar-placeholder-small">{channel[0]}</div>
<div>
<div className="post-user-name">{channel}</div>
<div className="post-time">Yesterday at 14:00</div>
</div>
</div>
<MoreHorizontal size={20} color="var(--fb-text-secondary)" />
</div>
<div className="video-content">{title}</div>
<div className="video-placeholder">
<div className="play-icon-large"><Play fill="white" size={48} /></div>
</div>
<div className="post-stats">
<div className="likes">👍 1.2k</div>
<div className="comments-shares">40 comments 120 views</div>
</div>
<hr className="post-divider" />
<div className="post-actions">
<div className="post-action"><ThumbsUp size={20} /> Like</div>
<div className="post-action"><MessageSquare size={20} /> Comment</div>
<div className="post-action"><Share2 size={20} /> Share</div>
</div>
</div>
);
const Watch: React.FC = () => {
return (
<div className="watch-page">
<div className="watch-sidebar">
<h2>Watch</h2>
<div className="friends-menu-item active">Home</div>
<div className="friends-menu-item">Live</div>
<div className="friends-menu-item">Shows</div>
<div className="friends-menu-item">Saved Videos</div>
</div>
<div className="watch-content">
<VideoPost channel="National Geographic" title="Amazing nature documentary about lions." />
<VideoPost channel="Tech World" title="New iPhone 17 Leaks and Rumors!" />
<VideoPost channel="Chef Gourmet" title="How to make the perfect pasta in 5 minutes." />
</div>
</div>
);
};
export default Watch;

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})

24
Google (Gemini 3.1)/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,16 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

View File

@@ -0,0 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
])

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>google--gemini-3-1-</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

2988
Google (Gemini 3.1)/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
{
"name": "google--gemini-3-1-",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"lucide-react": "^0.577.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"vite": "^7.3.1"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@@ -0,0 +1,27 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import SearchResults from './pages/SearchResults';
import ImageResults from './pages/ImageResults';
import VideoResults from './pages/VideoResults';
import NewsResults from './pages/NewsResults';
import MapResults from './pages/MapResults';
import Placeholder from './pages/Placeholder';
import './index.css';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/search" element={<SearchResults />} />
<Route path="/images" element={<ImageResults />} />
<Route path="/videos" element={<VideoResults />} />
<Route path="/news" element={<NewsResults />} />
<Route path="/maps" element={<MapResults />} />
<Route path="/placeholder" element={<Placeholder />} />
</Routes>
</Router>
);
}
export default App;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,68 @@
.results-footer {
background-color: var(--bg-hover);
color: var(--text-secondary);
font-size: 14px;
margin-top: 40px;
}
@media (prefers-color-scheme: dark) {
.results-footer {
background-color: #171717;
}
}
.results-footer .footer-top {
padding: 12px 172px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
gap: 15px;
}
.results-footer .location {
font-weight: bold;
color: var(--text-primary);
}
.results-footer .location-info {
display: flex;
align-items: center;
gap: 5px;
}
.results-footer .location-info::before {
content: '';
display: inline-block;
width: 10px;
height: 10px;
background-color: var(--text-secondary);
border-radius: 50%;
}
.results-footer .footer-bottom {
padding: 0 172px;
}
.results-footer .footer-links {
display: flex;
gap: 25px;
flex-wrap: wrap;
}
.results-footer .footer-links a {
padding: 12px 0;
color: var(--text-secondary);
}
.results-footer .footer-links a:hover {
color: var(--text-primary);
}
@media (max-width: 800px) {
.results-footer .footer-top,
.results-footer .footer-bottom {
padding-left: 20px;
padding-right: 20px;
}
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { Link } from 'react-router-dom';
import './Footer.css';
const Footer = () => {
return (
<footer className="results-footer">
<div className="footer-top">
<span className="location">France</span>
<span className="location-info">Based on your past activity</span>
</div>
<div className="footer-bottom">
<div className="footer-links">
<Link to="/placeholder">Help</Link>
<Link to="/placeholder">Send feedback</Link>
<Link to="/placeholder">Privacy</Link>
<Link to="/placeholder">Terms</Link>
</div>
</div>
</footer>
);
};
export default Footer;

View File

@@ -0,0 +1,83 @@
.results-header {
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-color);
position: sticky;
top: 0;
z-index: 100;
padding-top: 15px;
}
.header-top {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px 20px 20px;
}
.header-left {
display: flex;
align-items: center;
flex-grow: 1;
}
.header-besearch-logo {
font-size: 26px;
font-weight: 500;
letter-spacing: -1px;
margin-right: 40px;
font-family: 'Product Sans', Arial, sans-serif;
line-height: 1;
}
.brand-b {
color: var(--google-blue);
}
.brand-e {
color: var(--google-red);
}
.brand-S {
color: var(--google-yellow);
}
.brand-e2 {
color: var(--google-blue);
}
.brand-a {
color: var(--google-green);
}
.brand-r {
color: var(--google-red);
}
.brand-c {
color: var(--google-yellow);
}
.brand-h {
color: var(--google-blue);
}
.header-search-wrapper {
width: 100%;
max-width: 690px;
}
.header-search-wrapper .search-bar-container {
height: 44px;
box-shadow: 0 1px 6px rgba(32, 33, 36, .28);
border-color: rgba(223, 225, 229, 0);
}
.header-search-wrapper .search-bar-container:hover {
box-shadow: 0 1px 6px rgba(32, 33, 36, .28);
}
.header-right {
display: flex;
align-items: center;
gap: 15px;
}

View File

@@ -0,0 +1,38 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import SearchBar from './SearchBar';
import { Settings, Grid } from 'lucide-react';
import './Header.css';
const Header = ({ query }) => {
return (
<header className="results-header">
<div className="header-top">
<div className="header-left">
<Link to="/" style={{ textDecoration: 'none' }}>
<div className="header-besearch-logo">
<span className="brand-b">b</span>
<span className="brand-e">e</span>
<span className="brand-S">S</span>
<span className="brand-e2">e</span>
<span className="brand-a">a</span>
<span className="brand-r">r</span>
<span className="brand-c">c</span>
<span className="brand-h">h</span>
</div>
</Link>
<div className="header-search-wrapper">
<SearchBar initialQuery={query} />
</div>
</div>
<div className="header-right">
<button className="icon-button"><Settings size={22} color="#5f6368" /></button>
<button className="icon-button"><Grid size={22} color="#5f6368" /></button>
<button className="sign-in-button">Sign in</button>
</div>
</div>
</header>
);
};
export default Header;

View File

@@ -0,0 +1,86 @@
.navigation-container {
display: flex;
align-items: center;
padding: 0 40px 0 172px;
/* aligns roughly with search input */
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-color);
position: sticky;
top: 79px;
/* adjust based on header height */
z-index: 99;
}
@media (max-width: 800px) {
.navigation-container {
padding: 0 20px;
overflow-x: auto;
}
}
.navigation-tabs {
display: flex;
gap: 20px;
}
.nav-link {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-secondary);
font-size: 14px;
padding: 12px 0 10px 0;
text-decoration: none;
cursor: pointer;
position: relative;
white-space: nowrap;
}
.nav-link:hover {
text-decoration: none;
color: var(--text-primary);
}
.nav-link.active {
color: #1a73e8;
}
.nav-link.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 3px;
background-color: #1a73e8;
border-radius: 3px 3px 0 0;
}
@media (prefers-color-scheme: dark) {
.nav-link.active,
.nav-link.active svg {
color: #8ab4f8;
}
.nav-link.active::after {
background-color: #8ab4f8;
}
}
.navigation-tools {
margin-left: auto;
padding-left: 20px;
}
.tools-button {
color: var(--text-secondary);
font-size: 14px;
padding: 6px 16px;
border-radius: 4px;
}
.tools-button:hover {
background-color: var(--bg-hover);
color: var(--text-primary);
}

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { NavLink, useSearchParams } from 'react-router-dom';
import { Search, Image, PlaySquare, Book, MoreVertical, Map } from 'lucide-react';
import './Navigation.css';
const Navigation = () => {
const [searchParams] = useSearchParams();
const query = searchParams.get('q') || '';
const queryString = query ? `?q=${encodeURIComponent(query)}` : '';
return (
<div className="navigation-container">
<div className="navigation-tabs">
<NavLink
to={`/search${queryString}`}
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
<Search size={16} />
<span>All</span>
</NavLink>
<NavLink
to={`/images${queryString}`}
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
<Image size={16} />
<span>Images</span>
</NavLink>
<NavLink
to={`/videos${queryString}`}
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
<PlaySquare size={16} />
<span>Videos</span>
</NavLink>
<NavLink
to={`/news${queryString}`}
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
<Book size={16} />
<span>News</span>
</NavLink>
<NavLink
to={`/maps${queryString}`}
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
<Map size={16} />
<span>Maps</span>
</NavLink>
<NavLink
to={`/placeholder${queryString}`}
className="nav-link"
>
<MoreVertical size={16} />
<span>More</span>
</NavLink>
</div>
<div className="navigation-tools">
<button className="tools-button">Tools</button>
</div>
</div>
);
};
export default Navigation;

View File

@@ -0,0 +1,34 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Search, Mic, Camera } from 'lucide-react';
const SearchBar = ({ initialQuery = '', autoFocus = false }) => {
const [query, setQuery] = useState(initialQuery);
const navigate = useNavigate();
const handleSearch = (e) => {
e.preventDefault();
if (query.trim()) {
navigate(`/search?q=${encodeURIComponent(query)}`);
}
};
return (
<form className="search-bar-container" onSubmit={handleSearch}>
<Search className="search-icon" size={20} />
<input
type="text"
className="search-input"
value={query}
onChange={(e) => setQuery(e.target.value)}
autoFocus={autoFocus}
/>
<div className="search-actions">
<Mic className="action-icon" size={20} title="Search by voice" />
<Camera className="action-icon" size={20} title="Search by image" />
</div>
</form>
);
};
export default SearchBar;

View File

@@ -0,0 +1,70 @@
:root {
--google-blue: #8ab4f8; /* Dark mode colors, we'll default to dark mode or support both. Actually let's just do light mode for a classic look, or support both. Let's do light mode by default. */
--google-red: #ea4335;
--google-yellow: #fbbc05;
--google-green: #34a853;
--text-primary: #202124;
--text-secondary: #5f6368;
--text-tertiary: #70757a;
--link-color: #1a0dab;
--link-visited: #681da8;
--bg-color: #ffffff;
--bg-hover: #f1f3f4;
--border-color: #dfe1e5;
--border-focus: #dfe1e5;
--shadow-color: rgba(32, 33, 36, 0.28);
}
@media (prefers-color-scheme: dark) {
:root {
--google-blue: #8ab4f8;
--google-red: #f28b82;
--google-yellow: #fde293;
--google-green: #81c995;
--text-primary: #e8eaed;
--text-secondary: #9aa0a6;
--text-tertiary: #bdc1c6;
--link-color: #8ab4f8;
--link-visited: #c58af9;
--bg-color: #202124;
--bg-hover: #303134;
--border-color: #5f6368;
--border-focus: #5f6368;
--shadow-color: rgba(23, 23, 23, 0.8);
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: arial, sans-serif;
color: var(--text-primary);
background-color: var(--bg-color);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
color: var(--link-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
font-family: arial, sans-serif;
background: none;
border: none;
cursor: pointer;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}

Some files were not shown because too many files have changed in this diff Show More