Modulrss / index.html
Wavetype's picture
Update index.html
53fd259 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wavetype Frequency Grid - Auto-Auth Edition</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.1/font/bootstrap-icons.min.css" rel="stylesheet">
<style>
:root {
--border-html: #ff0000;
--border-python: #0000ff;
--border-image: #00ff00;
--border-video: #800080;
--border-ai: #ff00ff;
--border-url: #00ffff;
--bg-shade: 0.1;
}
body, html {
margin: 0; padding: 0; width: 100%; height: 100vh;
overflow: hidden; background-color: #000;
font-family: "Courier New", Courier, monospace; color: white;
}
/* Sidebar */
#sidebar {
position: fixed; left: 0; top: 0; width: 320px; height: 100vh;
background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%);
border-right: 2px solid #333; z-index: 2000;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.5); overflow-y: auto;
}
#sidebar.collapsed { transform: translateX(-320px); }
#sidebar-toggle {
position: fixed; left: 320px; top: 20px; width: 40px; height: 40px;
background: #1a1a1a; border: 2px solid #333; border-left: none;
border-radius: 0 8px 8px 0; cursor: pointer; z-index: 2001;
display: flex; align-items: center; justify-content: center; color: white;
transition: all 0.3s;
}
#sidebar.collapsed ~ #sidebar-toggle { left: 0; }
.auth-section { padding: 15px; border-bottom: 1px solid #333; background: #111; }
.token-badge { font-size: 10px; padding: 2px 6px; border-radius: 4px; background: #28a745; color: white; display: none; }
#grid-container {
display: grid; width: 100vw; height: 100vh; gap: 4px; padding: 4px;
padding-left: 324px; transition: padding-left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
#sidebar.collapsed ~ #grid-container { padding-left: 4px; }
.block {
position: relative; background-color: rgba(255, 255, 255, var(--bg-shade));
border: 2px solid #333; display: flex; flex-direction: column;
align-items: center; justify-content: center; cursor: pointer;
overflow: hidden; transition: all 0.5s;
}
.type-ai { border-color: var(--border-ai); }
.block.active {
position: fixed; top: 0; left: 0; width: 100vw !important;
height: 100vh !important; z-index: 2500; background: #000; padding: 40px;
cursor: default;
}
.close-btn {
display: none; position: absolute; top: 20px; right: 20px;
background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px;
}
.block.active .close-btn { display: block; }
.ai-interface { display: none; width: 100%; max-width: 800px; flex-direction: column; gap: 15px; }
.block.active .ai-interface { display: flex; }
.gen-image-preview {
width: 100%; max-height: 50vh; object-fit: contain;
border: 2px solid var(--border-ai); display: none; background: #111;
}
.add-block-btn {
position: absolute; bottom: 8px; right: 8px; width: 28px; height: 28px;
background: #00ff00; border-radius: 50%; color: #000; border: none; font-weight: bold;
}
</style>
</head>
<body>
<div id="sidebar">
<div class="auth-section">
<div class="d-flex justify-content-between align-items-center">
<span class="small text-muted">HF AUTH STATUS</span>
<span id="tokenStatus" class="token-badge">AUTO-LOADED</span>
</div>
<div id="login-section" class="mt-2">
<button class="btn btn-outline-primary btn-sm w-100" onclick="handleLogin('Cloud')">
<i class="bi bi-cloud-arrow-up"></i> Sync VPS Profile
</button>
</div>
<div id="user-profile" style="display:none" class="mt-2 text-success small fw-bold">
<i class="bi bi-check-circle-fill"></i> Connected to Flux Node
</div>
</div>
<div class="p-3">
<div class="mb-4">
<label class="form-label small">GRID NODES</label>
<input type="range" class="form-range" id="blockSlider" min="1" max="25" value="4" oninput="updateGrid()">
<div class="d-flex justify-content-between small text-muted">
<span id="countVal">4</span> Instances
<span id="saveStatus">Synced</span>
</div>
</div>
<div class="mb-4">
<label class="form-label small">HF ACCESS TOKEN</label>
<input type="password" class="form-control form-control-sm bg-dark text-white border-secondary" id="hfToken" placeholder="hf_..." onchange="saveToCache()">
<div class="form-text text-muted" style="font-size: 9px;">Leave empty if using URL ?token=...</div>
</div>
<button class="btn btn-success btn-sm w-100 mb-2" onclick="saveToCache()">PERSIST CONFIG</button>
<button class="btn btn-outline-danger btn-sm w-100" onclick="clearGrid()">PURGE CACHE</button>
</div>
</div>
<button id="sidebar-toggle" onclick="toggleSidebar()"><i class="bi bi-chevron-left"></i></button>
<div id="grid-container"></div>
<script>
const container = document.getElementById("grid-container");
const countDisplay = document.getElementById("countVal");
let blocks = [];
let currentUser = null;
/**
* AUTO-TOKEN DETECTION LOGIC
* 1. Check URL parameters (?token=hf_...)
* 2. Check localStorage
* 3. (Optional) Define a hardcoded fallback for private VPS use
*/
function detectToken() {
const urlParams = new URLSearchParams(window.location.search);
const urlToken = urlParams.get('token');
const hfInput = document.getElementById("hfToken");
if (urlToken) {
hfInput.value = urlToken;
document.getElementById("tokenStatus").style.display = "inline";
console.log("Token auto-loaded from URL.");
// Clean URL to prevent token leaking in screenshots
window.history.replaceState({}, document.title, window.location.pathname);
} else {
const cache = localStorage.getItem("wavetype_vps_cache");
if (cache) {
const data = JSON.parse(cache);
if (data.token) {
hfInput.value = data.token;
document.getElementById("tokenStatus").style.display = "inline";
}
}
}
}
function saveToCache() {
const data = {
blocks,
token: document.getElementById("hfToken").value,
user: currentUser
};
localStorage.setItem("wavetype_vps_cache", JSON.stringify(data));
showStatus("Saved");
if(data.token) document.getElementById("tokenStatus").style.display = "inline";
}
function loadFromCache() {
const cache = localStorage.getItem("wavetype_vps_cache");
if (cache) {
const data = JSON.parse(cache);
blocks = data.blocks || [];
document.getElementById("blockSlider").value = blocks.length || 4;
if (data.user) handleLogin('Cloud');
renderGrid();
} else {
updateGrid();
}
detectToken();
}
function updateGrid() {
const count = parseInt(document.getElementById("blockSlider").value);
countDisplay.innerText = count;
while (blocks.length < count) {
blocks.push({ id: Date.now() + Math.random(), type: 'ai', prompt: "" });
}
while (blocks.length > count) blocks.pop();
renderGrid();
saveToCache();
}
function renderGrid() {
const cols = Math.ceil(Math.sqrt(blocks.length)) || 1;
container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
container.innerHTML = "";
blocks.forEach((b, i) => {
const el = document.createElement("div");
el.className = `block type-${b.type}`;
el.onclick = () => { if(!el.classList.contains('active')) el.classList.add('active'); };
el.innerHTML = `
<div class="ai-interface">
<h4 style="color:var(--border-ai)">FLUX.1 [KLEIN-9B]</h4>
<textarea class="form-control bg-dark text-white border-secondary mb-2" id="prompt-${i}" rows="3" placeholder="Enter prompt...">${b.prompt || ""}</textarea>
<button class="btn btn-primary w-100" onclick="generateFlux(${i}, this)">EXECUTE VPS INFERENCE</button>
<div id="loader-${i}" class="text-center mt-2" style="display:none"><div class="spinner-grow spinner-grow-sm text-info"></div> GEN IN PROGRESS...</div>
<img id="img-${i}" class="gen-image-preview mt-3">
</div>
<div class="small fw-bold opacity-50">NODE_0${i+1}</div>
<button class="close-btn" onclick="event.stopPropagation(); this.parentElement.classList.remove('active')">DISCONNECT</button>
<button class="add-block-btn" onclick="event.stopPropagation(); updateGrid()">+</button>
`;
container.appendChild(el);
});
}
async function generateFlux(index, btn) {
event.stopPropagation();
const prompt = document.getElementById(`prompt-${index}`).value;
const token = document.getElementById("hfToken").value;
const imgEl = document.getElementById(`img-${index}`);
const loader = document.getElementById(`loader-${index}`);
if (!token) {
alert("Auth Error: HuggingFace Token Missing.");
return;
}
loader.style.display = "block";
imgEl.style.display = "none";
blocks[index].prompt = prompt;
saveToCache();
try {
const response = await fetch(
"https://api-inference.huggingface.co/models/Comfy-Org/flux2-klein-9B",
{
headers: { Authorization: `Bearer ${token}` },
method: "POST",
body: JSON.stringify({ inputs: prompt }),
}
);
const blob = await response.blob();
imgEl.src = URL.createObjectURL(blob);
imgEl.style.display = "block";
} catch (err) {
console.error(err);
} finally {
loader.style.display = "none";
}
}
function handleLogin(type) {
currentUser = { name: "VPS_ADMIN" };
document.getElementById("login-section").style.display = "none";
document.getElementById("user-profile").style.display = "block";
}
function showStatus(msg) {
document.getElementById("saveStatus").innerText = msg;
setTimeout(() => document.getElementById("saveStatus").innerText = "Synced", 2000);
}
function toggleSidebar() { document.getElementById("sidebar").classList.toggle("collapsed"); }
function clearGrid() { localStorage.clear(); location.reload(); }
window.onload = loadFromCache;
</script>
</body>
</html>