/**
* Real-Time Bug Tracker Dashboard
* Shows all auto-detected errors and fix progress
*/
(function() {
'use strict';
// Error state storage
window.bugTracker = {
errors: [],
fixesInProgress: new Map(),
fixesCompleted: new Map(),
activityLog: [], // New: stores AI activity stream
addError(error) {
// Skip 'info' type errors - they're for learning, not bugs
if (error.type === 'info') {
console.log('[BugTracker] Skipping info-type error:', error.message);
return null;
}
const errorId = this.generateErrorId(error);
const existingError = this.errors.find(e => e.id === errorId);
if (!existingError) {
const errorWithMeta = {
id: errorId,
...error,
detectedAt: new Date().toISOString(),
status: 'detected',
count: 1,
activity: [] // Activity for this error
};
this.errors.push(errorWithMeta);
this.updateDashboard();
// Auto-fix notification disabled - errors logged to dashboard only
// if (typeof showErrorNotification === 'function') {
// showErrorNotification(errorWithMeta);
// }
} else {
existingError.count++;
existingError.lastSeen = new Date().toISOString();
}
return errorId;
},
startFix(errorId) {
const error = this.errors.find(e => e.id === errorId);
if (error) {
error.status = 'fixing';
error.fixStartedAt = new Date().toISOString();
this.fixesInProgress.set(errorId, true);
this.addActivity(errorId, '🤖', 'AI agent started analyzing error...');
this.updateDashboard();
}
},
// Add activity to error's activity log
addActivity(errorId, icon, message, type = 'info') {
// Add to global activity log
this.activityLog.unshift({
errorId,
icon,
message,
type,
timestamp: new Date().toISOString()
});
// Keep only last 50 global activities
if (this.activityLog.length > 50) {
this.activityLog = this.activityLog.slice(0, 50);
}
// Add to specific error's activity
const error = this.errors.find(e => e.id === errorId);
if (error) {
if (!error.activity) error.activity = [];
error.activity.unshift({
icon,
message,
type,
timestamp: new Date().toISOString()
});
if (error.activity.length > 20) {
error.activity = error.activity.slice(0, 20);
}
}
this.updateActivityStream();
},
// Update the activity stream display
updateActivityStream() {
const stream = document.getElementById('activity-stream');
if (!stream) return;
// Show last 10 activities globally
const recentActivities = this.activityLog.slice(0, 10);
stream.innerHTML = recentActivities.map(activity => {
const timeAgo = this.getTimeAgo(activity.timestamp);
return `
${activity.icon}
${this.escapeHtml(activity.message)}
${timeAgo}
`;
}).join('');
},
completeFix(errorId, fixDetails) {
const error = this.errors.find(e => e.id === errorId);
if (error) {
error.status = 'fixed';
error.fixedAt = new Date().toISOString();
error.fixDetails = fixDetails;
this.fixesInProgress.delete(errorId);
this.fixesCompleted.set(errorId, true);
this.updateDashboard();
}
},
generateErrorId(error) {
const parts = [
error.type,
error.message.substring(0, 50),
error.filename || 'unknown'
];
return btoa(parts.join('::')).substring(0, 20);
},
updateDashboard() {
const dashboard = document.getElementById('bug-tracker-dashboard');
if (!dashboard) return;
const content = dashboard.querySelector('#bug-tracker-content');
const stats = dashboard.querySelector('#bug-tracker-stats');
if (!content) return;
// Update stats
if (stats) {
const totalErrors = this.errors.length;
const activeErrors = this.errors.filter(e => e.status === 'detected').length;
const fixingErrors = this.errors.filter(e => e.status === 'fixing').length;
const fixedErrors = this.errors.filter(e => e.status === 'fixed').length;
stats.innerHTML = `
Total:
${totalErrors}
🔴 Active:
${activeErrors}
🔧 Fixing:
${fixingErrors}
✅ Fixed:
${fixedErrors}
`;
}
// Sort errors: fixing first, then detected, then fixed
const sortedErrors = [...this.errors].sort((a, b) => {
const statusOrder = { 'fixing': 0, 'detected': 1, 'fixed': 2 };
return statusOrder[a.status] - statusOrder[b.status];
});
content.innerHTML = this.renderErrors(sortedErrors);
},
renderErrors(errors) {
if (errors.length === 0) {
return `
✨
No bugs detected!
The code is running smoothly
`;
}
return errors.map(error => this.renderError(error)).join('');
},
renderError(error) {
const statusIcons = {
'detected': '🔴',
'fixing': '🔧',
'fixed': '✅'
};
const statusClasses = {
'detected': 'status-detected',
'fixing': 'status-fixing',
'fixed': 'status-fixed'
};
const timeAgo = this.getTimeAgo(error.detectedAt || error.timestamp);
return `
${this.escapeHtml(error.message.substring(0, 100))}${error.message.length > 100 ? '...' : ''}
${error.filename ? `
📄 ${error.filename.split('/').pop()}:${error.line || ''}
` : ''}
${error.fixDetails ? `
✨ ${error.fixDetails}
` : ''}
${error.status === 'detected' ? `
` : ''}
`;
},
triggerManualFix(errorId) {
const error = this.errors.find(e => e.id === errorId);
if (!error) {
console.error('[BugTracker] Error not found:', errorId);
return;
}
// Report to server to trigger fix
fetch('/claude/api/log-error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...error,
manualTrigger: true
})
})
.then(res => {
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
return res.json();
})
.then(data => {
console.log('[BugTracker] Manual fix triggered:', data);
// Update error status to 'fixing'
this.startFix(errorId);
// Show success feedback
if (typeof showToast === 'function') {
showToast('Auto-fix agent triggered', 'success');
}
})
.catch(err => {
console.error('[BugTracker] Failed to trigger fix:', err);
if (typeof showToast === 'function') {
showToast('Failed to trigger auto-fix', 'error');
}
});
},
getTimeAgo(timestamp) {
const now = new Date();
const then = new Date(timestamp);
const diffMs = now - then;
const diffSecs = Math.floor(diffMs / 1000);
const diffMins = Math.floor(diffSecs / 60);
if (diffSecs < 60) return `${diffSecs}s ago`;
if (diffMins < 60) return `${diffMins}m ago`;
return `${Math.floor(diffMins / 60)}h ago`;
},
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
},
toggle() {
const dashboard = document.getElementById('bug-tracker-dashboard');
if (dashboard) {
dashboard.classList.toggle('visible');
dashboard.classList.toggle('hidden');
}
}
};
// Create dashboard UI
function createDashboard() {
// Create toggle button
const toggleBtn = document.createElement('button');
toggleBtn.id = 'bug-tracker-toggle';
toggleBtn.className = 'bug-tracker-toggle';
toggleBtn.innerHTML = `
🐛
0
`;
toggleBtn.onclick = () => window.bugTracker.toggle();
// Create dashboard
const dashboard = document.createElement('div');
dashboard.id = 'bug-tracker-dashboard';
dashboard.className = 'bug-tracker-dashboard hidden';
dashboard.innerHTML = `
`;
// Add styles
const style = document.createElement('style');
style.id = 'bug-tracker-styles';
style.textContent = `
.bug-tracker-toggle {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
cursor: pointer;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.bug-tracker-toggle:hover {
transform: scale(1.1);
box-shadow: 0 6px 30px rgba(102, 126, 234, 0.6);
}
.toggle-icon {
font-size: 24px;
}
.toggle-badge {
position: absolute;
top: -5px;
right: -5px;
background: #ff6b6b;
color: white;
font-size: 12px;
font-weight: bold;
padding: 2px 8px;
border-radius: 10px;
min-width: 20px;
text-align: center;
}
.bug-tracker-dashboard {
position: fixed;
top: 50%;
right: 20px;
transform: translateY(-50%);
width: 400px;
max-height: 80vh;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 12px;
box-shadow: 0 10px 60px rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
.bug-tracker-dashboard.hidden {
display: none;
}
.bug-tracker-dashboard.visible {
display: flex;
}
.bug-tracker-header {
padding: 16px 20px;
border-bottom: 1px solid #333;
display: flex;
justify-content: space-between;
align-items: center;
}
.bug-tracker-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 16px;
font-weight: 600;
color: #e0e0e0;
}
.bug-tracker-close {
background: none;
border: none;
color: #888;
font-size: 24px;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.bug-tracker-close:hover {
color: #e0e0e0;
}
.bug-tracker-content {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.bug-item {
background: #252525;
border: 1px solid #333;
border-radius: 8px;
padding: 12px;
margin-bottom: 12px;
transition: all 0.2s ease;
}
.bug-item:hover {
border-color: #4a9eff;
}
.bug-item.status-fixing {
border-color: #ffa94d;
background: #2a2520;
}
.bug-item.status-fixed {
border-color: #51cf66;
opacity: 0.7;
}
.bug-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.bug-status {
font-size: 12px;
font-weight: 600;
padding: 4px 8px;
border-radius: 4px;
}
.status-detected {
background: rgba(255, 107, 107, 0.2);
color: #ff6b6b;
}
.status-fixing {
background: rgba(255, 169, 77, 0.2);
color: #ffa94d;
}
.status-fixed {
background: rgba(81, 207, 102, 0.2);
color: #51cf66;
}
.bug-time {
font-size: 11px;
color: #888;
}
.bug-count {
background: #ff6b6b;
color: white;
font-size: 10px;
font-weight: bold;
padding: 2px 6px;
border-radius: 10px;
}
.bug-message {
color: #e0e0e0;
font-size: 13px;
margin-bottom: 8px;
}
.bug-location {
color: #888;
font-size: 11px;
font-family: monospace;
}
.bug-fix-details {
color: #51cf66;
font-size: 12px;
margin-top: 8px;
padding: 8px;
background: rgba(81, 207, 102, 0.1);
border-radius: 4px;
}
.bug-fix-btn {
width: 100%;
padding: 8px;
background: linear-gradient(135deg, #4a9eff 0%, #a78bfa 100%);
border: none;
border-radius: 6px;
color: white;
font-size: 13px;
font-weight: 600;
cursor: pointer;
margin-top: 8px;
}
.bug-fix-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(74, 158, 255, 0.4);
}
.bug-tracker-empty {
text-align: center;
padding: 40px 20px;
}
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-title {
font-size: 18px;
font-weight: 600;
color: #e0e0e0;
margin-bottom: 8px;
}
.empty-subtitle {
font-size: 14px;
color: #888;
}
.activity-stream-header {
padding: 12px 20px;
border-bottom: 1px solid #333;
background: rgba(255, 107, 107, 0.05);
}
.activity-title {
font-size: 13px;
font-weight: 600;
color: #ff6b6b;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.activity-stream {
max-height: 200px;
overflow-y: auto;
padding: 12px;
background: #0d0d0d;
border-bottom: 1px solid #333;
}
.activity-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
margin-bottom: 6px;
background: #1a1a1a;
border-radius: 6px;
border-left: 3px solid #4a9eff;
transition: all 0.2s ease;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.activity-item:hover {
background: #252525;
border-left-color: #a78bfa;
}
.activity-item.activity-error {
border-left-color: #ff6b6b;
background: rgba(255, 107, 107, 0.05);
}
.activity-item.activity-success {
border-left-color: #51cf66;
background: rgba(81, 207, 102, 0.05);
}
.activity-item.activity-warning {
border-left-color: #ffa94d;
background: rgba(255, 169, 77, 0.05);
}
.activity-icon {
font-size: 16px;
flex-shrink: 0;
}
.activity-message {
flex: 1;
font-size: 12px;
color: #e0e0e0;
line-height: 1.4;
}
.activity-time {
font-size: 10px;
color: #888;
flex-shrink: 0;
}
.bug-tracker-stats {
padding: 12px 20px;
border-bottom: 1px solid #333;
display: flex;
gap: 20px;
font-size: 12px;
background: #151515;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
color: #888;
}
.stat-value {
font-weight: 600;
color: #e0e0e0;
}
`;
document.head.appendChild(style);
document.body.appendChild(toggleBtn);
document.body.appendChild(dashboard);
// Auto-update error count badge
setInterval(() => {
const badge = document.getElementById('bug-count-badge');
if (badge) {
const activeErrors = window.bugTracker.errors.filter(e => e.status !== 'fixed').length;
badge.textContent = activeErrors;
badge.style.display = activeErrors > 0 ? 'block' : 'none';
}
}, 1000);
}
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createDashboard);
} else {
createDashboard();
}
console.log('[BugTracker] Real-time bug tracker initialized');
})();