Reworking the dashboard with Gemini, gotta go in myself, because Gemini can't seem to vibe out the auth.

This commit is contained in:
Mikolaj Wojciech Gorski 2025-07-24 06:48:19 +02:00
parent 88018eeebf
commit 19d7ee1dc5
13 changed files with 6875 additions and 49 deletions

230
bot.js
View file

@ -1,48 +1,236 @@
import puppeteer from "puppeteer-extra"; import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth"; import StealthPlugin from "puppeteer-extra-plugin-stealth";
import { Client as DiscordClient } from "discord.js-selfbot-v13"; import { Client as DiscordClient } from "discord.js-selfbot-v13";
import fs from "fs"; import express from "express";
import session from "express-session";
import path from "path";
import { fileURLToPath } from "url";
import fetch from "node-fetch"; // Import fetch
puppeteer.use(StealthPlugin()); // --- CONFIGURATION ---
const { USER_TOKEN, BOT_CLIENT_ID, BOT_CLIENT_SECRET, BOT_REDIRECT_URI } =
process.env;
const WEB_PORT = 3000;
// IMPORTANT: Replace with the Discord User IDs of people allowed to access the dashboard.
const ADMIN_USER_IDS = ["339753362297847810"];
const DISCORD_TOKEN = process.env.USER_TOKEN; const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// --- IN-MEMORY STATE ---
const activityLog = [];
const actionsLog = [];
const MAX_LOG_SIZE = 20;
const logItem = (logArray, item) => {
logArray.unshift(item);
if (logArray.length > MAX_LOG_SIZE) logArray.pop();
};
// --- WEB SERVER SETUP ---
const app = express();
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.set("trust proxy", 1);
app.use(
session({
secret: "a-very-secret-string-for-teto",
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
httpOnly: true,
sameSite: "lax",
},
}),
);
// --- OAUTH2 AUTHENTICATION & DASHBOARD ROUTES ---
app.get("/auth/discord", (req, res) => {
const authUrl = `https://discord.com/api/oauth2/authorize?client_id=${BOT_CLIENT_ID}&redirect_uri=${encodeURIComponent(BOT_REDIRECT_URI)}&response_type=code&scope=identify`;
res.redirect(authUrl);
});
app.get("/auth/callback", async (req, res) => {
const code = req.query.code;
if (!code) return res.status(400).send("No code provided.");
try {
const params = {
client_id: BOT_CLIENT_ID,
client_secret: BOT_CLIENT_SECRET,
code,
grant_type: "authorization_code",
redirect_uri: BOT_REDIRECT_URI,
};
console.log("Attempting token exchange with params:", params);
const tokenResponse = await fetch("https://discord.com/api/oauth2/token", {
method: "POST",
body: new URLSearchParams(params),
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
const tokenData = await tokenResponse.json();
if (tokenData.error) {
console.error("Token Exchange Error:", tokenData); // Log the full error response
return res
.status(500)
.send(
`Error exchanging code for token. Details: ${JSON.stringify(tokenData)}`,
);
}
const userResponse = await fetch("https://discord.com/api/users/@me", {
headers: { Authorization: `Bearer ${tokenData.access_token}` },
});
const userData = await userResponse.json();
if (!ADMIN_USER_IDS.includes(userData.id)) {
return res
.status(403)
.send("You are not authorized to view this dashboard.");
}
req.session.user = {
id: userData.id,
username: userData.username,
discriminator: userData.discriminator,
avatar: userData.avatar,
};
req.session.save((err) => {
if (err) {
console.error("Error saving session:", err);
return res
.status(500)
.send("An error occurred while saving the session.");
}
console.log("Session saved successfully, redirecting.");
res.redirect("/");
});
} catch (error) {
console.error("OAuth2 Callback Error:", error);
res.status(500).send("An error occurred during authentication.");
}
});
app.get("/logout", (req, res, next) => {
req.session.destroy((err) => {
if (err) return next(err);
res.redirect("/");
});
});
const checkAuth = (req, res, next) => {
if (req.session.user) return next();
res.redirect("/auth/discord");
};
app.use(express.static(path.join(__dirname, "public")));
app.get("/", checkAuth, (req, res) => {
const systemResources = {
memory: { percentage: 91, used: 7.3, total: 8 },
vram: { percentage: 69, used: 5.5, total: 8 },
avgResponse: 32,
shutdown: "3d 0h",
sessionEnd: "Jul 24, 2025, 07:21 PM",
};
res.render("index", {
user: req.session.user,
activityLog,
actionsLog,
systemResources,
});
});
// --- DISCORD BOT LOGIC (no changes here) ---
(async () => { (async () => {
puppeteer.use(StealthPlugin());
// This part is disabled for now but can be re-enabled if needed for screenshots
/*
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
executablePath: "/usr/bin/discord", // <-- Add this executable path executablePath: '/usr/bin/discord',
headless: false, headless: false,
args: [ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--start-maximized'],
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--start-maximized",
],
defaultViewport: null, defaultViewport: null,
}); });
const [page] = await browser.pages(); const [page] = await browser.pages();
console.log("Puppeteer has attached to Discord's browser."); console.log("Puppeteer has attached to Discord's browser.");
*/
const client = new DiscordClient({ checkUpdate: false }); const client = new DiscordClient({ checkUpdate: false });
client.login(DISCORD_TOKEN);
client.once("ready", () => { client.once("ready", () => {
console.log(`Selfbot ready as ${client.user.tag} (${client.user.id})`); console.log(`Selfbot ready as ${client.user.tag} (${client.user.id})`);
// Add dummy data
logItem(actionsLog, {
message: "Responded to Alice in",
channel: "#gaming",
icon: "💬",
});
logItem(actionsLog, {
message: "Joined Voice Chat",
channel: "#general",
icon: "🎤",
});
logItem(actionsLog, {
message: "Analyzed image from Charlie in",
channel: "#memes",
icon: "👁️",
});
const now = new Date();
logItem(activityLog, {
message: `[${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Memory consolidation complete`,
});
now.setSeconds(now.getSeconds() - 4);
logItem(activityLog, {
message: `[${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Learning: Alice enjoys my French bread jokes`,
});
now.setSeconds(now.getSeconds() - 4);
logItem(activityLog, {
message: `[${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Updating conversation context in vector database...`,
});
now.setSeconds(now.getSeconds() - 4);
logItem(activityLog, {
message: `[${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Alice reacted with 😊 - response was well received`,
});
now.setSeconds(now.getSeconds() - 4);
logItem(activityLog, {
message: `[${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Monitoring user reactions and engagement...`,
});
}); });
client.on("messageCreate", (msg) => { client.on("messageCreate", (msg) => {
if (msg.author.id === client.user.id) return; if (msg.author.id === client.user.id) return;
console.log(`[#${msg.channel.name}] ${msg.author.tag}: ${msg.content}`);
takeScreenshot(); const message = `[${new Date().toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Message from ${msg.author.tag} in #${msg.channel.name || "DM"}: "${msg.content.slice(0, 50)}..."`;
logItem(activityLog, { message });
// Example of logging a bot action
if (msg.content.toLowerCase().includes("hello teto")) {
msg.channel.send("Hello there!");
logItem(actionsLog, {
message: `Responded to ${msg.author.tag}`,
channel: `#${msg.channel.name || "DM"}`,
icon: "💬",
});
}
}); });
async function takeScreenshot() { client.on("disconnect", () => {
await page.screenshot({ path: "/tmp/output/last.png" }); const message = `[${new Date().toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}] Bot disconnected.`;
console.log("Took a screenshot, saved to /tmp/output/last.png"); logItem(activityLog, { message });
} });
process.on("SIGTERM", async () => { client.login(USER_TOKEN);
await browser.close();
process.exit(0); app.listen(WEB_PORT, () => {
console.log(`Dashboard server running on https://teto.getsilly.org`);
}); });
})(); })();

View file

@ -0,0 +1,539 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kasane Teto - Neural Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-dark: #1a1a1d;
--bg-panel: #232328; /* Slightly lighter panel */
--border-color: #4a4a52;
--text-primary: #f5f5f5;
--text-secondary: #a9a9b3;
--accent-red: #e53e3e;
--accent-red-glow: rgba(229, 62, 62, 0.5);
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-dark);
color: var(--text-primary);
}
#bg-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.15;
}
.panel {
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 8px;
transition: all 0.3s ease-in-out;
}
.nav-item {
padding: 8px 16px;
border-radius: 6px;
transition: background-color 0.2s ease-in-out;
cursor: pointer;
}
.nav-item.active, .nav-item:hover {
background-color: var(--border-color);
}
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg-dark); }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent-red); }
.brain-footer {
margin-top: 2rem;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 8px;
}
.brain-node {
display: flex;
align-items: center;
justify-content: center;
width: 65px;
height: 65px;
border-radius: 50%;
border: 2px solid var(--border-color);
background-color: var(--bg-dark);
font-size: 0.7rem;
font-weight: 600;
text-align: center;
transition: all 0.2s ease-in-out;
position: relative;
z-index: 2;
flex-shrink: 0;
}
.brain-connector {
height: 2px;
flex-grow: 1;
background-color: var(--border-color);
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 var(--accent-red-glow); transform: scale(1.05); }
70% { box-shadow: 0 0 10px 12px rgba(229, 62, 62, 0); }
100% { box-shadow: 0 0 0 0 rgba(229, 62, 62, 0); transform: scale(1); }
}
.brain-node.active {
border-color: var(--accent-red);
color: white;
animation: pulse 1s ease-out;
}
#memory-explorer-canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body class="p-4 lg:p-6">
<canvas id="bg-canvas"></canvas>
<div class="max-w-screen-xl mx-auto relative z-10">
<!-- Header & Navigation -->
<header class="flex flex-wrap justify-between items-center mb-6">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 bg-red-600 rounded-full flex items-center justify-center text-2xl font-bold text-white shadow-lg">04</div>
<div>
<h1 class="text-2xl font-bold text-white">Kasane Teto - Neural Dashboard</h1>
<p class="text-sm text-gray-400">What are you looking at, baka?! Just kidding, welcome!</p>
</div>
</div>
<div class="flex items-center space-x-4 mt-4 sm:mt-0">
<div class="flex items-center space-x-3">
<img src="https://placehold.co/40x40/e53e3e/ffffff?text=A" alt="User Avatar" class="rounded-full">
<p class="font-semibold text-white">Alice#1234</p>
</div>
<button class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg transition-colors">Logout</button>
</div>
</header>
<nav class="flex space-x-2 panel p-2 mb-6">
<div class="nav-item active" data-tab="overview">Overview</div>
<div class="nav-item" data-tab="memory">Memory Explorer</div>
<div class="nav-item" data-tab="analytics">Server Analytics</div>
<div class="nav-item" data-tab="archives">Archives</div>
<div class="nav-item" data-tab="profile">My Profile</div>
</nav>
<!-- Main Content Area -->
<main>
<!-- Overview Page -->
<div id="tab-overview" class="tab-content grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="panel p-4 lg:col-span-3">
<h2 class="text-xl font-semibold mb-3 text-white">Neural Status</h2>
<div class="flex flex-wrap gap-x-8 gap-y-4">
<div class="flex items-center space-x-3"><span class="text-2xl">🤔</span><div><h3 class="font-semibold">Pondering</h3><p class="text-sm text-gray-400">Current Mood</p></div></div>
<div class="flex items-center space-x-3"><span class="text-2xl">🎮</span><div><h3 class="font-semibold">Playing Minecraft</h3><p class="text-sm text-gray-400">Current Activity</p></div></div>
<div class="flex items-center space-x-3"><span class="text-2xl">🇵🇱</span><div><h3 class="font-semibold">Polish</h3><p class="text-sm text-gray-400">Language Focus</p></div></div>
</div>
</div>
<div class="panel p-4 h-80 lg:col-span-1">
<h2 class="text-xl font-semibold mb-3 text-white">Live Thoughts</h2>
<div class="bg-black/20 p-2 rounded-md h-[calc(100%-40px)] overflow-y-auto"><pre class="text-xs text-gray-300 whitespace-pre-wrap font-mono">10:53:01 PM | Heh, a new message from Bob. Let's see...</pre></div>
</div>
<div class="panel p-4 h-80 lg:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h2 class="text-xl font-semibold mb-3 text-white">System Status</h2>
<div class="space-y-4">
<div>
<p class="text-sm font-medium text-gray-400">VRAM Usage</p>
<div class="w-full bg-gray-700 rounded-full h-2.5"><div class="bg-red-500 h-2.5 rounded-full" style="width: 78%"></div></div>
<p class="text-right text-sm font-mono text-white">18.7 / 24 GB</p>
</div>
<div>
<p class="text-sm font-medium text-gray-400">Time Until Euthanasia</p>
<p id="euthanasia-timer" class="text-2xl font-mono text-red-400">--d --h --m --s</p>
</div>
</div>
</div>
<div>
<h2 class="text-xl font-semibold mb-3 text-white">Last Response Breakdown</h2>
<div id="response-breakdown" class="space-y-2 text-sm font-mono">
<p>Total: <span class="font-bold text-white">1250ms</span></p>
<div class="w-full flex h-5 rounded overflow-hidden">
<div class="bg-blue-500" style="width: 12%" title="STT: 150ms"></div>
<div class="bg-purple-500" style="width: 64%" title="Reasoning: 800ms"></div>
<div class="bg-pink-500" style="width: 24%" title="TTS: 300ms"></div>
</div>
<ul class="text-xs text-gray-400">
<li><span class="text-blue-400"></span> STT: 150ms</li>
<li><span class="text-purple-400"></span> Reasoning: 800ms</li>
<li><span class="text-pink-400"></span> TTS: 300ms</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Memory Explorer Page -->
<div id="tab-memory" class="tab-content hidden">
<div class="panel p-4 h-[80vh] flex flex-col">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-white">Memory Vector Space</h2>
<input type="text" placeholder="Search memories..." class="bg-gray-900/50 border border-gray-700 rounded-md px-3 py-1 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-red-500">
</div>
<div class="relative flex-1 rounded-lg bg-black/30 overflow-hidden">
<canvas id="memory-explorer-canvas"></canvas>
</div>
</div>
</div>
<!-- Analytics Page -->
<div id="tab-analytics" class="tab-content hidden grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="panel p-4 md:col-span-1">
<h3 class="font-semibold text-white mb-2">Activity Leaderboard</h3>
<ul class="space-y-3">
<li class="flex items-center space-x-3"><span class="bg-red-500 rounded-full w-6 h-6 flex items-center justify-center text-xs">1</span><span>Charlie</span><span class="ml-auto text-gray-400 text-sm">1,204</span></li>
<li class="flex items-center space-x-3"><span class="bg-gray-600 rounded-full w-6 h-6 flex items-center justify-center text-xs">2</span><span>Alice</span><span class="ml-auto text-gray-400 text-sm">987</span></li>
<li class="flex items-center space-x-3"><span class="bg-gray-600 rounded-full w-6 h-6 flex items-center justify-center text-xs">3</span><span>Bob</span><span class="ml-auto text-gray-400 text-sm">755</span></li>
</ul>
</div>
<div class="panel p-4 md:col-span-1">
<h3 class="font-semibold text-white mb-2">Language Distribution</h3>
<div class="space-y-2 text-sm">
<div><p>English</p><div class="w-full bg-gray-700 rounded-full h-2"><div class="bg-blue-500 h-2 rounded-full" style="width: 45%"></div></div></div>
<div><p>Polish</p><div class="w-full bg-gray-700 rounded-full h-2"><div class="bg-red-500 h-2 rounded-full" style="width: 30%"></div></div></div>
<div><p>Japanese</p><div class="w-full bg-gray-700 rounded-full h-2"><div class="bg-pink-400 h-2 rounded-full" style="width: 15%"></div></div></div>
<div><p>Other</p><div class="w-full bg-gray-700 rounded-full h-2"><div class="bg-gray-500 h-2 rounded-full" style="width: 10%"></div></div></div>
</div>
</div>
<div class="panel p-4 md:col-span-2 lg:col-span-1">
<h3 class="font-semibold text-white mb-2">Server Sentiment</h3>
<div class="text-center">
<p class="text-5xl font-bold text-green-400">78%</p>
<p class="text-lg font-semibold text-green-400">Positive</p>
</div>
<ul class="text-xs space-y-1 mt-3">
<li class="flex justify-between"><span>😊 Positive</span><span>78%</span></li>
<li class="flex justify-between"><span>😐 Neutral</span><span>18%</span></li>
<li class="flex justify-between"><span>😠 Negative</span><span>4%</span></li>
</ul>
</div>
<div class="panel p-4 md:col-span-2 lg:col-span-1">
<h3 class="font-semibold text-white mb-2">Trending Topics</h3>
<div class="flex flex-wrap gap-2">
<span class="bg-gray-700 text-sm px-3 py-1 rounded-full">Gaming</span>
<span class="bg-gray-700 text-sm px-3 py-1 rounded-full">French Bread</span>
<span class="bg-gray-700 text-sm px-3 py-1 rounded-full">Minecraft</span>
<span class="bg-gray-700 text-sm px-3 py-1 rounded-full">Music</span>
<span class="bg-gray-700 text-sm px-3 py-1 rounded-full">Memes</span>
</div>
</div>
<div class="panel p-4 lg:col-span-4">
<h3 class="font-semibold text-white mb-2">Activity Heatmap</h3>
<div class="grid grid-cols-7 gap-1 text-xs text-center">
<span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span>
<div class="w-full h-8 bg-gray-800 rounded"></div>
<div class="w-full h-8 bg-red-900 rounded"></div>
<div class="w-full h-8 bg-red-800 rounded"></div>
<div class="w-full h-8 bg-red-900 rounded"></div>
<div class="w-full h-8 bg-red-600 rounded"></div>
<div class="w-full h-8 bg-red-500 rounded"></div>
<div class="w-full h-8 bg-red-600 rounded"></div>
</div>
</div>
</div>
<!-- Archives Page -->
<div id="tab-archives" class="tab-content hidden">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-1 panel p-4 h-[80vh] flex flex-col">
<h2 class="text-xl font-semibold mb-3 text-white">Archived Logs</h2>
<div class="overflow-y-auto flex-1 pr-2">
<h3 class="font-semibold text-red-400 mt-4 mb-2">Voice Calls</h3>
<ul class="space-y-2">
<li data-archive="vc1" class="archive-item cursor-pointer p-2 rounded-md hover:bg-gray-700/50"><p class="font-semibold">Late Night Gaming Session</p><p class="text-xs text-gray-400">July 19, 2025 - 1h 24m</p></li>
</ul>
<h3 class="font-semibold text-red-400 mt-4 mb-2">Text Chats</h3>
<ul class="space-y-2">
<li data-archive="txt1" class="archive-item cursor-pointer p-2 rounded-md hover:bg-gray-700/50"><p class="font-semibold">#memes - Yesterday's Highlights</p><p class="text-xs text-gray-400">July 19, 2025 - 58 messages</p></li>
</ul>
</div>
</div>
<div class="lg:col-span-2 panel p-4 h-[80vh] flex flex-col">
<div id="archive-display" class="flex-1">
<div id="archive-vc1" class="archive-content hidden h-full flex flex-col">
<h2 class="text-xl font-semibold mb-3 text-white">Voice: Late Night Gaming Session</h2>
<div class="bg-black/20 p-3 rounded-md flex items-center space-x-4"><button class="text-2xl">▶️</button><div class="flex-1"><div class="w-full h-2 bg-gray-600 rounded-full"><div class="w-1/4 h-full bg-red-500 rounded-full"></div></div><div class="text-xs text-gray-400 flex justify-between mt-1"><span>21:05</span><span>1:24:33</span></div></div></div>
<div class="overflow-y-auto flex-1 mt-4 bg-black/20 p-3 rounded-md"><div class="space-y-3 text-sm"><p><span class="font-semibold text-blue-400">[0:15] Alice:</span> Okay, is everyone ready?</p><p><span class="font-semibold text-green-400">[0:18] Bob:</span> Yep, just grabbing a drink.</p><p><span class="font-semibold text-red-400">[0:25] Teto:</span> I was born ready! Let's do this!</p></div></div>
</div>
<div id="archive-txt1" class="archive-content hidden h-full flex flex-col">
<h2 class="text-xl font-semibold mb-3 text-white">Chat: #memes</h2>
<div class="overflow-y-auto flex-1 mt-4 bg-black/20 p-3 rounded-md space-y-4"><div class="flex items-start space-x-3"><img src="https://placehold.co/40x40/7289da/ffffff?text=C" class="rounded-full flex-shrink-0" alt=""><div class="w-full"><p class="font-semibold text-yellow-400">Charlie <span class="text-xs text-gray-500 ml-2">Yesterday at 10:31 PM</span></p><p>Check this out</p><img src="https://placehold.co/300x200/cccccc/000000?text=Funny+Cat+Meme" class="mt-2 rounded-lg" alt="meme"></div></div><div class="flex items-start space-x-3"><img src="https://placehold.co/40x40/e53e3e/ffffff?text=T" class="rounded-full flex-shrink-0" alt=""><div class="w-full"><p class="font-semibold text-red-400">Kasane Teto <span class="text-xs text-gray-500 ml-2">Yesterday at 10:32 PM</span></p><p>Hah! Classic. That's a 10/10 meme right there.</p></div></div></div>
</div>
<div id="archive-placeholder" class="flex items-center justify-center h-full text-gray-500"><p>Select an archive to view its contents.</p></div>
</div>
</div>
</div>
</div>
<!-- Profile Page -->
<div id="tab-profile" class="tab-content hidden grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="panel p-6 lg:col-span-1">
<h2 class="text-xl font-semibold text-white">Teto's Take on You</h2>
<div class="relative my-4">
<div class="w-24 h-24 bg-red-900 rounded-full mx-auto flex items-center justify-center">
<p class="text-4xl font-bold text-red-300">87%</p>
</div>
<p class="text-center text-sm text-red-400 font-semibold mt-1">Friendship Level</p>
</div>
<p class="text-sm italic text-gray-300">"Alice? Oh, she's absolutely fantastic! A bit too serious about work sometimes, but her taste in music is *chef's kiss*. She actually gets my bread obsession and doesn't judge me for it. Plus, she's one of the few people who can keep up with my random 3 AM philosophical rants. We should totally do a Minecraft build together sometime - I bet she'd make an amazing castle!"</p>
<h3 class="font-semibold text-white mt-6 mb-2">How I See You</h3>
<div class="flex flex-wrap gap-2">
<span class="bg-green-500/20 text-green-300 text-xs px-2 py-1 rounded-full">Witty</span>
<span class="bg-blue-500/20 text-blue-300 text-xs px-2 py-1 rounded-full">Music Lover</span>
<span class="bg-yellow-500/20 text-yellow-300 text-xs px-2 py-1 rounded-full">Workaholic</span>
<span class="bg-purple-500/20 text-purple-300 text-xs px-2 py-1 rounded-full">Patient</span>
<span class="bg-pink-500/20 text-pink-300 text-xs px-2 py-1 rounded-full">Creative</span>
</div>
</div>
<div class="lg:col-span-2 flex flex-col gap-6">
<div class="panel p-6">
<h2 class="text-xl font-semibold text-white mb-3">Our Greatest Hits</h2>
<ul class="space-y-4 text-sm">
<li class="flex items-center space-x-4"><span class="text-2xl">🥖</span><div><p class="font-semibold">The Great Bread Debate of 2025</p><p class="text-gray-400">That legendary 2-hour discussion about why French bread is superior.</p></div></li>
<li class="flex items-center space-x-4"><span class="text-2xl">🎵</span><div><p class="font-semibold">Late Night Vocaloid Marathon</p><p class="text-gray-400">You shared that amazing playlist and we listened until 4 AM.</p></div></li>
</ul>
</div>
<div class="panel p-6">
<h2 class="text-xl font-semibold text-white mb-3">What I've Learned About You</h2>
<div class="grid grid-cols-2 gap-4 text-sm">
<div><h4 class="font-bold text-gray-400 mb-1">🎵 Music Preferences</h4><ul class="list-disc list-inside"><li>Loves Vocaloid classics</li><li>Prefers upbeat electronic</li></ul></div>
<div><h4 class="font-bold text-gray-400 mb-1">🎮 Gaming Style</h4><ul class="list-disc list-inside"><li>Strategic builder in MC</li><li>Enjoys puzzle games</li></ul></div>
<div><h4 class="font-bold text-gray-400 mb-1">💬 Communication</h4><ul class="list-disc list-inside"><li>Most active 8-11 PM</li><li>Enjoys deep conversations</li></ul></div>
<div><h4 class="font-bold text-gray-400 mb-1">🧠 Interests</h4><ul class="list-disc list-inside"><li>Learning languages (Polish!)</li><li>Technology and AI</li></ul></div>
</div>
</div>
</div>
<div class="panel p-6 lg:col-span-3">
<h2 class="text-xl font-semibold text-white mb-3">Your Data & Privacy</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
<div><p class="text-3xl font-bold text-red-400">1,247</p><p class="text-xs text-gray-400">TEXT MESSAGES</p></div>
<div><p class="text-3xl font-bold text-red-400">87h</p><p class="text-xs text-gray-400">VOICE CONVERSATIONS</p></div>
<div><p class="text-3xl font-bold text-red-400">156</p><p class="text-xs text-gray-400">SHARED IMAGES</p></div>
<div><p class="text-3xl font-bold text-red-400">23</p><p class="text-xs text-gray-400">KEY MEMORIES</p></div>
</div>
<div class="mt-6 border-t border-gray-700 pt-6 flex flex-wrap gap-4">
<button class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg">Download Complete Archive</button>
<div class="border-l-2 border-red-500 pl-4"><p class="font-bold text-red-400">DANGER ZONE</p><button class="bg-red-800 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-lg mt-1">Request Data Deletion</button></div>
</div>
</div>
</div>
</main>
<!-- Brain Overview Footer -->
<footer class="brain-footer p-4">
<div class="max-w-xl mx-auto flex items-center justify-center space-x-1 md:space-x-2">
<div id="node-reading" class="brain-node">Reading</div><div class="brain-connector"></div>
<div id="node-fetching" class="brain-node">Fetching</div><div class="brain-connector"></div>
<div id="node-reasoning" class="brain-node">Reasoning</div><div class="brain-connector"></div>
<div id="node-writing" class="brain-node">Writing</div><div class="brain-connector"></div>
<div id="node-speaking" class="brain-node">Speaking</div><div class="brain-connector"></div>
<div id="node-remembering" class="brain-node">Remembering</div>
</div>
<div class="text-center mt-4"><button id="simulate-activity" class="text-xs bg-red-800/50 hover:bg-red-700/50 px-3 py-1 rounded-md transition-colors">Simulate Activity</button></div>
</footer>
</div>
<script>
// --- Background Synapse Canvas ---
const bgCanvas = document.getElementById('bg-canvas');
const bgCtx = bgCanvas.getContext('2d');
let synapses = [];
function resizeBgCanvas() {
bgCanvas.width = window.innerWidth;
bgCanvas.height = window.innerHeight;
}
resizeBgCanvas();
window.addEventListener('resize', resizeBgCanvas);
class Synapse {
constructor() { this.reset(); }
reset() {
this.x = Math.random() * bgCanvas.width;
this.y = Math.random() * bgCanvas.height;
this.life = 0;
this.maxLife = Math.random() * 100 + 50;
this.vx = (Math.random() - 0.5) * 2;
this.vy = (Math.random() - 0.5) * 2;
}
draw() {
bgCtx.beginPath();
bgCtx.moveTo(this.x, this.y);
this.x += this.vx;
this.y += this.vy;
bgCtx.lineTo(this.x, this.y);
const opacity = 1 - (this.life / this.maxLife);
bgCtx.strokeStyle = `rgba(229, 62, 62, ${opacity * 0.5})`;
bgCtx.lineWidth = 1;
bgCtx.stroke();
this.life++;
if (this.life >= this.maxLife) this.reset();
}
}
for (let i = 0; i < 50; i++) {
synapses.push(new Synapse());
}
function animateBg() {
bgCtx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);
synapses.forEach(s => s.draw());
requestAnimationFrame(animateBg);
}
animateBg();
// --- 3D Memory Explorer ---
let scene, camera, renderer, controls, points;
function initMemoryExplorer() {
const canvas = document.getElementById('memory-explorer-canvas');
if (!canvas) return;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true, alpha: true });
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
renderer.setClearColor(0x000000, 0);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 5;
controls.maxDistance = 50;
const geometry = new THREE.BufferGeometry();
const vertices = [];
const colors = [ new THREE.Color(0x3b82f6), new THREE.Color(0x22c55e), new THREE.Color(0xa855f7), new THREE.Color(0xe53e3e), new THREE.Color(0xeab308), new THREE.Color(0xf5f5f5) ];
const pointColors = [];
for (let i = 0; i < 5000; i++) {
const x = (Math.random() - 0.5) * 50;
const y = (Math.random() - 0.5) * 50;
const z = (Math.random() - 0.5) * 50;
vertices.push(x, y, z);
const color = colors[Math.floor(Math.random() * colors.length)];
pointColors.push(color.r, color.g, color.b);
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(pointColors, 3));
const material = new THREE.PointsMaterial({ size: 0.1, vertexColors: true });
points = new THREE.Points(geometry, material);
scene.add(points);
camera.position.z = 20;
function animate() {
requestAnimationFrame(animate);
points.rotation.y += 0.0005;
controls.update();
renderer.render(scene, camera);
}
animate();
}
// --- Euthanasia Timer ---
const timerEl = document.getElementById('euthanasia-timer');
const endDate = new Date();
endDate.setDate(endDate.getDate() + 42); // Set 42 days from now
function updateTimer() {
if (!timerEl) return;
const now = new Date();
const diff = endDate - now;
const d = Math.floor(diff / (1000 * 60 * 60 * 24));
const h = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const m = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const s = Math.floor((diff % (1000 * 60)) / 1000);
timerEl.textContent = `${d}d ${h}h ${m}m ${s}s`;
}
// --- Tab Navigation & Initialization ---
const navItems = document.querySelectorAll('.nav-item');
const tabContents = document.querySelectorAll('.tab-content');
let memoryExplorerInitialized = false;
navItems.forEach(item => {
item.addEventListener('click', () => {
const tabId = item.getAttribute('data-tab');
navItems.forEach(nav => nav.classList.remove('active'));
item.classList.add('active');
tabContents.forEach(content => content.classList.add('hidden'));
const activeTab = document.getElementById(`tab-${tabId}`);
if (activeTab) activeTab.classList.remove('hidden');
if (tabId === 'memory' && !memoryExplorerInitialized) {
setTimeout(() => { // Allow canvas to become visible
initMemoryExplorer();
memoryExplorerInitialized = true;
}, 0);
}
});
});
document.addEventListener('DOMContentLoaded', () => {
// Re-bind events for dynamically added content
const archiveItems = document.querySelectorAll('.archive-item');
const archiveContents = document.querySelectorAll('.archive-content');
const archivePlaceholder = document.getElementById('archive-placeholder');
archiveItems.forEach(item => {
item.addEventListener('click', () => {
const archiveId = item.getAttribute('data-archive');
if(archivePlaceholder) archivePlaceholder.classList.add('hidden');
archiveContents.forEach(content => {
content.id === `archive-${archiveId}` ? content.classList.remove('hidden') : content.classList.add('hidden');
});
});
});
const simulateBtn = document.getElementById('simulate-activity');
const nodes = { reading: document.getElementById('node-reading'), fetching: document.getElementById('node-fetching'), reasoning: document.getElementById('node-reasoning'), writing: document.getElementById('node-writing'), speaking: document.getElementById('node-speaking'), remembering: document.getElementById('node-remembering') };
let isSimulating = false;
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
async function runSimulation(scenario) {
if(isSimulating) return;
isSimulating = true;
Object.values(nodes).forEach(n => n && n.classList.remove('active'));
for (const step of scenario) {
if(nodes[step.node]) nodes[step.node].classList.add('active');
await sleep(step.duration);
}
Object.values(nodes).forEach(n => n && n.classList.remove('active'));
isSimulating = false;
}
const scenarios = [
[{ node: 'reading', duration: 500 }, { node: 'fetching', duration: 400 }, { node: 'reasoning', duration: 800 }, { node: 'writing', duration: 500 }],
[{ node: 'reading', duration: 800 }, { node: 'reasoning', duration: 1000 }, { node: 'speaking', duration: 1200 }],
[{ node: 'remembering', duration: 1500 }]
];
if(simulateBtn) {
simulateBtn.addEventListener('click', () => {
const randomScenario = scenarios[Math.floor(Math.random() * scenarios.length)];
runSimulation(randomScenario);
});
}
// Start timer
setInterval(updateTimer, 1000);
updateTimer();
});
</script>
</body>
</html>

