<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debug Log Viewer - Hammerspace MCP</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', monospace;
background: #f8fafc;
color: #1e3a8a;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: #1e3a8a;
padding: 15px 20px;
border-bottom: 2px solid #3b82f6;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 18px;
color: white;
font-weight: normal;
}
.controls {
display: flex;
gap: 10px;
}
button {
background: #3b82f6;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-family: inherit;
}
button:hover {
background: #2563eb;
}
button.danger {
background: #c72e0f;
}
button.danger:hover {
background: #e03e1e;
}
.log-container {
flex: 1;
overflow-y: auto;
padding: 20px;
font-size: 13px;
line-height: 1.6;
}
.log-line {
margin-bottom: 2px;
white-space: pre-wrap;
word-break: break-all;
padding: 4px 8px;
border-radius: 3px;
}
.log-line:nth-child(even) {
background-color: #f1f5f9;
}
.log-line:nth-child(odd) {
background-color: #ffffff;
}
.log-line.error {
color: #dc2626;
background: rgba(220, 38, 38, 0.1) !important;
}
.log-line.warning {
color: #d97706;
background: rgba(217, 119, 6, 0.1) !important;
}
.log-line.info {
color: #1e3a8a;
}
.log-line.mcp {
color: #3b82f6;
font-weight: bold;
}
.log-line.hstk {
color: #1e40af;
font-weight: bold;
}
.log-line.tool {
color: #7c3aed;
background: rgba(124, 58, 237, 0.1) !important;
}
.timestamp {
color: #64748b;
margin-right: 10px;
}
.highlight {
background: rgba(255, 255, 0, 0.2);
animation: fadeOut 2s;
}
@keyframes fadeOut {
from { background: rgba(255, 255, 0, 0.3); }
to { background: transparent; }
}
.stats {
background: #1e3a8a;
padding: 10px 20px;
border-top: 1px solid #3b82f6;
display: flex;
gap: 30px;
font-size: 12px;
}
.stat {
color: #cbd5e1;
}
.stat-value {
color: white;
font-weight: bold;
margin-left: 5px;
}
.auto-scroll {
background: #3b82f6;
color: white;
padding: 10px 20px;
position: fixed;
bottom: 60px;
right: 20px;
border-radius: 20px;
font-size: 12px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.auto-scroll.disabled {
background: #94a3b8;
}
</style>
</head>
<body>
<div class="header">
<h1>🔍 Real-Time Debug Log Viewer</h1>
<div class="controls">
<button onclick="window.open('/', '_blank')">Open Main UI</button>
<button onclick="clearLogs()" class="danger">Clear Display</button>
<button onclick="toggleAutoScroll()" id="autoScrollBtn">Auto-Scroll: ON</button>
</div>
</div>
<div class="log-container" id="logContainer"></div>
<div class="stats">
<div class="stat">Lines: <span class="stat-value" id="lineCount">0</span></div>
<div class="stat">MCP Calls: <span class="stat-value" id="mcpCount">0</span></div>
<div class="stat">HSTK Ops: <span class="stat-value" id="hstkCount">0</span></div>
<div class="stat">Errors: <span class="stat-value" id="errorCount">0</span></div>
</div>
<script>
let autoScroll = true;
let lineCount = 0;
let mcpCount = 0;
let hstkCount = 0;
let errorCount = 0;
const eventSource = new EventSource('/api/logs/stream');
const logContainer = document.getElementById('logContainer');
eventSource.onmessage = function(event) {
const lines = event.data.split('\n');
lines.forEach(line => {
if (!line.trim()) return;
const logLine = document.createElement('div');
logLine.className = 'log-line';
// Classify the log line
if (line.includes('ERROR') || line.includes('Error')) {
logLine.classList.add('error');
errorCount++;
} else if (line.includes('WARNING') || line.includes('Warning')) {
logLine.classList.add('warning');
} else if (line.includes('INFO')) {
logLine.classList.add('info');
}
if (line.includes('MCP') || line.includes('mcp')) {
logLine.classList.add('mcp');
if (line.includes('CallToolRequest')) mcpCount++;
} else if (line.includes('HSTK') || line.includes('hstk') || line.includes('aiq_hstk')) {
logLine.classList.add('hstk');
if (line.includes('Tool called')) hstkCount++;
} else if (line.includes('Executing MCP tool')) {
logLine.classList.add('tool');
}
logLine.textContent = line;
logLine.classList.add('highlight');
logContainer.appendChild(logLine);
lineCount++;
// Update stats
document.getElementById('lineCount').textContent = lineCount;
document.getElementById('mcpCount').textContent = mcpCount;
document.getElementById('hstkCount').textContent = hstkCount;
document.getElementById('errorCount').textContent = errorCount;
// Auto-scroll to bottom if enabled
if (autoScroll) {
logContainer.scrollTop = logContainer.scrollHeight;
}
// Limit number of lines to prevent memory issues
if (logContainer.children.length > 1000) {
logContainer.removeChild(logContainer.firstChild);
}
});
};
eventSource.onerror = function(error) {
console.error('EventSource error:', error);
};
function clearLogs() {
if (confirm('Clear the log display? (This does not delete actual log files)')) {
logContainer.innerHTML = '';
lineCount = 0;
mcpCount = 0;
hstkCount = 0;
errorCount = 0;
document.getElementById('lineCount').textContent = '0';
document.getElementById('mcpCount').textContent = '0';
document.getElementById('hstkCount').textContent = '0';
document.getElementById('errorCount').textContent = '0';
}
}
function toggleAutoScroll() {
autoScroll = !autoScroll;
const btn = document.getElementById('autoScrollBtn');
btn.textContent = 'Auto-Scroll: ' + (autoScroll ? 'ON' : 'OFF');
btn.style.background = autoScroll ? '#0e639c' : '#6e6e6e';
}
// Scroll to bottom on manual scroll near bottom
logContainer.addEventListener('scroll', function() {
const isNearBottom = logContainer.scrollHeight - logContainer.scrollTop - logContainer.clientHeight < 100;
if (!autoScroll && isNearBottom) {
autoScroll = true;
document.getElementById('autoScrollBtn').textContent = 'Auto-Scroll: ON';
document.getElementById('autoScrollBtn').style.background = '#0e639c';
} else if (autoScroll && !isNearBottom) {
autoScroll = false;
document.getElementById('autoScrollBtn').textContent = 'Auto-Scroll: OFF';
document.getElementById('autoScrollBtn').style.background = '#6e6e6e';
}
});
</script>
</body>
</html>