- Expose toggleTracePanel() to global scope for onclick access - Add localStorage state persistence for collapsed/expanded state - Add restoreTracePanelState() to remember user preference - Update onclick handler to safely call global function - Bump cache-bust version to force browser reload The trace panel toggle button now works correctly and remembers its state across page reloads. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
797 lines
37 KiB
HTML
797 lines
37 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Claude Code IDE</title>
|
|
<meta http-equiv=1769027229"Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
<meta http-equiv=1769027229"Pragma" content="no-cache">
|
|
<meta http-equiv=1769027229"Expires" content="0">
|
|
|
|
<!-- ============================================================
|
|
PRELOAD: Extract Session ID from URL BEFORE any other JS runs
|
|
This fixes race conditions where loadChatView() runs before ide.js
|
|
============================================================ -->
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
// Extract session ID from route-based URL: /claude/ide/session/{sessionId}
|
|
// This runs IMMEDIATELY, before any other scripts
|
|
const pathname = window.location.pathname;
|
|
const sessionMatch = pathname.match(/\/claude\/ide\/session\/([^\/]+)$/);
|
|
|
|
if (sessionMatch && sessionMatch[1]) {
|
|
window.PRELOAD_SESSION_ID = sessionMatch[1];
|
|
console.log('[PRELOAD] Session ID extracted from URL:', window.PRELOAD_SESSION_ID);
|
|
console.log('[PRELOAD] Full URL:', window.location.href);
|
|
if (window.traceExecution) {
|
|
window.traceExecution('PRELOAD', 'Session ID extracted', { sessionId: sessionMatch[1], url: window.location.href });
|
|
}
|
|
} else {
|
|
console.log('[PRELOAD] No session ID in URL pathname');
|
|
if (window.traceExecution) {
|
|
window.traceExecution('PRELOAD', 'No session ID in URL', { pathname: window.location.pathname });
|
|
}
|
|
}
|
|
|
|
// Also check for legacy query parameter
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const querySessionId = urlParams.get('session');
|
|
if (querySessionId && !window.PRELOAD_SESSION_ID) {
|
|
window.PRELOAD_SESSION_ID = querySessionId;
|
|
console.log('[PRELOAD] Session ID from query param:', window.PRELOAD_SESSION_ID);
|
|
if (window.traceExecution) {
|
|
window.traceExecution('PRELOAD', 'Session ID from query param', { sessionId: querySessionId });
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<!-- ============================================================
|
|
REAL-TIME EXECUTION TRACER - For debugging session attachment
|
|
============================================================ -->
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
|
|
window.EXECUTION_TRACE = [];
|
|
const MAX_TRACE_ENTRIES = 100;
|
|
|
|
function trace(component, event, data) {
|
|
const timestamp = new Date().toISOString().split('T')[1].slice(0, 12);
|
|
const entry = {
|
|
timestamp,
|
|
component,
|
|
event,
|
|
data
|
|
};
|
|
|
|
window.EXECUTION_TRACE.push(entry);
|
|
|
|
// Keep only recent entries
|
|
if (window.EXECUTION_TRACE.length > MAX_TRACE_ENTRIES) {
|
|
window.EXECUTION_TRACE.shift();
|
|
}
|
|
|
|
// Log to console with color
|
|
const colors = {
|
|
'PRELOAD': '#00ff00',
|
|
'Cache-Bust': '#ff00ff',
|
|
'ide.js': '#00ffff',
|
|
'session-picker': '#ffa500',
|
|
'chat-functions': '#ffff00',
|
|
'loadChatView': '#ff69b4',
|
|
'attachToSession': '#00ff00',
|
|
'switchView': '#87ceeb'
|
|
};
|
|
|
|
const color = colors[component] || '#ffffff';
|
|
console.log(
|
|
`%c[${timestamp}] %c${component}%c: ${event}`,
|
|
'color: #888',
|
|
`color: ${color}; font-weight: bold`,
|
|
'color: #ccc',
|
|
data
|
|
);
|
|
|
|
// Update trace panel if it exists
|
|
updateTracePanel();
|
|
}
|
|
|
|
function createTracePanel() {
|
|
const panel = document.createElement('div');
|
|
panel.id = 'execution-trace-panel';
|
|
panel.innerHTML = `
|
|
<div id="trace-container" style="position: fixed; top: 10px; right: 10px; z-index: 99999; background: rgba(0,0,0,0.95); border: 2px solid #00ff00; border-radius: 8px; font-family: monospace; font-size: 11px; color: #00ff00; max-width: 400px; max-height: 300px; overflow: hidden;">
|
|
<div id="trace-header" style="display: flex; justify-content: space-between; align-items: center; padding: 8px; border-bottom: 1px solid #00ff00; cursor: pointer;" onclick="window.toggleTracePanel && window.toggleTracePanel()">
|
|
<strong>🔍 EXECUTION TRACE</strong>
|
|
<button id="trace-toggle" style="background: transparent; border: 1px solid #00ff00; color: #00ff00; cursor: pointer; padding: 2px 6px;">▼</button>
|
|
</div>
|
|
<div id="trace-content" style="padding: 8px; max-height: 250px; overflow-y: auto; font-size: 10px; line-height: 1.4;">
|
|
<div style="color: #888;">Waiting for events...</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(panel);
|
|
}
|
|
|
|
function updateTracePanel() {
|
|
const content = document.getElementById('trace-content');
|
|
if (!content) return;
|
|
|
|
if (window.EXECUTION_TRACE.length === 0) {
|
|
content.innerHTML = '<div style="color: #888;">Waiting for events...</div>';
|
|
return;
|
|
}
|
|
|
|
const html = window.EXECUTION_TRACE.map(e => {
|
|
const colors = {
|
|
'PRELOAD': '#00ff00',
|
|
'Cache-Bust': '#ff00ff',
|
|
'ide.js': '#00ffff',
|
|
'session-picker': '#ffa500',
|
|
'chat-functions': '#ffff00',
|
|
'sse-client': '#ff69b4',
|
|
'loadChatView': '#ff69b4',
|
|
'attachToSession': '#00ff00',
|
|
'switchView': '#87ceeb'
|
|
};
|
|
const color = colors[e.component] || '#ffffff';
|
|
return `<div style="margin-bottom: 2px;">
|
|
<span style="color: #666;">[${e.timestamp}]</span>
|
|
<span style="color: ${color}; font-weight: bold;">${e.component}</span>:
|
|
<span style="color: #ccc;">${e.event}</span>
|
|
${e.data ? `<span style="color: #666;">${JSON.stringify(e.data)}</span>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
|
|
content.innerHTML = html;
|
|
content.scrollTop = content.scrollHeight;
|
|
}
|
|
|
|
function toggleTracePanel() {
|
|
const content = document.getElementById('trace-content');
|
|
const toggle = document.getElementById('trace-toggle');
|
|
if (content && toggle) {
|
|
const isCollapsed = content.style.display === 'none';
|
|
|
|
if (isCollapsed) {
|
|
content.style.display = 'block';
|
|
toggle.textContent = '▼';
|
|
localStorage.setItem('tracePanelCollapsed', 'false');
|
|
} else {
|
|
content.style.display = 'none';
|
|
toggle.textContent = '▶';
|
|
localStorage.setItem('tracePanelCollapsed', 'true');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore collapsed state from localStorage
|
|
function restoreTracePanelState() {
|
|
const wasCollapsed = localStorage.getItem('tracePanelCollapsed') === 'true';
|
|
const content = document.getElementById('trace-content');
|
|
const toggle = document.getElementById('trace-toggle');
|
|
|
|
if (content && toggle && wasCollapsed) {
|
|
content.style.display = 'none';
|
|
toggle.textContent = '▶';
|
|
}
|
|
}
|
|
|
|
// Call restore after panel is created
|
|
const originalCreateTracePanel = createTracePanel;
|
|
createTracePanel = function() {
|
|
originalCreateTracePanel();
|
|
setTimeout(restoreTracePanelState, 0);
|
|
};
|
|
|
|
// Expose toggleTracePanel globally so onclick can access it
|
|
window.toggleTracePanel = toggleTracePanel;
|
|
|
|
// Initialize on DOM ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', createTracePanel);
|
|
} else {
|
|
createTracePanel();
|
|
}
|
|
|
|
// Auto-upload trace every 10 seconds
|
|
setInterval(() => {
|
|
if (window.EXECUTION_TRACE.length > 0) {
|
|
uploadTrace();
|
|
}
|
|
}, 10000);
|
|
|
|
// Function to upload trace to server
|
|
async function uploadTrace() {
|
|
try {
|
|
const sessionId = window.PRELOAD_SESSION_ID || window.attachedSessionId || 'unknown';
|
|
const response = await fetch('/claude/api/telemetry/trace', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
sessionId: sessionId,
|
|
trace: window.EXECUTION_TRACE
|
|
})
|
|
});
|
|
if (response.ok) {
|
|
console.log('[TraceTelemetry] Trace uploaded successfully');
|
|
}
|
|
} catch (error) {
|
|
console.error('[TraceTelemetry] Failed to upload trace:', error);
|
|
}
|
|
}
|
|
|
|
// Export trace function and upload globally
|
|
window.traceExecution = trace;
|
|
window.uploadTrace = uploadTrace;
|
|
})();
|
|
</script>
|
|
|
|
<!-- Cache-Busting Script - MUST run first -->
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
const EXPECTED_JS_VERSION = '1769083000000'; // Cache bust for trace panel collapse fix
|
|
const CACHE_BUST_KEY = '_claude_cache_bust';
|
|
|
|
// Check if we need to force reload
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const forceReload = urlParams.get('_force') === 'true';
|
|
|
|
// Check if we just reloaded
|
|
if (sessionStorage.getItem(CACHE_BUST_KEY)) {
|
|
sessionStorage.removeItem(CACHE_BUST_KEY);
|
|
console.log('[Cache-Bust] Fresh load confirmed');
|
|
return;
|
|
}
|
|
|
|
// Clear all caches and reload
|
|
async function forceFreshLoad() {
|
|
console.log('[Cache-Bust] Forcing fresh load...');
|
|
|
|
// Clear caches API
|
|
if ('caches' in window) {
|
|
const cacheNames = await caches.keys();
|
|
await Promise.all(cacheNames.map(name => caches.delete(name)));
|
|
console.log('[Cache-Bust] Cleared', cacheNames.length, 'caches');
|
|
}
|
|
|
|
// Unregister service workers
|
|
if ('serviceWorker' in navigator) {
|
|
const regs = await navigator.serviceWorker.getRegistrations();
|
|
await Promise.all(regs.map(r => r.unregister()));
|
|
console.log('[Cache-Bust] Unregistered', regs.length, 'service workers');
|
|
}
|
|
|
|
// Mark that we're reloading
|
|
sessionStorage.setItem(CACHE_BUST_KEY, 'true');
|
|
|
|
// Reload with cache busting
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('_t', Date.now().toString());
|
|
url.searchParams.set('_v', EXPECTED_JS_VERSION);
|
|
window.location.href = url.toString();
|
|
}
|
|
|
|
// Detect if this is a stale cached page
|
|
if (!forceReload) {
|
|
// Check if page was loaded more than 5 minutes ago
|
|
const loadTime = performance.timing.responseStart;
|
|
const age = Date.now() - loadTime;
|
|
|
|
if (age > 300000) { // 5 minutes
|
|
console.warn('[Cache-Bust] Page is stale, forcing reload...');
|
|
forceFreshLoad();
|
|
return;
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<link rel="stylesheet" href="/claude/css/style.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/ide.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/tag-renderer.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/preview-manager.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/chat-enhanced.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/terminal.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/components/monaco-editor.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/components/enhanced-chat-input.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/components/session-picker.css?v=1769027229">
|
|
<link rel="stylesheet" href="/claude/claude-ide/components/approval-card.css?v=1769027229">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
|
|
|
<!-- Monaco Editor (VS Code Editor) - AMD Loader -->
|
|
<script src="https://unpkg.com/monaco-editor@0.45.0/min/vs/loader.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<!-- Navigation -->
|
|
<nav class="navbar">
|
|
<div class="nav-brand">
|
|
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu">☰</button>
|
|
<h1>Claude Code IDE</h1>
|
|
</div>
|
|
<div class="nav-menu" id="nav-menu">
|
|
<button class="nav-item active" data-view="dashboard">Dashboard</button>
|
|
<button class="nav-item" data-view="chat">💬 Chat</button>
|
|
<button class="nav-item" data-view="sessions">Sessions</button>
|
|
<button class="nav-item" data-view="projects">Projects</button>
|
|
<button class="nav-item" data-view="files">Files</button>
|
|
<button class="nav-item" data-view="terminal">🖥️ Terminal</button>
|
|
</div>
|
|
<div class="nav-user">
|
|
<button id="logout-btn" class="btn-secondary">Logout</button>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Dashboard View -->
|
|
<div id="dashboard-view" class="view active">
|
|
<div class="dashboard-grid">
|
|
<!-- Stats Cards -->
|
|
<div class="stat-card">
|
|
<h3>Active Sessions</h3>
|
|
<div class="stat-value" id="active-sessions-count">0</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<h3>Total Projects</h3>
|
|
<div class="stat-value" id="total-projects-count">0</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<h3>Historical Sessions</h3>
|
|
<div class="stat-value" id="historical-sessions-count">0</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<h3>Quick Actions</h3>
|
|
<div class="stat-actions">
|
|
<button class="btn-primary" onclick="createNewSession()">New Session</button>
|
|
<button class="btn-secondary" onclick="createNewProject()">New Project</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Sessions Panel -->
|
|
<div class="panel">
|
|
<div class="panel-header">
|
|
<h2>Active Sessions</h2>
|
|
<button class="btn-secondary btn-sm" onclick="refreshSessions()">Refresh</button>
|
|
</div>
|
|
<div class="panel-content" id="active-sessions-list">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Projects Panel -->
|
|
<div class="panel">
|
|
<div class="panel-header">
|
|
<h2>Recent Projects</h2>
|
|
<button class="btn-secondary btn-sm" onclick="showProjects()">View All</button>
|
|
</div>
|
|
<div class="panel-content" id="recent-projects-list">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sessions View -->
|
|
<div id="sessions-view" class="view">
|
|
<div class="sessions-layout">
|
|
<div class="sessions-sidebar">
|
|
<div class="sidebar-header">
|
|
<h2>Sessions</h2>
|
|
<button class="btn-primary" onclick="createNewSession()">+ New</button>
|
|
</div>
|
|
<div class="sessions-list" id="sessions-list">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
<div class="sessions-main">
|
|
<div id="session-detail" class="session-detail">
|
|
<div class="placeholder">
|
|
<h2>Select a session</h2>
|
|
<p>Choose a session from the sidebar to view details</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Projects View -->
|
|
<div id="projects-view" class="view">
|
|
<div class="projects-header">
|
|
<h2>Projects</h2>
|
|
<button class="btn-primary" onclick="createNewProject()">+ New Project</button>
|
|
</div>
|
|
<div class="projects-grid" id="projects-grid">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat View -->
|
|
<div id="chat-view" class="view">
|
|
<div class="chat-layout">
|
|
<div class="chat-sidebar-overlay" id="chat-sidebar-overlay"></div>
|
|
<div class="chat-sidebar" id="chat-sidebar">
|
|
<div class="sidebar-header">
|
|
<h2>Chat</h2>
|
|
<button class="btn-primary" onclick="startNewChat()">+ New</button>
|
|
</div>
|
|
<div class="chat-history-list" id="chat-history-list">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
<div class="chat-main">
|
|
<div class="chat-header" id="chat-header">
|
|
<button class="chat-sidebar-toggle" id="chat-sidebar-toggle" aria-label="Toggle chat history">☰</button>
|
|
<div class="chat-session-info">
|
|
<h2 id="chat-title">New Chat</h2>
|
|
<span class="chat-session-id" id="current-session-id"></span>
|
|
</div>
|
|
<div class="chat-actions">
|
|
<button class="btn-secondary btn-sm" onclick="clearChat()" title="Clear chat">Clear</button>
|
|
<button class="btn-secondary btn-sm" onclick="showChatSettings()" title="Settings">⚙️</button>
|
|
</div>
|
|
</div>
|
|
<div class="chat-messages" id="chat-messages">
|
|
<div class="chat-welcome">
|
|
<h2>👋 Welcome to Claude Code Chat!</h2>
|
|
<p>Start a conversation with Claude Code. Your session will be saved automatically.</p>
|
|
<div class="chat-tips">
|
|
<h3>Quick Tips:</h3>
|
|
<ul>
|
|
<li>Type your message and press Enter to send</li>
|
|
<li>Shift+Enter for a new line</li>
|
|
<li>Use <code>/help</code> to see available commands</li>
|
|
<li>Attach files from your vault using <code>@filename</code></li>
|
|
</ul>
|
|
</div>
|
|
<div class="chat-connection-info">
|
|
<h3>💡 Pro Tip: Continue from CLI</h3>
|
|
<p>To continue a CLI session in the web interface:</p>
|
|
<ol>
|
|
<li>In your terminal, note the session ID shown by Claude Code</li>
|
|
<li>Click "Attach CLI Session" below</li>
|
|
<li>Enter the session ID to connect</li>
|
|
</ol>
|
|
<button class="btn-secondary" onclick="showAttachCliModal()" style="margin-top: 1rem;">Attach CLI Session</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Chat Mode Buttons -->
|
|
<div class="chat-modes-bar" id="chat-modes-bar">
|
|
<button class="mode-btn active" data-mode="auto" onclick="setChatMode('auto')" title="Chat Mode - AI assisted conversations">
|
|
<span class="mode-icon">💬</span>
|
|
<span class="mode-label">Chat</span>
|
|
</button>
|
|
<button class="mode-btn" data-mode="native" onclick="setChatMode('native')" title="Native Mode - Commands execute directly on your system">
|
|
<span class="mode-icon">⚡</span>
|
|
<span class="mode-label">Native</span>
|
|
</button>
|
|
<button class="mode-btn" data-mode="webcontainer" onclick="setChatMode('webcontainer')" title="Terminal Mode - Persistent terminal session">
|
|
<span class="mode-icon">🖥️</span>
|
|
<span class="mode-label">Terminal</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="chat-input-container" id="chat-input-container">
|
|
<div class="chat-input-wrapper">
|
|
<textarea id="chat-input"
|
|
placeholder="Type your message to Claude Code... (Enter to send, Shift+Enter for new line)"
|
|
rows="1"
|
|
onkeydown="handleChatKeypress(event)"></textarea>
|
|
<div class="chat-input-actions">
|
|
<button class="btn-icon" onclick="attachFile()" title="Attach file">📎</button>
|
|
<button class="btn-primary btn-send" onclick="sendChatMessage()">Send</button>
|
|
</div>
|
|
</div>
|
|
<div class="chat-input-info">
|
|
<span class="token-usage" id="token-usage">0 tokens used</span>
|
|
<span class="char-count" id="char-count">0 characters</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Files View -->
|
|
<div id="files-view" class="view">
|
|
<div class="files-layout">
|
|
<div class="files-sidebar">
|
|
<div class="sidebar-header">
|
|
<h2>Files</h2>
|
|
</div>
|
|
<div class="search-box">
|
|
<input type="text" id="file-search" placeholder="Search files...">
|
|
</div>
|
|
<div class="file-tree" id="file-tree">
|
|
<div class="loading">Loading...</div>
|
|
</div>
|
|
</div>
|
|
<div class="files-main">
|
|
<div id="file-editor" class="file-editor">
|
|
<div class="placeholder">
|
|
<h2>Select a file</h2>
|
|
<p>Choose a file from the sidebar to view and edit</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Terminal View -->
|
|
<div id="terminal-view" class="view">
|
|
<div class="terminal-layout">
|
|
<div class="terminal-header">
|
|
<h2>🖥️ Terminals</h2>
|
|
<button class="btn-primary" id="btn-new-terminal">+ New Terminal</button>
|
|
</div>
|
|
<div class="terminal-tabs" id="terminal-tabs">
|
|
<!-- Terminal tabs will be added here -->
|
|
</div>
|
|
<div class="terminals-container" id="terminals-container">
|
|
<!-- Terminal instances will be added here -->
|
|
<div class="terminal-placeholder">
|
|
<h3>No terminals open</h3>
|
|
<p>Click "+ New Terminal" to get started</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Debug Panel - Collapsible -->
|
|
<div id="terminal-debug-panel" style="margin: 20px; background: #1a1a1a; border: 1px solid #ff6b6b; border-radius: 8px; font-family: monospace; font-size: 12px; color: #e0e0e0; overflow: hidden; transition: max-height 0.3s ease-in-out;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; padding: 15px; cursor: pointer;" onclick="toggleDebugPanel()" id="debug-panel-header" onmouseover="this.style.background='#2a2a2a'" onmouseout="this.style.background='transparent'">
|
|
<h3 style="margin: 0; color: #ff6b6b;">🐛 Terminal Debug Panel</h3>
|
|
<button id="debug-panel-toggle" style="background: transparent; border: 1px solid #ff6b6b; color: #ff6b6b; font-size: 18px; cursor: pointer; padding: 4px 8px; border-radius: 4px; transition: all 0.2s ease;" aria-label="Toggle debug panel" onmouseover="this.style.background='#ff6b6b'; this.style.color='#1a1a1a'" onmouseout="this.style.background='transparent'; this.style.color='#ff6b6b'">▼</button>
|
|
</div>
|
|
<div id="terminal-debug-content-wrapper" style="overflow: hidden; transition: max-height 0.3s ease-in-out;">
|
|
<div id="terminal-debug-content" style="max-height: 300px; overflow-y: auto; padding: 0 15px 15px 15px;">
|
|
<div style="color: #888;">Waiting for terminal activity...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modals -->
|
|
<div id="modal-overlay" class="modal-overlay hidden">
|
|
<div id="new-session-modal" class="modal hidden">
|
|
<div class="modal-header">
|
|
<h2>New Session</h2>
|
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Working Directory</label>
|
|
<input type="text" id="session-working-dir" value="/home/uroma/obsidian-vault">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Project (optional)</label>
|
|
<input type="text" id="session-project" placeholder="e.g., DedicatedNodes">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
|
|
<button class="btn-primary" onclick="submitNewSession()">Create</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="new-project-modal" class="modal hidden">
|
|
<div class="modal-header">
|
|
<h2>New Project</h2>
|
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Project Name</label>
|
|
<input type="text" id="project-name" placeholder="My Project">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Description</label>
|
|
<textarea id="project-description" rows="3" placeholder="Project description..."></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Type</label>
|
|
<select id="project-type">
|
|
<option value="general">General</option>
|
|
<option value="web">Web Development</option>
|
|
<option value="mobile">Mobile App</option>
|
|
<option value="infrastructure">Infrastructure</option>
|
|
<option value="research">Research</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
|
|
<button class="btn-primary" onclick="submitNewProject()">Create</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="attach-cli-modal" class="modal hidden">
|
|
<div class="modal-header">
|
|
<h2>Attach CLI Session</h2>
|
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="modal-info">
|
|
Enter the session ID from your Claude Code CLI session to continue it in the web interface.
|
|
</p>
|
|
<div class="form-group">
|
|
<label>Session ID</label>
|
|
<input type="text" id="cli-session-id" placeholder="e.g., session-1234567890-abc123">
|
|
<small style="display: block; margin-top: 0.5rem; color: var(--text-secondary);">
|
|
Tip: When you start Claude Code in the terminal, it shows the session ID at the top.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
|
|
<button class="btn-primary" onclick="submitAttachCliSession()">Attach</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/claude/claude-ide/error-monitor.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/semantic-validator.js?v1769082165881"></script>
|
|
<!-- ============================================================
|
|
HYBRID APPROACH: SSE Client for real-time session events
|
|
============================================================ -->
|
|
<script src="/claude/claude-ide/sse-client.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/components/approval-card.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/command-tracker.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/bug-tracker.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/ide.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/chat-functions.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/tag-renderer.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/preview-manager.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/chat-enhanced.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/terminal.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/components/monaco-editor.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/components/enhanced-chat-input.js?v1769082165881"></script>
|
|
<script src="/claude/claude-ide/components/session-picker.js?v1769082165881"></script>
|
|
|
|
<!-- Debug Panel Toggle Script -->
|
|
<script>
|
|
// Debug panel collapse state
|
|
let debugPanelCollapsed = localStorage.getItem('debugPanelCollapsed') === 'true';
|
|
|
|
// Initialize debug panel state on page load
|
|
function initDebugPanel() {
|
|
const panel = document.getElementById('terminal-debug-panel');
|
|
const contentWrapper = document.getElementById('terminal-debug-content-wrapper');
|
|
const toggle = document.getElementById('debug-panel-toggle');
|
|
|
|
if (debugPanelCollapsed) {
|
|
contentWrapper.style.maxHeight = '0px';
|
|
toggle.textContent = '▶';
|
|
toggle.style.transform = 'rotate(-90deg)';
|
|
} else {
|
|
contentWrapper.style.maxHeight = '315px'; // 300px content + 15px padding
|
|
toggle.textContent = '▼';
|
|
toggle.style.transform = 'rotate(0deg)';
|
|
}
|
|
}
|
|
|
|
// Toggle debug panel collapse/expand
|
|
function toggleDebugPanel() {
|
|
debugPanelCollapsed = !debugPanelCollapsed;
|
|
localStorage.setItem('debugPanelCollapsed', debugPanelCollapsed);
|
|
|
|
const contentWrapper = document.getElementById('terminal-debug-content-wrapper');
|
|
const toggle = document.getElementById('debug-panel-toggle');
|
|
|
|
if (debugPanelCollapsed) {
|
|
contentWrapper.style.maxHeight = '0px';
|
|
toggle.textContent = '▶';
|
|
toggle.style.transform = 'rotate(-90deg)';
|
|
} else {
|
|
contentWrapper.style.maxHeight = '315px'; // 300px content + 15px padding
|
|
toggle.textContent = '▼';
|
|
toggle.style.transform = 'rotate(0deg)';
|
|
}
|
|
}
|
|
|
|
// Initialize on page load
|
|
document.addEventListener('DOMContentLoaded', initDebugPanel);
|
|
</script>
|
|
|
|
<!-- Mobile Menu and Sidebar Toggle Script -->
|
|
<script>
|
|
// Mobile Navigation Menu Toggle
|
|
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
|
|
const navMenu = document.getElementById('nav-menu');
|
|
|
|
if (mobileMenuToggle) {
|
|
mobileMenuToggle.addEventListener('click', () => {
|
|
navMenu.classList.toggle('active');
|
|
});
|
|
}
|
|
|
|
// Close menu when clicking outside
|
|
document.addEventListener('click', (e) => {
|
|
if (!e.target.closest('.navbar')) {
|
|
navMenu?.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// Close menu when clicking a nav item
|
|
document.querySelectorAll('.nav-item').forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
navMenu?.classList.remove('active');
|
|
});
|
|
});
|
|
|
|
// Chat Sidebar Toggle
|
|
const chatSidebarToggle = document.getElementById('chat-sidebar-toggle');
|
|
const chatSidebar = document.getElementById('chat-sidebar');
|
|
const chatSidebarOverlay = document.getElementById('chat-sidebar-overlay');
|
|
|
|
function toggleChatSidebar() {
|
|
if (!chatSidebar || !chatSidebarOverlay) return;
|
|
chatSidebar.classList.toggle('active');
|
|
chatSidebarOverlay.classList.toggle('active');
|
|
}
|
|
|
|
function closeChatSidebar() {
|
|
if (!chatSidebar || !chatSidebarOverlay) return;
|
|
chatSidebar.classList.remove('active');
|
|
chatSidebarOverlay.classList.remove('active');
|
|
}
|
|
|
|
if (chatSidebarToggle) {
|
|
chatSidebarToggle.addEventListener('click', toggleChatSidebar);
|
|
}
|
|
|
|
if (chatSidebarOverlay) {
|
|
chatSidebarOverlay.addEventListener('click', closeChatSidebar);
|
|
}
|
|
|
|
// Close sidebar when switching to a different view
|
|
const originalSwitchView = window.switchView;
|
|
if (typeof originalSwitchView === 'function') {
|
|
window.switchView = function(viewName) {
|
|
closeChatSidebar();
|
|
return originalSwitchView.call(this, viewName);
|
|
};
|
|
}
|
|
</script>
|
|
|
|
<!-- SSE Event Handler Registration -->
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Register SSE event handlers after all scripts have loaded
|
|
function registerSSEHandlers() {
|
|
// Extract sessionId from URL path: /claude/ide/session/{sessionId}
|
|
const pathMatch = window.location.pathname.match(/\/claude\/ide\/session\/([^/]+)$/);
|
|
|
|
if (pathMatch && pathMatch[1]) {
|
|
const sessionId = decodeURIComponent(pathMatch[1]);
|
|
|
|
// Check if SSE client and handler function are available
|
|
if (typeof window.sseClient === 'object' && typeof registerSSEEventHandlers === 'function') {
|
|
console.log('[HTML-SSE] Registering SSE handlers for session:', sessionId);
|
|
registerSSEEventHandlers(sessionId);
|
|
} else {
|
|
console.error('[HTML-SSE] SSE client or registerSSEEventHandlers not available', {
|
|
sseClient: typeof window.sseClient,
|
|
registerSSEEventHandlers: typeof registerSSEEventHandlers
|
|
});
|
|
// Retry after a delay
|
|
setTimeout(registerSSEHandlers, 500);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', registerSSEHandlers);
|
|
} else {
|
|
registerSSEHandlers();
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|