jarvis/index.html

816 lines
27 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
<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 {
font-weight: bold;
margin-bottom: 5px;
padding: 5px;
border-radius: 3px;
}
.thought-summary.plan { background-color: #2c3e50; }
.thought-summary.decision { background-color: #34495e; }
.thought-summary.tool_call { background-color: #16a085; }
.thought-summary.tool_result { background-color: #27ae60; }
.thought-summary.think_more { background-color: #2980b9; }
.thought-summary.answer { background-color: #8e44ad; }
.thought-summary.reply { background-color: #f39c12; }
.thought-summary.thoughts { background-color: #f39c12; }
.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);
}
}
.conversation-history-container {
margin-top: 20px;
background-color: #222;
border-radius: 5px;
padding: 10px;
}
#conversation-history {
color: #fff;
font-family: 'Noto Sans Mono', monospace;
font-size: 12px;
}
.history-card {
background-color: #2c3e50;
border-radius: 5px;
padding: 10px;
margin-bottom: 10px;
}
.history-role {
font-weight: bold;
margin-bottom: 5px;
}
.history-content {
white-space: pre-wrap;
word-break: break-word;
}
.error-message {
background-color: #ff6b6b;
color: #fff;
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
}
.retrying {
background-color: #feca57;
color: #333;
}
#clear-history-button {
background-color: #e74c3c;
color: white;
border: none;
padding: 10px;
margin-bottom: 10px;
cursor: pointer;
font-family: 'Noto Sans Mono', monospace;
font-size: 14px;
border-radius: 5px;
}
#clear-history-button:hover {
background-color: #c0392b;
}
#chat-tabs {
display: flex;
background-color: #222;
padding: 10px 10px 0 10px;
}
.chat-tab {
background-color: #444;
color: #fff;
border: none;
padding: 10px 20px;
margin-right: 5px;
cursor: pointer;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.chat-tab.active {
background-color: #666;
}
#new-chat-button {
background-color: #27ae60;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.close-tab {
margin-left: 10px;
color: #ff6b6b;
cursor: pointer;
}
.thinking-section {
margin-bottom: 20px;
border-left: 2px solid #444;
padding-left: 10px;
}
</style>
</head>
<body>
<div id="main-container">
<div id="chat-area">
<div id="chat-tabs"></div>
<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>
<!-- Add this new section for conversation history -->
<div class="conversation-history-container">
<div class="graph-title">Conversation History</div>
<div id="conversation-history"></div>
</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');
const chatTabs = document.getElementById('chat-tabs');
let currentChatId = null;
let chats = {};
function createNewChat() {
const chatId = Date.now().toString();
chats[chatId] = {
messages: [],
thinkingSections: []
};
addChatTab(chatId);
switchToChat(chatId);
saveChats();
}
function addChatTab(chatId) {
const tab = document.createElement('button');
tab.classList.add('chat-tab');
tab.textContent = `Chat ${Object.keys(chats).length}`;
tab.onclick = () => switchToChat(chatId);
const closeButton = document.createElement('span');
closeButton.classList.add('close-tab');
closeButton.textContent = '×';
closeButton.onclick = (e) => {
e.stopPropagation();
closeChat(chatId);
};
tab.appendChild(closeButton);
chatTabs.insertBefore(tab, chatTabs.lastElementChild);
}
function switchToChat(chatId) {
currentChatId = chatId;
document.querySelectorAll('.chat-tab').forEach(tab => tab.classList.remove('active'));
document.querySelector(`.chat-tab:nth-child(${Object.keys(chats).indexOf(chatId) + 1})`).classList.add('active');
renderChat(chatId);
}
function closeChat(chatId) {
delete chats[chatId];
saveChats();
const tabToRemove = Array.from(chatTabs.children).find(tab => tab.textContent.includes(`Chat ${Object.keys(chats).indexOf(chatId) + 1}`));
if (tabToRemove) {
chatTabs.removeChild(tabToRemove);
}
if (currentChatId === chatId) {
const remainingChatIds = Object.keys(chats);
if (remainingChatIds.length > 0) {
switchToChat(remainingChatIds[0]);
} else {
createNewChat();
}
}
}
function renderChat(chatId) {
chatContainer.innerHTML = '';
const chat = chats[chatId];
chat.messages.forEach(message => addMessage(message.content, message.isUser));
chat.thinkingSections.forEach(section => {
const thinkingSection = createThinkingSection();
section.thoughts.forEach(thought => addThought(thought.type, thought.content, thought.details, thinkingSection));
});
}
function createThinkingSection() {
const section = document.createElement('div');
section.classList.add('thinking-section');
chatContainer.appendChild(section);
return section;
}
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;
if (currentChatId) {
chats[currentChatId].messages.push({ content: message, isUser: isUser });
saveChats();
}
}
function addThought(type, content, details = '', thinkingSection) {
const stepElement = document.createElement('div');
stepElement.classList.add('thought-summary', 'collapsible', type);
stepElement.textContent = type.charAt(0).toUpperCase() + type.slice(1).replace('_', ' ') + ':';
stepElement.onclick = toggleStepDetails;
const stepDetails = document.createElement('div');
stepDetails.classList.add('thought-details');
if (type === 'error') {
stepElement.classList.add('error-message');
if (content.includes('retrying')) {
stepElement.classList.add('retrying');
}
stepDetails.innerHTML = marked.parse(content + '\n\nDetails:\n```\n' + details + '\n```');
} else {
stepDetails.innerHTML = marked.parse(content);
}
thinkingSection.appendChild(stepElement);
thinkingSection.appendChild(stepDetails);
chatContainer.scrollTop = chatContainer.scrollHeight;
if (currentChatId) {
const currentThinkingSection = chats[currentChatId].thinkingSections[chats[currentChatId].thinkingSections.length - 1];
currentThinkingSection.thoughts.push({ type, content, details });
saveChats();
}
}
function toggleStepDetails() {
this.classList.toggle('open');
const details = this.nextElementSibling;
if (details) {
details.style.display = details.style.display === 'none' ? 'block' : 'none';
}
}
function saveChats() {
localStorage.setItem('chats', JSON.stringify(chats));
}
function loadChats() {
const storedChats = localStorage.getItem('chats');
if (storedChats) {
chats = JSON.parse(storedChats);
Object.keys(chats).forEach(chatId => addChatTab(chatId));
if (Object.keys(chats).length > 0) {
switchToChat(Object.keys(chats)[0]);
} else {
createNewChat();
}
} else {
createNewChat();
}
}
function sendMessage() {
const message = userInput.value.trim();
if (message && currentChatId) {
addMessage(message, true);
chats[currentChatId].thinkingSections.push({ thoughts: [] });
socket.emit('chat_request', {
message: message,
conversation_history: chats[currentChatId].messages.filter(m => !m.isUser).map(m => ({ role: 'assistant', content: m.content }))
.concat(chats[currentChatId].messages.filter(m => m.isUser).map(m => ({ role: 'user', content: m.content })))
});
userInput.value = '';
}
}
socket.on('thinking', (data) => {
if (currentChatId) {
const newThinkingSection = createThinkingSection();
chats[currentChatId].thinkingSections.push({ thoughts: [] });
addThought(data.step, 'Started', '', newThinkingSection);
}
});
socket.on('thought', (data) => {
if (currentChatId) {
const currentThinkingSection = chatContainer.querySelector('.thinking-section:last-child');
addThought(data.type, data.content, data.details, currentThinkingSection);
}
});
socket.on('chat_response', (data) => {
if (currentChatId) {
addMessage(data.response, false);
}
});
socket.on('error', (data) => {
if (currentChatId) {
const currentThinkingSection = chatContainer.querySelector('.thinking-section:last-child');
if (data.type === 'retrying') {
addThought('error', data.content, '', currentThinkingSection);
} else {
addThought('error', data.message, '', currentThinkingSection);
}
}
});
sendButton.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Add new chat button
const newChatButton = document.createElement('button');
newChatButton.id = 'new-chat-button';
newChatButton.textContent = '+ New Chat';
newChatButton.onclick = createNewChat;
chatTabs.appendChild(newChatButton);
// Load chats when the page loads
loadChats();
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
// Add this new function to update the conversation history
function updateConversationHistory(history) {
const conversationHistoryElement = document.getElementById('conversation-history');
conversationHistoryElement.innerHTML = '';
history.forEach(item => {
const card = document.createElement('div');
card.classList.add('history-card');
const role = document.createElement('div');
role.classList.add('history-role');
role.textContent = item.role.charAt(0).toUpperCase() + item.role.slice(1);
const content = document.createElement('pre');
content.classList.add('history-content');
content.innerHTML = hljs.highlightAuto(item.content).value;
card.appendChild(role);
card.appendChild(content);
conversationHistoryElement.appendChild(card);
});
}
// Add this new socket listener
socket.on('conversation_history', (data) => {
updateConversationHistory(data.history);
});
// Add event listener for the clear history button
clearHistoryButton.addEventListener('click', () => {
if (confirm('Are you sure you want to clear the conversation history?')) {
clearConversationHistory();
}
});
</script>
</body>
</html>