<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DWS Intelligence</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"></script> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@400;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Noto Sans Mono', monospace; background-color: #000; color: #fff; margin: 0; padding: 0; display: flex; flex-direction: column; height: 100vh; overflow: hidden; } #chat-container { border: 2px solid #444; flex: 1; overflow-y: auto; padding: 10px; background-color: #111; box-sizing: border-box; } #input-container { display: flex; flex-direction: column; padding: 10px; background-color: #222; box-sizing: border-box; } #user-input { width: 100%; padding: 10px; background-color: #000; color: #fff; border: 1px solid #444; font-family: 'Noto Sans Mono', monospace; font-size: 16px; margin-bottom: 10px; box-sizing: border-box; } #send-button { width: 100%; padding: 10px; background-color: #444; color: #fff; border: none; cursor: pointer; font-family: 'Noto Sans Mono', monospace; font-size: 16px; box-sizing: border-box; } .message { margin-bottom: 10px; font-size: 16px; } .user-message { text-align: right; color: #0ff; } .bot-message { text-align: left; color: #fff; } .bot-message pre { background-color: #222; padding: 10px; border-radius: 5px; overflow-x: auto; } .bot-message code { font-family: 'Noto Sans Mono', monospace; font-size: 14px; } .thinking { font-style: italic; color: #888; } .thought-summary { cursor: pointer; color: #fff; margin-bottom: 5px; font-weight: bold; display: flex; align-items: center; } .thought-details { display: none; margin-left: 20px; border-left: 2px solid #444; padding-left: 10px; margin-bottom: 10px; white-space: pre-wrap; font-family: 'Noto Sans Mono', monospace; background-color: #222; } .collapsible::before { content: '▶ '; display: inline-block; transition: transform 0.3s; } .collapsible.open::before { transform: rotate(90deg); } .led { width: 10px; height: 10px; border-radius: 50%; background-color: #f00; margin-right: 10px; position: relative; } .led::after { content: ''; position: absolute; top: -5px; left: -5px; right: -5px; bottom: -5px; background-color: #f00; border-radius: 50%; filter: blur(5px); opacity: 0; transition: opacity 0.5s ease-in-out; } .led.blinking { animation: blink 1s step-start infinite; } .led.blinking::after { animation: glow 1s ease-in-out infinite alternate; } @keyframes blink { 50% { opacity: 0; } } @keyframes glow { 0% { opacity: 0; } 100% { opacity: 0.5; } } /* PDP-11 inspired styles */ #chat-container::-webkit-scrollbar { width: 12px; } #chat-container::-webkit-scrollbar-track { background: #222; } #chat-container::-webkit-scrollbar-thumb { background-color: #444; border-radius: 6px; border: 3px solid #222; } .pdp-panel { background-color: #333; border: 2px solid #555; border-radius: 5px; padding: 10px; margin-bottom: 10px; } .pdp-label { font-size: 14px; color: #888; margin-bottom: 5px; } #main-container { display: flex; height: 100vh; } #chat-area { flex: 1; display: flex; flex-direction: column; } #sidebar { width: 300px; background-color: #222; padding: 10px; box-sizing: border-box; overflow-y: auto; transition: transform 0.3s ease-in-out; } #sidebar.collapsed { transform: translateX(100%); } #sidebar-toggle { position: fixed; top: 10px; right: 10px; z-index: 1000; background-color: #444; color: #fff; border: none; padding: 5px 10px; cursor: pointer; } .graph-container { margin-bottom: 20px; height: 150px; } .graph-title { color: #888; font-size: 14px; margin-bottom: 5px; } @media (max-width: 768px) { #sidebar { position: fixed; right: 0; top: 0; bottom: 0; width: 100%; max-width: 300px; transform: translateX(100%); } #sidebar.collapsed { transform: translateX(0); } } </style> </head> <body> <div id="main-container"> <div id="chat-area"> <div id="chat-container"></div> <div id="input-container" class="pdp-panel"> <div class="pdp-label">INPUT:</div> <textarea id="user-input" placeholder="Type your message here..." rows="3"></textarea> <button id="send-button">EXECUTE</button> </div> </div> <button id="sidebar-toggle">Toggle Charts</button> <div id="sidebar" class="collapsed"> <div class="graph-container"> <div class="graph-title">CPU Load</div> <canvas id="cpuChart"></canvas> </div> <div class="graph-container"> <div class="graph-title">Memory Usage</div> <canvas id="memoryChart"></canvas> </div> <div class="graph-container"> <div class="graph-title">Disk I/O</div> <canvas id="diskChart"></canvas> </div> <div class="graph-container"> <div class="graph-title">GPU Load</div> <canvas id="gpuChart"></canvas> </div> <div class="graph-container"> <div class="graph-title">GPU Memory</div> <canvas id="gpuMemoryChart"></canvas> </div> </div> </div> <script> const socket = io(); const chatContainer = document.getElementById('chat-container'); const userInput = document.getElementById('user-input'); const sendButton = document.getElementById('send-button'); let thinkingElement = null; let thinkingDetails = null; let thinkingStartTime = null; function addMessage(message, isUser) { const messageElement = document.createElement('div'); messageElement.classList.add('message'); messageElement.classList.add(isUser ? 'user-message' : 'bot-message'); messageElement.innerHTML = isUser ? message : marked.parse(message); chatContainer.appendChild(messageElement); chatContainer.scrollTop = chatContainer.scrollHeight; } function startThinking() { thinkingElement = document.createElement('div'); thinkingElement.classList.add('thought-summary', 'collapsible'); const led = document.createElement('div'); led.classList.add('led', 'blinking'); const textNode = document.createTextNode('Thinking...'); thinkingElement.appendChild(led); thinkingElement.appendChild(textNode); thinkingElement.onclick = toggleThinkingDetails; thinkingDetails = document.createElement('div'); thinkingDetails.classList.add('thought-details'); chatContainer.appendChild(thinkingElement); chatContainer.appendChild(thinkingDetails); thinkingStartTime = Date.now(); chatContainer.scrollTop = chatContainer.scrollHeight; } function addThought(step, content) { if (thinkingDetails) { const stepElement = document.createElement('div'); stepElement.classList.add('thought-summary', 'collapsible'); stepElement.textContent = step; stepElement.onclick = toggleStepDetails; const stepDetails = document.createElement('div'); stepDetails.classList.add('thought-details'); stepDetails.innerHTML = content; thinkingDetails.appendChild(stepElement); thinkingDetails.appendChild(stepDetails); chatContainer.scrollTop = chatContainer.scrollHeight; } } function endThinking(thinkingTime) { if (thinkingElement) { const textNode = thinkingElement.childNodes[1]; textNode.nodeValue = `Thinking... (${thinkingTime}s)`; const led = thinkingElement.querySelector('.led'); led.classList.remove('blinking'); led.style.backgroundColor = '#0f0'; led.style.boxShadow = '0 0 10px #0f0'; thinkingStartTime = null; } } function toggleThinkingDetails() { this.classList.toggle('open'); const details = this.nextElementSibling; if (details) { details.style.display = details.style.display === 'none' ? 'block' : 'none'; } } function toggleStepDetails() { this.classList.toggle('open'); const details = this.nextElementSibling; if (details) { details.style.display = details.style.display === 'none' ? 'block' : 'none'; } } socket.on('thinking', (data) => { if (!thinkingElement) startThinking(); addThought(data.step, 'Started'); }); socket.on('thought', (data) => { addThought('Result', data.content); }); socket.on('chat_response', (data) => { endThinking(data.thinking_time); addMessage(data.response, false); }); socket.on('error', (data) => { endThinking(data.thinking_time); addMessage(`Error: ${data.message}`, false); }); function sendMessage() { const message = userInput.value.trim(); if (message) { addMessage(message, true); socket.emit('chat_request', { message: message }); userInput.value = ''; } } sendButton.addEventListener('click', sendMessage); userInput.addEventListener('keypress', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); const chartOptions = { type: 'line', options: { responsive: true, maintainAspectRatio: false, animation: false, elements: { line: { tension: 0 }, point: { radius: 0 } }, scales: { x: { type: 'time', time: { unit: 'second', displayFormats: { second: 'HH:mm:ss' } }, ticks: { display: false } }, y: { beginAtZero: true, max: 100, ticks: { callback: function(value) { return value + '%'; } } } }, plugins: { legend: { display: false } } } }; const cpuChart = new Chart(document.getElementById('cpuChart').getContext('2d'), { ...chartOptions, data: { datasets: [{ label: 'CPU Load', data: [], borderColor: 'rgb(75, 192, 192)', fill: false }] } }); const memoryChart = new Chart(document.getElementById('memoryChart').getContext('2d'), { ...chartOptions, data: { datasets: [{ label: 'Memory Usage', data: [], borderColor: 'rgb(255, 159, 64)', fill: false }] } }); const diskChart = new Chart(document.getElementById('diskChart').getContext('2d'), { ...chartOptions, options: { ...chartOptions.options, scales: { ...chartOptions.options.scales, y: { beginAtZero: true, ticks: { callback: function(value) { return (value / 1024 / 1024).toFixed(2) + ' MB/s'; } } } } }, data: { datasets: [{ label: 'Disk Read', data: [], borderColor: 'rgb(54, 162, 235)', fill: false }, { label: 'Disk Write', data: [], borderColor: 'rgb(255, 99, 132)', fill: false }] } }); const gpuChart = new Chart(document.getElementById('gpuChart').getContext('2d'), { ...chartOptions, data: { datasets: [{ label: 'GPU Load', data: [], borderColor: 'rgb(153, 102, 255)', fill: false }] } }); const gpuMemoryChart = new Chart(document.getElementById('gpuMemoryChart').getContext('2d'), { ...chartOptions, data: { datasets: [{ label: 'GPU Memory', data: [], borderColor: 'rgb(255, 206, 86)', fill: false }] } }); function updateCharts(data) { if (sidebar.classList.contains('collapsed')) return; const now = Date.now(); const thirtySecondsAgo = now - 30000; function updateChart(chart, value) { chart.data.datasets[0].data.push({x: now, y: value}); chart.data.datasets[0].data = chart.data.datasets[0].data.filter(point => point.x > thirtySecondsAgo); chart.update('none'); } updateChart(cpuChart, data.cpu_load); updateChart(memoryChart, data.memory_usage); updateChart(gpuChart, data.gpu_load); updateChart(gpuMemoryChart, data.gpu_memory); // Update disk chart (it has two datasets) diskChart.data.datasets[0].data.push({x: now, y: data.disk_read_rate}); diskChart.data.datasets[1].data.push({x: now, y: data.disk_write_rate}); diskChart.data.datasets[0].data = diskChart.data.datasets[0].data.filter(point => point.x > thirtySecondsAgo); diskChart.data.datasets[1].data = diskChart.data.datasets[1].data.filter(point => point.x > thirtySecondsAgo); diskChart.update('none'); } // Listen for system resource updates socket.on('system_resources', (data) => { updateCharts(data); }); const sidebar = document.getElementById('sidebar'); const sidebarToggle = document.getElementById('sidebar-toggle'); sidebarToggle.addEventListener('click', () => { sidebar.classList.toggle('collapsed'); }); function checkWindowSize() { if (window.innerWidth <= 768) { sidebar.classList.add('collapsed'); } else { sidebar.classList.remove('collapsed'); } } window.addEventListener('resize', checkWindowSize); checkWindowSize(); // Initial check </script> </body> </html>