Spaces:
Sleeping
Sleeping
| // ────────────────────────────── static/projects.js ────────────────────────────── | |
| (function() { | |
| // DOM elements | |
| const newProjectBtn = document.getElementById('new-project-btn'); | |
| const projectList = document.getElementById('project-list'); | |
| const newProjectModal = document.getElementById('new-project-modal'); | |
| const newProjectForm = document.getElementById('new-project-form'); | |
| const cancelProjectBtn = document.getElementById('cancel-project'); | |
| const welcomeNewProjectBtn = document.getElementById('welcome-new-project'); | |
| const projectHeader = document.getElementById('project-header'); | |
| const currentProjectName = document.getElementById('current-project-name'); | |
| const currentProjectDescription = document.getElementById('current-project-description'); | |
| const deleteProjectBtn = document.getElementById('delete-project-btn'); | |
| const welcomeScreen = document.getElementById('welcome-screen'); | |
| const projectContent = document.getElementById('project-content'); | |
| // State | |
| let currentProject = null; | |
| let projects = []; | |
| // Initialize | |
| init(); | |
| function init() { | |
| setupEventListeners(); | |
| loadProjects(); | |
| } | |
| function setupEventListeners() { | |
| newProjectBtn.addEventListener('click', showNewProjectModal); | |
| welcomeNewProjectBtn.addEventListener('click', showNewProjectModal); | |
| cancelProjectBtn.addEventListener('click', hideNewProjectModal); | |
| newProjectForm.addEventListener('submit', handleCreateProject); | |
| deleteProjectBtn.addEventListener('click', handleDeleteProject); | |
| } | |
| function handleDeleteProject() { | |
| if (!currentProject) return; | |
| if (confirm(`Are you sure you want to delete "${currentProject.name}"? This will remove all associated files and chat history.`)) { | |
| deleteProject(currentProject.project_id); | |
| } | |
| } | |
| async function loadProjects() { | |
| const user = window.__sb_get_user(); | |
| if (!user) return; | |
| try { | |
| const response = await fetch(`/projects?user_id=${user.user_id}`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| projects = data.projects || []; | |
| renderProjectList(); | |
| // If no projects, show welcome screen | |
| if (projects.length === 0) { | |
| showWelcomeScreen(); | |
| } else { | |
| // Select first project by default | |
| selectProject(projects[0]); | |
| } | |
| // Update upload button after projects are loaded | |
| if (window.__sb_update_upload_button) { | |
| window.__sb_update_upload_button(); | |
| } | |
| } | |
| } catch (error) { | |
| console.error('Failed to load projects:', error); | |
| } | |
| } | |
| function renderProjectList() { | |
| projectList.innerHTML = ''; | |
| projects.forEach(project => { | |
| const projectItem = document.createElement('div'); | |
| projectItem.className = 'project-item'; | |
| if (currentProject && currentProject.project_id === project.project_id) { | |
| projectItem.classList.add('active'); | |
| } | |
| projectItem.innerHTML = ` | |
| <div class="project-item-icon">📁</div> | |
| <div class="project-item-info"> | |
| <div class="project-item-name">${project.name}</div> | |
| <div class="project-item-description">${project.description || 'No description'}</div> | |
| </div> | |
| <div class="project-item-actions"> | |
| <button class="project-item-delete" title="Delete project">🗑️</button> | |
| </div> | |
| `; | |
| // Add click handlers | |
| projectItem.addEventListener('click', (e) => { | |
| if (!e.target.classList.contains('project-item-delete')) { | |
| selectProject(project); | |
| } | |
| }); | |
| // Delete button handler | |
| const deleteBtn = projectItem.querySelector('.project-item-delete'); | |
| deleteBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| if (confirm(`Are you sure you want to delete "${project.name}"? This will remove all associated files and chat history.`)) { | |
| deleteProject(project.project_id); | |
| } | |
| }); | |
| projectList.appendChild(projectItem); | |
| }); | |
| } | |
| function selectProject(project) { | |
| currentProject = project; | |
| // Update UI | |
| currentProjectName.textContent = project.name; | |
| currentProjectDescription.textContent = project.description || 'No description'; | |
| // Show project content | |
| projectHeader.style.display = 'flex'; | |
| welcomeScreen.style.display = 'none'; | |
| projectContent.style.display = 'block'; | |
| // Show both upload and chat sections by default | |
| const uploadSection = document.getElementById('upload-section'); | |
| const chatSection = document.getElementById('chat-section'); | |
| if (uploadSection) uploadSection.style.display = 'block'; | |
| if (chatSection) chatSection.style.display = 'block'; | |
| // Enable chat functionality when project is selected | |
| if (window.__sb_enable_chat) { | |
| window.__sb_enable_chat(); | |
| } | |
| // Update project list | |
| renderProjectList(); | |
| // Load chat history | |
| loadChatHistory(); | |
| // Store current project in localStorage | |
| localStorage.setItem('sb_current_project', JSON.stringify(project)); | |
| // Enable chat if user is authenticated | |
| const user = window.__sb_get_user(); | |
| if (user) { | |
| enableChat(); | |
| } | |
| // Update page title to show project name | |
| if (window.__sb_update_page_title) { | |
| window.__sb_update_page_title(`Project: ${project.name}`); | |
| } | |
| // Dispatch custom event to notify other scripts that project has changed | |
| const event = new CustomEvent('projectChanged', { detail: { project } }); | |
| document.dispatchEvent(event); | |
| // Update upload button if the function exists | |
| if (window.__sb_update_upload_button) { | |
| window.__sb_update_upload_button(); | |
| } | |
| // Ensure stored files are loaded immediately | |
| if (window.__sb_load_stored_files) { | |
| window.__sb_load_stored_files(); | |
| } | |
| } | |
| function showWelcomeScreen() { | |
| currentProject = null; | |
| projectHeader.style.display = 'none'; | |
| welcomeScreen.style.display = 'flex'; | |
| projectContent.style.display = 'none'; | |
| localStorage.removeItem('sb_current_project'); | |
| } | |
| function showNewProjectModal() { | |
| newProjectModal.classList.remove('hidden'); | |
| document.getElementById('project-name').focus(); | |
| } | |
| function hideNewProjectModal() { | |
| newProjectModal.classList.add('hidden'); | |
| newProjectForm.reset(); | |
| } | |
| async function handleCreateProject(e) { | |
| e.preventDefault(); | |
| const user = window.__sb_get_user(); | |
| if (!user) { | |
| alert('Please sign in to create a project'); | |
| return; | |
| } | |
| const name = document.getElementById('project-name').value.trim(); | |
| const description = document.getElementById('project-description').value.trim(); | |
| if (!name) { | |
| alert('Project name is required'); | |
| return; | |
| } | |
| try { | |
| const formData = new FormData(); | |
| formData.append('user_id', user.user_id); | |
| formData.append('name', name); | |
| formData.append('description', description); | |
| const response = await fetch('/projects/create', { method: 'POST', body: formData }); | |
| if (response.ok) { | |
| const project = await response.json(); | |
| projects.unshift(project); | |
| renderProjectList(); | |
| selectProject(project); | |
| hideNewProjectModal(); | |
| } else { | |
| const error = await response.json(); | |
| alert(error.detail || 'Failed to create project'); | |
| } | |
| } catch (error) { | |
| alert('Failed to create project. Please try again.'); | |
| } | |
| } | |
| async function deleteProject(projectId) { | |
| const user = window.__sb_get_user(); | |
| if (!user) return; | |
| try { | |
| const response = await fetch(`/projects/${projectId}?user_id=${user.user_id}`, { | |
| method: 'DELETE' | |
| }); | |
| if (response.ok) { | |
| // Remove from local list | |
| projects = projects.filter(p => p.project_id !== projectId); | |
| // If this was the current project, clear it | |
| if (currentProject && currentProject.project_id === projectId) { | |
| currentProject = null; | |
| if (projects.length > 0) { | |
| selectProject(projects[0]); | |
| } else { | |
| showWelcomeScreen(); | |
| } | |
| } | |
| renderProjectList(); | |
| } else { | |
| alert('Failed to delete project'); | |
| } | |
| } catch (error) { | |
| alert('Failed to delete project. Please try again.'); | |
| } | |
| } | |
| async function loadChatHistory() { | |
| if (!currentProject) return; | |
| const user = window.__sb_get_user(); | |
| if (!user) return; | |
| try { | |
| const response = await fetch(`/chat/history?user_id=${user.user_id}&project_id=${currentProject.project_id}`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const messages = data.messages || []; | |
| // Clear existing messages | |
| const messagesContainer = document.getElementById('messages'); | |
| messagesContainer.innerHTML = ''; | |
| // Load chat history using global markdown-aware renderer | |
| messages.forEach(msg => { | |
| if (typeof window.appendMessage === 'function') { | |
| window.appendMessage(msg.role, msg.content); | |
| if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) { | |
| if (typeof window.appendSources === 'function') { | |
| window.appendSources(msg.sources); | |
| } | |
| } | |
| } else { | |
| // Fallback if not yet defined | |
| const messagesContainer = document.getElementById('messages'); | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `msg ${msg.role}`; | |
| messageDiv.textContent = msg.content; | |
| messagesContainer.appendChild(messageDiv); | |
| // Minimal fallback for sources | |
| if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) { | |
| const srcDiv = document.createElement('div'); | |
| srcDiv.className = 'sources'; | |
| srcDiv.textContent = 'Sources: ' + msg.sources.map(s => s.filename).join(', '); | |
| messagesContainer.appendChild(srcDiv); | |
| } | |
| } | |
| }); | |
| // Scroll to bottom | |
| if (messages.length > 0) { | |
| messagesContainer.scrollTop = messagesContainer.scrollHeight; | |
| } | |
| } | |
| } catch (error) { | |
| console.error('Failed to load chat history:', error); | |
| } | |
| } | |
| // Do not shadow global appendMessage from script.js | |
| function enableChat() { | |
| const questionInput = document.getElementById('question'); | |
| const askBtn = document.getElementById('send-btn'); | |
| const chatHint = document.getElementById('chat-hint'); | |
| if (currentProject) { | |
| questionInput.disabled = false; | |
| askBtn.disabled = false; | |
| chatHint.style.display = 'none'; | |
| } | |
| } | |
| // Public API | |
| window.__sb_get_current_project = () => currentProject; | |
| window.__sb_load_chat_history = loadChatHistory; | |
| window.__sb_enable_chat = enableChat; | |
| window.__sb_load_projects = loadProjects; | |
| // Load current project from localStorage on page load | |
| window.addEventListener('load', () => { | |
| const savedProject = localStorage.getItem('sb_current_project'); | |
| if (savedProject) { | |
| try { | |
| const project = JSON.parse(savedProject); | |
| // Check if project still exists in our list | |
| const exists = projects.find(p => p.project_id === project.project_id); | |
| if (exists) { | |
| selectProject(exists); | |
| } | |
| } catch (e) { | |
| localStorage.removeItem('sb_current_project'); | |
| } | |
| } | |
| }); | |
| })(); | |