aiui / index.html
basheer1414's picture
add glow wffect for orb - Follow Up Deployment
9236c45 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Particle Corn-Field Animation</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Full-screen canvas sits behind everything */
#particleCanvas {
position: fixed;
inset: 0;
z-index: -1;
background: linear-gradient(135deg, #0f172a, #312e81, #581c87);
}
/* From Uiverse.io by krokettenkoal */
:root {
--bg-col: #010101;
--space-col: #2d093a;
--galaxy-col: #460a42;
}
.card {
--bg-col: #010101;
--space-col: #2d093a;
--galaxy-col: #460a42;
--space-gradient: radial-gradient(ellipse at top, var(--bg-col), transparent),
radial-gradient(ellipse at bottom, var(--galaxy-col) 10%, transparent 60%),
radial-gradient(ellipse at bottom right, var(--space-col), transparent);
--space-gradient-alt: radial-gradient(ellipse at top left, var(--space-col), transparent),
radial-gradient(ellipse at bottom, var(--galaxy-col) 10%, transparent 60%),
radial-gradient(ellipse at bottom right, var(--bg-col), transparent);
--default-left: 50%;
--default-top: 50%;
--stars: radial-gradient(circle at 52% 54%, rgba(255, 255, 255, 0.582) 3px, transparent 4px),
radial-gradient(circle at 22% 24%, rgba(255, 255, 255, 0.582) 2px, transparent 3px),
radial-gradient(circle at 14% 18%, rgba(255, 255, 255, 0.582) 3px, transparent 8px),
radial-gradient(circle at 18% 21%, rgba(255, 255, 255, 0.582) 4px, transparent 5px),
radial-gradient(circle at 36% 9%, rgba(255, 255, 255, 0.582) 3px, transparent 5px),
radial-gradient(circle at 28% 31%, rgba(255, 255, 255, 0.39) 2px, transparent 3px),
radial-gradient(circle at 62% 61%, rgba(255, 255, 255, 0.532) 3px, transparent 4px),
radial-gradient(circle at 57% 66%, rgba(255, 255, 255, 0.842) 6px, transparent 8px),
radial-gradient(circle at 65% 71%, rgba(255, 255, 255, 0.534) 1px, transparent 3px),
radial-gradient(circle at 67% 68%, rgba(255, 255, 255, 0.651) 3px, transparent 3px),
radial-gradient(circle at 43% 44%, rgba(255, 255, 255, 0.74) 2px, transparent 6px),
radial-gradient(circle at 40% 39%, rgba(183, 243, 255, 0.842) 4px, transparent 10px),
radial-gradient(circle at 41% 40%, rgba(255, 255, 255, 0.699) 5px, transparent 6px),
radial-gradient(circle at 38% 38%, rgba(255, 255, 255, 0.349) 2px, transparent 4px),
radial-gradient(circle at 39% 42%, rgba(255, 255, 255, 0.747) 5px, transparent 7px),
radial-gradient(circle at 80% 31%, rgba(255, 255, 255, 0.781) 4px, transparent 6px),
radial-gradient(circle at 25% 64%, rgba(255, 255, 255, 0.425) 3px, transparent 4px),
radial-gradient(circle at 41% 49%, rgba(255, 255, 255, 0.678) 3px, transparent 6px),
radial-gradient(circle at 50% 37%, rgba(255, 255, 255, 0.336) 1px, transparent 3px),
radial-gradient(circle at 4% 37%, rgba(255, 255, 255, 0.336) 1px, transparent 3px),
radial-gradient(circle at 8% 60%, rgba(255, 255, 255, 0.336) 1px, transparent 4px),
radial-gradient(circle at 12% 54%, rgba(255, 255, 255, 0.336) 1px, transparent 5px),
radial-gradient(circle at 6% 59%, rgba(255, 255, 255, 0.336) 2px, transparent 10px),
radial-gradient(circle at 9% 57%, rgba(255, 255, 255, 0.336) 1px, transparent 2px),
radial-gradient(circle at 14% 61%, rgba(255, 255, 255, 0.336) 2px, transparent 6px);
width: 700px;
height: 700px;
padding: 0;
border-radius: 2rem;
left: var(--default-left);
top: var(--default-top);
transform: translate(-50%, -50%);
background-color: #010101;
background-image: var(--space-gradient), var(--stars);
background-size: 175% 200%;
background-repeat: no-repeat;
box-shadow: 5px 7px 20px var(--bg-col);
overflow: clip;
animation: space-drift 180s ease-in-out infinite;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
cursor: grab;
user-select: none;
}
.heading {
font-size: 0.9rem;
text-align: center;
color: rgb(189, 188, 141);
}
.heading span {
font-size: 2.2rem;
font-weight: bold;
display: block;
font-style: italic;
margin-top: 0.25rem;
background-clip: text;
-webkit-background-clip: text;
-moz-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 15px 10px 55px plum;
text-transform: uppercase;
letter-spacing: 1rem;
z-index: 99;
animation: heading-stretch 0.7s forwards ease-out;
}
.heading span::before,
.heading span::after {
content: '—';
}
.content {
display: grid;
place-items: center;
padding: 2rem;
z-index: 1;
}
.item {
--item-duration: 8s;
--idx: 0;
display: flex;
grid-area: 1 / 1;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
gap: 0.5rem;
font-size: 1.1rem;
text-transform: lowercase;
font-style: italic;
opacity: 0;
animation: item-fade var(--item-duration) infinite ease-in-out;
animation-delay: calc(var(--idx) * var(--item-duration) / 3);
}
.item svg {
width: 3rem;
height: 3rem;
}
.item--create {
--idx: 0;
}
.item--post {
--idx: 1;
}
.item--inspire {
--idx: 2;
}
/* Animation keyframes for card */
@keyframes space-drift {
0% { background-position: 0% 50%; }
33% { background-position: 80% 0%; }
67% { background-position: 80% 100%; }
100% { background-position: 0% 50%; }
}
@keyframes heading-stretch {
from {
opacity: 0.8;
transform: scale(0.8);
letter-spacing: normal;
filter: blur(50px);
text-shadow: none;
}
to {
opacity: unset;
transform: unset;
letter-spacing: 1rem;
filter: unset;
}
}
@keyframes item-fade {
0%, 20% {
opacity: 0;
transform: translateX(10px);
filter: blur(5px);
}
40%, 60% {
opacity: 1;
transform: unset;
filter: unset;
}
70%, 100% {
opacity: 0;
transform: translateX(-10px);
filter: blur(5px);
}
}
:root {
--bg-col: #010101;
--space-col: #2d093a;
--galaxy-col: #460a42;
}
/* Tiny Settings Panel */
#settingsPanel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 20;
width: 240px;
background: rgba(15,15,15,0.95);
border-radius: 0.5rem;
padding: 0.75rem;
display: none;
flex-direction: column;
gap: 0.5rem;
font-size: 0.6rem;
color: #fff;
backdrop-filter: blur(4px);
}
#settingsPanel label {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
#settingsPanel input[type="range"],
#settingsPanel input[type="number"] {
width: 100%;
height: 0.5rem;
font-size: 0.6rem;
padding: 0.1rem;
}
/* Modern colour pellets */
.colourPellet {
width: 0.8rem;
height: 0.8rem;
border-radius: 50%;
border: none;
cursor: pointer;
display: inline-block;
margin-right: 0.15rem;
}
/* ---------- 2-D Red-Orange Glowing Orb ---------- */
.spinner {
position: absolute;
width: 130px;
height: 130px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ff1a1a, #cc0000 40%, #990000 100%);
box-shadow:
0 0 30px rgba(255, 26, 26, 0.4),
0 0 60px rgba(255, 26, 26, 0.3),
0 0 90px rgba(255, 26, 26, 0.2),
0 0 120px rgba(255, 26, 26, 0.1),
inset 0 0 20px rgba(255, 255, 255, 0.3);
filter: blur(0.5px);
cursor: grab;
z-index: 20;
animation: pulseGlow 2s ease-in-out infinite;
}
@keyframes pulseGlow {
0%, 100% {
box-shadow:
0 0 30px rgba(255, 26, 26, 0.4),
0 0 60px rgba(255, 26, 26, 0.3),
0 0 90px rgba(255, 26, 26, 0.2),
0 0 120px rgba(255, 26, 26, 0.1),
inset 0 0 20px rgba(255, 255, 255, 0.3);
}
50% {
box-shadow:
0 0 40px rgba(255, 26, 26, 0.6),
0 0 80px rgba(255, 26, 26, 0.5),
0 0 110px rgba(255, 26, 26, 0.4),
0 0 150px rgba(255, 26, 26, 0.3),
inset 0 0 25px rgba(255, 255, 255, 0.4);
}
}
</style>
</head>
<body class="relative min-h-screen text-white flex flex-col items-center justify-center font-sans">
<canvas id="particleCanvas"></canvas>
<!-- 3-D Red-Orange Glowing Orb -->
<div id="orb" class="spinner" style="top:50%; left:50%; transform:translate(-50%,-50%);"></div>
<!-- From Uiverse.io by krokettenkoal -->
<div id="draggableCard" class="card relative flex flex-col min-w-0 w-full mx-auto !min-h-[28rem] !rounded-xl !p-0">
<!-- Header -->
<header class="flex items-center justify-between px-4 py-2 border-b border-white/20">
<div class="flex items-center gap-2">
<button id="newChatBtn" title="New Chat" class="p-1.5 rounded-full hover:bg-white/10 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 4v16m8-8H4"/></svg>
</button>
<button id="historyBtn" title="History" class="p-1.5 rounded-full hover:bg-white/10 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 8v4l3 3m7-7a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</button>
</div>
<div class="flex items-center gap-2">
<select id="modelSelect" class="bg-slate-900/80 text-sm border border-white/20 rounded px-2 py-1 outline-none">
<option value="gpt-4o-mini">GPT-4o-mini</option>
<option value="gpt-4o">GPT-4o</option>
<option value="gpt-4-turbo">GPT-4-turbo</option>
<option value="claude-3-5-sonnet">Claude-3.5-Sonnet</option>
<option value="claude-3-5-haiku">Claude-3.5-Haiku</option>
<option value="claude-3-opus">Claude-3-Opus</option>
<option value="gemini-1.5-pro">Gemini-1.5-Pro</option>
<option value="gemini-1.5-flash">Gemini-1.5-Flash</option>
<option value="gemini-2-flash">Gemini-2-Flash</option>
<option value="llama-3.1-8b">Llama-3.1-8B</option>
<option value="llama-3.1-70b">Llama-3.1-70B</option>
<option value="llama-3.3-70b">Llama-3.3-70B</option>
<option value="mistral-large">Mistral-Large</option>
<option value="mistral-nemo">Mistral-Nemo</option>
<option value="command-r-plus">Cohere-Command-R+</option>
<option value="command-r">Cohere-Command-R</option>
<option value="gpt-3.5-turbo">GPT-3.5-Turbo</option>
<option value="claude-3-haiku">Claude-3-Haiku</option>
<option value="gemini-1.0-pro">Gemini-1.0-Pro</option>
<option value="llama-3-8b">Llama-3-8B</option>
<option value="mistral-7b">Mistral-7B</option>
<option value="command-light">Cohere-Command-Light</option>
</select>
<button id="settingsButton" class="p-1.5 rounded-full hover:bg-white/10 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6m9-9h-6m-6 0H3m15.364-6.364l-4.242 4.242m-4.242 4.242l-4.242 4.242m12.728 0l-4.242-4.242m-4.242-4.242l-4.242-4.242"/></svg>
</button>
<button id="lockButton" class="p-1.5 rounded-full hover:bg-white/10 transition">
<svg id="lockIcon" class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><circle cx="12" cy="16" r="1"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
<svg id="unlockIcon" class="w-5 h-5 hidden" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><circle cx="12" cy="16" r="1"/><path d="M7 11V7a5 5 0 019.9-1"/></svg>
</button>
</div>
</header>
<!-- Message list -->
<main id="chatWindow" class="flex-1 flex flex-col gap-3 p-3 overflow-y-auto text-sm">
<!-- messages appear here -->
</main>
<!-- Input row -->
<footer class="border-t border-white/20 p-3">
<div class="flex items-center gap-2">
<button id="micBtn" class="p-2 rounded-full hover:bg-white/10 transition shrink-0">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/><path d="M19 10v2a7 7 0 01-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
</button>
<button id="attachBtn" class="p-2 rounded-full hover:bg-white/10 transition shrink-0">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>
</button>
<div class="flex-1 relative">
<input id="chatInput" type="text" placeholder="Type your message…" class="w-full bg-transparent border border-white/20 rounded-xl px-3 py-2 outline-none focus:border-white/40 transition"/>
</div>
<button id="sendBtn" class="p-2 rounded-full hover:bg-white/10 transition shrink-0">
<svg class="w-5 h-5 rotate-90" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>
</button>
</div>
</footer>
<script>
const chatInput = document.getElementById('chatInput');
const sendBtn = document.getElementById('sendBtn');
const chatWindow = document.getElementById('chatWindow');
sendBtn.addEventListener('click', () => {
const text = chatInput.value.trim();
if (!text) return;
const msgDiv = document.createElement('div');
msgDiv.className = 'self-end bg-sky-600/80 rounded-lg px-3 py-2 max-w-xs break-words';
msgDiv.textContent = text;
chatWindow.appendChild(msgDiv);
chatInput.value = '';
chatWindow.scrollTop = chatWindow.scrollHeight;
});
chatInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendBtn.click();
}
});
</script>
<!-- History panel -->
<div id="historyPanel" class="hidden absolute top-12 left-3 w-64 max-h-60 border border-white/20 rounded-lg shadow-2xl z-20 overflow-y-auto text-sm">
<ul class="divide-y divide-white/10">
<li class="px-3 py-2 hover:bg-white/5 cursor-pointer">Chat 1 – Yesterday</li>
<li class="px-3 py-2 hover:bg-white/5 cursor-pointer">Chat 2 – 3 days ago</li>
</ul>
</div>
<!-- Resize handles -->
<!-- Corners -->
<div class="absolute top-0 left-0 w-3 h-3 cursor-nw-resize" data-corner="nw"></div>
<div class="absolute top-0 right-0 w-3 h-3 cursor-ne-resize" data-corner="ne"></div>
<div class="absolute bottom-0 right-0 w-3 h-3 cursor-se-resize" data-corner="se"></div>
<div class="absolute bottom-0 left-0 w-3 h-3 cursor-sw-resize" data-corner="sw"></div>
<!-- Edges -->
<div class="absolute top-0 left-3 right-3 h-2 cursor-n-resize" data-edge="n"></div>
<div class="absolute right-0 top-3 bottom-3 w-2 cursor-e-resize" data-edge="e"></div>
<div class="absolute bottom-0 left-3 right-3 h-2 cursor-s-resize" data-edge="s"></div>
<div class="absolute left-0 top-3 bottom-3 w-2 cursor-w-resize" data-edge="w"></div>
</div>
<!-- API Key Modal -->
<div id="apiModal" class="fixed inset-0 bg-black/50 z-30 flex items-center justify-center hidden">
<div class="bg-slate-800 rounded-lg p-6 w-80 text-white">
<h2 class="text-base font-semibold mb-2">Enter API Key</h2>
<p id="apiModelName" class="text-sm text-slate-300 mb-3"></p>
<input id="apiKeyInput" type="password" placeholder="Paste your API key here…"
class="w-full bg-slate-700 border border-slate-600 rounded px-3 py-2 mb-3 text-sm outline-none focus:border-sky-500">
<div class="flex justify-end gap-2">
<button id="apiCancel" class="px-3 py-1 text-sm rounded bg-slate-600 hover:bg-slate-500">Cancel</button>
<button id="apiSave" class="px-3 py-1 text-sm rounded bg-sky-600 hover:bg-sky-500">Save</button>
</div>
<p id="apiSavedMsg" class="hidden text-xs text-green-400 mt-2">✓ API saved successfully</p>
</div>
</div>
<!-- Tiny Settings Panel -->
<div id="settingsPanel">
<label>
Opacity
<input type="range" id="opacitySlider" min="0" max="1" step="0.05" value="1">
</label>
<label>
Shadow Blur
<input type="range" id="shadowSlider" min="0" max="50" step="1" value="0">
</label>
<label>
Corner Curve
<input type="range" id="cornerSlider" min="0" max="50" step="1" value="24">
</label>
<label>
Colour Gradient
<div id="colourPalette">
<button class="colourPellet" style="background:#ff0055"></button>
<button class="colourPellet" style="background:#ff6b35"></button>
<button class="colourPellet" style="background:#ffcf40"></button>
<button class="colourPellet" style="background:#44d62c"></button>
<button class="colourPellet" style="background:#00bfff"></button>
<button class="colourPellet" style="background:#0047ab"></button>
<button class="colourPellet" style="background:#9d00ff"></button>
<button class="colourPellet" style="background:#ff00ff"></button>
<button class="colourPellet" style="background:#fd79a8"></button>
<button class="colourPellet" style="background:#a29bfe"></button>
</div>
</label>
</div>
<script>
/* ---------- DRAGGABLE & RESIZABLE CARD ---------- */
const card = document.getElementById('draggableCard');
const resizeHandles = document.querySelectorAll('[data-corner], [data-edge]');
// Dragging variables
let isDragging = false;
let isResizing = false;
let startMouseX, startMouseY, startLeft, startTop;
// Resizing variables
let startWidth, startHeight, startX, startY, resizeDirection;
const lockButton = document.getElementById('lockButton');
const lockIcon = document.getElementById('lockIcon');
const unlockIcon = document.getElementById('unlockIcon');
let isLocked = false;
card.addEventListener('mousedown', dragStart);
resizeHandles.forEach(h => h.addEventListener('mousedown', resizeStart));
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
lockButton.addEventListener('click', () => {
isLocked = !isLocked;
const handles = document.querySelectorAll('[data-corner], [data-edge]');
if (isLocked) {
lockIcon.classList.add('hidden');
unlockIcon.classList.remove('hidden');
card.style.cursor = 'default';
handles.forEach(h => h.style.display = 'none');
} else {
lockIcon.classList.remove('hidden');
unlockIcon.classList.add('hidden');
card.style.cursor = 'grab';
handles.forEach(h => h.style.display = 'block');
}
});
function dragStart(e) {
if (isLocked || e.target.tagName === 'BUTTON') return;
const rect = card.getBoundingClientRect();
// Remove centering transform once at start
if (card.style.transform.includes('translate')) {
card.style.transform = 'none';
card.style.left = `${rect.left}px`;
card.style.top = `${rect.top}px`;
}
startMouseX = e.clientX;
startMouseY = e.clientY;
startLeft = parseFloat(card.style.left) || rect.left;
startTop = parseFloat(card.style.top) || rect.top;
isDragging = true;
}
function resizeStart(e) {
if (isLocked) return;
e.stopPropagation();
const rect = card.getBoundingClientRect();
// Remove centering transform once at start
if (card.style.transform.includes('translate')) {
card.style.transform = 'none';
card.style.left = `${rect.left}px`;
card.style.top = `${rect.top}px`;
}
startX = e.clientX;
startY = e.clientY;
startLeft = parseFloat(card.style.left) || rect.left;
startTop = parseFloat(card.style.top) || rect.top;
startWidth = parseInt(getComputedStyle(card).width, 10);
startHeight = parseInt(getComputedStyle(card).height, 10);
resizeDirection = e.target.dataset.corner || e.target.dataset.edge;
isResizing = true;
}
function dragMove(e) {
if (!isLocked && isDragging) {
e.preventDefault();
const dx = e.clientX - startMouseX;
const dy = e.clientY - startMouseY;
card.style.left = `${startLeft + dx}px`;
card.style.top = `${startTop + dy}px`;
} else if (!isLocked && isResizing) {
e.preventDefault();
// Remove transform-centering on first resize
if (card.style.transform.includes('translate')) {
const rect = card.getBoundingClientRect();
card.style.transform = 'none';
card.style.left = `${rect.left}px`;
card.style.top = `${rect.top}px`;
}
let newWidth = startWidth;
let newHeight = startHeight;
let newLeft = startLeft;
let newTop = startTop;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
if (resizeDirection.includes('e')) newWidth = startWidth + dx;
if (resizeDirection.includes('w')) {
newWidth = startWidth - dx;
newLeft = startLeft + dx;
}
if (resizeDirection.includes('s')) newHeight = startHeight + dy;
if (resizeDirection.includes('n')) {
newHeight = startHeight - dy;
newTop = startTop + dy;
}
newWidth = Math.max(300, newWidth);
newHeight = Math.max(250, newHeight);
card.style.width = `${newWidth}px`;
card.style.height = `${newHeight}px`;
card.style.left = `${newLeft}px`;
card.style.top = `${newTop}px`;
}
}
function dragEnd() {
isDragging = false;
isResizing = false;
}
/* ---------- TINY SETTINGS ---------- */
const settingsButton = document.getElementById('settingsButton');
const settingsPanel = document.getElementById('settingsPanel');
settingsButton.addEventListener('click', () => {
settingsPanel.style.display = settingsPanel.style.display === 'flex' ? 'none' : 'flex';
});
/* ---------- API-KEY MODAL LOGIC ---------- */
const modelSelect = document.getElementById('modelSelect');
const apiModal = document.getElementById('apiModal');
const apiModelName= document.getElementById('apiModelName');
const apiKeyInput = document.getElementById('apiKeyInput');
const apiSaveBtn = document.getElementById('apiSave');
const apiCancelBtn= document.getElementById('apiCancel');
const apiSavedMsg = document.getElementById('apiSavedMsg');
modelSelect.addEventListener('change', () => {
apiModelName.textContent = `Model: ${modelSelect.value}`;
apiKeyInput.value = '';
apiSavedMsg.classList.add('hidden');
apiModal.classList.remove('hidden');
});
apiSaveBtn.addEventListener('click', () => {
localStorage.setItem(`api-${modelSelect.value}`, apiKeyInput.value.trim());
apiSavedMsg.classList.remove('hidden');
setTimeout(() => apiModal.classList.add('hidden'), 800);
});
apiCancelBtn.addEventListener('click', () => apiModal.classList.add('hidden'));
// Persist current size & position as the default
const cardRect = card.getBoundingClientRect();
document.documentElement.style.setProperty('--default-left', `${cardRect.left}px`);
document.documentElement.style.setProperty('--default-top', `${cardRect.top}px`);
document.documentElement.style.setProperty('--default-width', `${cardRect.width}px`);
document.documentElement.style.setProperty('--default-height', `${cardRect.height}px`);
card.style.left = `${cardRect.left}px`;
card.style.top = `${cardRect.top}px`;
card.style.transform = 'none';
/* ---------- CONFIG ---------- */
const CONFIG = {
particleCount: 120,
particleRadius: () => Math.random() * 2.5 + 1, // 1–3.5
speed: () => Math.random() * 0.4 + 0.1, // 0.1–0.5
hueShiftSpeed: 0.8,
};
/* ---------- CANVAS ---------- */
const canvas = document.getElementById('particleCanvas');
const ctx = canvas.getContext('2d');
let width, height, particles = [];
let hue = 200; // Start on a blue tone instead of yellow
/* ---------- RESIZE HANDLER ---------- */
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
if (particles.length === 0) initParticles();
}
/* ---------- PARTICLE ---------- */
class Particle {
constructor() {
this.reset();
this.hue = Math.random() * 360;
this.saturation = 75 + Math.random() * 25;
this.lightness = 50 + Math.random() * 30;
this.shiny = Math.random() > 0.7;
this.blurred = Math.random() > 0.8;
}
reset() {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.radius = CONFIG.particleRadius();
this.speed = CONFIG.speed();
this.angle = Math.random() * Math.PI * 2;
}
update() {
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
// Wrap around edges
if (this.x < -this.radius) this.x = width + this.radius;
if (this.x > width + this.radius) this.x = -this.radius;
if (this.y < -this.radius) this.y = height + this.radius;
if (this.y > height + this.radius) this.y = -this.radius;
}
draw() {
ctx.save();
if (this.blurred) {
ctx.filter = 'blur(2px)';
}
if (this.shiny) {
ctx.shadowBlur = 10;
ctx.shadowColor = `hsl(${this.hue}, ${this.saturation}%, ${this.lightness + 20}%)`;
}
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`;
ctx.fill();
ctx.restore();
}
}
/* ---------- INITIALIZE PARTICLES ---------- */
function initParticles() {
particles = [];
for (let i = 0; i < CONFIG.particleCount; i++) {
particles.push(new Particle());
}
}
/* ---------- ANIMATION LOOP ---------- */
function animate() {
ctx.clearRect(0, 0, width, height);
particles.forEach(p => {
p.update();
p.draw();
});
requestAnimationFrame(animate);
}
/* ---------- EVENT LISTENERS ---------- */
window.addEventListener('resize', resize);
/* ---------- START ---------- */
resize();
animate();
/* ---------- Settings Controls ---------- */
const opacitySlider = document.getElementById('opacitySlider');
const shadowSlider = document.getElementById('shadowSlider');
const cornerSlider = document.getElementById('cornerSlider');
const colourPalette = document.getElementById('colourPalette');
opacitySlider.addEventListener('input', () => {
card.style.opacity = opacitySlider.value;
});
shadowSlider.addEventListener('input', () => {
card.style.boxShadow = `0 0 ${shadowSlider.value}px rgba(255,255,255,0.3)`;
});
cornerSlider.addEventListener('input', () => {
card.style.borderRadius = cornerSlider.value + 'px';
});
colourPalette.addEventListener('click', (e) => {
if (e.target.classList.contains('colourPellet')) {
const colour = e.target.style.background;
card.style.background = `linear-gradient(135deg, ${colour}, rgba(0,0,0,0.8))`;
}
});
/* ---------- ORB DRAG ---------- */
const orb = document.getElementById('orb');
let orbDragging = false;
let orbStartX, orbStartY, orbStartLeft, orbStartTop;
orb.addEventListener('mousedown', (e) => {
orbDragging = true;
orbStartX = e.clientX;
orbStartY = e.clientY;
const rect = orb.getBoundingClientRect();
// remove centering
if (orb.style.transform.includes('translate')) {
orb.style.transform = 'none';
orb.style.left = `${rect.left}px`;
orb.style.top = `${rect.top}px`;
}
orbStartLeft = parseFloat(orb.style.left) || rect.left;
orbStartTop = parseFloat(orb.style.top) || rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!orbDragging) return;
const dx = e.clientX - orbStartX;
const dy = e.clientY - orbStartY;
orb.style.left = `${orbStartLeft + dx}px`;
orb.style.top = `${orbStartTop + dy}px`;
});
document.addEventListener('mouseup', () => {
orbDragging = false;
});
</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=basheer1414/aiui" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>