Files
SuperCharged-Claude-Code-Up…/public/claude-ide/chat-enhanced.js
uroma efb3ecfb19 feat: AI auto-fix bug tracker with real-time error monitoring
- Real-time error monitoring system with WebSocket
- Auto-fix agent that triggers on browser errors
- Bug tracker dashboard with floating button (🐛)
- Live activity stream showing AI thought process
- Fixed 4 JavaScript errors (SyntaxError, TypeError)
- Fixed SessionPicker API endpoint error
- Enhanced chat input with Monaco editor
- Session picker component for project management

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-21 10:53:11 +00:00

445 lines
15 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
/**
* Enhanced Chat Interface - Similar to chat.z.ai
* Features: Better input, chat history, session resumption, smooth animations
*/
// ============================================
// Enhanced Chat Input Experience
// ============================================
// Auto-focus chat input when switching to chat view
function focusChatInput() {
setTimeout(() => {
const input = document.getElementById('chat-input');
if (input) {
input.focus();
// Move cursor to end
input.setSelectionRange(input.value.length, input.value.length);
}
}, 100);
}
// Smooth textarea resize with animation
function enhanceChatInput() {
const input = document.getElementById('chat-input');
if (!input) return;
// Auto-resize with smooth transition
input.style.transition = 'height 0.2s ease';
input.addEventListener('input', function() {
this.style.height = 'auto';
const newHeight = Math.min(this.scrollHeight, 200);
this.style.height = newHeight + 'px';
});
// Focus animation
input.addEventListener('focus', function() {
this.parentElement.classList.add('input-focused');
});
input.addEventListener('blur', function() {
this.parentElement.classList.remove('input-focused');
});
}
// ============================================
// Chat History & Session Management
// ============================================
// Auto-load chat history when page loads
(async function loadChatHistoryOnLoad() {
try {
const res = await fetch('/claude/api/claude/sessions');
const data = await res.json();
const historyList = document.getElementById('chat-history-list');
if (!historyList) return;
// Combine active and historical sessions
const allSessions = [
...(data.active || []).map(s => ({...s, status: 'active'})),
...(data.historical || []).map(s => ({...s, status: 'historical'}))
];
// Sort by creation date (newest first)
allSessions.sort((a, b) => new Date(b.createdAt || b.created_at) - new Date(a.createdAt || a.created_at));
if (allSessions.length === 0) {
historyList.innerHTML = '<div class="chat-history-empty">No chat history yet</div>';
return;
}
historyList.innerHTML = allSessions.map(session => {
const title = session.metadata?.project ||
session.project ||
session.id.substring(0, 12) + '...';
const date = new Date(session.createdAt || session.created_at).toLocaleDateString();
const isActive = session.id === (window.attachedSessionId || null);
return `
<div class="chat-history-item ${isActive ? 'active' : ''} ${session.status === 'historical' ? 'historical' : ''}"
onclick="${session.status === 'historical' ? `resumeSession('${session.id}')` : `attachToSession('${session.id}')`}">
<div class="chat-history-icon">
${session.status === 'historical' ? '📁' : '💬'}
</div>
<div class="chat-history-content">
<div class="chat-history-title">${title}</div>
<div class="chat-history-meta">
<span class="chat-history-date">${date}</span>
<span class="chat-history-status ${session.status}">
${session.status === 'historical' ? 'Historical' : 'Active'}
</span>
</div>
</div>
${session.status === 'historical' ? '<span class="resume-badge">Resume</span>' : ''}
</div>
`;
}).join('');
} catch (error) {
console.error('[loadChatHistoryOnLoad] Error loading chat history:', error);
}
})();
// Resume historical session
async function resumeSession(sessionId) {
console.log('Resuming historical session:', sessionId);
// Show loading message
if (typeof appendSystemMessage === 'function') {
appendSystemMessage('📂 Loading historical session...');
}
try {
// Load the historical session
const res = await fetch('/claude/api/claude/sessions/' + sessionId);
// Check if response is OK
if (!res.ok) {
const errorText = await res.text();
console.error('Session fetch error:', res.status, errorText);
// Handle 404 - session not found
if (res.status === 404) {
if (typeof appendSystemMessage === 'function') {
appendSystemMessage('❌ Session not found. It may have been deleted or the ID is incorrect.');
}
return;
}
throw new Error(`HTTP ${res.status}: ${errorText}`);
}
// Parse JSON with error handling
let data;
try {
data = await res.json();
} catch (jsonError) {
const responseText = await res.text();
console.error('JSON parse error:', jsonError);
console.error('Response text:', responseText);
throw new Error('Invalid JSON response from server');
}
if (data.session) {
if (typeof attachToSession === 'function') {
attachToSession(sessionId);
}
// Update UI
const sessionIdEl = document.getElementById('current-session-id');
if (sessionIdEl) sessionIdEl.textContent = sessionId;
// Load session messages
if (typeof clearChatDisplay === 'function') {
clearChatDisplay();
}
// Add historical messages
if (data.session.outputBuffer && data.session.outputBuffer.length > 0) {
data.session.outputBuffer.forEach(entry => {
if (typeof appendMessage === 'function') {
appendMessage('assistant', entry.content, false);
}
});
}
// Show resume message
const sessionDate = new Date(data.session.createdAt || data.session.created_at);
if (typeof appendSystemMessage === 'function') {
appendSystemMessage('✅ Resumed historical session from ' + sessionDate.toLocaleString());
appendSystemMessage(' This is a read-only historical session. Start a new chat to continue working.');
}
// Update active state in sidebar
if (typeof loadChatHistory === 'function') {
loadChatHistory();
}
// Subscribe to session (for any future updates)
if (typeof subscribeToSession === 'function') {
subscribeToSession(sessionId);
}
} else {
throw new Error('No session data in response');
}
} catch (error) {
console.error('Error resuming session:', error);
if (typeof appendSystemMessage === 'function') {
appendSystemMessage('❌ Failed to resume session: ' + error.message);
// Remove the loading message
const messagesContainer = document.getElementById('chat-messages');
if (messagesContainer) {
const loadingMessages = messagesContainer.querySelectorAll('.chat-system');
loadingMessages.forEach(msg => {
if (msg.textContent.includes('Loading historical session')) {
msg.remove();
}
});
}
}
}
}
// ============================================
// Enhanced Message Rendering
// ============================================
// Enhanced append with animations
function appendMessageWithAnimation(role, content, animate = true) {
const messagesContainer = document.getElementById('chat-messages');
if (!messagesContainer) return;
const messageDiv = document.createElement('div');
messageDiv.className = `chat-message chat-message-${role} ${animate ? 'message-appear' : ''}`;
const avatar = role === 'user' ? '👤' : '🤖';
const label = role === 'user' ? 'You' : 'Claude';
// Strip dyad tags for display
const displayContent = stripDyadTags(content);
messageDiv.innerHTML = `
<div class="message-avatar">${avatar}</div>
<div class="message-content">
<div class="message-header">
<span class="message-label">${label}</span>
<span class="message-time">${new Date().toLocaleTimeString()}</span>
</div>
<div class="message-text">${formatMessageText(displayContent)}</div>
</div>
`;
messagesContainer.appendChild(messageDiv);
// Scroll to bottom
messagesContainer.scrollTop = messagesContainer.scrollHeight;
// Update token usage
if (typeof updateTokenUsage === 'function') {
updateTokenUsage(content.length);
}
}
// Strip dyad tags from message for display
function stripDyadTags(content) {
let stripped = content;
// Remove dyad-write tags and replace with placeholder
stripped = stripped.replace(/<dyad-write\s+path="[^"]+">([\s\S]*?)<\/dyad-write>/g, (match, content) => {
return `
<div class="code-operation">
<div class="operation-header">
<span class="operation-icon">📄</span>
<span class="operation-label">Code generated</span>
</div>
<pre class="operation-code"><code>${escapeHtml(content.trim())}</code></pre>
</div>
`;
});
// Remove other dyad tags
stripped = stripped.replace(/<dyad-[^>]+>/g, (match) => {
const tagType = match.match(/dyad-(\w+)/)?.[1] || 'operation';
const icons = {
'rename': '✏️',
'delete': '🗑️',
'add-dependency': '📦',
'command': '⚡'
};
return `<span class="tag-placeholder">${icons[tagType] || '⚙️'} ${tagType}</span>`;
});
return stripped;
}
// Format message text with markdown-like rendering
function formatMessageText(text) {
// Basic markdown-like formatting
let formatted = escapeHtml(text);
// Code blocks
formatted = formatted.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
return `<pre><code class="language-${lang || 'text'}">${escapeHtml(code.trim())}</code></pre>`;
});
// Inline code
formatted = formatted.replace(/`([^`]+)`/g, '<code>$1</code>');
// Bold
formatted = formatted.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
// Links
formatted = formatted.replace(/https?:\/\/[^\s]+/g, '<a href="$&" target="_blank">$&</a>');
// Line breaks
formatted = formatted.replace(/\n/g, '<br>');
return formatted;
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// ============================================
// Quick Actions & Suggestions
// ============================================
// Show quick action suggestions
function showQuickActions() {
const messagesContainer = document.getElementById('chat-messages');
if (!messagesContainer) return;
const quickActions = document.createElement('div');
quickActions.className = 'quick-actions';
quickActions.innerHTML = `
<div class="quick-actions-title">💡 Quick Actions</div>
<div class="quick-actions-grid">
<button class="quick-action-btn" onclick="executeQuickAction('create-react')">
<span class="action-icon">⚛️</span>
<span class="action-label">Create React App</span>
</button>
<button class="quick-action-btn" onclick="executeQuickAction('create-nextjs')">
<span class="action-icon">▲</span>
<span class="action-label">Create Next.js App</span>
</button>
<button class="quick-action-btn" onclick="executeQuickAction('create-vue')">
<span class="action-icon">💚</span>
<span class="action-label">Create Vue App</span>
</button>
<button class="quick-action-btn" onclick="executeQuickAction('create-html')">
<span class="action-icon">📄</span>
<span class="action-label">Create HTML Page</span>
</button>
<button class="quick-action-btn" onclick="executeQuickAction('explain-code')">
<span class="action-icon">📖</span>
<span class="action-label">Explain Codebase</span>
</button>
<button class="quick-action-btn" onclick="executeQuickAction('fix-bug')">
<span class="action-icon">🐛</span>
<span class="action-label">Fix Bug</span>
</button>
</div>
`;
messagesContainer.appendChild(quickActions);
}
// Execute quick action
function executeQuickAction(action) {
const actions = {
'create-react': 'Create a React app with components and routing',
'create-nextjs': 'Create a Next.js app with server-side rendering',
'create-vue': 'Create a Vue 3 app with composition API',
'create-html': 'Create a responsive HTML5 page with modern styling',
'explain-code': 'Explain the codebase structure and main files',
'fix-bug': 'Help me fix a bug in my code'
};
const prompt = actions[action];
if (prompt) {
const input = document.getElementById('chat-input');
if (input) {
input.value = prompt;
input.focus();
// Auto-send after short delay
setTimeout(() => {
if (typeof sendChatMessage === 'function') {
sendChatMessage();
}
}, 300);
}
}
}
// ============================================
// Enhanced Chat View Loading
// ============================================
// Hook into loadChatView to add enhancements
document.addEventListener('DOMContentLoaded', () => {
// Wait for chat-functions.js to load
setTimeout(() => {
// Override loadChatView to add enhancements
if (typeof window.loadChatView === 'function') {
const originalLoadChatView = window.loadChatView;
window.loadChatView = async function() {
// Call original function first
await originalLoadChatView.call(this);
// Add our enhancements
setTimeout(() => {
enhanceChatInput();
focusChatInput();
// Show quick actions on first load
const messagesContainer = document.getElementById('chat-messages');
if (messagesContainer && messagesContainer.querySelector('.chat-welcome')) {
showQuickActions();
}
}, 100);
};
}
}, 1000);
});
// Auto-start enhancements when chat view is active
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.target.id === 'chat-view' && mutation.target.classList.contains('active')) {
enhanceChatInput();
focusChatInput();
}
});
});
// Start observing after DOM loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
const chatView = document.getElementById('chat-view');
if (chatView) observer.observe(chatView, { attributes: true });
}, 1500);
});
} else {
setTimeout(() => {
const chatView = document.getElementById('chat-view');
if (chatView) observer.observe(chatView, { attributes: true });
}, 1500);
}
// Export functions
if (typeof window !== 'undefined') {
window.resumeSession = resumeSession;
window.executeQuickAction = executeQuickAction;
window.showQuickActions = showQuickActions;
window.enhanceChatInput = enhanceChatInput;
window.focusChatInput = focusChatInput;
window.appendMessageWithAnimation = appendMessageWithAnimation;
}