suke / index.html
dai's picture
chore: Edit mail, twitter username.
d71888e verified
<!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 */
.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">
<!-- Grid lines effect -->
<div class="grid-lines"></div>
<!-- Navbar -->
<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">
<!-- Logo -->
<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>
<!-- Nav Items -->
<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:"
class="text-sm hover:underline block">daisuke at duck.com</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/daisuke" target="_blank" class="text-sm hover:underline block">@daisuke</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>
<!-- Mobile menu button -->
<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>
<!-- Mobile menu -->
<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>
<!-- Main container -->
<div class="container mx-auto px-4 py-8 relative z-10 mt-20">
<!-- Header -->
<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>
<!-- Time and date display -->
<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>
<!-- Portals grid -->
<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">
<!-- Default portals will be added here by JavaScript -->
</div>
</div>
<!-- Add Portal Modal -->
<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>
// Initialize default portals
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' }
];
// DOM elements
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');
// Current state
let portals = JSON.parse(localStorage.getItem('portals')) || defaultPortals;
let currentThemeColor = '#05d9e8'; // Default neon blue
let currentBgImage = localStorage.getItem('bgImage') || '';
// Initialize the app
document.addEventListener('DOMContentLoaded', function() {
renderPortals();
updateTime();
setInterval(updateTime, 1000);
loadBackgroundImage();
// Mobile menu toggle
mobileMenuButton.addEventListener('click', function() {
mobileMenu.classList.toggle('hidden');
});
// Event listeners for form
portalForm.addEventListener('submit', handlePortalFormSubmit);
// Show custom icon options when custom icon is selected
portalIconSelect.addEventListener('change', function() {
if (this.value === 'custom-icon') {
customIconOptions.style.display = 'block';
} else {
customIconOptions.style.display = 'none';
}
});
// Image preview for background upload
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);
}
});
// Image preview for custom icon upload
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);
}
});
});
// Render all portals
function renderPortals() {
portalsGrid.innerHTML = '';
portals.forEach((portal, index) => {
const portalElement = document.createElement('div');
// Apply the portal's color for text and border glow
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);
});
// Update portal count
portalCount.textContent = portals.length;
}
// Handle form submission
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;
// Handle custom icon
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) {
// Convert image to base64 to store in local storage
icon = e.target.result;
addPortalToArray(name, url, icon, color);
};
reader.readAsDataURL(iconFile);
return; // Exit early as we'll call addPortalToArray in the callback
} else {
alert('Please provide an icon URL or upload an icon');
return;
}
}
addPortalToArray(name, url, icon, color);
}
// Add a new portal to the array and update UI
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();
}
// Delete a portal
function deletePortal(index) {
if (confirm('Are you sure you want to delete this portal?')) {
portals.splice(index, 1);
savePortals();
renderPortals();
}
}
// Reset portal form
function resetPortalForm() {
document.getElementById('portal-form').reset();
customIconOptions.style.display = 'none';
iconPreview.style.display = 'none';
document.getElementById('portal-color').value = '#05d9e8';
}
// Save portals to localStorage
function savePortals() {
localStorage.setItem('portals', JSON.stringify(portals));
}
// Show add portal modal
function showAddLinkModal() {
document.getElementById('add-portal-modal').classList.remove('hidden');
mobileMenu.classList.add('hidden');
}
// Update time display
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;
}
// Select portal color
function selectPortalColor(color, element) {
// Remove selected class from all options
document.querySelectorAll('.color-picker-container .color-option').forEach(el => {
el.classList.remove('selected');
});
// Add selected class to clicked option
element.classList.add('selected');
// Update hidden input value
document.getElementById('portal-color').value = color;
}
// Select theme color
function selectThemeColor(color, element) {
// Remove selected class from all options
document.querySelectorAll('.dropdown .color-option').forEach(el => {
el.classList.remove('selected');
});
// Add selected class to clicked option
element.classList.add('selected');
// Update current theme color
currentThemeColor = color;
updateThemeColor(color);
}
// Update theme color
function updateThemeColor(color) {
// Update CSS variables
document.documentElement.style.setProperty('--neon-blue', color);
// Update all elements with the neon-blue color classes
// This is a simplified version - in a real app you might need more comprehensive updates
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}`;
}
});
}
// Toggle custom color input in settings
function toggleCustomColorInput() {
const wrapper = document.getElementById('custom-color-wrapper');
wrapper.style.display = wrapper.style.display === 'none' ? 'block' : 'none';
}
// Toggle custom color input in portal form
function togglePortalCustomColorInput() {
const input = document.getElementById('portal-custom-color');
input.style.display = input.style.display === 'none' ? 'block' : 'none';
}
// Apply custom portal color
function applyCustomPortalColor(color) {
selectPortalColor(color, document.querySelector('.portal-form .color-option.selected'));
}
// Confirm custom theme color
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';
}
// Set background image from URL
function setBackgroundImage() {
// Check if we're on mobile or desktop
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}')`;
}
}
// Apply uploaded background
function applyUploadedBackground() {
if (bgPreview.src && bgPreview.src !== '#') {
currentBgImage = bgPreview.src;
localStorage.setItem('bgImage', currentBgImage);
document.body.style.backgroundImage = `url('${currentBgImage}')`;
}
}
// Load background image from localStorage
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';
}
}
// Reset background image
function resetBackgroundImage() {
currentBgImage = '';
localStorage.removeItem('bgImage');
document.body.style.backgroundImage = 'none';
document.body.style.backgroundColor = '#05010e';
// Reset input fields
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=dai/dai" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>