| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Neon Portal</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&display=swap'); |
| |
| :root { |
| --neon-blue: #05d9e8; |
| --neon-pink: #ff2a6d; |
| --neon-purple: #d300c5; |
| --dark-bg: #05010e; |
| --darker-bg: #0d0221; |
| --nav-dark: #12092a; |
| } |
| |
| body { |
| font-family: 'Orbitron', sans-serif; |
| background-color: var(--dark-bg); |
| color: white; |
| min-height: 100vh; |
| background-image: var(--bg-image, none); |
| background-size: cover; |
| background-attachment: fixed; |
| background-position: center; |
| } |
| |
| .glitch-effect { |
| position: relative; |
| color: white; |
| } |
| |
| .glitch-effect::before, .glitch-effect::after { |
| content: attr(data-text); |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| opacity: 0.8; |
| } |
| |
| .glitch-effect::before { |
| color: var(--neon-blue); |
| z-index: -1; |
| animation: glitch-effect 3s infinite; |
| } |
| |
| .glitch-effect::after { |
| color: var(--neon-pink); |
| z-index: -2; |
| animation: glitch-effect 2s infinite reverse; |
| } |
| |
| @keyframes glitch-effect { |
| 0% { transform: translate(0); } |
| 20% { transform: translate(-3px, 3px); } |
| 40% { transform: translate(-3px, -3px); } |
| 60% { transform: translate(3px, 3px); } |
| 80% { transform: translate(3px, -3px); } |
| 100% { transform: translate(0); } |
| } |
| |
| .neon-text-blue { |
| color: var(--neon-blue); |
| text-shadow: 0 0 5px var(--neon-blue), 0 0 10px var(--neon-blue), 0 0 20px var(--neon-blue); |
| } |
| |
| .neon-text-pink { |
| color: var(--neon-pink); |
| text-shadow: 0 0 5px var(--neon-pink), 0 0 10px var(--neon-pink), 0 0 20px var(--neon-pink); |
| } |
| |
| .neon-text-purple { |
| color: var(--neon-purple); |
| text-shadow: 0 0 5px var(--neon-purple), 0 0 10px var(--neon-purple), 0 0 20px var(--neon-purple); |
| } |
| |
| .neon-border-blue { |
| border: 1px solid var(--neon-blue); |
| box-shadow: 0 0 5px var(--neon-blue), inset 0 0 5px var(--neon-blue), 0 0 20px var(--neon-blue); |
| } |
| |
| .neon-border-pink { |
| border: 1px solid var(--neon-pink); |
| box-shadow: 0 0 5px var(--neon-pink), inset 0 0 5px var(--neon-pink), 0 0 20px var(--neon-pink); |
| } |
| |
| .neon-hover:hover { |
| text-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor; |
| transition: text-shadow 0.3s ease; |
| } |
| |
| .nav-item:hover { |
| transform: translateY(-2px); |
| transition: transform 0.3s ease; |
| } |
| |
| .dropdown { |
| display: none; |
| background: var(--nav-dark); |
| border: 1px solid var(--neon-blue); |
| box-shadow: 0 0 20px var(--neon-blue); |
| transition: all 0.3s ease; |
| z-index: 100; |
| } |
| |
| .group:hover .dropdown { |
| display: block; |
| } |
| |
| .portal-card { |
| transition: all 0.3s ease; |
| backdrop-filter: blur(10px); |
| perspective: 1000px; |
| transform-style: preserve-3d; |
| } |
| |
| .portal-card:hover { |
| transform: translateY(-5px) scale(1.05); |
| box-shadow: 0 0 20px currentColor; |
| } |
| |
| .portal-card-inner { |
| transition: transform 0.6s; |
| transform-style: preserve-3d; |
| } |
| |
| .portal-card:hover .portal-card-inner { |
| transform: rotateY(15deg) rotateX(10deg); |
| } |
| |
| .modal-overlay { |
| background: rgba(5, 1, 14, 0.9); |
| backdrop-filter: blur(5px); |
| animation: fadeIn 0.3s ease; |
| } |
| |
| .modal-content { |
| animation: slideIn 0.3s ease; |
| background: var(--darker-bg); |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| @keyframes slideIn { |
| from { transform: translateY(-50px); opacity: 0; } |
| to { transform: translateY(0); opacity: 1; } |
| } |
| |
| .color-option { |
| width: 30px; |
| height: 30px; |
| border-radius: 50%; |
| display: inline-block; |
| margin: 5px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| position: relative; |
| } |
| |
| .color-option:hover { |
| transform: scale(1.1); |
| } |
| |
| .color-option.selected::after { |
| content: "✓"; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| color: white; |
| font-weight: bold; |
| } |
| |
| .custom-color-toggle { |
| padding: 8px; |
| cursor: pointer; |
| border-radius: 5px; |
| margin-top: 10px; |
| text-align: center; |
| border: 1px dashed var(--neon-blue); |
| } |
| |
| .custom-color-toggle:hover { |
| background: rgba(5, 217, 232, 0.1); |
| } |
| |
| .custom-color-input { |
| width: 100%; |
| height: 30px; |
| border: none; |
| background: transparent; |
| cursor: pointer; |
| margin-top: 10px; |
| } |
| |
| .bg-preview, .icon-preview { |
| max-width: 100%; |
| height: auto; |
| margin-top: 10px; |
| border-radius: 5px; |
| display: none; |
| } |
| |
| .grid-lines { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-image: linear-gradient(to right, rgba(5, 217, 232, 0.05) 1px, transparent 1px), |
| linear-gradient(to bottom, rgba(5, 217, 232, 0.05) 1px, transparent 1px); |
| background-size: 30px 30px; |
| z-index: -1; |
| pointer-events: none; |
| } |
| |
| .neon-underline { |
| position: relative; |
| display: inline-block; |
| } |
| |
| .neon-underline::after { |
| content: ''; |
| position: absolute; |
| bottom: -5px; |
| left: 0; |
| width: 100%; |
| height: 2px; |
| background: linear-gradient(to right, var(--neon-blue), var(--neon-pink)); |
| transform: scaleX(0); |
| transform-origin: bottom right; |
| transition: transform 0.5s ease-out; |
| } |
| |
| .neon-underline:hover::after { |
| transform: scaleX(1); |
| transform-origin: bottom left; |
| } |
| |
| |
| .holographic-effect { |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .holographic-effect::before { |
| content: ''; |
| position: absolute; |
| top: -50%; |
| left: -50%; |
| width: 200%; |
| height: 200%; |
| background: linear-gradient( |
| to bottom right, |
| rgba(255, 255, 255, 0) 45%, |
| rgba(5, 217, 232, 0.2) 50%, |
| rgba(255, 255, 255, 0) 55% |
| ); |
| transform: rotate(30deg); |
| animation: holographic 6s linear infinite; |
| } |
| |
| @keyframes holographic { |
| 0% { transform: translateY(-100%) rotate(30deg); } |
| 100% { transform: translateY(100%) rotate(30deg); } |
| } |
| </style> |
| </head> |
| <body class="min-h-screen relative"> |
| |
| <div class="grid-lines"></div> |
|
|
| |
| <nav class="navbar fixed top-0 left-0 right-0 z-50 bg-[#05010e]/90 backdrop-blur-md border-b border-[#12092a]"> |
| <div class="container mx-auto px-4"> |
| <div class="flex justify-between items-center h-16"> |
| |
| <div class="flex items-center"> |
| <div class="neon-border-pink rounded-full p-2"> |
| <i class="fas fa-terminal text-xl neon-text-pink"></i> |
| </div> |
| <span class="ml-3 font-bold text-xl neon-text-blue">NEON PORTAL</span> |
| </div> |
|
|
| |
| <div class="hidden md:flex items-center space-x-6"> |
| <div class="nav-item relative group"> |
| <button class="flex items-center space-x-1 neon-text-blue neon-underline"> |
| <i class="fas fa-cog"></i> |
| <span>Settings</span> |
| <i class="fas fa-chevron-down text-xs"></i> |
| </button> |
| <div class="dropdown absolute right-0 mt-2 w-80 rounded-lg dropdown-content p-4"> |
| <h3 class="text-sm neon-text-pink mb-3 uppercase tracking-wider">Portal Management</h3> |
| <button onclick="showAddLinkModal()" |
| class="w-full py-2 neon-border-blue rounded flex items-center justify-center mb-3 hover:bg-[#05d9e8] hover:text-[#0d0221] transition-all"> |
| <i class="fas fa-plus mr-2"></i>Add New Portal |
| </button> |
|
|
| <h3 class="text-sm neon-text-blue mt-4 mb-3 uppercase tracking-wider">Customization</h3> |
| <div class="space-y-4"> |
| <div> |
| <label for="bg-image" class="block mb-2 text-xs uppercase tracking-wider">Background Image URL</label> |
| <div class="flex"> |
| <input type="url" id="bg-image" class="flex-grow p-2 rounded-l-lg bg-[#12092a]" |
| placeholder="https://example.com/image.jpg"> |
| <button onclick="setBackgroundImage()" |
| class="px-3 py-2 bg-[#05d9e8] text-[#0d0221] rounded-r-lg hover:bg-[#00c4d2] transition-all"> |
| <i class="fas fa-check"></i> |
| </button> |
| </div> |
| <div class="bg-upload-container"> |
| <label class="block mt-2 text-xs uppercase tracking-wider">Upload Background</label> |
| <input type="file" id="bg-upload" class="w-full mt-1" accept="image/*"> |
| <img id="bg-preview" class="bg-preview" src="#" alt="Background Preview"> |
| <button onclick="applyUploadedBackground()" |
| class="mt-2 px-3 py-1 rounded border border-[#ff2a6d] hover:bg-[#ff2a6d]/20 transition-all text-xs"> |
| Apply Uploaded Image |
| </button> |
| </div> |
| <button onclick="resetBackgroundImage()" |
| class="mt-2 text-xs px-3 py-1 rounded border border-[#ff2a6d] hover:bg-[#ff2a6d]/20 transition-all"> |
| Reset Background |
| </button> |
| </div> |
|
|
| <div> |
| <label class="block mb-2 text-xs uppercase tracking-wider">Theme Color</label> |
| <div class="color-picker-container neon-border-blue rounded-lg p-4"> |
| <div class="color-option selected" style="background-color: #05d9e8;" |
| onclick="selectThemeColor('#05d9e8', this)"></div> |
| <div class="color-option" style="background-color: #ff2a6d;" |
| onclick="selectThemeColor('#ff2a6d', this)"></div> |
| <div class="color-option" style="background-color: #d300c5;" |
| onclick="selectThemeColor('#d300c5', this)"></div> |
| <div class="color-option" style="background-color: #00ff9d;" |
| onclick="selectThemeColor('#00ff9d', this)"></div> |
| <div class="color-option" style="background-color: #ffcc00;" |
| onclick="selectThemeColor('#ffcc00', this)"></div> |
| <div class="color-option" style="background-color: #ff3333;" |
| onclick="selectThemeColor('#ff3333', this)"></div> |
| <div class="color-option" style="background-color: #4267B2;" |
| onclick="selectThemeColor('#4267B2', this)"></div> |
| <div class="color-option" style="background-color: #9147ff;" |
| onclick="selectThemeColor('#9147ff', this)"></div> |
| <div class="color-option" style="background-color: #25F4EE;" |
| onclick="selectThemeColor('#25F4EE', this)"></div> |
| <div class="custom-color-toggle w-full" onclick="toggleCustomColorInput()"> |
| Custom Color |
| </div> |
| <div id="custom-color-wrapper" class="custom-color-option" style="display: none;"> |
| <input type="color" id="custom-color" class="custom-color-input"> |
| <button class="custom-color-confirm mt-2 px-3 py-1 rounded border border-[#05d9e8] hover:bg-[#05d9e8]/20 transition-all" onclick="confirmCustomColor()">OK</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <h3 class="text-sm neon-text-blue mt-4 mb-3 uppercase tracking-wider">System Info</h3> |
| <div class="text-xs space-y-2"> |
| <div class="flex justify-between"> |
| <span>Version</span> |
| <span class="text-[#05d9e8]">v2.3.5</span> |
| </div> |
| <div class="flex justify-between"> |
| <span>Portals</span> |
| <span id="portal-count" class="text-[#ff2a6d]">0</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="nav-item relative group"> |
| <button class="flex items-center space-x-1 neon-text-blue neon-underline"> |
| <i class="fas fa-address-card"></i> |
| <span>Contact</span> |
| <i class="fas fa-chevron-down text-xs"></i> |
| </button> |
| <div class="dropdown absolute right-0 mt-2 w-72 rounded-lg dropdown-content p-4"> |
| <h3 class="text-sm neon-text-pink mb-3 uppercase tracking-wider">Connection Channels</h3> |
| <div class="space-y-3"> |
| <div class="contact-card p-3 rounded border border-[#05d9e8] hover:bg-[#05d9e8]/10 transition-all"> |
| <div class="flex items-center"> |
| <i class="fas fa-envelope text-lg text-[#05d9e8] mr-3"></i> |
| <div> |
| <h4 class="font-bold">Encrypted Mail</h4> |
| <a href="mailto:contact@neonportal.dev" |
| class="text-sm hover:underline block">contact@neonportal.dev</a> |
| </div> |
| </div> |
| </div> |
| |
| <div class="contact-card p-3 rounded border border-[#ff2a6d] hover:bg-[#ff2a6d]/10 transition-all"> |
| <div class="flex items-center"> |
| <i class="fab fa-discord text-lg text-[#5865F2] mr-3"></i> |
| <div> |
| <h4 class="font-bold">Discord</h4> |
| <span class="text-sm">NeonPortal#0001</span> |
| </div> |
| </div> |
| </div> |
| |
| <div class="contact-card p-3 rounded border border-[#25F4EE] hover:bg-[#25F4EE]/10 transition-all"> |
| <div class="flex items-center"> |
| <i class="fab fa-twitter text-lg text-[#1DA1F2] mr-3"></i> |
| <div> |
| <h4 class="font-bold">Twitter</h4> |
| <a href="https://twitter.com/neonportal" target="_blank" class="text-sm hover:underline block">@neonportal</a> |
| </div> |
| </div> |
| </div> |
| |
| <div class="contact-card p-3 rounded border border-[#9147ff] hover:bg-[#9147ff]/10 transition-all"> |
| <div class="flex items-center"> |
| <i class="fab fa-patreon text-lg text-[#FF424D] mr-3"></i> |
| <div> |
| <h4 class="font-bold">Support</h4> |
| <a href="https://patreon.com/neonportal" target="_blank" class="text-sm hover:underline block">Patreon</a> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="md:hidden"> |
| <button id="mobile-menu-button" class="neon-text-pink text-2xl focus:outline-none"> |
| <i class="fas fa-bars"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="mobile-menu" class="md:hidden hidden bg-[#05010e] border-t border-[#ff2a6d]"> |
| <div class="px-2 py-3 space-y-1"> |
| <button onclick="showAddLinkModal()" class="block px-3 py-2 rounded-md neon-text-blue w-full text-left hover:bg-[#05d9e8]/20"> |
| <i class="fas fa-plus mr-2"></i>Add New Portal |
| </button> |
| <button class="block px-3 py-2 rounded-md neon-text-blue w-full text-left hover:bg-[#05d9e8]/20"> |
| <i class="fas fa-cog mr-2"></i>Settings |
| </button> |
| <button class="block px-3 py-2 rounded-md neon-text-blue w-full text-left hover:bg-[#05d9e8]/20"> |
| <i class="fas fa-address-card mr-2"></i>Contact |
| </button> |
| <div class="px-3 py-2"> |
| <label class="block text-xs neon-text-blue mb-1">Background Image</label> |
| <div class="flex mb-2"> |
| <input type="url" id="bg-image-mobile" class="flex-grow p-2 rounded-l-lg bg-[#12092a]" |
| placeholder="Image URL"> |
| <button onclick="setBackgroundImage()" |
| class="px-3 py-2 bg-[#05d9e8] text-[#0d0221] rounded-r-lg hover:bg-[#00c4d2] transition-all"> |
| <i class="fas fa-check"></i> |
| </button> |
| </div> |
| <button onclick="resetBackgroundImage()" |
| class="w-full px-3 py-1.5 text-xs rounded border border-[#ff2a6d] hover:bg-[#ff2a6d]/20 transition-all"> |
| Reset Background |
| </button> |
| </div> |
| </div> |
| </div> |
| </nav> |
|
|
| |
| <div class="container mx-auto px-4 py-8 relative z-10 mt-20"> |
| |
| <header class="text-center mb-12"> |
| <div class="relative inline-block"> |
| <h1 class="glitch-effect text-5xl md:text-7xl font-bold mb-4" data-text="NEON PORTAL">NEON PORTAL</h1> |
| <span |
| class="absolute -bottom-3 left-1/2 transform -translate-x-1/2 w-48 h-1 bg-gradient-to-r from-transparent via-[#05d9e8] to-transparent"></span> |
| <span |
| class="absolute -bottom-5 left-1/2 transform -translate-x-1/2 w-32 h-1 bg-gradient-to-r from-transparent via-[#ff2a6d] to-transparent"></span> |
| </div> |
| <p class="text-xl neon-text-purple uppercase tracking-wider mt-8">Your gateway to the digital neonverse</p> |
| </header> |
|
|
| |
| <div class="flex justify-center mb-12"> |
| <div class="neon-border-blue rounded-lg p-4 text-center backdrop-blur-sm bg-[#0d0221]/50 holographic-effect"> |
| <div id="time" class="text-3xl font-bold neon-text-blue">00:00:00</div> |
| <div id="date" class="text-lg uppercase tracking-wider mt-1">Loading...</div> |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6 mb-12" id="portals-grid"> |
| |
| </div> |
| </div> |
|
|
| |
| <div id="add-portal-modal" class="fixed inset-0 flex items-center justify-center z-50 hidden modal-overlay"> |
| <div class="modal-content neon-border-blue rounded-xl p-8 max-w-md w-full mx-4 relative"> |
| <button id="close-portal-modal" |
| class="absolute top-4 right-4 text-xl hover:text-[#ff2a6d] transition-all">×</button> |
| <h2 class="text-2xl neon-text-pink mb-6 uppercase tracking-wider">Create New Portal</h2> |
|
|
| <form id="portal-form" class="space-y-6"> |
| <div> |
| <label for="portal-name" class="block mb-2 neon-text-blue uppercase text-sm tracking-wider">Portal Name</label> |
| <input type="text" id="portal-name" class="w-full p-3 rounded-lg bg-[#12092a] focus:outline-none focus:border focus:border-[#05d9e8]" required> |
| </div> |
|
|
| <div> |
| <label for="portal-url" |
| class="block mb-2 neon-text-blue uppercase text-sm tracking-wider">Destination URL</label> |
| <input type="url" id="portal-url" class="w-full p-3 rounded-lg bg-[#12092a] focus:outline-none focus:border focus:border-[#05d9e8]" placeholder="https://" |
| required> |
| </div> |
|
|
| <div> |
| <label class="block mb-2 neon-text-blue uppercase text-sm tracking-wider">Icon</label> |
| <select id="portal-icon" class="w-full p-3 rounded-lg bg-[#0d0221] border border-[#05d9e8] focus:outline-none focus:border focus:border-[#ff2a6d]"> |
| <option value="fab fa-youtube">YouTube</option> |
| <option value="fab fa-twitter">Twitter</option> |
| <option value="fab fa-github">GitHub</option> |
| <option value="fab fa-reddit">Reddit</option> |
| <option value="fab fa-discord">Discord</option> |
| <option value="fab fa-facebook">Facebook</option> |
| <option value="fab fa-google-drive">Drive</option> |
| <option value="fas fa-envelope">Mail</option> |
| <option value="fas fa-music">Music</option> |
| <option value="fas fa-gamepad">Games</option> |
| <option value="fas fa-film">Movies</option> |
| <option value="fas fa-newspaper">News</option> |
| <option value="fas fa-shopping-cart">Shop</option> |
| <option value="custom-icon">Custom Icon</option> |
| </select> |
|
|
| <div id="custom-icon-options" class="custom-icon-option mt-3" style="display: none;"> |
| <div class="flex space-x-4"> |
| <div class="flex-1"> |
| <label class="block mb-2 text-xs uppercase tracking-wider">Upload Icon</label> |
| <input type="file" id="icon-upload" class="w-full" accept="image/*"> |
| </div> |
| <div class="flex-1"> |
| <label class="block mb-2 text-xs uppercase tracking-wider">Icon URL</label> |
| <input type="url" id="icon-url" class="w-full p-2 rounded bg-[#0d0221]" |
| placeholder="https://example.com/icon.png"> |
| </div> |
| </div> |
| <img id="icon-preview" class="icon-preview" src="#" alt="Icon Preview"> |
| </div> |
| </div> |
|
|
| <div> |
| <label class="block mb-2 neon-text-blue uppercase text-sm tracking-wider">Color</label> |
| <div class="color-picker-container neon-border-blue rounded-lg p-4"> |
| <div class="color-option" style="background-color: #ff2a6d;" |
| onclick="selectPortalColor('#ff2a6d', this)"></div> |
| <div class="color-option selected" style="background-color: #05d9e8;" |
| onclick="selectPortalColor('#05d9e8', this)"></div> |
| <div class="color-option" style="background-color: #d300c5;" |
| onclick="selectPortalColor('#d300c5', this)"></div> |
| <div class="color-option" style="background-color: #00ff9d;" |
| onclick="selectPortalColor('#00ff9d', this)"></div> |
| <div class="color-option" style="background-color: #ffcc00;" |
| onclick="selectPortalColor('#ffcc00', this)"></div> |
| <div class="color-option" style="background-color: #ffffff;" |
| onclick="selectPortalColor('#ffffff', this)"></div> |
| <div class="color-option" style="background-color: #4267B2;" |
| onclick="selectPortalColor('#4267B2', this)"></div> |
| <div class="color-option" style="background-color: #9147ff;" |
| onclick="selectPortalColor('#9147ff', this)"></div> |
| <div class="custom-color-toggle w-full" onclick="togglePortalCustomColorInput()"> |
| Custom Color |
| </div> |
| <input type="color" id="portal-custom-color" class="custom-color-input" |
| onchange="applyCustomPortalColor(this.value)"> |
| </div> |
| <input type="hidden" id="portal-color" value="#05d9e8"> |
| </div> |
|
|
| <div class="flex justify-end space-x-3"> |
| <button type="button" onclick="document.getElementById('add-portal-modal').classList.add('hidden')" |
| class="px-4 py-2 rounded-lg border border-[#ff2a6d] hover:bg-[#ff2a6d] hover:text-white transition-all"> |
| Cancel |
| </button> |
| <button type="submit" |
| class="px-6 py-2 neon-border-blue rounded-lg hover:bg-[#05d9e8] hover:text-[#0d0221] transition-all font-bold"> |
| Create Portal |
| </button> |
| </div> |
| </form> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const defaultPortals = [ |
| { name: 'YouTube', url: 'https://youtube.com', icon: 'fab fa-youtube', color: '#ff0000' }, |
| { name: 'Twitter', url: 'https://twitter.com', icon: 'fab fa-twitter', color: '#1DA1F2' }, |
| { name: 'GitHub', url: 'https://github.com', icon: 'fab fa-github', color: '#ffffff' }, |
| { name: 'Reddit', url: 'https://reddit.com', icon: 'fab fa-reddit', color: '#FF5700' }, |
| { name: 'Discord', url: 'https://discord.com', icon: 'fab fa-discord', color: '#5865F2' }, |
| { name: 'Google Drive', url: 'https://drive.google.com', icon: 'fab fa-google-drive', color: '#34A853' }, |
| { name: 'Gmail', url: 'https://mail.google.com', icon: 'fas fa-envelope', color: '#EA4335' }, |
| { name: 'Spotify', url: 'https://open.spotify.com', icon: 'fas fa-music', color: '#1DB954' } |
| ]; |
| |
| |
| const portalsGrid = document.getElementById('portals-grid'); |
| const portalForm = document.getElementById('portal-form'); |
| const portalCount = document.getElementById('portal-count'); |
| const mobileMenuButton = document.getElementById('mobile-menu-button'); |
| const mobileMenu = document.getElementById('mobile-menu'); |
| const timeDisplay = document.getElementById('time'); |
| const dateDisplay = document.getElementById('date'); |
| const portalIconSelect = document.getElementById('portal-icon'); |
| const customIconOptions = document.getElementById('custom-icon-options'); |
| const bgUpload = document.getElementById('bg-upload'); |
| const bgPreview = document.getElementById('bg-preview'); |
| const iconUpload = document.getElementById('icon-upload'); |
| const iconPreview = document.getElementById('icon-preview'); |
| |
| |
| let portals = JSON.parse(localStorage.getItem('portals')) || defaultPortals; |
| let currentThemeColor = '#05d9e8'; |
| let currentBgImage = localStorage.getItem('bgImage') || ''; |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| renderPortals(); |
| updateTime(); |
| setInterval(updateTime, 1000); |
| loadBackgroundImage(); |
| |
| |
| mobileMenuButton.addEventListener('click', function() { |
| mobileMenu.classList.toggle('hidden'); |
| }); |
| |
| |
| portalForm.addEventListener('submit', handlePortalFormSubmit); |
| |
| |
| portalIconSelect.addEventListener('change', function() { |
| if (this.value === 'custom-icon') { |
| customIconOptions.style.display = 'block'; |
| } else { |
| customIconOptions.style.display = 'none'; |
| } |
| }); |
| |
| |
| bgUpload.addEventListener('change', function(e) { |
| const file = e.target.files[0]; |
| if (file) { |
| const reader = new FileReader(); |
| reader.onload = function(event) { |
| bgPreview.src = event.target.result; |
| bgPreview.style.display = 'block'; |
| }; |
| reader.readAsDataURL(file); |
| } |
| }); |
| |
| |
| iconUpload.addEventListener('change', function(e) { |
| const file = e.target.files[0]; |
| if (file) { |
| const reader = new FileReader(); |
| reader.onload = function(event) { |
| iconPreview.src = event.target.result; |
| iconPreview.style.display = 'block'; |
| }; |
| reader.readAsDataURL(file); |
| } |
| }); |
| }); |
| |
| |
| function renderPortals() { |
| portalsGrid.innerHTML = ''; |
| |
| portals.forEach((portal, index) => { |
| const portalElement = document.createElement('div'); |
| |
| |
| const textColorStyle = `color: ${portal.color}; text-shadow: 0 0 5px ${portal.color}, 0 0 10px ${portal.color};`; |
| const borderStyle = `border: 1px solid ${portal.color}; box-shadow: 0 0 5px ${portal.color}, inset 0 0 5px ${portal.color}, 0 0 20px ${portal.color};`; |
| |
| portalElement.innerHTML = ` |
| <div class="portal-card h-full" style="${borderStyle}"> |
| <a href="${portal.url}" target="_blank" class="block h-full p-6 rounded-lg text-center relative overflow-hidden backdrop-blur-sm bg-[#0d0221]/50 transition-all duration-300"> |
| <div class="portal-card-inner"> |
| ${portal.icon.startsWith('http') ? |
| `<img src="${portal.icon}" class="mx-auto h-12 w-12 mb-4" alt="${portal.name} icon">` : |
| `<i class="${portal.icon} text-4xl mb-4" style="${textColorStyle}"></i>`} |
| <h3 class="font-bold uppercase tracking-wider" style="${textColorStyle}">${portal.name}</h3> |
| </div> |
| <button onclick="deletePortal(${index}); event.preventDefault();" |
| class="absolute top-1 right-1 text-xs text-red-500 hover:text-red-400"> |
| <i class="fas fa-times"></i> |
| </button> |
| </a> |
| </div> |
| `; |
| |
| portalsGrid.appendChild(portalElement); |
| }); |
| |
| |
| portalCount.textContent = portals.length; |
| } |
| |
| |
| function handlePortalFormSubmit(e) { |
| e.preventDefault(); |
| |
| const name = document.getElementById('portal-name').value; |
| const url = document.getElementById('portal-url').value; |
| let icon = document.getElementById('portal-icon').value; |
| const color = document.getElementById('portal-color').value; |
| |
| |
| if (icon === 'custom-icon') { |
| const iconUrl = document.getElementById('icon-url').value; |
| const iconFile = document.getElementById('icon-upload').files[0]; |
| |
| if (iconUrl) { |
| icon = iconUrl; |
| } else if (iconFile) { |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| |
| icon = e.target.result; |
| addPortalToArray(name, url, icon, color); |
| }; |
| reader.readAsDataURL(iconFile); |
| return; |
| } else { |
| alert('Please provide an icon URL or upload an icon'); |
| return; |
| } |
| } |
| |
| addPortalToArray(name, url, icon, color); |
| } |
| |
| |
| function addPortalToArray(name, url, icon, color) { |
| const newPortal = { |
| name: name, |
| url: url, |
| icon: icon, |
| color: color |
| }; |
| |
| portals.push(newPortal); |
| savePortals(); |
| renderPortals(); |
| document.getElementById('add-portal-modal').classList.add('hidden'); |
| resetPortalForm(); |
| } |
| |
| |
| function deletePortal(index) { |
| if (confirm('Are you sure you want to delete this portal?')) { |
| portals.splice(index, 1); |
| savePortals(); |
| renderPortals(); |
| } |
| } |
| |
| |
| function resetPortalForm() { |
| document.getElementById('portal-form').reset(); |
| customIconOptions.style.display = 'none'; |
| iconPreview.style.display = 'none'; |
| document.getElementById('portal-color').value = '#05d9e8'; |
| } |
| |
| |
| function savePortals() { |
| localStorage.setItem('portals', JSON.stringify(portals)); |
| } |
| |
| |
| function showAddLinkModal() { |
| document.getElementById('add-portal-modal').classList.remove('hidden'); |
| mobileMenu.classList.add('hidden'); |
| } |
| |
| |
| function updateTime() { |
| const now = new Date(); |
| const timeString = now.toLocaleTimeString(); |
| const dateString = now.toLocaleDateString(undefined, { |
| weekday: 'long', |
| year: 'numeric', |
| month: 'long', |
| day: 'numeric' |
| }); |
| |
| timeDisplay.textContent = timeString; |
| dateDisplay.textContent = dateString; |
| } |
| |
| |
| function selectPortalColor(color, element) { |
| |
| document.querySelectorAll('.color-picker-container .color-option').forEach(el => { |
| el.classList.remove('selected'); |
| }); |
| |
| |
| element.classList.add('selected'); |
| |
| |
| document.getElementById('portal-color').value = color; |
| } |
| |
| |
| function selectThemeColor(color, element) { |
| |
| document.querySelectorAll('.dropdown .color-option').forEach(el => { |
| el.classList.remove('selected'); |
| }); |
| |
| |
| element.classList.add('selected'); |
| |
| |
| currentThemeColor = color; |
| updateThemeColor(color); |
| } |
| |
| |
| function updateThemeColor(color) { |
| |
| document.documentElement.style.setProperty('--neon-blue', color); |
| |
| |
| |
| const neonElements = document.querySelectorAll('.neon-text-blue, .neon-border-blue'); |
| neonElements.forEach(el => { |
| if (el.classList.contains('neon-text-blue')) { |
| el.style.color = color; |
| el.style.textShadow = `0 0 5px ${color}, 0 0 10px ${color}, 0 0 20px ${color}`; |
| } |
| if (el.classList.contains('neon-border-blue')) { |
| el.style.borderColor = color; |
| el.style.boxShadow = `0 0 5px ${color}, inset 0 0 5px ${color}, 0 0 20px ${color}`; |
| } |
| }); |
| } |
| |
| |
| function toggleCustomColorInput() { |
| const wrapper = document.getElementById('custom-color-wrapper'); |
| wrapper.style.display = wrapper.style.display === 'none' ? 'block' : 'none'; |
| } |
| |
| |
| function togglePortalCustomColorInput() { |
| const input = document.getElementById('portal-custom-color'); |
| input.style.display = input.style.display === 'none' ? 'block' : 'none'; |
| } |
| |
| |
| function applyCustomPortalColor(color) { |
| selectPortalColor(color, document.querySelector('.portal-form .color-option.selected')); |
| } |
| |
| |
| function confirmCustomColor() { |
| const customColor = document.getElementById('custom-color').value; |
| selectThemeColor(customColor, document.querySelector('.dropdown .color-option.selected')); |
| document.getElementById('custom-color-wrapper').style.display = 'none'; |
| } |
| |
| |
| function setBackgroundImage() { |
| |
| const bgUrlInput = document.getElementById('bg-image-mobile') || document.getElementById('bg-image'); |
| |
| if (bgUrlInput && bgUrlInput.value) { |
| currentBgImage = bgUrlInput.value; |
| localStorage.setItem('bgImage', currentBgImage); |
| document.body.style.backgroundImage = `url('${currentBgImage}')`; |
| } |
| } |
| |
| |
| function applyUploadedBackground() { |
| if (bgPreview.src && bgPreview.src !== '#') { |
| currentBgImage = bgPreview.src; |
| localStorage.setItem('bgImage', currentBgImage); |
| document.body.style.backgroundImage = `url('${currentBgImage}')`; |
| } |
| } |
| |
| |
| function loadBackgroundImage() { |
| if (currentBgImage) { |
| document.body.style.backgroundImage = `url('${currentBgImage}')`; |
| document.body.style.backgroundSize = 'cover'; |
| document.body.style.backgroundAttachment = 'fixed'; |
| document.body.style.backgroundColor = 'transparent'; |
| } |
| } |
| |
| |
| function resetBackgroundImage() { |
| currentBgImage = ''; |
| localStorage.removeItem('bgImage'); |
| document.body.style.backgroundImage = 'none'; |
| document.body.style.backgroundColor = '#05010e'; |
| |
| |
| const bgInputs = document.querySelectorAll('[id^=bg-image]'); |
| bgInputs.forEach(input => input.value = ''); |
| |
| if (bgPreview) bgPreview.style.display = 'none'; |
| } |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Shiroaki085/hubv2" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p><p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jayare562/portasl" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |