Add close button functionality to project tabs in Claude IDE

Fix missing 'x' close buttons on project tabs (Level 1). Session tabs
( Level 2) already had close buttons implemented.

Changes:
- project-manager.js: Added close button element to renderProjectTab()
- Added closeProject() method with confirmation dialog
- Added getSessionName() helper for session name display
- Auto-switches to next project when active project is closed
- project-tabs.css: Added .tab-close styling for project tabs
- Close button shows on hover, red highlight on hover
- Mobile responsive: always visible on small screens

The close button removes project tab from view but sessions remain
accessible via API/reload. Confirmation shows session count and list
for non-empty projects.

Resolves: https://rommark.dev/claude/ide/session/session-1769083280612-mdof554ot

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
uroma
2026-01-22 12:28:18 +00:00
Unverified
parent 2419d2646e
commit efb506009a
3 changed files with 138 additions and 103 deletions

View File

@@ -189,6 +189,7 @@ class ProjectManager {
<span class="tab-icon">📁</span>
<span class="tab-label">${escapeHtml(project.name)}</span>
${sessionCount > 0 ? `<span class="tab-count">${sessionCount}</span>` : ''}
<span class="tab-close" onclick="event.stopPropagation(); window.projectManager.closeProject('${project.id}')">×</span>
</button>
`;
}
@@ -411,6 +412,84 @@ class ProjectManager {
}
}
/**
* Close a project (with confirmation)
*/
async closeProject(projectId) {
const projectKey = projectId.replace('project-', '');
const project = this.projects.get(projectKey);
if (!project) return;
// Check if project has sessions
if (project.sessions.length > 0) {
const sessionList = project.sessions.map(s => this.getSessionName(s)).join(', ');
if (!confirm(`Close project "${project.name}"?\n\nThis project contains ${project.sessions.length} session(s):\n${sessionList}\n\nThe sessions will remain accessible but the project tab will be removed.`)) {
return;
}
} else {
if (!confirm(`Close project "${project.name}"?`)) {
return;
}
}
console.log('[ProjectManager] Closing project:', projectId);
// Remove project from map
this.projects.delete(projectKey);
// If we closed the active project, switch to another
if (this.activeProjectId === projectId) {
const remainingProjects = Array.from(this.projects.values());
if (remainingProjects.length > 0) {
// Switch to the next available project
await this.switchProject(remainingProjects[0].id);
} else {
// No projects left
this.activeProjectId = null;
if (window.sessionTabs) {
window.sessionTabs.setSessions([]);
window.sessionTabs.render();
}
// Show empty state
const messagesContainer = document.getElementById('chat-messages');
if (messagesContainer) {
messagesContainer.innerHTML = `
<div class="empty-project-state">
<div class="empty-icon">📁</div>
<h3>No Projects</h3>
<p>Create a new project to get started</p>
<button class="btn-primary" onclick="window.projectManager.createNewProject()">
Create Project
</button>
</div>
`;
}
}
}
this.renderProjectTabs();
}
/**
* Get display name for a session
*/
getSessionName(session) {
// Try to get name from metadata
if (session.metadata) {
if (session.metadata.project) return session.metadata.project;
if (session.metadata.title) return session.metadata.title;
if (session.metadata.name) return session.metadata.name;
}
// Use working directory
if (session.workingDir) {
return session.workingDir.split('/').pop();
}
// Fallback to session ID
return session.id.substring(0, 8);
}
/**
* Refresh all projects
*/