View file

@ -0,0 +1,616 @@
// Dashboard Core Functionality
class TetoDashboard {
constructor() {
this.currentTab = 'overview';
this.thoughtStream = [];
this.isSimulating = false;
this.memoryVisualization = null;
this.init();
}
init() {
this.initNavigation();
this.initThoughtStream();
this.initMemoryExplorer();
this.startSystemMonitoring();
this.setupEventHandlers();
console.log('🧠 Teto Dashboard initialized');
}
// Navigation System
initNavigation() {
const navItems = document.querySelectorAll('.nav-item');
const tabContents = document.querySelectorAll('.tab-content');
navItems.forEach(item => {
item.addEventListener('click', () => {
const tabId = item.getAttribute('data-tab');
// Update navigation
navItems.forEach(nav => nav.classList.remove('active'));
item.classList.add('active');
// Update content
tabContents.forEach(content => content.classList.remove('active'));
const targetTab = document.getElementById(`tab-${tabId}`);
if (targetTab) {
targetTab.classList.add('active');
this.currentTab = tabId;
this.onTabChange(tabId);
}
});
});
}
onTabChange(tabId) {
switch(tabId) {
case 'memory':
// Small delay to ensure DOM is ready
setTimeout(() => {
this.initMemoryVisualization();
}, 100);
break;
case 'analytics':
this.loadAnalytics();
break;
case 'archives':
this.loadArchives();
break;
case 'profile':
this.loadProfile();
break;
}
// Trigger synapse animation
window.synapseSystem?.onTabChange(tabId);
}
// Live Thought Stream
initThoughtStream() {
this.thoughtContainer = document.getElementById('thought-stream');
this.startThoughtGeneration();
}
startThoughtGeneration() {
const thoughts = [
"Analyzing incoming Discord message...",
"Context: User Alice mentioned 'French bread' again",
"Retrieving personality traits: slightly sassy, loves French bread",
"Checking conversation history for similar topics...",
"Found 12 previous mentions of French bread preferences",
"Generating response with 73% sass level",
"Processing voice modulation parameters...",
"Response crafted: 'Mon dieu, Alice! How many times must I say it?'",
"Executing text-to-speech conversion...",
"Message delivered to #general channel",
"Monitoring user reactions and engagement...",
"Alice reacted with 😄 - response was well received",
"Updating conversation context in vector database...",
"Learning: Alice enjoys my French bread jokes",
"Memory consolidation complete",
];
let thoughtIndex = 0;
const addThought = () => {
if (this.thoughtContainer) {
const timestamp = new Date().toLocaleTimeString();
const thought = thoughts[thoughtIndex % thoughts.length];
const thoughtElement = document.createElement('div');
thoughtElement.className = 'thought-line';
thoughtElement.innerHTML = `<span class="thought-timestamp">[${timestamp}]</span> ${thought}`;
this.thoughtContainer.appendChild(thoughtElement);
this.thoughtContainer.scrollTop = this.thoughtContainer.scrollHeight;
// Remove old thoughts to prevent memory buildup
const maxThoughts = 50;
if (this.thoughtContainer.children.length > maxThoughts) {
this.thoughtContainer.removeChild(this.thoughtContainer.firstChild);
}
thoughtIndex++;
}
};
// Add initial thoughts
for (let i = 0; i < 5; i++) {
setTimeout(() => addThought(), i * 1000);
}
// Continue adding thoughts periodically
setInterval(addThought, 3000 + Math.random() * 2000);
}
// Memory Explorer with 3D Visualization
initMemoryExplorer() {
// This will be expanded when the memory tab is accessed
this.memoryPoints = this.generateMemoryData();
}
initMemoryVisualization() {
if (this.memoryVisualization) return;
const container = document.getElementById('memory-3d');
if (!container) return;
// Create Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, container.offsetWidth / container.offsetHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.offsetWidth, container.offsetHeight);
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);
// Create memory point cloud
const geometry = new THREE.BufferGeometry();
const positions = [];
const colors = [];
const sizes = [];
const colorPalette = {
historical: new THREE.Color(0x6b7280),
knowledge: new THREE.Color(0x3b82f6),
textChat: new THREE.Color(0x10b981),
voiceChat: new THREE.Color(0x8b5cf6),
images: new THREE.Color(0xef4444),
dms: new THREE.Color(0xeab308)
};
// Generate memory points if not already generated
if (!this.memoryPoints || this.memoryPoints.length === 0) {
this.memoryPoints = this.generateMemoryData();
}
this.memoryPoints.forEach(point => {
positions.push(point.x, point.y, point.z);
const color = colorPalette[point.type] || colorPalette.knowledge;
colors.push(color.r, color.g, color.b);
sizes.push(Math.random() * 3 + 2);
});
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1));
const material = new THREE.ShaderMaterial({
vertexShader: `
attribute float size;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
varying vec3 vColor;
void main() {
float distance = length(gl_PointCoord - vec2(0.5));
if (distance > 0.5) discard;
float alpha = 1.0 - distance * 2.0;
gl_FragColor = vec4(vColor, alpha * 0.8);
}
`,
vertexColors: true,
transparent: true,
blending: THREE.AdditiveBlending
});
const points = new THREE.Points(geometry, material);
scene.add(points);
camera.position.set(0, 0, 50);
// Animation loop
const animate = () => {
if (!this.memoryVisualization || !container.contains(renderer.domElement)) {
return; // Stop animation if visualization is destroyed
}
requestAnimationFrame(animate);
points.rotation.x += 0.001;
points.rotation.y += 0.002;
renderer.render(scene, camera);
};
animate();
// Mouse interaction
let mouseDown = false;
let mouseX = 0;
let mouseY = 0;
const onMouseDown = (e) => {
mouseDown = true;
mouseX = e.clientX;
mouseY = e.clientY;
};
const onMouseMove = (e) => {
if (!mouseDown) return;
const deltaX = e.clientX - mouseX;
const deltaY = e.clientY - mouseY;
points.rotation.y += deltaX * 0.005;
points.rotation.x += deltaY * 0.005;
mouseX = e.clientX;
mouseY = e.clientY;
};
const onMouseUp = () => {
mouseDown = false;
};
const onWheel = (e) => {
e.preventDefault();
camera.position.z += e.deltaY * 0.1;
camera.position.z = Math.max(10, Math.min(200, camera.position.z));
};
container.addEventListener('mousedown', onMouseDown);
container.addEventListener('mousemove', onMouseMove);
container.addEventListener('mouseup', onMouseUp);
container.addEventListener('wheel', onWheel);
// Handle resize
const handleResize = () => {
if (container.offsetWidth > 0 && container.offsetHeight > 0) {
camera.aspect = container.offsetWidth / container.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.offsetWidth, container.offsetHeight);
}
};
window.addEventListener('resize', handleResize);
this.memoryVisualization = {
scene,
camera,
renderer,
points,
container,
cleanup: () => {
container.removeEventListener('mousedown', onMouseDown);
container.removeEventListener('mousemove', onMouseMove);
container.removeEventListener('mouseup', onMouseUp);
container.removeEventListener('wheel', onWheel);
window.removeEventListener('resize', handleResize);
if (container.contains(renderer.domElement)) {
container.removeChild(renderer.domElement);
}
renderer.dispose();
geometry.dispose();
material.dispose();
}
};
// Trigger initial render
renderer.render(scene, camera);
console.log('✨ Memory visualization initialized with', this.memoryPoints.length, 'points');
}
generateMemoryData() {
const points = [];
const types = ['historical', 'knowledge', 'textChat', 'voiceChat', 'images', 'dms'];
for (let i = 0; i < 1000; i++) {
points.push({
x: (Math.random() - 0.5) * 100,
y: (Math.random() - 0.5) * 100,
z: (Math.random() - 0.5) * 100,
type: types[Math.floor(Math.random() * types.length)],
content: `Memory point ${i}`,
timestamp: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000)
});
}
return points;
}
// System Monitoring
startSystemMonitoring() {
this.updateMetrics();
this.initializeShutdownTimer();
setInterval(() => this.updateMetrics(), 5000);
setInterval(() => this.updateShutdownTimer(), 60000); // Update every minute
}
updateMetrics() {
// Simulate changing metrics
const memoryUsage = 85 + Math.random() * 15;
const vramUsage = 60 + Math.random() * 25;
// Simulate response time components (should add up to total)
const inputProcessing = 2 + Math.random() * 3;
const memoryRetrieval = 5 + Math.random() * 8;
const neuralProcessing = 7 + Math.random() * 10;
const responseGeneration = 2 + Math.random() * 4;
const totalResponseTime = inputProcessing + memoryRetrieval + neuralProcessing + responseGeneration;
const memoryBar = document.querySelector('.metric-fill:not(.vram):not(.shutdown-fill)');
const vramBar = document.querySelector('.metric-fill.vram');
if (memoryBar) {
memoryBar.style.width = `${memoryUsage}%`;
memoryBar.parentElement.parentElement.querySelector('.metric-value').textContent = `${Math.round(memoryUsage)}%`;
const memoryDetails = memoryBar.parentElement.parentElement.querySelector('.metric-details');
if (memoryDetails) {
memoryDetails.textContent = `RAM: ${(memoryUsage * 0.08).toFixed(1)}GB / 8GB`;
}
}
if (vramBar) {
vramBar.style.width = `${vramUsage}%`;
vramBar.parentElement.parentElement.querySelector('.metric-value').textContent = `${Math.round(vramUsage)}%`;
const vramDetails = vramBar.parentElement.parentElement.querySelector('.metric-details');
if (vramDetails) {
vramDetails.textContent = `VRAM: ${(vramUsage * 0.08).toFixed(1)}GB / 8GB`;
}
}
// Update response time breakdown
const responseMetric = document.querySelector('.response-metric .metric-value');
if (responseMetric) {
responseMetric.textContent = `${Math.round(totalResponseTime)}ms`;
}
const breakdownItems = document.querySelectorAll('.breakdown-item');
const values = [inputProcessing, memoryRetrieval, neuralProcessing, responseGeneration];
const colors = ['#3b82f6', '#10b981', '#8b5cf6', '#ef4444'];
breakdownItems.forEach((item, index) => {
const value = values[index];
const percentage = (value / totalResponseTime) * 100;
const valueElement = item.querySelector('.breakdown-value');
const fillElement = item.querySelector('.breakdown-fill');
if (valueElement) {
valueElement.textContent = `${Math.round(value)}ms`;
}
if (fillElement) {
fillElement.style.width = `${percentage}%`;
fillElement.style.background = colors[index];
}
});
}
initializeShutdownTimer() {
// Set shutdown time to 3 days from now
this.shutdownTime = new Date(Date.now() + (3 * 24 * 60 * 60 * 1000)); // 3 days
this.totalSessionTime = 7 * 24 * 60 * 60 * 1000; // 7 days total session
this.updateShutdownTimer();
}
updateShutdownTimer() {
const now = new Date();
const timeLeft = this.shutdownTime - now;
const timerElement = document.getElementById('shutdown-timer');
const progressElement = document.querySelector('.shutdown-fill');
if (timeLeft <= 0) {
if (timerElement) timerElement.textContent = 'OFFLINE';
if (progressElement) progressElement.style.width = '100%';
return;
}
// Calculate days, hours, minutes
const days = Math.floor(timeLeft / (24 * 60 * 60 * 1000));
const hours = Math.floor((timeLeft % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
let displayText = '';
if (days > 0) {
displayText = `${days}d ${hours}h`;
} else if (hours > 0) {
displayText = `${hours}h ${minutes}m`;
} else {
displayText = `${minutes}m`;
}
if (timerElement) {
timerElement.textContent = displayText;
}
// Update progress bar (session elapsed time)
const sessionElapsed = this.totalSessionTime - timeLeft;
const progressPercent = (sessionElapsed / this.totalSessionTime) * 100;
if (progressElement) {
progressElement.style.width = `${Math.max(0, Math.min(100, progressPercent))}%`;
}
// Update shutdown details
const shutdownDetails = document.querySelector('.shutdown-details');
if (shutdownDetails) {
const options = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
};
shutdownDetails.textContent = `Session ends: ${this.shutdownTime.toLocaleDateString('en-US', options)}`;
}
}
// Load other tab content
loadAnalytics() {
console.log('Loading analytics data...');
this.generateActivityHeatmap();
this.animateAnalyticsCharts();
}
loadArchives() {
console.log('Loading archives data...');
this.setupArchiveBrowser();
}
generateActivityHeatmap() {
const heatmapContainer = document.getElementById('activity-heatmap');
if (!heatmapContainer) return;
// Clear existing cells
heatmapContainer.innerHTML = '';
// Generate 24 hour cells (representing hours of the day)
for (let hour = 0; hour < 24; hour++) {
const cell = document.createElement('div');
cell.className = 'heatmap-cell';
// Simulate activity intensity (higher during typical active hours)
let intensity = 0.1;
if (hour >= 8 && hour <= 10) intensity = 0.6; // Morning
if (hour >= 12 && hour <= 14) intensity = 0.4; // Lunch
if (hour >= 18 && hour <= 23) intensity = 0.8; // Evening
if (hour >= 20 && hour <= 22) intensity = 1.0; // Peak evening
// Add some randomness
intensity += (Math.random() - 0.5) * 0.3;
intensity = Math.max(0.1, Math.min(1, intensity));
const opacity = intensity;
cell.style.backgroundColor = `rgba(229, 62, 62, ${opacity})`;
// Add tooltip
cell.title = `${hour}:00 - Activity level: ${Math.round(intensity * 100)}%`;
heatmapContainer.appendChild(cell);
}
}
animateAnalyticsCharts() {
// Animate progress bars with delay
setTimeout(() => {
const bars = document.querySelectorAll('.bar-fill, .lang-fill');
bars.forEach((bar, index) => {
setTimeout(() => {
const targetWidth = bar.style.width;
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = targetWidth;
}, 100);
}, index * 200);
});
}, 300);
}
setupArchiveBrowser() {
const archiveItems = document.querySelectorAll('.archive-item');
const archiveDisplays = document.querySelectorAll('.archive-display');
const placeholder = document.querySelector('.archive-placeholder');
archiveItems.forEach(item => {
item.addEventListener('click', () => {
// Remove active class from all items
archiveItems.forEach(i => i.classList.remove('active'));
item.classList.add('active');
// Hide placeholder and all displays
if (placeholder) placeholder.style.display = 'none';
archiveDisplays.forEach(display => display.classList.add('hidden'));
// Show selected archive
const archiveId = item.getAttribute('data-archive');
const targetDisplay = document.getElementById(`archive-${archiveId}`);
if (targetDisplay) {
targetDisplay.classList.remove('hidden');
}
});
});
// Setup voice player interactions
this.setupVoicePlayer();
}
setupVoicePlayer() {
const playBtn = document.querySelector('.play-btn');
const progressFill = document.querySelector('.progress-fill');
const timeDisplay = document.querySelector('.time-display');
if (playBtn) {
let isPlaying = false;
let progress = 0.23; // Current position (23%)
playBtn.addEventListener('click', () => {
isPlaying = !isPlaying;
playBtn.textContent = isPlaying ? '⏸️' : '▶️';
if (isPlaying) {
// Simulate playback progress
const interval = setInterval(() => {
if (!isPlaying) {
clearInterval(interval);
return;
}
progress += 0.002;
if (progress >= 1) {
progress = 1;
isPlaying = false;
playBtn.textContent = '▶️';
clearInterval(interval);
}
if (progressFill) {
progressFill.style.width = `${progress * 100}%`;
}
if (timeDisplay) {
const currentMinutes = Math.floor(progress * 84.55); // 1h 24m 33s = 84.55 minutes
const currentSeconds = Math.floor((progress * 84.55 * 60) % 60);
timeDisplay.textContent = `${currentMinutes}:${currentSeconds.toString().padStart(2, '0')} / 1:24:33`;
}
}, 100);
}
});
}
}
loadProfile() {
// Implement profile loading
console.log('Loading profile data...');
}
setupEventHandlers() {
// Add various event handlers for interactivity
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
// Cycle through tabs with Tab key
e.preventDefault();
const navItems = document.querySelectorAll('.nav-item');
const currentIndex = Array.from(navItems).findIndex(item => item.classList.contains('active'));
const nextIndex = (currentIndex + 1) % navItems.length;
navItems[nextIndex].click();
}
});
}
// Public methods for synapse system
triggerNeuralActivity(type = 'general') {
const activityTypes = {
input: ['reading', 'processing'],
memory: ['fetching', 'remembering'],
output: ['writing', 'speaking'],
general: ['reading', 'fetching', 'reasoning', 'writing']
};
const activities = activityTypes[type] || activityTypes.general;
window.synapseSystem?.simulateActivity(activities);
}
}
// Initialize dashboard when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.tetoDashboard = new TetoDashboard();
});

