Fix project isolation: Make loadChatHistory respect active project sessions
- Modified loadChatHistory() to check for active project before fetching all sessions - When active project exists, use project.sessions instead of fetching from API - Added detailed console logging to debug session filtering - This prevents ALL sessions from appearing in every project's sidebar Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,11 +48,9 @@ function enhanceChatInput() {
|
||||
|
||||
// Auto-load chat history when page loads
|
||||
// Make this a named function so it can be called to refresh the sidebar
|
||||
async function loadChatHistory() {
|
||||
// @param {Array} sessionsToRender - Optional: specific sessions to render (for project filtering)
|
||||
async function loadChatHistory(sessionsToRender = null) {
|
||||
try {
|
||||
const res = await fetch('/claude/api/claude/sessions');
|
||||
const data = await res.json();
|
||||
|
||||
const historyList = document.getElementById('chat-history-list');
|
||||
if (!historyList) return;
|
||||
|
||||
@@ -63,17 +61,61 @@ async function loadChatHistory() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine active and historical sessions
|
||||
const allSessions = [
|
||||
...(data.active || []).map(s => ({...s, status: 'active'})),
|
||||
...(data.historical || []).map(s => ({...s, status: 'historical'}))
|
||||
];
|
||||
let allSessions;
|
||||
|
||||
if (sessionsToRender) {
|
||||
// Use provided sessions (for project filtering)
|
||||
allSessions = sessionsToRender;
|
||||
console.log('[loadChatHistory] Rendering provided sessions:', allSessions.length);
|
||||
} else {
|
||||
// CRITICAL FIX: If no sessions provided, check if there's an active project
|
||||
// If there is, we should NOT fetch from API - instead wait for project to provide sessions
|
||||
if (window.projectManager && window.projectManager.activeProjectId) {
|
||||
const activeProject = window.projectManager.projects.get(
|
||||
window.projectManager.activeProjectId.replace('project-', '')
|
||||
);
|
||||
if (activeProject) {
|
||||
// Use the active project's sessions
|
||||
allSessions = activeProject.sessions || [];
|
||||
console.log('[loadChatHistory] Using active project sessions:', allSessions.length, 'project:', activeProject.name);
|
||||
} else {
|
||||
// No project found, fetch from API as fallback
|
||||
const res = await fetch('/claude/api/claude/sessions');
|
||||
const data = await res.json();
|
||||
allSessions = [
|
||||
...(data.active || []).map(s => ({...s, status: 'active'})),
|
||||
...(data.historical || []).map(s => ({...s, status: 'historical'}))
|
||||
];
|
||||
console.log('[loadChatHistory] No active project found, loaded from API:', allSessions.length);
|
||||
}
|
||||
} else {
|
||||
// No project manager or no active project, fetch all sessions
|
||||
const res = await fetch('/claude/api/claude/sessions');
|
||||
const data = await res.json();
|
||||
allSessions = [
|
||||
...(data.active || []).map(s => ({...s, status: 'active'})),
|
||||
...(data.historical || []).map(s => ({...s, status: 'historical'}))
|
||||
];
|
||||
console.log('[loadChatHistory] No active project, loaded all sessions from API:', allSessions.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by creation date (newest first)
|
||||
allSessions.sort((a, b) => new Date(b.createdAt || b.created_at) - new Date(a.createdAt || a.created_at));
|
||||
|
||||
// CRITICAL DEBUG: Log session details for debugging
|
||||
console.log('[loadChatHistory] Total sessions to render:', allSessions.length);
|
||||
allSessions.forEach((s, i) => {
|
||||
console.log(`[loadChatHistory] Session ${i}:`, {
|
||||
id: s.id.substring(0, 8),
|
||||
workingDir: s.workingDir,
|
||||
project: s.metadata?.project,
|
||||
status: s.status
|
||||
});
|
||||
});
|
||||
|
||||
if (allSessions.length === 0) {
|
||||
historyList.innerHTML = '<div class="chat-history-empty">No chat history yet</div>';
|
||||
historyList.innerHTML = '<div class="chat-history-empty">No sessions in this project</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,24 +128,35 @@ async function loadChatHistory() {
|
||||
|
||||
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>
|
||||
data-session-id="${session.id}">
|
||||
<div class="chat-history-main" 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>
|
||||
${session.status === 'historical' ? '<span class="resume-badge">Resume</span>' : ''}
|
||||
<button class="chat-history-archive" onclick="event.stopPropagation(); archiveSession('${session.id}')" title="Archive session">
|
||||
📦
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// CRITICAL FIX: Also update session tabs with the same sessions
|
||||
if (window.sessionTabs && typeof window.sessionTabs.setSessions === 'function') {
|
||||
window.sessionTabs.setSessions(allSessions);
|
||||
console.log('[loadChatHistory] Updated session tabs with', allSessions.length, 'sessions');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[loadChatHistory] Error loading chat history:', error);
|
||||
}
|
||||
@@ -157,7 +210,10 @@ async function resumeSession(sessionId) {
|
||||
throw new Error('Invalid JSON response from server');
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
// CRITICAL FIX: API returns session directly, not wrapped in {session: ...}
|
||||
const session = data.session || data;
|
||||
|
||||
if (session && session.id) {
|
||||
if (typeof attachToSession === 'function') {
|
||||
attachToSession(sessionId);
|
||||
}
|
||||
@@ -172,8 +228,8 @@ async function resumeSession(sessionId) {
|
||||
}
|
||||
|
||||
// Add historical messages
|
||||
if (data.session.outputBuffer && data.session.outputBuffer.length > 0) {
|
||||
data.session.outputBuffer.forEach(entry => {
|
||||
if (session.outputBuffer && session.outputBuffer.length > 0) {
|
||||
session.outputBuffer.forEach(entry => {
|
||||
if (typeof appendMessage === 'function') {
|
||||
appendMessage('assistant', entry.content, false);
|
||||
}
|
||||
@@ -181,7 +237,7 @@ async function resumeSession(sessionId) {
|
||||
}
|
||||
|
||||
// Show resume message
|
||||
const sessionDate = new Date(data.session.createdAt || data.session.created_at);
|
||||
const sessionDate = new Date(session.createdAt || 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.');
|
||||
@@ -449,6 +505,251 @@ if (document.readyState === 'loading') {
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Archive & Merge Sessions
|
||||
// ============================================
|
||||
|
||||
// Track selected sessions for merge
|
||||
window.selectedSessionsForMerge = new Set();
|
||||
|
||||
/**
|
||||
* Archive a session
|
||||
*/
|
||||
async function archiveSession(sessionId) {
|
||||
console.log('[Archive] Archiving session:', sessionId);
|
||||
|
||||
const confirmed = confirm('Archive this session? It will be hidden from the main list but can be unarchived later.');
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`/claude/api/claude/sessions/${sessionId}/archive`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
// Try to get more error details
|
||||
let errorMessage = 'Failed to archive session';
|
||||
try {
|
||||
const errorData = await res.json();
|
||||
errorMessage = errorData.message || errorData.error || errorMessage;
|
||||
} catch (e) {
|
||||
errorMessage = `HTTP ${res.status}: ${res.statusText}`;
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// Refresh the session list
|
||||
if (typeof loadChatHistory === 'function') {
|
||||
await loadChatHistory();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('✅ Session archived successfully');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Archive] Error:', error);
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('❌ Failed to archive session: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle session selection for merge
|
||||
*/
|
||||
function toggleSessionSelection(sessionId) {
|
||||
if (window.selectedSessionsForMerge.has(sessionId)) {
|
||||
window.selectedSessionsForMerge.delete(sessionId);
|
||||
} else {
|
||||
window.selectedSessionsForMerge.add(sessionId);
|
||||
}
|
||||
|
||||
// Update UI
|
||||
const item = document.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
if (item) {
|
||||
item.classList.toggle('selected-for-merge', window.selectedSessionsForMerge.has(sessionId));
|
||||
}
|
||||
|
||||
// Show/hide merge button
|
||||
updateMergeButtonVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update merge button visibility based on selection
|
||||
*/
|
||||
function updateMergeButtonVisibility() {
|
||||
const mergeBtn = document.getElementById('merge-sessions-btn');
|
||||
if (!mergeBtn) return;
|
||||
|
||||
if (window.selectedSessionsForMerge.size >= 2) {
|
||||
mergeBtn.style.display = 'flex';
|
||||
mergeBtn.textContent = `🔀 Emerge ${window.selectedSessionsForMerge.size} Sessions`;
|
||||
} else {
|
||||
mergeBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge selected sessions
|
||||
*/
|
||||
async function mergeSessions() {
|
||||
const sessionIds = Array.from(window.selectedSessionsForMerge);
|
||||
|
||||
if (sessionIds.length < 2) {
|
||||
alert('Please select at least 2 sessions to merge');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Merge] Merging sessions:', sessionIds);
|
||||
|
||||
const confirmed = confirm(`Merge ${sessionIds.length} sessions into one? This will create a new session with all messages from the selected sessions.`);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('🔀 Merging sessions...');
|
||||
}
|
||||
|
||||
const res = await fetch('/claude/api/claude/sessions/merge', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionIds })
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error('Failed to merge sessions');
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success && data.session) {
|
||||
// Clear selection
|
||||
window.selectedSessionsForMerge.clear();
|
||||
updateMergeButtonVisibility();
|
||||
|
||||
// Remove all selected classes
|
||||
document.querySelectorAll('.selected-for-merge').forEach(el => {
|
||||
el.classList.remove('selected-for-merge');
|
||||
});
|
||||
|
||||
// Refresh the session list
|
||||
if (typeof loadChatHistory === 'function') {
|
||||
await loadChatHistory();
|
||||
}
|
||||
|
||||
// Attach to the new merged session
|
||||
if (typeof attachToSession === 'function') {
|
||||
await attachToSession(data.session.id);
|
||||
}
|
||||
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('✅ Sessions merged successfully!');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Merge] Error:', error);
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('❌ Failed to merge sessions: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show archived sessions view
|
||||
*/
|
||||
async function showArchivedSessions() {
|
||||
console.log('[Archive] Loading archived sessions...');
|
||||
|
||||
try {
|
||||
const res = await fetch('/claude/api/claude/sessions?archived=true');
|
||||
const data = await res.json();
|
||||
|
||||
const archivedSessions = data.archived || [];
|
||||
const historyList = document.getElementById('chat-history-list');
|
||||
|
||||
if (!historyList) return;
|
||||
|
||||
if (archivedSessions.length === 0) {
|
||||
historyList.innerHTML = `
|
||||
<div class="chat-history-empty">
|
||||
<div>No archived sessions</div>
|
||||
<button onclick="loadChatHistory()" style="margin-top: 12px; padding: 8px 16px; background: #333; border: none; border-radius: 6px; color: #fff; cursor: pointer;">
|
||||
← Back to Sessions
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update header to show back button
|
||||
const historyHeader = document.querySelector('.chat-history-header h3');
|
||||
if (historyHeader) {
|
||||
historyHeader.innerHTML = `
|
||||
<button onclick="loadChatHistory()" style="background: none; border: none; color: #888; cursor: pointer; font-size: 16px;">
|
||||
← Back
|
||||
</button>
|
||||
<span style="margin-left: 8px;">Archived Sessions</span>
|
||||
`;
|
||||
}
|
||||
|
||||
historyList.innerHTML = archivedSessions.map(session => {
|
||||
const title = session.metadata?.project ||
|
||||
session.workingDir?.split('/').pop() ||
|
||||
session.id.substring(0, 12) + '...';
|
||||
const archivedDate = new Date(session.archivedAt).toLocaleDateString();
|
||||
|
||||
return `
|
||||
<div class="chat-history-item historical" data-session-id="${session.id}">
|
||||
<div class="chat-history-main" onclick="resumeSession('${session.id}')">
|
||||
<div class="chat-history-icon">📦</div>
|
||||
<div class="chat-history-content">
|
||||
<div class="chat-history-title">${title}</div>
|
||||
<div class="chat-history-meta">
|
||||
<span class="chat-history-date">Archived: ${archivedDate}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="chat-history-unarchive" onclick="event.stopPropagation(); unarchiveSession('${session.id}')" title="Unarchive session">
|
||||
📤
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Archive] Error loading archived sessions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unarchive a session
|
||||
*/
|
||||
async function unarchiveSession(sessionId) {
|
||||
console.log('[Archive] Unarchiving session:', sessionId);
|
||||
|
||||
try {
|
||||
const res = await fetch(`/claude/api/claude/sessions/${sessionId}/unarchive`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error('Failed to unarchive session');
|
||||
|
||||
// Refresh the archived list
|
||||
await showArchivedSessions();
|
||||
|
||||
// Show success message
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('✅ Session unarchived successfully');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Archive] Error:', error);
|
||||
if (typeof appendSystemMessage === 'function') {
|
||||
appendSystemMessage('❌ Failed to unarchive session: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export functions
|
||||
if (typeof window !== 'undefined') {
|
||||
window.resumeSession = resumeSession;
|
||||
@@ -457,4 +758,9 @@ if (typeof window !== 'undefined') {
|
||||
window.enhanceChatInput = enhanceChatInput;
|
||||
window.focusChatInput = focusChatInput;
|
||||
window.appendMessageWithAnimation = appendMessageWithAnimation;
|
||||
window.archiveSession = archiveSession;
|
||||
window.toggleSessionSelection = toggleSessionSelection;
|
||||
window.mergeSessions = mergeSessions;
|
||||
window.showArchivedSessions = showArchivedSessions;
|
||||
window.unarchiveSession = unarchiveSession;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user