616 lines
22 KiB
JavaScript
616 lines
22 KiB
JavaScript
// 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();
|
|
});
|