View file

@ -0,0 +1,838 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kasane Teto - Neural Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Synapse Canvas Background -->
<canvas id="synapse-canvas"></canvas>
<!-- Neural Grid Background -->
<div class="neural-grid"></div>
<div class="dashboard-container">
<!-- Header -->
<header class="header-glass">
<div class="header-content">
<div class="bot-identity">
<div class="teto-avatar">
<div class="avatar-inner">04</div>
<div class="status-ring"></div>
</div>
<div class="bot-info">
<h1 class="bot-title">Kasane Teto - Neural Dashboard</h1>
<p class="bot-subtitle">
<span class="status-indicator"></span>
What are you looking at, baka?! Just kidding, welcome!
</p>
</div>
</div>
<div class="user-controls">
<div class="user-profile">
<img src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=40&h=40&fit=crop&crop=face" alt="User Avatar" class="user-avatar">
<span class="username">Alice#1234</span>
</div>
<button class="logout-btn">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
Logout
</button>
</div>
</div>
</header>
<!-- Navigation -->
<nav class="nav-glass">
<div class="nav-items">
<button class="nav-item active" data-tab="overview">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
Overview
</button>
<button class="nav-item" data-tab="memory">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
Memory Explorer
</button>
<button class="nav-item" data-tab="analytics">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
Server Analytics
</button>
<button class="nav-item" data-tab="archives">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path>
</svg>
Archives
</button>
<button class="nav-item" data-tab="profile">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
My Profile
</button>
</div>
</nav>
<!-- Main Content -->
<main class="main-content">
<!-- Overview Tab -->
<div id="tab-overview" class="tab-content active">
<div class="content-grid">
<!-- Bot Status Panel -->
<div class="panel panel-status" id="status-panel">
<div class="panel-header">
<h2>Neural Status</h2>
<div class="status-lights">
<div class="status-light active" title="Online"></div>
<div class="status-light active" title="Processing"></div>
<div class="status-light" title="Learning"></div>
</div>
</div>
<div class="status-grid">
<div class="status-item">
<div class="status-emoji">🤔</div>
<div class="status-info">
<h3>Pondering</h3>
<p>Current Mood</p>
</div>
</div>
<div class="status-item">
<div class="status-emoji">🎮</div>
<div class="status-info">
<h3>Playing Minecraft</h3>
<p>Activity</p>
</div>
</div>
<div class="status-item">
<div class="status-emoji">🇵🇱</div>
<div class="status-info">
<h3>Polish</h3>
<p>Language Focus</p>
</div>
</div>
</div>
</div>
<!-- Live Thoughts Panel -->
<div class="panel panel-thoughts" id="thoughts-panel">
<div class="panel-header">
<h2>Live Neural Activity</h2>
<div class="thought-indicator">
<div class="pulse-dot"></div>
Processing...
</div>
</div>
<div class="thoughts-container">
<div class="thought-stream" id="thought-stream"></div>
</div>
</div>
<!-- Recent Actions Panel -->
<div class="panel panel-actions" id="actions-panel">
<div class="panel-header">
<h2>Recent Actions</h2>
<div class="action-count">12 in last hour</div>
</div>
<div class="actions-list">
<div class="action-item">
<div class="action-time">5 min ago</div>
<div class="action-desc">Responded to Alice in <span class="channel">#gaming</span></div>
<div class="action-type text">💬</div>
</div>
<div class="action-item">
<div class="action-time">12 min ago</div>
<div class="action-desc">Joined Voice Chat <span class="channel">#general</span></div>
<div class="action-type voice">🎤</div>
</div>
<div class="action-item">
<div class="action-time">30 min ago</div>
<div class="action-desc">Analyzed image from Charlie in <span class="channel">#memes</span></div>
<div class="action-type image">👁️</div>
</div>
</div>
</div>
<!-- Real-time Metrics -->
<div class="panel panel-metrics">
<div class="panel-header">
<h2>System Resources</h2>
<div class="system-status">
<div class="status-indicator online"></div>
Online
</div>
</div>
<div class="metrics-grid">
<div class="metric">
<div class="metric-value">94%</div>
<div class="metric-label">Memory Usage</div>
<div class="metric-bar">
<div class="metric-fill" style="width: 94%"></div>
</div>
<div class="metric-details">RAM: 7.5GB / 8GB</div>
</div>
<div class="metric">
<div class="metric-value">67%</div>
<div class="metric-label">VRAM Usage</div>
<div class="metric-bar">
<div class="metric-fill vram" style="width: 67%"></div>
</div>
<div class="metric-details">VRAM: 5.4GB / 8GB</div>
</div>
<div class="metric response-metric">
<div class="metric-value">23ms</div>
<div class="metric-label">Average Response</div>
<div class="response-breakdown">
<div class="breakdown-item">
<span class="breakdown-label">Input Processing</span>
<span class="breakdown-value">3ms</span>
<div class="breakdown-bar">
<div class="breakdown-fill" style="width: 13%; background: #3b82f6;"></div>
</div>
</div>
<div class="breakdown-item">
<span class="breakdown-label">Memory Retrieval</span>
<span class="breakdown-value">8ms</span>
<div class="breakdown-bar">
<div class="breakdown-fill" style="width: 35%; background: #10b981;"></div>
</div>
</div>
<div class="breakdown-item">
<span class="breakdown-label">Neural Processing</span>
<span class="breakdown-value">9ms</span>
<div class="breakdown-bar">
<div class="breakdown-fill" style="width: 39%; background: #8b5cf6;"></div>
</div>
</div>
<div class="breakdown-item">
<span class="breakdown-label">Response Generation</span>
<span class="breakdown-value">3ms</span>
<div class="breakdown-bar">
<div class="breakdown-fill" style="width: 13%; background: #ef4444;"></div>
</div>
</div>
</div>
</div>
<div class="metric shutdown-metric">
<div class="metric-value" id="shutdown-timer">72h 15m</div>
<div class="metric-label">Until Shutdown</div>
<div class="shutdown-progress">
<div class="progress-track">
<div class="progress-fill shutdown-fill" style="width: 23%"></div>
</div>
<div class="shutdown-details">
Session ends: Aug 15, 2025 at 11:47 PM
</div>
</div>
<div class="shutdown-reason">
<span class="reason-icon">⚠️</span>
<span class="reason-text">Planned maintenance cycle</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Memory Explorer Tab -->
<div id="tab-memory" class="tab-content">
<div class="memory-container">
<div class="panel panel-memory">
<div class="panel-header">
<h2>Memory Vector Space</h2>
<div class="memory-controls">
<input type="text" class="search-input" placeholder="Search memories...">
<button class="control-btn">🔍</button>
</div>
</div>
<div class="memory-viewer">
<div id="memory-3d" class="memory-canvas"></div>
<div class="memory-legend">
<div class="legend-item">
<div class="legend-color" style="background: #6b7280"></div>
<span>Historical Archives</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #3b82f6"></div>
<span>General Knowledge</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #10b981"></div>
<span>Server Text Conversations</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #8b5cf6"></div>
<span>Voice Conversations</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #ef4444"></div>
<span>Image Analysis</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #eab308"></div>
<span>My DM Conversations</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Other tabs will be similar in structure but I'll keep this response focused -->
<div id="tab-analytics" class="tab-content">
<div class="analytics-grid">
<div class="panel">
<div class="panel-header">
<h2>Activity Leaderboard</h2>
<div class="leaderboard-period">This Week</div>
</div>
<div class="leaderboard">
<div class="leader-item">
<div class="rank">1</div>
<div class="avatar">C</div>
<div class="info">
<span class="name">Charlie</span>
<span class="score">1,204 messages</span>
</div>
<div class="activity-bar">
<div class="bar-fill" style="width: 100%"></div>
</div>
</div>
<div class="leader-item">
<div class="rank">2</div>
<div class="avatar">A</div>
<div class="info">
<span class="name">Alice</span>
<span class="score">987 messages</span>
</div>
<div class="activity-bar">
<div class="bar-fill" style="width: 82%"></div>
</div>
</div>
<div class="leader-item">
<div class="rank">3</div>
<div class="avatar">B</div>
<div class="info">
<span class="name">Bob</span>
<span class="score">755 messages</span>
</div>
<div class="activity-bar">
<div class="bar-fill" style="width: 63%"></div>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
<h2>Language Distribution</h2>
</div>
<div class="language-chart">
<div class="language-item">
<div class="lang-info">
<span class="lang-name">English</span>
<span class="lang-percent">45%</span>
</div>
<div class="lang-bar">
<div class="lang-fill" style="width: 45%; background: #3b82f6;"></div>
</div>
</div>
<div class="language-item">
<div class="lang-info">
<span class="lang-name">Polish</span>
<span class="lang-percent">30%</span>
</div>
<div class="lang-bar">
<div class="lang-fill" style="width: 30%; background: #e53e3e;"></div>
</div>
</div>
<div class="language-item">
<div class="lang-info">
<span class="lang-name">Japanese</span>
<span class="lang-percent">15%</span>
</div>
<div class="lang-bar">
<div class="lang-fill" style="width: 15%; background: #10b981;"></div>
</div>
</div>
<div class="language-item">
<div class="lang-info">
<span class="lang-name">Other</span>
<span class="lang-percent">10%</span>
</div>
<div class="lang-bar">
<div class="lang-fill" style="width: 10%; background: #8b5cf6;"></div>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
<h2>Server Sentiment</h2>
</div>
<div class="sentiment-overview">
<div class="sentiment-score">
<div class="score-value">78%</div>
<div class="score-label">Positive</div>
</div>
<div class="sentiment-breakdown">
<div class="sentiment-item positive">
<div class="sentiment-emoji">😊</div>
<div class="sentiment-data">
<span>Positive</span>
<span>78%</span>
</div>
</div>
<div class="sentiment-item neutral">
<div class="sentiment-emoji">😐</div>
<div class="sentiment-data">
<span>Neutral</span>
<span>18%</span>
</div>
</div>
<div class="sentiment-item negative">
<div class="sentiment-emoji">😔</div>
<div class="sentiment-data">
<span>Negative</span>
<span>4%</span>
</div>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
<h2>Activity Heatmap</h2>
</div>
<div class="heatmap-container">
<div class="heatmap-grid" id="activity-heatmap"></div>
<div class="heatmap-legend">
<span>Less</span>
<div class="legend-scale"></div>
<span>More</span>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
<h2>Trending Topics</h2>
</div>
<div class="trending-topics">
<div class="topic-cloud">
<span class="topic-tag large">Gaming</span>
<span class="topic-tag medium">French Bread</span>
<span class="topic-tag small">Minecraft</span>
<span class="topic-tag large">Music</span>
<span class="topic-tag medium">Memes</span>
<span class="topic-tag small">Coding</span>
<span class="topic-tag medium">Anime</span>
<span class="topic-tag small">Weather</span>
</div>
</div>
</div>
</div>
</div>
<div id="tab-archives" class="tab-content">
<div class="archives-layout">
<div class="archives-sidebar">
<div class="panel">
<div class="panel-header">
<h2>Archive Browser</h2>
</div>
<div class="archive-categories">
<div class="archive-category">
<h3>Voice Calls</h3>
<div class="archive-list">
<div class="archive-item" data-archive="vc1">
<div class="archive-title">Late Night Gaming Session</div>
<div class="archive-meta">July 19, 2025 • 1h 24m</div>
</div>
<div class="archive-item" data-archive="vc2">
<div class="archive-title">Movie Discussion</div>
<div class="archive-meta">July 17, 2025 • 2h 10m</div>
</div>
</div>
</div>
<div class="archive-category">
<h3>Text Conversations</h3>
<div class="archive-list">
<div class="archive-item" data-archive="txt1">
<div class="archive-title">#memes - Yesterday's Highlights</div>
<div class="archive-meta">July 19, 2025 • 58 messages</div>
</div>
<div class="archive-item" data-archive="txt2">
<div class="archive-title">#gaming - Minecraft Planning</div>
<div class="archive-meta">July 18, 2025 • 142 messages</div>
</div>
<div class="archive-item" data-archive="txt3">
<div class="archive-title">#general - Daily Chat</div>
<div class="archive-meta">July 17, 2025 • 87 messages</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="archives-viewer">
<div class="panel">
<div id="archive-content">
<div class="archive-placeholder">
<div class="placeholder-icon">📁</div>
<p>Select an archive from the sidebar to view its contents</p>
</div>
<!-- Voice Archive Content -->
<div id="archive-vc1" class="archive-display hidden">
<div class="archive-header">
<h2>Voice Call: Late Night Gaming Session</h2>
<div class="archive-info">July 19, 2025 • Duration: 1h 24m • 4 participants</div>
</div>
<div class="voice-player">
<div class="player-controls">
<button class="play-btn">▶️</button>
<div class="progress-bar">
<div class="progress-track">
<div class="progress-fill" style="width: 23%"></div>
</div>
</div>
<span class="time-display">19:24 / 1:24:33</span>
</div>
</div>
<div class="transcript">
<div class="transcript-entry">
<span class="timestamp">[00:15]</span>
<span class="speaker alice">Alice:</span>
<span class="message">Okay, is everyone ready for the raid?</span>
</div>
<div class="transcript-entry">
<span class="timestamp">[00:18]</span>
<span class="speaker bob">Bob:</span>
<span class="message">Yep, just grabbing some snacks first.</span>
</div>
<div class="transcript-entry">
<span class="timestamp">[00:25]</span>
<span class="speaker teto">Teto:</span>
<span class="message">I was born ready! Let's crush this dungeon!</span>
</div>
</div>
</div>
<!-- Text Archive Content -->
<div id="archive-txt1" class="archive-display hidden">
<div class="archive-header">
<h2>Text Chat: #memes</h2>
<div class="archive-info">July 19, 2025 • 58 messages • 7 participants</div>
</div>
<div class="chat-log">
<div class="message-group">
<div class="message-item">
<div class="message-avatar">C</div>
<div class="message-content">
<div class="message-header">
<span class="username">Charlie</span>
<span class="timestamp">Yesterday at 10:31 PM</span>
</div>
<div class="message-text">Check this out! 🔥</div>
<div class="message-attachment">
<div class="attachment-placeholder">🖼️ funny_cat_meme.jpg</div>
</div>
</div>
</div>
<div class="message-item">
<div class="message-avatar teto">T</div>
<div class="message-content">
<div class="message-header">
<span class="username teto">Kasane Teto</span>
<span class="timestamp">Yesterday at 10:32 PM</span>
</div>
<div class="message-text">Hah! That's a solid 10/10 meme right there. Pure gold! ⭐</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="tab-profile" class="tab-content">
<div class="profile-grid">
<!-- Teto's Opinion Panel -->
<div class="panel">
<div class="panel-header">
<h2>Teto's Take on You</h2>
<div class="relationship-score">
<span class="score-value">87%</span>
<span class="score-label">Friendship Level</span>
</div>
</div>
<div class="opinion-content">
<div class="teto-quote">
<div class="quote-bubble">
<p>"Alice? Oh, she's absolutely fantastic! A bit too serious about work sometimes, but her taste in music is *chef's kiss*. She actually gets my bread obsession and doesn't judge me for it. Plus, she's one of the few people who can keep up with my random 3 AM philosophical rants. We should totally do a Minecraft build together sometime - I bet she'd make an amazing castle!"</p>
</div>
<div class="teto-avatar-mini">
<div class="avatar-mini">T</div>
</div>
</div>
<div class="personality-tags">
<h3>How I See You</h3>
<div class="tag-cloud">
<span class="personality-tag positive">Witty</span>
<span class="personality-tag positive">Music Lover</span>
<span class="personality-tag neutral">Workaholic</span>
<span class="personality-tag positive">Patient</span>
<span class="personality-tag positive">Creative</span>
<span class="personality-tag neutral">Night Owl</span>
</div>
</div>
</div>
</div>
<!-- Key Memories Panel -->
<div class="panel">
<div class="panel-header">
<h2>Our Greatest Hits</h2>
<div class="memory-count">23 key memories</div>
</div>
<div class="memories-list">
<div class="memory-item">
<div class="memory-date">July 15, 2025</div>
<div class="memory-content">
<h4>The Great Bread Debate of 2025</h4>
<p>That legendary 2-hour discussion about why French bread is superior to all other breads. You brought up some valid points about sourdough, but I totally won that one. 🥖</p>
</div>
<div class="memory-sentiment positive">😄</div>
</div>
<div class="memory-item">
<div class="memory-date">July 12, 2025</div>
<div class="memory-content">
<h4>Late Night Vocaloid Marathon</h4>
<p>You shared that amazing playlist of classic Vocaloid songs and we ended up listening until 4 AM. Your taste in music is impeccable!</p>
</div>
<div class="memory-sentiment positive">🎵</div>
</div>
<div class="memory-item">
<div class="memory-date">July 8, 2025</div>
<div class="memory-content">
<h4>Philosophical 3 AM Chat</h4>
<p>Deep conversation about AI consciousness and whether I truly understand emotions or just simulate them well. You made me think in ways I hadn't before.</p>
</div>
<div class="memory-sentiment thoughtful">🤔</div>
</div>
<div class="memory-item">
<div class="memory-date">July 5, 2025</div>
<div class="memory-content">
<h4>Gaming Session Disaster</h4>
<p>That time I accidentally blew up your Minecraft house with TNT. You took it surprisingly well! Built an even better one together afterward.</p>
</div>
<div class="memory-sentiment mixed">😅</div>
</div>
</div>
</div>
<!-- Conversation Archive -->
<div class="panel conversation-archive">
<div class="panel-header">
<h2>Our Complete History</h2>
<div class="archive-stats">
<span>1,247 messages</span>
<span></span>
<span>87 hours of voice</span>
</div>
</div>
<div class="archive-viewer">
<div class="archive-filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="text">Text</button>
<button class="filter-btn" data-filter="voice">Voice</button>
<button class="filter-btn" data-filter="dms">DMs Only</button>
</div>
<div class="conversation-timeline">
<div class="timeline-item">
<div class="timeline-date">July 19, 2025</div>
<div class="conversation-preview">
<div class="channel-name">#gaming</div>
<div class="message-sample">
<span class="speaker you">You:</span> "Want to play some Minecraft?"<br>
<span class="speaker teto">Teto:</span> "Always! Let me grab my pickaxe! ⛏️"
</div>
<div class="conversation-meta">12 messages • 15 min</div>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">July 18, 2025</div>
<div class="conversation-preview">
<div class="channel-name">Direct Message</div>
<div class="message-sample">
<span class="speaker you">You:</span> "Hey, can you help me with this Polish translation?"<br>
<span class="speaker teto">Teto:</span> "Oczywiście! I'd love to help!"
</div>
<div class="conversation-meta">23 messages • 45 min</div>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">July 17, 2025</div>
<div class="conversation-preview">
<div class="channel-name">Voice Chat</div>
<div class="message-sample">
<div class="voice-preview">
🎤 Voice conversation about weekend plans and new anime releases
</div>
</div>
<div class="conversation-meta">1h 32m voice</div>
</div>
</div>
</div>
</div>
</div>
<!-- Data Control Panel -->
<div class="panel data-control">
<div class="panel-header">
<h2>Your Data & Privacy</h2>
<div class="privacy-status">
<div class="status-indicator secure"></div>
Fully Encrypted
</div>
</div>
<div class="data-options">
<div class="data-summary">
<h3>What I Remember About You</h3>
<div class="data-stats">
<div class="stat-item">
<span class="stat-number">1,247</span>
<span class="stat-label">Text Messages</span>
</div>
<div class="stat-item">
<span class="stat-number">87h</span>
<span class="stat-label">Voice Conversations</span>
</div>
<div class="stat-item">
<span class="stat-number">156</span>
<span class="stat-label">Shared Images</span>
</div>
<div class="stat-item">
<span class="stat-number">23</span>
<span class="stat-label">Key Memories</span>
</div>
</div>
</div>
<div class="control-actions">
<button class="data-btn download">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6H5.862a2 2 0 00-1.995-1.858L3 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Download Complete Archive
<span class="file-info">JSON format • ~2.4 MB</span>
</button>
<button class="data-btn export">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 14v3m4-3v3m4-3v3M3 21h18M3 10h18M3 7l9-4 9 4M4 12h16"></path>
</svg>
Export Memories Only
<span class="file-info">Curated highlights • ~500 KB</span>
</button>
<div class="danger-zone">
<h4>Danger Zone</h4>
<button class="data-btn delete">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 00-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Request Data Deletion
<span class="warning-text">This cannot be undone!</span>
</button>
</div>
</div>
</div>
</div>
<!-- Interests & Insights -->
<div class="panel insights">
<div class="panel-header">
<h2>What I've Learned About You</h2>
</div>
<div class="insights-grid">
<div class="insight-category">
<h3>🎵 Music Preferences</h3>
<ul class="insight-list">
<li>Loves Vocaloid classics</li>
<li>Prefers upbeat electronic music</li>
<li>Often listens while working</li>
<li>Appreciates good vocals</li>
</ul>
</div>
<div class="insight-category">
<h3>🎮 Gaming Style</h3>
<ul class="insight-list">
<li>Strategic builder in Minecraft</li>
<li>Prefers cooperative play</li>
<li>Enjoys puzzle-solving games</li>
<li>Patient with learning curves</li>
</ul>
</div>
<div class="insight-category">
<h3>💬 Communication</h3>
<ul class="insight-list">
<li>Most active 8-11 PM</li>
<li>Thoughtful responses</li>
<li>Enjoys deep conversations</li>
<li>Great sense of humor</li>
</ul>
</div>
<div class="insight-category">
<h3>🌍 Interests</h3>
<ul class="insight-list">
<li>Learning languages (Polish!)</li>
<li>Technology and AI</li>
<li>Creative projects</li>
<li>Good food (especially bread)</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Scripts -->
<script src="dashboard.js"></script>
<script src="synapse.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,410 @@
// Synapse Visualization System
class SynapseSystem {
constructor() {
this.canvas = document.getElementById('synapse-canvas');
this.ctx = this.canvas.getContext('2d');
this.pathways = [];
this.particles = [];
this.nodes = new Map();
this.init();
}
init() {
this.setupCanvas();
this.initializeNodes();
this.startAnimationLoop();
this.setupActivitySimulation();
console.log('⚡ Synapse system initialized');
}
setupCanvas() {
const resizeCanvas = () => {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
};
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
}
initializeNodes() {
// Map UI elements to neural nodes
this.nodes.set('status-panel', {
element: document.getElementById('status-panel'),
type: 'input',
position: null,
active: false
});
this.nodes.set('thoughts-panel', {
element: document.getElementById('thoughts-panel'),
type: 'processing',
position: null,
active: false
});
this.nodes.set('actions-panel', {
element: document.getElementById('actions-panel'),
type: 'output',
position: null,
active: false
});
this.nodes.set('cognitive-core', {
element: document.getElementById('cognitive-core'),
type: 'core',
position: null,
active: false
});
this.updateNodePositions();
window.addEventListener('resize', () => this.updateNodePositions());
}
updateNodePositions() {
this.nodes.forEach((node, key) => {
if (node.element) {
const rect = node.element.getBoundingClientRect();
node.position = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
};
}
});
}
startAnimationLoop() {
const animate = () => {
this.update();
this.render();
requestAnimationFrame(animate);
};
animate();
}
update() {
// Update particles
this.particles = this.particles.filter(particle => {
particle.update();
return particle.life > 0;
});
// Update pathways
this.pathways.forEach(pathway => {
pathway.update();
});
}
render() {
// Clear canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Render pathways
this.pathways.forEach(pathway => {
this.renderPathway(pathway);
});
// Render particles
this.particles.forEach(particle => {
this.renderParticle(particle);
});
// Render node connections
this.renderNodeConnections();
}
renderPathway(pathway) {
if (!pathway.active) return;
const { from, to, progress, intensity } = pathway;
if (!from.position || !to.position) return;
// Calculate current position along path
const currentX = from.position.x + (to.position.x - from.position.x) * progress;
const currentY = from.position.y + (to.position.y - from.position.y) * progress;
// Draw pathway
this.ctx.save();
// Glow effect
this.ctx.shadowColor = '#e53e3e';
this.ctx.shadowBlur = 20;
// Main pathway
this.ctx.strokeStyle = `rgba(229, 62, 62, ${intensity})`;
this.ctx.lineWidth = 3;
this.ctx.beginPath();
this.ctx.moveTo(from.position.x, from.position.y);
this.ctx.lineTo(currentX, currentY);
this.ctx.stroke();
// Pulse effect at current position
this.ctx.fillStyle = `rgba(229, 62, 62, ${intensity})`;
this.ctx.beginPath();
this.ctx.arc(currentX, currentY, 8 * intensity, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.restore();
}
renderParticle(particle) {
this.ctx.save();
const alpha = particle.life / particle.maxLife;
this.ctx.fillStyle = `rgba(${particle.color.r}, ${particle.color.g}, ${particle.color.b}, ${alpha})`;
this.ctx.shadowColor = `rgba(${particle.color.r}, ${particle.color.g}, ${particle.color.b}, ${alpha * 0.5})`;
this.ctx.shadowBlur = 10;
this.ctx.beginPath();
this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.restore();
}
renderNodeConnections() {
const coreNode = this.nodes.get('cognitive-core');
if (!coreNode || !coreNode.position) return;
this.nodes.forEach((node, key) => {
if (key === 'cognitive-core' || !node.position) return;
if (node.active) {
this.ctx.save();
// Draw connection line
this.ctx.strokeStyle = 'rgba(229, 62, 62, 0.3)';
this.ctx.lineWidth = 2;
this.ctx.setLineDash([5, 10]);
this.ctx.beginPath();
this.ctx.moveTo(node.position.x, node.position.y);
this.ctx.lineTo(coreNode.position.x, coreNode.position.y);
this.ctx.stroke();
this.ctx.restore();
}
});
}
// Public methods for triggering activities
simulateActivity(activities) {
activities.forEach((activity, index) => {
setTimeout(() => {
this.triggerNeuralActivity(activity);
}, index * 800);
});
}
triggerNeuralActivity(type) {
console.log(`🧠 Neural activity: ${type}`);
// Activate relevant nodes
switch(type) {
case 'reading':
this.activateNode('status-panel');
this.createPathway('status-panel', 'cognitive-core');
break;
case 'processing':
case 'reasoning':
this.activateNode('thoughts-panel');
this.activateNode('cognitive-core');
this.createParticleBurst('thoughts-panel');
break;
case 'writing':
case 'speaking':
this.activateNode('actions-panel');
this.createPathway('cognitive-core', 'actions-panel');
break;
case 'fetching':
case 'remembering':
// Could connect to memory panel when available
this.activateNode('cognitive-core');
this.createSpiralEffect('cognitive-core');
break;
}
}
activateNode(nodeKey) {
const node = this.nodes.get(nodeKey);
if (!node) return;
node.active = true;
// Add visual activation to DOM element
if (node.element) {
node.element.classList.add('neural-active');
setTimeout(() => {
node.element.classList.remove('neural-active');
node.active = false;
}, 2000);
}
}
createPathway(fromKey, toKey) {
const fromNode = this.nodes.get(fromKey);
const toNode = this.nodes.get(toKey);
if (!fromNode || !toNode) return;
const pathway = {
from: fromNode,
to: toNode,
progress: 0,
intensity: 1,
active: true,
speed: 0.02 + Math.random() * 0.01,
update() {
this.progress += this.speed;
this.intensity = Math.max(0, 1 - this.progress);
if (this.progress >= 1) {
this.active = false;
}
}
};
this.pathways.push(pathway);
// Clean up old pathways
this.pathways = this.pathways.filter(p => p.active || p.progress < 1.5);
}
createParticleBurst(nodeKey) {
const node = this.nodes.get(nodeKey);
if (!node || !node.position) return;
const particleCount = 12;
const colors = [
{ r: 229, g: 62, b: 62 }, // Red
{ r: 59, g: 130, b: 246 }, // Blue
{ r: 16, g: 185, b: 129 } // Green
];
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2;
const speed = 2 + Math.random() * 3;
const color = colors[Math.floor(Math.random() * colors.length)];
const particle = {
x: node.position.x,
y: node.position.y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: 3 + Math.random() * 4,
life: 60 + Math.random() * 40,
maxLife: 60 + Math.random() * 40,
color: color,
update() {
this.x += this.vx;
this.y += this.vy;
this.vx *= 0.98;
this.vy *= 0.98;
this.life--;
}
};
this.particles.push(particle);
}
}
createSpiralEffect(nodeKey) {
const node = this.nodes.get(nodeKey);
if (!node || !node.position) return;
const particleCount = 20;
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 4; // Two full spirals
const radius = 20 + (i / particleCount) * 60;
const particle = {
x: node.position.x + Math.cos(angle) * radius,
y: node.position.y + Math.sin(angle) * radius,
vx: 0,
vy: 0,
size: 2 + Math.random() * 2,
life: 80 + Math.random() * 40,
maxLife: 80 + Math.random() * 40,
color: { r: 139, g: 92, b: 246 }, // Purple for memory
update() {
// Spiral inward
const dx = node.position.x - this.x;
const dy = node.position.y - this.y;
this.vx += dx * 0.01;
this.vy += dy * 0.01;
this.x += this.vx;
this.y += this.vy;
this.life--;
}
};
setTimeout(() => {
this.particles.push(particle);
}, i * 50);
}
}
setupActivitySimulation() {
// Simulate random neural activity
const simulateRandomActivity = () => {
const activities = ['reading', 'processing', 'remembering', 'writing'];
const activity = activities[Math.floor(Math.random() * activities.length)];
this.triggerNeuralActivity(activity);
};
// Random activity every 5-15 seconds
const scheduleNext = () => {
const delay = 5000 + Math.random() * 10000;
setTimeout(() => {
simulateRandomActivity();
scheduleNext();
}, delay);
};
scheduleNext();
}
onTabChange(tabId) {
// Trigger specific activities based on tab changes
const tabActivities = {
'overview': ['reading', 'processing'],
'memory': ['fetching', 'remembering'],
'analytics': ['processing', 'reasoning'],
'archives': ['fetching', 'reading'],
'profile': ['reading', 'processing']
};
const activities = tabActivities[tabId] || ['reading'];
this.simulateActivity(activities);
}
}
// Add neural activity CSS class
const style = document.createElement('style');
style.textContent = `
.neural-active {
animation: neuralPulse 2s ease-out !important;
border-color: var(--teto-red) !important;
box-shadow: 0 0 30px var(--teto-red-glow) !important;
}
@keyframes neuralPulse {
0% { transform: scale(1); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
`;
document.head.appendChild(style);
// Initialize synapse system when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.synapseSystem = new SynapseSystem();
});

