- Created a new About page (index.html) detailing the BotServer platform, its features, and technology stack. - Developed a Login page (login.html) with sign-in and sign-up functionality, including form validation and user feedback messages. - Removed the empty style.css file as it is no longer needed.
340 lines
12 KiB
HTML
340 lines
12 KiB
HTML
|
||
<script type="module">
|
||
// Import icons from Lucide (using CDN)
|
||
import { HardDrive, Terminal, ChevronLeft, ChevronRight } from 'https://cdn.jsdelivr.net/npm/lucide@0.331.0/+esm';
|
||
|
||
export default {
|
||
// Component state
|
||
data() {
|
||
return {
|
||
examples: [
|
||
{ name: "Chat", href: "/chat", color: "#25D366" },
|
||
{ name: "Paper", href: "/paper", color: "#6366F1" },
|
||
{ name: "Mail", href: "/mail", color: "#FFD700" },
|
||
{ name: "Calendar", href: "/calendar", color: "#1DB954" },
|
||
{ name: "Meet", href: "/meet", color: "#059669" },
|
||
{ name: "Drive", href: "/drive", color: "#10B981" },
|
||
{ name: "Editor", href: "/editor", color: "#2563EB" },
|
||
{ name: "Player", href: "/player", color: "Yellow" },
|
||
{ name: "Tables", href: "/tables", color: "#8B5CF6" },
|
||
{ name: "Dashboard", href: "/dashboard", color: "#6366F1" },
|
||
{ name: "Sources", href: "/sources", color: "#F59E0B" },
|
||
{ name: "Settings", href: "/settings", color: "#6B7280" },
|
||
],
|
||
pathname: window.location.pathname,
|
||
scrollContainer: null,
|
||
navItems: [],
|
||
isLoggedIn: false,
|
||
showLoginMenu: false,
|
||
showScrollButtons: false,
|
||
loginMenu: null,
|
||
showThemeMenu: false,
|
||
themeMenu: null,
|
||
currentTime: new Date(),
|
||
theme: { name: "default", label: "Default", icon: "🎨" },
|
||
themes: [
|
||
{ name: "retrowave", label: "Retrowave", icon: "🌌" },
|
||
{ name: "vapordream", label: "Vapordream", icon: "🌀" },
|
||
{ name: "y2kglow", label: "Y2K Glow", icon: "💿" },
|
||
{ name: "mellowgold", label: "Mellow Gold", icon: "☮️" },
|
||
{ name: "arcadeflash", label: "Arcade Flash", icon: "🕹️" },
|
||
{ name: "polaroidmemories", label: "Polaroid Memories", icon: "📸" },
|
||
{ name: "midcenturymod", label: "Mid‑Century Mod", icon: "🪑" },
|
||
{ name: "grungeera", label: "Grunge Era", icon: "🎸" },
|
||
{ name: "discofever", label: "Disco Fever", icon: "🪩" },
|
||
{ name: "saturdaycartoons", label: "Saturday Cartoons", icon: "📺" },
|
||
{ name: "oldhollywood", label: "Old Hollywood", icon: "🎬" },
|
||
{ name: "cyberpunk", label: "Cyberpunk", icon: "🤖" },
|
||
{ name: "seasidepostcard", label: "Seaside Postcard", icon: "🏖️" },
|
||
{ name: "typewriter", label: "Typewriter", icon: "⌨️" },
|
||
{ name: "jazzage", label: "Jazz Age", icon: "🎷" },
|
||
{ name: "xtreegold", label: "X‑Tree Gold", icon: "X" },
|
||
],
|
||
};
|
||
},
|
||
|
||
// Lifecycle: component mounted
|
||
mounted() {
|
||
// References
|
||
this.scrollContainer = this.root.querySelector('.nav-scroll');
|
||
this.loginMenu = this.root.querySelector('.login-menu');
|
||
this.themeMenu = this.root.querySelector('.theme-menu');
|
||
|
||
// Initialize nav item refs
|
||
this.navItems = Array.from(this.root.querySelectorAll('.nav-item'));
|
||
|
||
// Time update interval
|
||
this.timeInterval = setInterval(() => {
|
||
this.currentTime = new Date();
|
||
this.update();
|
||
}, 1000);
|
||
|
||
// Scroll button visibility
|
||
this.checkScrollNeeded();
|
||
|
||
// Resize listener
|
||
window.addEventListener('resize', this.checkScrollNeeded);
|
||
|
||
// Click‑outside handling
|
||
document.addEventListener('mousedown', this.handleClickOutside);
|
||
},
|
||
|
||
// Cleanup
|
||
unmounted() {
|
||
clearInterval(this.timeInterval);
|
||
window.removeEventListener('resize', this.checkScrollNeeded);
|
||
document.removeEventListener('mousedown', this.handleClickOutside);
|
||
},
|
||
|
||
// Methods
|
||
formatTime(date) {
|
||
return date.toLocaleTimeString('en-US', {
|
||
hour12: false,
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit',
|
||
});
|
||
},
|
||
|
||
formatDate(date) {
|
||
return date.toLocaleDateString('en-US', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
});
|
||
},
|
||
|
||
isActive(href) {
|
||
if (href === '/') return this.pathname === href;
|
||
return this.pathname.startsWith(href);
|
||
},
|
||
|
||
handleLogin() {
|
||
this.isLoggedIn = true;
|
||
this.showLoginMenu = false;
|
||
this.update();
|
||
},
|
||
|
||
handleLogout() {
|
||
this.isLoggedIn = false;
|
||
this.showLoginMenu = false;
|
||
this.update();
|
||
},
|
||
|
||
checkScrollNeeded() {
|
||
if (this.scrollContainer) {
|
||
const container = this.scrollContainer;
|
||
const isScrollable = container.scrollWidth > container.clientWidth;
|
||
this.showScrollButtons = isScrollable;
|
||
this.update();
|
||
}
|
||
},
|
||
|
||
handleClickOutside(event) {
|
||
if (this.loginMenu && !this.loginMenu.contains(event.target)) {
|
||
this.showLoginMenu = false;
|
||
}
|
||
if (this.themeMenu && !this.themeMenu.contains(event.target)) {
|
||
this.showThemeMenu = false;
|
||
}
|
||
this.update();
|
||
},
|
||
|
||
scrollLeft() {
|
||
if (this.scrollContainer) {
|
||
this.scrollContainer.scrollBy({ left: -150, behavior: 'smooth' });
|
||
}
|
||
},
|
||
|
||
scrollRight() {
|
||
if (this.scrollContainer) {
|
||
this.scrollContainer.scrollBy({ left: 150, behavior: 'smooth' });
|
||
}
|
||
},
|
||
|
||
getThemeIcon() {
|
||
const found = this.themes.find(t => t.name === this.theme.name);
|
||
return found ? found.icon : '🎨';
|
||
},
|
||
|
||
setTheme(name) {
|
||
const found = this.themes.find(t => t.name === name);
|
||
if (found) {
|
||
this.theme = found;
|
||
this.showThemeMenu = false;
|
||
this.update();
|
||
}
|
||
},
|
||
|
||
navigate(href) {
|
||
window.location.href = href;
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
/* Basic styles - the original Tailwind classes are kept as comments for reference */
|
||
.fixed { position: fixed; }
|
||
.top-0 { top: 0; }
|
||
.left-0 { left: 0; }
|
||
.right-0 { right: 0; }
|
||
.z-50 { z-index: 50; }
|
||
.bg-gray-800 { background-color: #2d3748; }
|
||
.text-green-400 { color: #68d391; }
|
||
.font-mono { font-family: monospace; }
|
||
.border-b { border-bottom: 1px solid transparent; }
|
||
.border-green-600 { border-color: #38a169; }
|
||
.text-xs { font-size: .75rem; }
|
||
.flex { display: flex; }
|
||
.items-center { align-items: center; }
|
||
.justify-between { justify-content: space-between; }
|
||
.px-4 { padding-left: 1rem; padding-right: 1rem; }
|
||
.py-1 { padding-top: .25rem; padding-bottom: .25rem; }
|
||
.gap-4 > * + * { margin-left: 1rem; }
|
||
.gap-2 > * + * { margin-left: .5rem; }
|
||
.w-3 { width: .75rem; }
|
||
.h-3 { height: .75rem; }
|
||
.text-green-300 { color: #9ae6b4; }
|
||
.w-2 { width: .5rem; }
|
||
.h-2 { height: .5rem; }
|
||
.bg-green-500 { background-color: #48bb78; }
|
||
.rounded-full { border-radius: 9999px; }
|
||
.animate-pulse { animation: pulse 2s infinite; }
|
||
.nav-container { position: relative; }
|
||
.nav-inner { overflow: hidden; }
|
||
.nav-content { display: flex; align-items: center; gap: .5rem; }
|
||
.logo-container img { display: block; }
|
||
.nav-scroll { overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
|
||
.nav-scroll::-webkit-scrollbar { display: none; }
|
||
.nav-items { display: flex; gap: .25rem; }
|
||
.nav-item { padding: .25rem .5rem; border: 1px solid transparent; border-radius: .25rem; cursor: pointer; transition: all .2s; }
|
||
.nav-item.active { background-color: #2d3748; border-color: #68d391; color: #68d391; }
|
||
.nav-item:hover { border-color: currentColor; }
|
||
.auth-controls { display: flex; gap: .5rem; }
|
||
.login-button, .theme-toggle { background: none; border: none; color: inherit; cursor: pointer; }
|
||
.login-menu, .theme-menu { position: absolute; background: #1a202c; border: 1px solid #4a5568; padding: .5rem; margin-top: .25rem; border-radius: .25rem; }
|
||
.menu-item { display: block; width: 100%; text-align: left; padding: .25rem; background: none; border: none; color: #a0aec0; cursor: pointer; }
|
||
.menu-item:hover { background: #2d3748; }
|
||
.active-theme { font-weight: bold; }
|
||
</style>
|
||
|
||
<!-- Markup -->
|
||
<div class="fixed top-0 left-0 right-0 z-50 bg-gray-800 text-green-400 font-mono border-b border-green-600 text-xs">
|
||
<div class="flex items-center justify-between px-4 py-1">
|
||
<div class="flex items-center gap-4">
|
||
<div class="flex items-center gap-2">
|
||
<HardDrive class="w-3 h-3 text-green-400" />
|
||
<span class="text-green-300">RETRO NAVIGATOR v4.0</span>
|
||
</div>
|
||
<div class="flex items-center gap-1">
|
||
<div class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
|
||
<span class="text-green-400">READY</span>
|
||
</div>
|
||
<div class="flex items-center gap-1">
|
||
<span class="text-green-300">THEME:</span>
|
||
<span class="text-yellow-400">{theme.label}</span>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center gap-4">
|
||
<span class="text-green-300">{formatDate(currentTime)}</span>
|
||
<span class="text-green-300">{formatTime(currentTime)}</span>
|
||
<div class="flex items-center gap-1">
|
||
<Terminal class="w-3 h-3 text-green-400" />
|
||
<span class="text-green-400">SYS</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-container" style="top:24px;">
|
||
<div class="nav-inner">
|
||
<div class="nav-content">
|
||
<div class="logo-container">
|
||
<img src="/images/generalbots-logo.svg" alt="Logo" width="64" height="24" />
|
||
</div>
|
||
|
||
{#if showScrollButtons}
|
||
<button class="w-8 h-8 bg-gray-800 border border-green-600 text-green-400 rounded hover:bg-green-900/30 hover:border-green-500 hover:text-green-300 transition-all flex items-center justify-center flex-shrink-0 mx-1"
|
||
@click="scrollLeft" aria-label="Scroll left">
|
||
<ChevronLeft class="w-4 h-4" />
|
||
</button>
|
||
{/if}
|
||
|
||
<div class="nav-scroll">
|
||
<div class="nav-items">
|
||
{#each examples as example, index}
|
||
<button class="nav-item {isActive(example.href) ? 'active' : ''}"
|
||
@click="{() => navigate(example.href)}"
|
||
style="--neon-color:{example.color}"
|
||
@mouseenter="{(e) => {
|
||
e.target.style.boxShadow = `0 0 15px ${example.color}60`;
|
||
e.target.style.borderColor = example.color;
|
||
e.target.style.color = example.color;
|
||
e.target.style.textShadow = `0 0 8px ${example.color}80`;
|
||
}}"
|
||
@mouseleave="{(e) => {
|
||
if (!isActive(example.href)) {
|
||
e.target.style.boxShadow = 'none';
|
||
e.target.style.borderColor = '';
|
||
e.target.style.color = '';
|
||
e.target.style.textShadow = 'none';
|
||
}
|
||
}}">
|
||
{example.name}
|
||
<div class="neon-glow"></div>
|
||
</button>
|
||
{/each}
|
||
</div>
|
||
</div>
|
||
|
||
{#if showScrollButtons}
|
||
<button class="w-8 h-8 bg-gray-800 border border-green-600 text-green-400 rounded hover:bg-green-900/30 hover:border-green-500 hover:text-green-300 transition-all flex items-center justify-center flex-shrink-0 mx-1"
|
||
@click="scrollRight" aria-label="Scroll right">
|
||
<ChevronRight class="w-4 h-4" />
|
||
</button>
|
||
{/if}
|
||
|
||
<div class="auth-controls">
|
||
<div class="login-container" bind:this="{loginMenu}">
|
||
<button @click="{() => showLoginMenu = !showLoginMenu}"
|
||
class="login-button"
|
||
aria-label="{isLoggedIn ? 'User menu' : 'Login'}">
|
||
{isLoggedIn ? '👤' : '🔐'}
|
||
</button>
|
||
|
||
{#if showLoginMenu}
|
||
<div class="login-menu">
|
||
{#if !isLoggedIn}
|
||
<button @click="handleLogin" class="menu-item">Login</button>
|
||
{:else}
|
||
<button @click="handleLogout" class="menu-item">Logout</button>
|
||
{/if}
|
||
</div>
|
||
{/if}
|
||
</div>
|
||
|
||
<div class="theme-container" bind:this="{themeMenu}">
|
||
<button @click="{() => showThemeMenu = !showThemeMenu}"
|
||
class="theme-toggle"
|
||
aria-label="Change theme">
|
||
{getThemeIcon()}
|
||
</button>
|
||
|
||
{#if showThemeMenu}
|
||
<div class="theme-menu">
|
||
{#each themes as t}
|
||
<button @click="{() => setTheme(t.name)}"
|
||
class="theme-menu-item {theme.name === t.name ? 'active-theme' : ''}">
|
||
{t.label}
|
||
</button>
|
||
{/each}
|
||
</div>
|
||
{/if}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-spacer" style="height:88px;"></div>
|
||
</div>
|