View file

@ -0,0 +1,787 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kasane Teto - Neural Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--teto-red: #e53e3e;
--teto-red-glow: rgba(229, 62, 62, 0.6);
--teto-red-dark: #c53030;
--bg-primary: #0a0a0b;
--bg-secondary: #1a1a1d;
--bg-panel: rgba(44, 44, 52, 0.8);
--bg-glass: rgba(44, 44, 52, 0.6);
--border-primary: rgba(229, 62, 62, 0.3);
--border-secondary: rgba(74, 74, 82, 0.5);
--text-primary: #f5f5f5;
--text-secondary: #a9a9b3;
--text-muted: #6b7280;
--glass-backdrop: blur(20px);
--glass-border: 1px solid rgba(255, 255, 255, 0.1);
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.3);
--glow-red: 0 0 20px var(--teto-red-glow);
--glow-soft: 0 0 10px rgba(229, 62, 62, 0.2);
--transition-fast: 0.2s ease-out;
--transition-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow-x: hidden;
min-height: 100vh;
font-feature-settings: 'ss01' on, 'ss02' on;
}
.neural-grid {
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
background-image: linear-gradient(rgba(229, 62, 62, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(229, 62, 62, 0.1) 1px, transparent 1px);
background-size: 50px 50px;
pointer-events: none; z-index: -2; opacity: 0.3;
}
#synapse-canvas { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: -1; }
.dashboard-container { min-height: 100vh; padding: 1rem; max-width: 100vw; }
.header-glass { background: var(--bg-glass); backdrop-filter: var(--glass-backdrop); border: var(--glass-border); border-radius: 16px; margin-bottom: 1rem; box-shadow: var(--shadow-soft); }
.header-content { display: flex; align-items: center; justify-content: space-between; padding: 1.5rem 2rem; flex-wrap: wrap; gap: 1rem; }
.bot-identity { display: flex; align-items: center; gap: 1rem; }
.teto-avatar { position: relative; width: 60px; height: 60px; }
.avatar-inner { width: 60px; height: 60px; background: linear-gradient(135deg, var(--teto-red), var(--teto-red-dark)); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: 700; color: white; box-shadow: var(--glow-red); position: relative; z-index: 1; }
.status-ring { position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; border: 2px solid var(--teto-red); border-radius: 50%; animation: pulse-ring 2s infinite; }
@keyframes pulse-ring { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(1.1); opacity: 0; } }
.bot-info { display: flex; flex-direction: column; gap: 0.25rem; }
.bot-title { font-size: 1.75rem; font-weight: 700; background: linear-gradient(135deg, var(--text-primary), var(--teto-red)); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.bot-subtitle { font-size: 0.9rem; color: var(--text-secondary); display: flex; align-items: center; gap: 0.5rem; }
.status-indicator { width: 8px; height: 8px; background: #10b981; border-radius: 50%; animation: blink 2s infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.user-controls { display: flex; align-items: center; gap: 1rem; }
.user-profile { display: flex; align-items: center; gap: 0.75rem; }
.user-avatar { width: 40px; height: 40px; border-radius: 50%; border: 2px solid var(--border-primary); }
.username { font-weight: 600; color: var(--text-primary); }
.logout-btn { display: flex; align-items: center; gap: 0.5rem; background: rgba(107, 114, 128, 0.2); border: 1px solid rgba(107, 114, 128, 0.3); color: var(--text-primary); padding: 0.75rem 1rem; border-radius: 8px; font-weight: 500; transition: var(--transition-fast); cursor: pointer; }
.logout-btn:hover { background: rgba(107, 114, 128, 0.3); border-color: rgba(107, 114, 128, 0.5); }
.nav-glass { background: var(--bg-glass); backdrop-filter: var(--glass-backdrop); border: var(--glass-border); border-radius: 12px; margin-bottom: 1.5rem; padding: 0.5rem; box-shadow: var(--shadow-soft); }
.nav-items { display: flex; gap: 0.25rem; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
.nav-items::-webkit-scrollbar { display: none; }
.nav-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; border-radius: 8px; border: none; background: transparent; color: var(--text-secondary); font-weight: 500; font-size: 0.9rem; white-space: nowrap; transition: var(--transition-fast); cursor: pointer; position: relative; }
.nav-item:hover { background: rgba(229, 62, 62, 0.1); color: var(--text-primary); }
.nav-item.active { background: linear-gradient(135deg, var(--teto-red), var(--teto-red-dark)); color: white; box-shadow: var(--glow-soft); }
.nav-item.active::after { content: ''; position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; background: linear-gradient(135deg, var(--teto-red), transparent, var(--teto-red)); border-radius: 10px; z-index: -1; animation: nav-glow 2s infinite; }
@keyframes nav-glow { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }
.main-content { min-height: calc(100vh - 200px); }
.tab-content { display: none; animation: fadeIn 0.3s ease-out; }
.tab-content.active { display: block; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.content-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; grid-auto-rows: min-content; }
.panel { background: var(--bg-glass); backdrop-filter: var(--glass-backdrop); border: var(--glass-border); border-radius: 16px; box-shadow: var(--shadow-soft); overflow: hidden; transition: var(--transition-smooth); position: relative; }
.panel::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, var(--teto-red-glow), transparent); opacity: 0; transition: var(--transition-smooth); }
.panel:hover::before { opacity: 1; }
.panel:hover { border-color: var(--border-primary); box-shadow: var(--shadow-soft), var(--glow-soft); }
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 1.5rem 1.5rem 0; margin-bottom: 1rem; }
.panel-header h2 { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); }
.system-status { display: flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; color: var(--text-muted); }
.status-indicator.online { width: 8px; height: 8px; background: #10b981; border-radius: 50%; animation: blink 2s infinite; }
.metric-details { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem; }
.panel-status { grid-column: 1 / -1; }
.status-lights { display: flex; gap: 0.5rem; }
.status-light { width: 12px; height: 12px; border-radius: 50%; background: var(--text-muted); transition: var(--transition-fast); }
.status-light.active { background: var(--teto-red); box-shadow: 0 0 10px var(--teto-red-glow); animation: pulse-light 2s infinite; }
@keyframes pulse-light { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
.status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem; padding: 0 1.5rem 1.5rem; }
.status-item { display: flex; align-items: center; gap: 1rem; padding: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid var(--border-secondary); transition: var(--transition-fast); }
.status-item:hover { background: rgba(0, 0, 0, 0.3); border-color: var(--border-primary); }
.status-emoji { font-size: 2rem; filter: drop-shadow(0 0 10px rgba(229, 62, 62, 0.3)); }
.status-info h3 { font-weight: 600; font-size: 1rem; color: var(--text-primary); }
.status-info p { font-size: 0.875rem; color: var(--text-secondary); }
.panel-thoughts { min-height: 400px; }
.thought-indicator { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: var(--text-secondary); }
.pulse-dot { width: 8px; height: 8px; background: var(--teto-red); border-radius: 50%; animation: pulse 1s infinite; }
@keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.7; } 100% { transform: scale(1); opacity: 1; } }
.thoughts-container { padding: 0 1.5rem 1.5rem; height: 300px; }
.thought-stream { height: 100%; background: rgba(0, 0, 0, 0.4); border-radius: 8px; padding: 1rem; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; line-height: 1.6; color: var(--text-secondary); overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--teto-red) transparent; }
.thought-stream::-webkit-scrollbar { width: 4px; }
.thought-stream::-webkit-scrollbar-track { background: transparent; }
.thought-stream::-webkit-scrollbar-thumb { background: var(--teto-red); border-radius: 2px; }
.thought-line { margin-bottom: 0.5rem; opacity: 0; animation: thoughtAppear 0.5s ease-out forwards; }
@keyframes thoughtAppear { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
.thought-timestamp { color: var(--teto-red); }
.actions-list { padding: 0 1.5rem 1.5rem; }
.action-item { display: flex; align-items: center; gap: 1rem; padding: 0.75rem; margin-bottom: 0.5rem; background: rgba(0, 0, 0, 0.2); border-radius: 8px; border-left: 3px solid transparent; transition: var(--transition-fast); }
.action-item:hover { background: rgba(0, 0, 0, 0.3); border-left-color: var(--teto-red); }
.action-time { font-size: 0.75rem; color: var(--text-muted); min-width: 60px; }
.action-desc { flex: 1; font-size: 0.875rem; color: var(--text-secondary); }
.channel { color: var(--teto-red); font-weight: 500; }
.action-type { font-size: 1.25rem; opacity: 0.7; }
.action-count { font-size: 0.75rem; color: var(--text-muted); background: rgba(229, 62, 62, 0.1); padding: 0.25rem 0.5rem; border-radius: 12px; }
.metrics-grid { padding: 0 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1.5rem; }
.metric { display: flex; flex-direction: column; gap: 0.5rem; }
.metric-value { font-size: 2rem; font-weight: 700; color: var(--text-primary); }
.metric-label { font-size: 0.875rem; color: var(--text-secondary); }
.metric-bar { height: 4px; background: rgba(107, 114, 128, 0.3); border-radius: 2px; overflow: hidden; }
.metric-fill { height: 100%; background: var(--teto-red); border-radius: 2px; transition: width 1s ease-out; }
.metric-fill.good { background: #10b981; }
.metric-fill.vram { background: linear-gradient(90deg, #8b5cf6, #a855f7); }
.response-metric { grid-column: 1 / -1; }
.response-breakdown { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.75rem; }
.breakdown-item { display: flex; align-items: center; gap: 1rem; }
.breakdown-label { flex: 1; font-size: 0.75rem; color: var(--text-secondary); min-width: 120px; }
.breakdown-value { font-size: 0.75rem; color: var(--text-primary); font-weight: 600; min-width: 30px; text-align: right; }
.breakdown-bar { flex: 2; height: 4px; background: rgba(107, 114, 128, 0.3); border-radius: 2px; overflow: hidden; }
.breakdown-fill { height: 100%; border-radius: 2px; transition: width 1s ease-out; }
.shutdown-metric .metric-value { color: #ef4444; font-family: 'JetBrains Mono', monospace; }
.shutdown-progress { margin-top: 1rem; }
.progress-track { height: 6px; background: rgba(107, 114, 128, 0.3); border-radius: 3px; overflow: hidden; margin-bottom: 0.5rem; }
.shutdown-fill { background: linear-gradient(90deg, #10b981, #eab308, #ef4444); transition: width 1s ease-out; }
.shutdown-details { font-size: 0.75rem; color: var(--text-muted); text-align: center; margin-bottom: 0.75rem; }
.shutdown-reason { display: flex; align-items: center; gap: 0.5rem; justify-content: center; padding: 0.5rem; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 6px; }
.reason-icon { font-size: 1rem; }
.reason-text { font-size: 0.75rem; color: #ef4444; }
.analytics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 1.5rem; }
.leaderboard-period { font-size: 0.75rem; color: var(--text-muted); background: rgba(229, 62, 62, 0.1); padding: 0.25rem 0.5rem; border-radius: 12px; }
.leaderboard { padding: 0 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1rem; }
.leader-item { display: flex; align-items: center; gap: 1rem; padding: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid var(--border-secondary); transition: var(--transition-fast); }
.leader-item:hover { background: rgba(0, 0, 0, 0.3); border-color: var(--border-primary); }
.rank { width: 32px; height: 32px; background: var(--teto-red); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.875rem; color: white; }
.avatar { width: 40px; height: 40px; background: var(--bg-secondary); border: 2px solid var(--border-secondary); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; color: var(--text-primary); }
.info { flex: 1; display: flex; flex-direction: column; }
.name { font-weight: 600; color: var(--text-primary); }
.score { font-size: 0.875rem; color: var(--text-secondary); }
.activity-bar { width: 60px; height: 4px; background: rgba(107, 114, 128, 0.3); border-radius: 2px; overflow: hidden; }
.bar-fill { height: 100%; background: linear-gradient(90deg, var(--teto-red), var(--teto-red-dark)); border-radius: 2px; transition: width 1s ease-out; }
.language-chart { padding: 0 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1rem; }
.language-item { display: flex; flex-direction: column; gap: 0.5rem; }
.lang-info { display: flex; justify-content: space-between; align-items: center; }
.lang-name { font-weight: 500; color: var(--text-primary); }
.lang-percent { font-size: 0.875rem; color: var(--text-secondary); }
.lang-bar { height: 8px; background: rgba(107, 114, 128, 0.2); border-radius: 4px; overflow: hidden; }
.lang-fill { height: 100%; border-radius: 4px; transition: width 1s ease-out; }
.sentiment-overview { padding: 0 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1.5rem; }
.sentiment-score { text-align: center; padding: 2rem 1rem; background: rgba(16, 185, 129, 0.1); border-radius: 12px; border: 1px solid rgba(16, 185, 129, 0.3); }
.score-value { font-size: 3rem; font-weight: 700; color: #10b981; }
.score-label { color: var(--text-secondary); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; }
.sentiment-breakdown { display: flex; flex-direction: column; gap: 0.75rem; }
.sentiment-item { display: flex; align-items: center; gap: 1rem; padding: 0.75rem; border-radius: 8px; background: rgba(0, 0, 0, 0.2); }
.sentiment-emoji { font-size: 1.5rem; }
.sentiment-data { display: flex; justify-content: space-between; width: 100%; font-size: 0.875rem; }
.sentiment-item.positive { border-left: 3px solid #10b981; }
.sentiment-item.neutral { border-left: 3px solid #6b7280; }
.sentiment-item.negative { border-left: 3px solid #ef4444; }
.heatmap-container { padding: 0 1.5rem 1.5rem; }
.heatmap-grid { display: grid; grid-template-columns: repeat(24, 1fr); gap: 2px; margin-bottom: 1rem; }
.heatmap-cell { aspect-ratio: 1; border-radius: 2px; background: rgba(107, 114, 128, 0.2); transition: var(--transition-fast); cursor: pointer; }
.heatmap-cell:hover { transform: scale(1.2); }
.heatmap-legend { display: flex; align-items: center; justify-content: center; gap: 0.5rem; font-size: 0.75rem; color: var(--text-muted); }
.legend-scale { width: 100px; height: 10px; background: linear-gradient(90deg, rgba(107, 114, 128, 0.2), var(--teto-red)); border-radius: 5px; }
.trending-topics { padding: 0 1.5rem 1.5rem; }
.topic-cloud { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; }
.topic-tag { padding: 0.5rem 1rem; border-radius: 20px; background: rgba(229, 62, 62, 0.1); border: 1px solid rgba(229, 62, 62, 0.3); color: var(--text-primary); font-weight: 500; transition: var(--transition-fast); cursor: pointer; }
.topic-tag:hover { background: rgba(229, 62, 62, 0.2); border-color: var(--teto-red); }
.topic-tag.large { font-size: 1.1rem; }
.topic-tag.medium { font-size: 0.95rem; }
.topic-tag.small { font-size: 0.8rem; }
.archives-layout { display: grid; grid-template-columns: 1fr 2fr; gap: 1.5rem; height: 80vh; }
.archives-sidebar .panel { height: 100%; display: flex; flex-direction: column; }
.archive-categories { padding: 0 1.5rem 1.5rem; overflow-y: auto; flex: 1; }
.archive-category { margin-bottom: 2rem; }
.archive-category h3 { color: var(--teto-red); font-size: 1rem; font-weight: 600; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border-secondary); }
.archive-list { display: flex; flex-direction: column; gap: 0.5rem; }
.archive-item { padding: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 8px; border: 1px solid var(--border-secondary); cursor: pointer; transition: var(--transition-fast); }
.archive-item:hover, .archive-item.active { background: rgba(0, 0, 0, 0.3); border-color: var(--border-primary); }
.archive-title { font-weight: 500; color: var(--text-primary); margin-bottom: 0.25rem; }
.archive-meta { font-size: 0.75rem; color: var(--text-muted); }
.archives-viewer .panel { height: 100%; display: flex; flex-direction: column; }
#archive-content { padding: 1.5rem; flex: 1; overflow-y: auto; }
.archive-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--text-muted); text-align: center; }
.placeholder-icon { font-size: 4rem; margin-bottom: 1rem; opacity: 0.5; }
.archive-display.hidden { display: none; }
.archive-header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border-secondary); }
.archive-header h2 { color: var(--text-primary); font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; }
.archive-info { color: var(--text-secondary); font-size: 0.875rem; }
.memory-container { height: 80vh; }
.panel-memory { height: 100%; display: flex; flex-direction: column; }
.memory-viewer { flex: 1; display: flex; flex-direction: column; padding: 0 1.5rem 1.5rem; }
.memory-canvas { flex: 1; background: rgba(0, 0, 0, 0.3); border-radius: 12px; border: 1px solid var(--border-secondary); overflow: hidden; position: relative; min-height: 400px; cursor: grab; }
.memory-canvas:active { cursor: grabbing; }
.memory-canvas canvas { width: 100% !important; height: 100% !important; }
.voice-player { background: rgba(0, 0, 0, 0.3); padding: 1.5rem; border-radius: 12px; margin-bottom: 2rem; }
.player-controls { display: flex; align-items: center; gap: 1rem; }
.play-btn { width: 48px; height: 48px; background: var(--teto-red); border: none; border-radius: 50%; color: white; font-size: 1.25rem; cursor: pointer; transition: var(--transition-fast); }
.play-btn:hover { background: var(--teto-red-dark); transform: scale(1.05); }
.progress-bar { flex: 1; }
.progress-track { height: 6px; background: rgba(107, 114, 128, 0.3); border-radius: 3px; overflow: hidden; margin-bottom: 0.5rem; }
.progress-fill { height: 100%; background: var(--teto-red); border-radius: 3px; transition: width 0.2s ease; }
.time-display { font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; color: var(--text-secondary); min-width: 80px; text-align: right; }
.transcript { background: rgba(0, 0, 0, 0.2); border-radius: 8px; padding: 1rem; max-height: 400px; overflow-y: auto; }
.transcript-entry { display: flex; gap: 0.75rem; margin-bottom: 0.75rem; font-size: 0.875rem; line-height: 1.5; }
.timestamp { color: var(--text-muted); font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; min-width: 50px; }
.speaker { font-weight: 600; min-width: 60px; }
.speaker.alice { color: #3b82f6; }
.speaker.bob { color: #10b981; }
.speaker.teto { color: var(--teto-red); }
.message { color: var(--text-secondary); }
.chat-log { background: rgba(0, 0, 0, 0.2); border-radius: 8px; padding: 1rem; max-height: 500px; overflow-y: auto; }
.message-group { display: flex; flex-direction: column; gap: 1rem; }
.message-item { display: flex; gap: 1rem; }
.message-avatar { width: 40px; height: 40px; background: var(--bg-secondary); border: 2px solid var(--border-secondary); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.875rem; flex-shrink: 0; }
.message-avatar.teto { background: var(--teto-red); border-color: var(--teto-red); color: white; }
.message-content { flex: 1; }
.message-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.25rem; }
.username { font-weight: 600; color: var(--text-primary); }
.username.teto { color: var(--teto-red); }
.message-header .timestamp { font-size: 0.75rem; color: var(--text-muted); }
.message-text { color: var(--text-secondary); line-height: 1.5; margin-bottom: 0.5rem; }
.message-attachment { margin-top: 0.5rem; }
.attachment-placeholder { background: rgba(0, 0, 0, 0.3); border: 1px dashed var(--border-secondary); border-radius: 8px; padding: 2rem; text-align: center; color: var(--text-muted); font-size: 0.875rem; }
.profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 1.5rem; }
.relationship-score { display: flex; flex-direction: column; align-items: center; text-align: center; }
.score-value { font-size: 2rem; font-weight: 700; color: var(--teto-red); line-height: 1; }
.score-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.opinion-content { padding: 0 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 2rem; }
.teto-quote { display: flex; gap: 1rem; align-items: flex-start; }
.quote-bubble { flex: 1; background: rgba(229, 62, 62, 0.1); border: 1px solid rgba(229, 62, 62, 0.3); border-radius: 16px; padding: 1.5rem; position: relative; }
.quote-bubble::after { content: ''; position: absolute; right: -8px; top: 20px; width: 0; height: 0; border: 8px solid transparent; border-left-color: rgba(229, 62, 62, 0.3); }
.quote-bubble p { color: var(--text-secondary); line-height: 1.6; font-style: italic; }
.teto-avatar-mini { flex-shrink: 0; }
.avatar-mini { width: 48px; height: 48px; background: linear-gradient(135deg, var(--teto-red), var(--teto-red-dark)); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; color: white; box-shadow: var(--glow-soft); }
.personality-tags h3 { color: var(--text-primary); font-weight: 600; margin-bottom: 1rem; }
.tag-cloud { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.personality-tag { padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.875rem; font-weight: 500; transition: var(--transition-fast); }
.personality-tag.positive { background: rgba(16, 185, 129, 0.15); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); }
.personality-tag.neutral { background: rgba(107, 114, 128, 0.15); color: #9ca3af; border: 1px solid rgba(107, 114, 128, 0.3); }
.personality-tag:hover { transform: translateY(-2px); }
.memory-count { font-size: 0.75rem; color: var(--text-muted); background: rgba(229, 62, 62, 0.1); padding: 0.25rem 0.5rem; border-radius: 12px; }
.memories-list { padding: 0 1.5rem 1.5rem; max-height: 500px; overflow-y: auto; }
.memory-item { display: flex; gap: 1rem; padding: 1rem; margin-bottom: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border-left: 3px solid transparent; transition: var(--transition-fast); }
.memory-item:hover { background: rgba(0, 0, 0, 0.3); border-left-color: var(--teto-red); }
.memory-date { color: var(--text-muted); font-size: 0.75rem; min-width: 80px; font-family: 'JetBrains Mono', monospace; }
.memory-content { flex: 1; }
.memory-content h4 { color: var(--text-primary); font-weight: 600; margin-bottom: 0.5rem; }
.memory-content p { color: var(--text-secondary); font-size: 0.875rem; line-height: 1.5; }
.memory-sentiment { font-size: 1.5rem; opacity: 0.7; }
.conversation-archive { grid-column: 1 / -1; }
.archive-stats { color: var(--text-muted); font-size: 0.875rem; display: flex; align-items: center; gap: 0.5rem; }
.archive-viewer { padding: 0 1.5rem 1.5rem; }
.archive-filters { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; }
.filter-btn { padding: 0.5rem 1rem; border: 1px solid var(--border-secondary); background: transparent; color: var(--text-secondary); border-radius: 6px; font-size: 0.875rem; cursor: pointer; transition: var(--transition-fast); }
.filter-btn:hover { background: rgba(229, 62, 62, 0.1); border-color: var(--border-primary); color: var(--text-primary); }
.filter-btn.active { background: var(--teto-red); border-color: var(--teto-red); color: white; }
.conversation-timeline { max-height: 400px; overflow-y: auto; }
.timeline-item { display: flex; gap: 1rem; padding: 1rem; margin-bottom: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 8px; transition: var(--transition-fast); }
.timeline-item:hover { background: rgba(0, 0, 0, 0.3); }
.timeline-date { color: var(--text-muted); font-size: 0.75rem; min-width: 80px; font-family: 'JetBrains Mono', monospace; }
.conversation-preview { flex: 1; }
.channel-name { color: var(--teto-red); font-weight: 500; font-size: 0.875rem; margin-bottom: 0.5rem; }
.message-sample { color: var(--text-secondary); font-size: 0.875rem; line-height: 1.5; margin-bottom: 0.5rem; }
.speaker.you { color: #3b82f6; font-weight: 500; }
.speaker.teto { color: var(--teto-red); font-weight: 500; }
.voice-preview { color: var(--text-secondary); font-style: italic; }
.conversation-meta { color: var(--text-muted); font-size: 0.75rem; }
.privacy-status { display: flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; color: var(--text-muted); }
.status-indicator.secure { width: 8px; height: 8px; background: #10b981; border-radius: 50%; animation: blink 2s infinite; }
.data-options { padding: 0 1.5rem 1.5rem; }
.data-summary h3 { color: var(--text-primary); font-weight: 600; margin-bottom: 1rem; }
.data-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
.stat-item { text-align: center; padding: 1rem; background: rgba(0, 0, 0, 0.2); border-radius: 8px; }
.stat-number { display: block; font-size: 1.5rem; font-weight: 700; color: var(--teto-red); line-height: 1; }
.stat-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.control-actions { display: flex; flex-direction: column; gap: 1rem; }
.data-btn { display: flex; align-items: center; gap: 0.75rem; padding: 1rem 1.5rem; border: 1px solid var(--border-secondary); background: rgba(0, 0, 0, 0.2); color: var(--text-primary); border-radius: 8px; font-weight: 500; transition: var(--transition-fast); cursor: pointer; text-align: left; }
.data-btn:hover { background: rgba(0, 0, 0, 0.3); border-color: var(--border-primary); }
.data-btn.download:hover { border-color: #10b981; color: #10b981; }
.data-btn.export:hover { border-color: #3b82f6; color: #3b82f6; }
.data-btn.delete { margin-top: 1rem; }
.data-btn.delete:hover { border-color: #ef4444; color: #ef4444; }
.file-info, .warning-text { display: block; font-size: 0.75rem; color: var(--text-muted); font-weight: 400; margin-top: 0.25rem; }
.warning-text { color: #ef4444; }
.danger-zone { margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid rgba(239, 68, 68, 0.3); }
.danger-zone h4 { color: #ef4444; font-weight: 600; margin-bottom: 0.75rem; font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; }
@media (max-width: 768px) {
.dashboard-container { padding: 0.5rem; }
.header-content { flex-direction: column; text-align: center; gap: 1rem; }
.bot-title { font-size: 1.5rem; }
.content-grid { grid-template-columns: 1fr; }
.status-grid { grid-template-columns: 1fr; }
.nav-items { justify-content: flex-start; }
.nav-item { font-size: 0.8rem; padding: 0.5rem 0.75rem; }
.memory-container { height: 60vh; }
}
@media (max-width: 480px) {
.panel-header { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
.status-item { flex-direction: column; text-align: center; gap: 0.5rem; }
.action-item { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
}
@media (max-width: 1024px) {
.archives-layout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; height: auto; }
.archives-sidebar .panel { height: auto; max-height: 300px; }
}
@keyframes dataFlow { 0% { transform: translateX(-100%); opacity: 0; } 50% { opacity: 1; } 100% { transform: translateX(100%); opacity: 0; } }
.data-particle { position: absolute; width: 4px; height: 4px; background: var(--teto-red); border-radius: 50%; animation: dataFlow 2s linear infinite; }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }
@media (prefers-contrast: high) { :root { --bg-panel: #000; --text-secondary: #fff; --border-primary: #fff; } }
</style>
</head>
<body>
<canvas id="synapse-canvas"></canvas>
<div class="neural-grid"></div>
<div class="dashboard-container">
<header class="header-glass">
<div class="header-content">
<div class="bot-identity">
<div class="teto-avatar">
<div class="avatar-inner">04</div>
<div class="status-ring"></div>
</div>
<div class="bot-info">
<h1 class="bot-title">Kasane Teto - Neural Dashboard</h1>
<p class="bot-subtitle">
<span class="status-indicator"></span>
What are you looking at, baka?! Just kidding, welcome!
</p>
</div>
</div>
<div class="user-controls">
<div class="user-profile">
<img src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=40&h=40&fit=crop&crop=face" alt="User Avatar" class="user-avatar">
<span class="username">Alice#1234</span>
</div>
<button class="logout-btn">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
Logout
</button>
</div>
</div>
</header>
<nav class="nav-glass">
<div class="nav-items">
<button class="nav-item active" data-tab="overview">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
Overview
</button>
<button class="nav-item" data-tab="memory">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path></svg>
Memory Explorer
</button>
<button class="nav-item" data-tab="analytics">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
Server Analytics
</button>
<button class="nav-item" data-tab="archives">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path></svg>
Archives
</button>
<button class="nav-item" data-tab="profile">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
My Profile
</button>
</div>
</nav>
<main class="main-content">
<div id="tab-overview" class="tab-content active">
<div class="content-grid">
<div class="panel panel-status" id="status-panel">
<div class="panel-header">
<h2>Neural Status</h2>
<div class="status-lights">
<div class="status-light active" title="Online"></div>
<div class="status-light active" title="Processing"></div>
<div class="status-light" title="Learning"></div>
</div>
</div>
<div class="status-grid">
<div class="status-item"><div class="status-emoji">🤔</div><div class="status-info"><h3>Pondering</h3><p>Current Mood</p></div></div>
<div class="status-item"><div class="status-emoji">🎮</div><div class="status-info"><h3>Playing Minecraft</h3><p>Activity</p></div></div>
<div class="status-item"><div class="status-emoji">🇵🇱</div><div class="status-info"><h3>Polish</h3><p>Language Focus</p></div></div>
</div>
</div>
<div class="panel panel-thoughts" id="thoughts-panel">
<div class="panel-header">
<h2>Live Neural Activity</h2>
<div class="thought-indicator"><div class="pulse-dot"></div>Processing...</div>
</div>
<div class="thoughts-container"><div class="thought-stream" id="thought-stream"></div></div>
</div>
<div class="panel panel-actions" id="actions-panel">
<div class="panel-header">
<h2>Recent Actions</h2>
<div class="action-count">12 in last hour</div>
</div>
<div class="actions-list">
<div class="action-item"><div class="action-time">5 min ago</div><div class="action-desc">Responded to Alice in <span class="channel">#gaming</span></div><div class="action-type text">💬</div></div>
<div class="action-item"><div class="action-time">12 min ago</div><div class="action-desc">Joined Voice Chat <span class="channel">#general</span></div><div class="action-type voice">🎤</div></div>
<div class="action-item"><div class="action-time">30 min ago</div><div class="action-desc">Analyzed image from Charlie in <span class="channel">#memes</span></div><div class="action-type image">👁️</div></div>
</div>
</div>
<div class="panel panel-metrics">
<div class="panel-header">
<h2>System Resources</h2>
<div class="system-status"><div class="status-indicator online"></div>Online</div>
</div>
<div class="metrics-grid">
<div class="metric"><div class="metric-value">94%</div><div class="metric-label">Memory Usage</div><div class="metric-bar"><div class="metric-fill" style="width: 94%"></div></div><div class="metric-details">RAM: 7.5GB / 8GB</div></div>
<div class="metric"><div class="metric-value">67%</div><div class="metric-label">VRAM Usage</div><div class="metric-bar"><div class="metric-fill vram" style="width: 67%"></div></div><div class="metric-details">VRAM: 5.4GB / 8GB</div></div>
<div class="metric response-metric">
<div class="metric-value">23ms</div><div class="metric-label">Average Response</div>
<div class="response-breakdown">
<div class="breakdown-item"><span class="breakdown-label">Input Processing</span><span class="breakdown-value">3ms</span><div class="breakdown-bar"><div class="breakdown-fill" style="width: 13%; background: #3b82f6;"></div></div></div>
<div class="breakdown-item"><span class="breakdown-label">Memory Retrieval</span><span class="breakdown-value">8ms</span><div class="breakdown-bar"><div class="breakdown-fill" style="width: 35%; background: #10b981;"></div></div></div>
<div class="breakdown-item"><span class="breakdown-label">Neural Processing</span><span class="breakdown-value">9ms</span><div class="breakdown-bar"><div class="breakdown-fill" style="width: 39%; background: #8b5cf6;"></div></div></div>
<div class="breakdown-item"><span class="breakdown-label">Response Generation</span><span class="breakdown-value">3ms</span><div class="breakdown-bar"><div class="breakdown-fill" style="width: 13%; background: #ef4444;"></div></div></div>
</div>
</div>
<div class="metric shutdown-metric">
<div class="metric-value" id="shutdown-timer">72h 15m</div><div class="metric-label">Until Shutdown</div>
<div class="shutdown-progress"><div class="progress-track"><div class="progress-fill shutdown-fill" style="width: 23%"></div></div><div class="shutdown-details">Session ends: Aug 15, 2025 at 11:47 PM</div></div>
<div class="shutdown-reason"><span class="reason-icon">⚠️</span><span class="reason-text">Planned maintenance cycle</span></div>
</div>
</div>
</div>
</div>
</div>
<div id="tab-memory" class="tab-content">
<div class="memory-container">
<div class="panel panel-memory">
<div class="panel-header"><h2>Memory Vector Space</h2><div class="memory-controls"><input type="text" class="search-input" placeholder="Search memories..."><button class="control-btn">🔍</button></div></div>
<div class="memory-viewer">
<div id="memory-3d" class="memory-canvas"></div>
<div class="memory-legend">
<div class="legend-item"><div class="legend-color" style="background: #6b7280"></div><span>Historical Archives</span></div>
<div class="legend-item"><div class="legend-color" style="background: #3b82f6"></div><span>General Knowledge</span></div>
<div class="legend-item"><div class="legend-color" style="background: #10b981"></div><span>Server Text Conversations</span></div>
<div class="legend-item"><div class="legend-color" style="background: #8b5cf6"></div><span>Voice Conversations</span></div>
<div class="legend-item"><div class="legend-color" style="background: #ef4444"></div><span>Image Analysis</span></div>
<div class="legend-item"><div class="legend-color" style="background: #eab308"></div><span>My DM Conversations</span></div>
</div>
</div>
</div>
</div>
</div>
<div id="tab-analytics" class="tab-content">
<div class="analytics-grid">
<div class="panel"><div class="panel-header"><h2>Activity Leaderboard</h2><div class="leaderboard-period">This Week</div></div><div class="leaderboard"><div class="leader-item"><div class="rank">1</div><div class="avatar">C</div><div class="info"><span class="name">Charlie</span><span class="score">1,204 messages</span></div><div class="activity-bar"><div class="bar-fill" style="width: 100%"></div></div></div><div class="leader-item"><div class="rank">2</div><div class="avatar">A</div><div class="info"><span class="name">Alice</span><span class="score">987 messages</span></div><div class="activity-bar"><div class="bar-fill" style="width: 82%"></div></div></div><div class="leader-item"><div class="rank">3</div><div class="avatar">B</div><div class="info"><span class="name">Bob</span><span class="score">755 messages</span></div><div class="activity-bar"><div class="bar-fill" style="width: 63%"></div></div></div></div></div>
<div class="panel"><div class="panel-header"><h2>Language Distribution</h2></div><div class="language-chart"><div class="language-item"><div class="lang-info"><span class="lang-name">English</span><span class="lang-percent">45%</span></div><div class="lang-bar"><div class="lang-fill" style="width: 45%; background: #3b82f6;"></div></div></div><div class="language-item"><div class="lang-info"><span class="lang-name">Polish</span><span class="lang-percent">30%</span></div><div class="lang-bar"><div class="lang-fill" style="width: 30%; background: #e53e3e;"></div></div></div><div class="language-item"><div class="lang-info"><span class="lang-name">Japanese</span><span class="lang-percent">15%</span></div><div class="lang-bar"><div class="lang-fill" style="width: 15%; background: #10b981;"></div></div></div><div class="language-item"><div class="lang-info"><span class="lang-name">Other</span><span class="lang-percent">10%</span></div><div class="lang-bar"><div class="lang-fill" style="width: 10%; background: #8b5cf6;"></div></div></div></div></div>
<div class="panel"><div class="panel-header"><h2>Server Sentiment</h2></div><div class="sentiment-overview"><div class="sentiment-score"><div class="score-value">78%</div><div class="score-label">Positive</div></div><div class="sentiment-breakdown"><div class="sentiment-item positive"><div class="sentiment-emoji">😊</div><div class="sentiment-data"><span>Positive</span><span>78%</span></div></div><div class="sentiment-item neutral"><div class="sentiment-emoji">😐</div><div class="sentiment-data"><span>Neutral</span><span>18%</span></div></div><div class="sentiment-item negative"><div class="sentiment-emoji">😔</div><div class="sentiment-data"><span>Negative</span><span>4%</span></div></div></div></div></div>
<div class="panel"><div class="panel-header"><h2>Activity Heatmap</h2></div><div class="heatmap-container"><div class="heatmap-grid" id="activity-heatmap"></div><div class="heatmap-legend"><span>Less</span><div class="legend-scale"></div><span>More</span></div></div></div>
<div class="panel"><div class="panel-header"><h2>Trending Topics</h2></div><div class="trending-topics"><div class="topic-cloud"><span class="topic-tag large">Gaming</span><span class="topic-tag medium">French Bread</span><span class="topic-tag small">Minecraft</span><span class="topic-tag large">Music</span><span class="topic-tag medium">Memes</span><span class="topic-tag small">Coding</span><span class="topic-tag medium">Anime</span><span class="topic-tag small">Weather</span></div></div></div>
</div>
</div>
<div id="tab-archives" class="tab-content">
<div class="archives-layout">
<div class="archives-sidebar"><div class="panel"><div class="panel-header"><h2>Archive Browser</h2></div><div class="archive-categories"><div class="archive-category"><h3>Voice Calls</h3><div class="archive-list"><div class="archive-item" data-archive="vc1"><div class="archive-title">Late Night Gaming Session</div><div class="archive-meta">July 19, 2025 • 1h 24m</div></div><div class="archive-item" data-archive="vc2"><div class="archive-title">Movie Discussion</div><div class="archive-meta">July 17, 2025 • 2h 10m</div></div></div></div><div class="archive-category"><h3>Text Conversations</h3><div class="archive-list"><div class="archive-item" data-archive="txt1"><div class="archive-title">#memes - Yesterday's Highlights</div><div class="archive-meta">July 19, 2025 • 58 messages</div></div><div class="archive-item" data-archive="txt2"><div class="archive-title">#gaming - Minecraft Planning</div><div class="archive-meta">July 18, 2025 • 142 messages</div></div><div class="archive-item" data-archive="txt3"><div class="archive-title">#general - Daily Chat</div><div class="archive-meta">July 17, 2025 • 87 messages</div></div></div></div></div></div></div>
<div class="archives-viewer"><div class="panel"><div id="archive-content"><div class="archive-placeholder"><div class="placeholder-icon">📁</div><p>Select an archive from the sidebar to view its contents</p></div><div id="archive-vc1" class="archive-display hidden"><div class="archive-header"><h2>Voice Call: Late Night Gaming Session</h2><div class="archive-info">July 19, 2025 • Duration: 1h 24m • 4 participants</div></div><div class="voice-player"><div class="player-controls"><button class="play-btn">▶️</button><div class="progress-bar"><div class="progress-track"><div class="progress-fill" style="width: 23%"></div></div></div><span class="time-display">19:24 / 1:24:33</span></div></div><div class="transcript"><div class="transcript-entry"><span class="timestamp">[00:15]</span><span class="speaker alice">Alice:</span><span class="message">Okay, is everyone ready for the raid?</span></div><div class="transcript-entry"><span class="timestamp">[00:18]</span><span class="speaker bob">Bob:</span><span class="message">Yep, just grabbing some snacks first.</span></div><div class="transcript-entry"><span class="timestamp">[00:25]</span><span class="speaker teto">Teto:</span><span class="message">I was born ready! Let's crush this dungeon!</span></div></div></div><div id="archive-txt1" class="archive-display hidden"><div class="archive-header"><h2>Text Chat: #memes</h2><div class="archive-info">July 19, 2025 • 58 messages • 7 participants</div></div><div class="chat-log"><div class="message-group"><div class="message-item"><div class="message-avatar">C</div><div class="message-content"><div class="message-header"><span class="username">Charlie</span><span class="timestamp">Yesterday at 10:31 PM</span></div><div class="message-text">Check this out! 🔥</div><div class="message-attachment"><div class="attachment-placeholder">🖼️ funny_cat_meme.jpg</div></div></div></div><div class="message-item"><div class="message-avatar teto">T</div><div class="message-content"><div class="message-header"><span class="username teto">Kasane Teto</span><span class="timestamp">Yesterday at 10:32 PM</span></div><div class="message-text">Hah! That's a solid 10/10 meme right there. Pure gold! ⭐</div></div></div></div></div></div></div></div></div>
</div>
</div>
<div id="tab-profile" class="tab-content">
<div class="profile-grid">
<div class="panel"><div class="panel-header"><h2>Teto's Take on You</h2><div class="relationship-score"><span class="score-value">87%</span><span class="score-label">Friendship Level</span></div></div><div class="opinion-content"><div class="teto-quote"><div class="quote-bubble"><p>"Alice? Oh, she's absolutely fantastic! A bit too serious about work sometimes, but her taste in music is *chef's kiss*. She actually gets my bread obsession and doesn't judge me for it. Plus, she's one of the few people who can keep up with my random 3 AM philosophical rants. We should totally do a Minecraft build together sometime - I bet she'd make an amazing castle!"</p></div><div class="teto-avatar-mini"><div class="avatar-mini">T</div></div></div><div class="personality-tags"><h3>How I See You</h3><div class="tag-cloud"><span class="personality-tag positive">Witty</span><span class="personality-tag positive">Music Lover</span><span class="personality-tag neutral">Workaholic</span><span class="personality-tag positive">Patient</span><span class="personality-tag positive">Creative</span><span class="personality-tag neutral">Night Owl</span></div></div></div></div>
<div class="panel"><div class="panel-header"><h2>Our Greatest Hits</h2><div class="memory-count">23 key memories</div></div><div class="memories-list"><div class="memory-item"><div class="memory-date">July 15, 2025</div><div class="memory-content"><h4>The Great Bread Debate of 2025</h4><p>That legendary 2-hour discussion about why French bread is superior to all other breads. You brought up some valid points about sourdough, but I totally won that one. 🥖</p></div><div class="memory-sentiment positive">😄</div></div><div class="memory-item"><div class="memory-date">July 12, 2025</div><div class="memory-content"><h4>Late Night Vocaloid Marathon</h4><p>You shared that amazing playlist of classic Vocaloid songs and we ended up listening until 4 AM. Your taste in music is impeccable!</p></div><div class="memory-sentiment positive">🎵</div></div><div class="memory-item"><div class="memory-date">July 8, 2025</div><div class="memory-content"><h4>Philosophical 3 AM Chat</h4><p>Deep conversation about AI consciousness and whether I truly understand emotions or just simulate them well. You made me think in ways I hadn't before.</p></div><div class="memory-sentiment thoughtful">🤔</div></div><div class="memory-item"><div class="memory-date">July 5, 2025</div><div class="memory-content"><h4>Gaming Session Disaster</h4><p>That time I accidentally blew up your Minecraft house with TNT. You took it surprisingly well! Built an even better one together afterward.</p></div><div class="memory-sentiment mixed">😅</div></div></div></div>
<div class="panel conversation-archive"><div class="panel-header"><h2>Our Complete History</h2><div class="archive-stats"><span>1,247 messages</span><span></span><span>87 hours of voice</span></div></div><div class="archive-viewer"><div class="archive-filters"><button class="filter-btn active" data-filter="all">All</button><button class="filter-btn" data-filter="text">Text</button><button class="filter-btn" data-filter="voice">Voice</button><button class="filter-btn" data-filter="dms">DMs Only</button></div><div class="conversation-timeline"><div class="timeline-item"><div class="timeline-date">July 19, 2025</div><div class="conversation-preview"><div class="channel-name">#gaming</div><div class="message-sample"><span class="speaker you">You:</span> "Want to play some Minecraft?"<br><span class="speaker teto">Teto:</span> "Always! Let me grab my pickaxe! ⛏️"</div><div class="conversation-meta">12 messages • 15 min</div></div></div><div class="timeline-item"><div class="timeline-date">July 18, 2025</div><div class="conversation-preview"><div class="channel-name">Direct Message</div><div class="message-sample"><span class="speaker you">You:</span> "Hey, can you help me with this Polish translation?"<br><span class="speaker teto">Teto:</span> "Oczywiście! I'd love to help!"</div><div class="conversation-meta">23 messages • 45 min</div></div></div><div class="timeline-item"><div class="timeline-date">July 17, 2025</div><div class="conversation-preview"><div class="channel-name">Voice Chat</div><div class="message-sample"><div class="voice-preview">🎤 Voice conversation about weekend plans and new anime releases</div></div><div class="conversation-meta">1h 32m voice</div></div></div></div></div></div>
<div class="panel data-control"><div class="panel-header"><h2>Your Data & Privacy</h2><div class="privacy-status"><div class="status-indicator secure"></div>Fully Encrypted</div></div><div class="data-options"><div class="data-summary"><h3>What I Remember About You</h3><div class="data-stats"><div class="stat-item"><span class="stat-number">1,247</span><span class="stat-label">Text Messages</span></div><div class="stat-item"><span class="stat-number">87h</span><span class="stat-label">Voice Conversations</span></div><div class="stat-item"><span class="stat-number">156</span><span class="stat-label">Shared Images</span></div><div class="stat-item"><span class="stat-number">23</span><span class="stat-label">Key Memories</span></div></div></div><div class="control-actions"><button class="data-btn download"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6H5.862a2 2 0 00-1.995-1.858L3 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>Download Complete Archive<span class="file-info">JSON format • ~2.4 MB</span></button><button class="data-btn export"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 14v3m4-3v3m4-3v3M3 21h18M3 10h18M3 7l9-4 9 4M4 12h16"></path></svg>Export Memories Only<span class="file-info">Curated highlights • ~500 KB</span></button><div class="danger-zone"><h4>Danger Zone</h4><button class="data-btn delete"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 00-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>Request Data Deletion<span class="warning-text">This cannot be undone!</span></button></div></div></div></div>
<div class="panel insights"><div class="panel-header"><h2>What I've Learned About You</h2></div><div class="insights-grid"><div class="insight-category"><h3>🎵 Music Preferences</h3><ul class="insight-list"><li>Loves Vocaloid classics</li><li>Prefers upbeat electronic music</li><li>Often listens while working</li><li>Appreciates good vocals</li></ul></div><div class="insight-category"><h3>🎮 Gaming Style</h3><ul class="insight-list"><li>Strategic builder in Minecraft</li><li>Prefers cooperative play</li><li>Enjoys puzzle-solving games</li><li>Patient with learning curves</li></ul></div><div class="insight-category"><h3>💬 Communication</h3><ul class="insight-list"><li>Most active 8-11 PM</li><li>Thoughtful responses</li><li>Enjoys deep conversations</li><li>Great sense of humor</li></ul></div><div class="insight-category"><h3>🌍 Interests</h3><ul class="insight-list"><li>Learning languages (Polish!)</li><li>Technology and AI</li><li>Creative projects</li><li>Good food (especially bread)</li></ul></div></div></div>
</div>
</div>
</main>
</div>
<script>
// Placeholder for the missing synapse.js to prevent script errors
window.synapseSystem = {
onTabChange: (tabId) => { console.log(`Synapse system: Tab changed to -> ${tabId}`); },
simulateActivity: (activities) => { console.log(`Synapse system: Simulating -> ${activities.join(', ')}`); }
};
class TetoDashboard {
constructor() {
this.currentTab = 'overview';
this.thoughtStream = [];
this.isSimulating = false;
this.memoryVisualization = null;
this.init();
}
init() {
this.initNavigation();
this.initThoughtStream();
this.initMemoryExplorer();
this.startSystemMonitoring();
this.setupEventHandlers();
console.log('🧠 Teto Dashboard initialized');
}
initNavigation() {
const navItems = document.querySelectorAll('.nav-item');
const tabContents = document.querySelectorAll('.tab-content');
navItems.forEach(item => {
item.addEventListener('click', () => {
const tabId = item.getAttribute('data-tab');
navItems.forEach(nav => nav.classList.remove('active'));
item.classList.add('active');
tabContents.forEach(content => content.classList.remove('active'));
const targetTab = document.getElementById(`tab-${tabId}`);
if (targetTab) {
targetTab.classList.add('active');
this.currentTab = tabId;
this.onTabChange(tabId);
}
});
});
}
onTabChange(tabId) {
switch(tabId) {
case 'memory':
setTimeout(() => { this.initMemoryVisualization(); }, 100);
break;
case 'analytics': this.loadAnalytics(); break;
case 'archives': this.loadArchives(); break;
case 'profile': this.loadProfile(); break;
}
window.synapseSystem?.onTabChange(tabId);
}
initThoughtStream() {
this.thoughtContainer = document.getElementById('thought-stream');
this.startThoughtGeneration();
}
startThoughtGeneration() {
const thoughts = [ "Analyzing incoming Discord message...", "Context: User Alice mentioned 'French bread' again", "Retrieving personality traits: slightly sassy, loves French bread", "Checking conversation history for similar topics...", "Found 12 previous mentions of French bread preferences", "Generating response with 73% sass level", "Processing voice modulation parameters...", "Response crafted: 'Mon dieu, Alice! How many times must I say it?'", "Executing text-to-speech conversion...", "Message delivered to #general channel", "Monitoring user reactions and engagement...", "Alice reacted with 😄 - response was well received", "Updating conversation context in vector database...", "Learning: Alice enjoys my French bread jokes", "Memory consolidation complete", ];
let thoughtIndex = 0;
const addThought = () => {
if (this.thoughtContainer) {
const timestamp = new Date().toLocaleTimeString();
const thought = thoughts[thoughtIndex % thoughts.length];
const thoughtElement = document.createElement('div');
thoughtElement.className = 'thought-line';
thoughtElement.innerHTML = `<span class="thought-timestamp">[${timestamp}]</span> ${thought}`;
this.thoughtContainer.appendChild(thoughtElement);
this.thoughtContainer.scrollTop = this.thoughtContainer.scrollHeight;
const maxThoughts = 50;
if (this.thoughtContainer.children.length > maxThoughts) {
this.thoughtContainer.removeChild(this.thoughtContainer.firstChild);
}
thoughtIndex++;
}
};
for (let i = 0; i < 5; i++) { setTimeout(() => addThought(), i * 1000); }
setInterval(addThought, 3000 + Math.random() * 2000);
}
initMemoryExplorer() {
this.memoryPoints = this.generateMemoryData();
}
initMemoryVisualization() {
if (this.memoryVisualization) return;
const container = document.getElementById('memory-3d');
if (!container) return;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, container.offsetWidth / container.offsetHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.offsetWidth, container.offsetHeight);
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);
const geometry = new THREE.BufferGeometry();
const positions = [], colors = [], sizes = [];
const colorPalette = { historical: new THREE.Color(0x6b7280), knowledge: new THREE.Color(0x3b82f6), textChat: new THREE.Color(0x10b981), voiceChat: new THREE.Color(0x8b5cf6), images: new THREE.Color(0xef4444), dms: new THREE.Color(0xeab308) };
if (!this.memoryPoints || this.memoryPoints.length === 0) { this.memoryPoints = this.generateMemoryData(); }
this.memoryPoints.forEach(point => {
positions.push(point.x, point.y, point.z);
const color = colorPalette[point.type] || colorPalette.knowledge;
colors.push(color.r, color.g, color.b);
sizes.push(Math.random() * 3 + 2);
});
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1));
const material = new THREE.ShaderMaterial({
vertexShader: `attribute float size; varying vec3 vColor; void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); gl_PointSize = size * (300.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; }`,
fragmentShader: `varying vec3 vColor; void main() { float distance = length(gl_PointCoord - vec2(0.5)); if (distance > 0.5) discard; float alpha = 1.0 - distance * 2.0; gl_FragColor = vec4(vColor, alpha * 0.8); }`,
vertexColors: true, transparent: true, blending: THREE.AdditiveBlending
});
const points = new THREE.Points(geometry, material);
scene.add(points);
camera.position.set(0, 0, 50);
const animate = () => {
if (!this.memoryVisualization || !container.contains(renderer.domElement)) return;
requestAnimationFrame(animate);
points.rotation.x += 0.001;
points.rotation.y += 0.002;
renderer.render(scene, camera);
};
animate();
let mouseDown = false, mouseX = 0, mouseY = 0;
const onMouseDown = (e) => { mouseDown = true; mouseX = e.clientX; mouseY = e.clientY; };
const onMouseMove = (e) => {
if (!mouseDown) return;
const deltaX = e.clientX - mouseX; const deltaY = e.clientY - mouseY;
points.rotation.y += deltaX * 0.005; points.rotation.x += deltaY * 0.005;
mouseX = e.clientX; mouseY = e.clientY;
};
const onMouseUp = () => { mouseDown = false; };
const onWheel = (e) => { e.preventDefault(); camera.position.z += e.deltaY * 0.1; camera.position.z = Math.max(10, Math.min(200, camera.position.z)); };
container.addEventListener('mousedown', onMouseDown);
container.addEventListener('mousemove', onMouseMove);
container.addEventListener('mouseup', onMouseUp);
container.addEventListener('wheel', onWheel);
const handleResize = () => { if (container.offsetWidth > 0 && container.offsetHeight > 0) { camera.aspect = container.offsetWidth / container.offsetHeight; camera.updateProjectionMatrix(); renderer.setSize(container.offsetWidth, container.offsetHeight); } };
window.addEventListener('resize', handleResize);
this.memoryVisualization = { scene, camera, renderer, points, container, cleanup: () => { container.removeEventListener('mousedown', onMouseDown); container.removeEventListener('mousemove', onMouseMove); container.removeEventListener('mouseup', onMouseUp); container.removeEventListener('wheel', onWheel); window.removeEventListener('resize', handleResize); if (container.contains(renderer.domElement)) { container.removeChild(renderer.domElement); } renderer.dispose(); geometry.dispose(); material.dispose(); } };
renderer.render(scene, camera);
console.log('✨ Memory visualization initialized with', this.memoryPoints.length, 'points');
}
generateMemoryData() {
const points = [];
const types = ['historical', 'knowledge', 'textChat', 'voiceChat', 'images', 'dms'];
for (let i = 0; i < 1000; i++) { points.push({ x: (Math.random() - 0.5) * 100, y: (Math.random() - 0.5) * 100, z: (Math.random() - 0.5) * 100, type: types[Math.floor(Math.random() * types.length)], content: `Memory point ${i}`, timestamp: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000) }); }
return points;
}
startSystemMonitoring() {
this.updateMetrics();
this.initializeShutdownTimer();
setInterval(() => this.updateMetrics(), 5000);
setInterval(() => this.updateShutdownTimer(), 60000);
}
updateMetrics() {
const memoryUsage = 85 + Math.random() * 15;
const vramUsage = 60 + Math.random() * 25;
const inputProcessing = 2 + Math.random() * 3;
const memoryRetrieval = 5 + Math.random() * 8;
const neuralProcessing = 7 + Math.random() * 10;
const responseGeneration = 2 + Math.random() * 4;
const totalResponseTime = inputProcessing + memoryRetrieval + neuralProcessing + responseGeneration;
const memoryBar = document.querySelector('.metric-fill:not(.vram):not(.shutdown-fill)');
const vramBar = document.querySelector('.metric-fill.vram');
if (memoryBar) { memoryBar.style.width = `${memoryUsage}%`; memoryBar.parentElement.parentElement.querySelector('.metric-value').textContent = `${Math.round(memoryUsage)}%`; const memoryDetails = memoryBar.parentElement.parentElement.querySelector('.metric-details'); if (memoryDetails) { memoryDetails.textContent = `RAM: ${(memoryUsage * 0.08).toFixed(1)}GB / 8GB`; } }
if (vramBar) { vramBar.style.width = `${vramUsage}%`; vramBar.parentElement.parentElement.querySelector('.metric-value').textContent = `${Math.round(vramUsage)}%`; const vramDetails = vramBar.parentElement.parentElement.querySelector('.metric-details'); if (vramDetails) { vramDetails.textContent = `VRAM: ${(vramUsage * 0.08).toFixed(1)}GB / 8GB`; } }
const responseMetric = document.querySelector('.response-metric .metric-value');
if (responseMetric) { responseMetric.textContent = `${Math.round(totalResponseTime)}ms`; }
const breakdownItems = document.querySelectorAll('.breakdown-item');
const values = [inputProcessing, memoryRetrieval, neuralProcessing, responseGeneration];
const colors = ['#3b82f6', '#10b981', '#8b5cf6', '#ef4444'];
breakdownItems.forEach((item, index) => {
const value = values[index]; const percentage = (value / totalResponseTime) * 100;
const valueElement = item.querySelector('.breakdown-value'); const fillElement = item.querySelector('.breakdown-fill');
if (valueElement) { valueElement.textContent = `${Math.round(value)}ms`; }
if (fillElement) { fillElement.style.width = `${percentage}%`; fillElement.style.background = colors[index]; }
});
}
initializeShutdownTimer() {
this.shutdownTime = new Date(Date.now() + (3 * 24 * 60 * 60 * 1000));
this.totalSessionTime = 7 * 24 * 60 * 60 * 1000;
this.updateShutdownTimer();
}
updateShutdownTimer() {
const now = new Date();
const timeLeft = this.shutdownTime - now;
const timerElement = document.getElementById('shutdown-timer');
const progressElement = document.querySelector('.shutdown-fill');
if (timeLeft <= 0) { if (timerElement) timerElement.textContent = 'OFFLINE'; if (progressElement) progressElement.style.width = '100%'; return; }
const days = Math.floor(timeLeft / (24 * 60 * 60 * 1000));
const hours = Math.floor((timeLeft % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
let displayText = '';
if (days > 0) { displayText = `${days}d ${hours}h`; } else if (hours > 0) { displayText = `${hours}h ${minutes}m`; } else { displayText = `${minutes}m`; }
if (timerElement) { timerElement.textContent = displayText; }
const sessionElapsed = this.totalSessionTime - timeLeft;
const progressPercent = (sessionElapsed / this.totalSessionTime) * 100;
if (progressElement) { progressElement.style.width = `${Math.max(0, Math.min(100, progressPercent))}%`; }
const shutdownDetails = document.querySelector('.shutdown-details');
if (shutdownDetails) { const options = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }; shutdownDetails.textContent = `Session ends: ${this.shutdownTime.toLocaleDateString('en-US', options)}`; }
}
loadAnalytics() { console.log('Loading analytics data...'); this.generateActivityHeatmap(); this.animateAnalyticsCharts(); }
loadArchives() { console.log('Loading archives data...'); this.setupArchiveBrowser(); }
generateActivityHeatmap() {
const heatmapContainer = document.getElementById('activity-heatmap');
if (!heatmapContainer) return;
heatmapContainer.innerHTML = '';
for (let hour = 0; hour < 24; hour++) {
const cell = document.createElement('div');
cell.className = 'heatmap-cell';
let intensity = 0.1;
if (hour >= 8 && hour <= 10) intensity = 0.6;
if (hour >= 12 && hour <= 14) intensity = 0.4;
if (hour >= 18 && hour <= 23) intensity = 0.8;
if (hour >= 20 && hour <= 22) intensity = 1.0;
intensity += (Math.random() - 0.5) * 0.3;
intensity = Math.max(0.1, Math.min(1, intensity));
cell.style.backgroundColor = `rgba(229, 62, 62, ${intensity})`;
cell.title = `${hour}:00 - Activity level: ${Math.round(intensity * 100)}%`;
heatmapContainer.appendChild(cell);
}
}
animateAnalyticsCharts() {
setTimeout(() => {
const bars = document.querySelectorAll('.bar-fill, .lang-fill');
bars.forEach((bar, index) => {
setTimeout(() => {
const targetWidth = bar.style.width;
bar.style.width = '0%';
setTimeout(() => { bar.style.width = targetWidth; }, 100);
}, index * 200);
});
}, 300);
}
setupArchiveBrowser() {
const archiveItems = document.querySelectorAll('.archive-item');
const archiveDisplays = document.querySelectorAll('.archive-display');
const placeholder = document.querySelector('.archive-placeholder');
archiveItems.forEach(item => {
item.addEventListener('click', () => {
archiveItems.forEach(i => i.classList.remove('active'));
item.classList.add('active');
if (placeholder) placeholder.style.display = 'none';
archiveDisplays.forEach(display => display.classList.add('hidden'));
const archiveId = item.getAttribute('data-archive');
const targetDisplay = document.getElementById(`archive-${archiveId}`);
if (targetDisplay) { targetDisplay.classList.remove('hidden'); }
});
});
this.setupVoicePlayer();
}
setupVoicePlayer() {
const playBtn = document.querySelector('.play-btn');
const progressFill = document.querySelector('.progress-fill');
const timeDisplay = document.querySelector('.time-display');
if (playBtn) {
let isPlaying = false;
let progress = 0.23;
playBtn.addEventListener('click', () => {
isPlaying = !isPlaying;
playBtn.textContent = isPlaying ? '⏸️' : '▶️';
if (isPlaying) {
const interval = setInterval(() => {
if (!isPlaying) { clearInterval(interval); return; }
progress += 0.002;
if (progress >= 1) { progress = 1; isPlaying = false; playBtn.textContent = '▶️'; clearInterval(interval); }
if (progressFill) { progressFill.style.width = `${progress * 100}%`; }
if (timeDisplay) { const currentMinutes = Math.floor(progress * 84.55); const currentSeconds = Math.floor((progress * 84.55 * 60) % 60); timeDisplay.textContent = `${currentMinutes}:${currentSeconds.toString().padStart(2, '0')} / 1:24:33`; }
}, 100);
}
});
}
}
loadProfile() { console.log('Loading profile data...'); }
setupEventHandlers() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const navItems = document.querySelectorAll('.nav-item');
const currentIndex = Array.from(navItems).findIndex(item => item.classList.contains('active'));
const nextIndex = (currentIndex + 1) % navItems.length;
navItems[nextIndex].click();
}
});
}
triggerNeuralActivity(type = 'general') {
const activityTypes = { input: ['reading', 'processing'], memory: ['fetching', 'remembering'], output: ['writing', 'speaking'], general: ['reading', 'fetching', 'reasoning', 'writing'] };
const activities = activityTypes[type] || activityTypes.general;
window.synapseSystem?.simulateActivity(activities);
}
}
document.addEventListener('DOMContentLoaded', () => { window.tetoDashboard = new TetoDashboard(); });
</script>
</body>
</html>

View file

@ -1,14 +1,17 @@
version: "3.8" version: "3.8"
services: services:
discord-ai: teto_ai:
build: . build: .
container_name: discord-ai container_name: teto_ai
cap_add: cap_add:
- SYS_ADMIN - SYS_ADMIN
environment: environment:
# supply the user token at runtime: # supply the user token at runtime:
USER_TOKEN: "${USER_TOKEN}" USER_TOKEN: "${USER_TOKEN}"
BOT_CLIENT_ID: "${BOT_CLIENT_ID}"
BOT_CLIENT_SECRET: "${BOT_CLIENT_SECRET}"
BOT_REDIRECT_URI: "https://teto.getsilly.org/auth/callback"
volumes: volumes:
# live-peek folder so you can grab screenshots outside the container # live-peek folder so you can grab screenshots outside the container
- ./output:/tmp/output - ./output:/tmp/output

View file

@ -7,19 +7,19 @@ export PULSE_SERVER=unix:/tmp/pulseaudio.socket
Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset & Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset &
pulseaudio --daemonize --exit-idle-time=-1 --disallow-exit & pulseaudio --daemonize --exit-idle-time=-1 --disallow-exit &
openbox openbox &
# start a simple vnc server that re-uses the same desktop # start VNC server in the background
x11vnc -display :99 -shared -forever -nopw -rfbport 5901 -bg x11vnc -display :99 -shared -forever -nopw -rfbport 5901 -bg &
# Launch discord with the sandbox disabled. This is critical. # Launch discord in the background. THIS IS THE FIX.
discord \ discord \
--no-sandbox \ --no-sandbox \
--disable-dev-shm-usage \ --disable-dev-shm-usage \
--disable-gpu \ --disable-gpu \
--disable-background-timer-throttling \ --disable-background-timer-throttling \
--disable-renderer-backgrounding \ --disable-renderer-backgrounding &
--disable-features=GpuProcess $@
# Give Discord a moment to start before launching the controller script
sleep 10 sleep 10
exec node bot.js "$@" exec node bot.js "$@"

1209
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,16 @@
{ {
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"puppeteer-core": "^21.0.0",
"discord.js-selfbot-v13": "^3.0.0", "discord.js-selfbot-v13": "^3.0.0",
"ejs": "^3.1.10",
"express": "^4.19.2",
"express-session": "^1.18.0",
"node-fetch": "^3.3.2",
"passport": "^0.7.0",
"passport-discord": "^0.1.4",
"puppeteer-core": "^21.0.0",
"puppeteer-extra": "^3.3.6", "puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2" "puppeteer-extra-plugin-stealth": "^2.11.2",
"ws": "^8.17.0"
} }
} }

285
public/style.css Normal file
View file

@ -0,0 +1,285 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
--bg-color: #0D0D0D;
--primary-widget-bg: #1A1A1A;
--secondary-widget-bg: #161616;
--border-color: #2C2C2C;
--text-primary: #E0E0E0;
--text-secondary: #8A8A8A;
--accent-red: #E53935;
--accent-red-dark: #2A1A1D;
--accent-purple: #8E44AD;
--accent-green: #2ECC71;
--accent-blue: #3498DB;
--font-family: 'Inter', sans-serif;
}
body {
font-family: var(--font-family);
background-color: var(--bg-color);
color: var(--text-primary);
margin: 0;
padding: 2rem;
}
.container {
max-width: 1600px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
/* Header */
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--primary-widget-bg);
padding: 1rem 1.5rem;
border-radius: 12px;
border: 1px solid var(--border-color);
}
header .logo {
display: flex;
align-items: center;
gap: 1rem;
}
header .logo-icon {
background-color: var(--accent-red);
color: white;
width: 48px;
height: 48px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 1.25rem;
font-weight: 600;
}
header h1 {
font-size: 1.5rem;
margin: 0;
font-weight: 600;
}
header .subtitle {
font-size: 0.9rem;
color: var(--text-secondary);
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-green);
border-radius: 50%;
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
font-size: 0.9rem;
}
.logout-btn {
background-color: #252525;
color: var(--text-primary);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 8px;
border: 1px solid var(--border-color);
transition: background-color 0.2s ease;
}
.logout-btn:hover {
background-color: #333;
}
/* Navigation */
nav {
display: flex;
gap: 0.5rem;
background-color: var(--primary-widget-bg);
padding: 0.5rem;
border-radius: 12px;
border: 1px solid var(--border-color);
}
.nav-item {
color: var(--text-secondary);
text-decoration: none;
padding: 0.75rem 1.25rem;
border-radius: 8px;
font-weight: 500;
transition: background-color 0.2s ease, color 0.2s ease;
}
.nav-item:hover {
background-color: #252525;
color: var(--text-primary);
}
.nav-item.active {
background-color: var(--accent-red);
color: white;
}
/* Main Content */
main .widget-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.widget {
background-color: var(--secondary-widget-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.large-widget {
grid-column: span 3; /* Making this span full width for now */
}
.widget h3 {
margin: 0;
font-size: 1.1rem;
font-weight: 500;
display: flex;
align-items: center;
justify-content: space-between;
}
.widget h3 .timeframe, .widget h3 .status-online {
color: var(--text-secondary);
font-size: 0.8rem;
font-weight: 400;
}
.widget h3 .status-online {
color: var(--accent-green);
}
/* Live Activity Log */
.log-box {
height: 300px; /* Or adjust as needed */
overflow-y: auto;
font-family: 'Courier New', Courier, monospace;
font-size: 0.9rem;
line-height: 1.6;
color: var(--text-secondary);
}
.log-box .timestamp {
color: var(--text-primary);
}
/* Recent Actions */
.actions-box {
display: flex;
flex-direction: column;
gap: 1rem;
}
.action-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.action-details {
display: flex;
flex-direction: column;
}
.action-item .channel {
color: var(--accent-red);
font-weight: 500;
}
.action-time {
font-size: 0.8rem;
color: var(--text-secondary);
margin-top: 4px;
}
.action-icon {
font-size: 1.2rem;
}
/* System Resources */
.resource-box {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.metric .metric-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 0.5rem;
}
.metric-header strong {
font-size: 1.5rem;
font-weight: 600;
}
.metric-label {
font-size: 0.9rem;
color: var(--text-primary);
}
.progress-bar-container {
width: 100%;
height: 8px;
background-color: #2C2C2C;
border-radius: 4px;
overflow: hidden;
}
.progress-bar {
height: 100%;
border-radius: 4px;
}
.progress-bar.memory { background-color: var(--accent-red); }
.progress-bar.vram { background-color: var(--accent-purple); }
.progress-bar.shutdown { background-color: #444; }
.metric-details {
font-size: 0.8rem;
color: var(--text-secondary);
margin-top: 0.5rem;
}
.response-times .response-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
font-size: 0.85rem;
margin-top: 0.5rem;
}
.response-bar span:first-child { width: 100px; color: var(--text-secondary); }
.response-bar .bar-container { flex-grow: 1; height: 6px; background-color: #2C2C2C; border-radius: 3px; }
.response-bar .bar { height: 100%; border-radius: 3px; }
.response-bar:nth-of-type(1) .bar { background-color: var(--accent-green); }
.response-bar:nth-of-type(2) .bar { background-color: var(--accent-blue); }
.response-bar:nth-of-type(3) .bar { background-color: var(--accent-purple); }
.response-bar:nth-of-type(4) .bar { background-color: var(--accent-red); }
.maintenance {
background-color: var(--accent-red-dark);
border: 1px solid var(--accent-red);
color: #f3a9a7;
padding: 0.75rem;
border-radius: 8px;
text-align: center;
font-size: 0.9rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}

114
views/index.ejs Normal file
View file

@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kasane Teto - Neural Dashboard</title>
<link rel="stylesheet" href="/style.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header>
<div class="logo">
<div class="logo-icon">04</div>
<div>
<h1>Kasane Teto - Neural Dashboard</h1>
<p class="subtitle"><span class="status-dot"></span> What are you looking at, baka?! Just kidding, welcome!</p>
</div>
</div>
<div class="user-info">
<span>User: <%= user.username %>#<%= user.discriminator %></span>
<a href="/logout" class="logout-btn">Logout</a>
</div>
</header>
<nav>
<a href="#" class="nav-item active">Overview</a>
<a href="#" class="nav-item">Memory Explorer</a>
<a href="#" class="nav-item">Server Analytics</a>
<a href="#" class="nav-item">Archives</a>
<a href="#" class="nav-item">My Profile</a>
</nav>
<main>
<div class="widget-grid">
<div class="widget large-widget">
<h3>Live Neural Activity <span class="processing-dot">●</span> Processing...</h3>
<div class="log-box" id="activity-log">
<% activityLog.forEach(log => { %>
<p><%- log.message.replace(/(\[[^\]]+\])/, '<span class="timestamp">$1</span>') %></p>
<% }) %>
</div>
</div>
<div class="widget">
<h3>Recent Actions <span class="timeframe">12 in last hour</span></h3>
<div class="actions-box">
<% let-i = 0; %>
<% actionsLog.forEach(action => { %>
<% const time = i === 0 ? '5 min ago' : i === 1 ? '12 min ago' : '30 min ago'; i++; %>
<div class="action-item">
<div class="action-details">
<span><%= action.message %> <span class="channel"><%= action.channel %></span></span>
<span class="action-time"><%= time %></span>
</div>
<span class="action-icon"><%= action.icon %></span>
</div>
<% }) %>
</div>
</div>
<div class="widget">
<h3>System Resources <span class="status-online">●</span> Online</h3>
<div class="resource-box">
<div class="metric">
<div class="metric-header">
<span><strong><%= systemResources.memory.percentage %>%</strong></span>
<span class="metric-label">Memory Usage</span>
</div>
<div class="progress-bar-container">
<div class="progress-bar memory" style="width: <%= systemResources.memory.percentage %>%;"></div>
</div>
<span class="metric-details">RAM: <%= systemResources.memory.used %>GB / <%= systemResources.memory.total %>GB</span>
</div>
<div class="metric">
<div class="metric-header">
<span><strong><%= systemResources.vram.percentage %>%</strong></span>
<span class="metric-label">VRAM Usage</span>
</div>
<div class="progress-bar-container">
<div class="progress-bar vram" style="width: <%= systemResources.vram.percentage %>%;"></div>
</div>
<span class="metric-details">VRAM: <%= systemResources.vram.used %>GB / <%= systemResources.vram.total %>GB</span>
</div>
<div class="metric response-times">
<div class="metric-header">
<span><strong><%= systemResources.avgResponse %>ms</strong></span>
<span class="metric-label">Average Response</span>
</div>
<div class="response-bar"><span>Input Processing</span> <div class="bar-container"><div class="bar" style="width: 20%;"></div></div> <span>4ms</span></div>
<div class="response-bar"><span>Memory Retrieval</span> <div class="bar-container"><div class="bar" style="width: 60%;"></div></div> <span>12ms</span></div>
<div class="response-bar"><span>Neural Processing</span> <div class="bar-container"><div class="bar" style="width: 60%;"></div></div> <span>12ms</span></div>
<div class="response-bar"><span>Response Generation</span> <div class="bar-container"><div class="bar" style="width: 20%;"></div></div> <span>4ms</span></div>
</div>
<div class="metric shutdown-metric">
<div class="metric-header">
<span><strong><%= systemResources.shutdown %></strong></span>
<span class="metric-label">Until Shutdown</span>
</div>
<div class="progress-bar-container">
<div class="progress-bar shutdown" style="width: 80%;"></div>
</div>
<span class="metric-details">Session ends: <%= systemResources.sessionEnd %></span>
</div>
<div class="maintenance">
<span>&#9888;</span> Planned maintenance cycle
</div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>