// Additional JavaScript functionality for Gobsidian // Mobile responsiveness function initMobileSupport() { const sidebar = document.getElementById('sidebar'); const sidebarToggle = document.getElementById('sidebar-toggle'); // Check if we're on mobile function isMobile() { return window.innerWidth < 768; } // Handle sidebar toggle on mobile function handleMobileToggle() { if (isMobile()) { sidebar.classList.toggle('mobile-open'); // Add/remove overlay let overlay = document.querySelector('.sidebar-overlay'); if (sidebar.classList.contains('mobile-open')) { if (!overlay) { overlay = document.createElement('div'); overlay.className = 'sidebar-overlay'; document.body.appendChild(overlay); overlay.addEventListener('click', () => { sidebar.classList.remove('mobile-open'); overlay.remove(); }); } } else if (overlay) { overlay.remove(); } } } // Override sidebar toggle for mobile if (isMobile()) { sidebarToggle.removeEventListener('click', toggleSidebar); sidebarToggle.addEventListener('click', handleMobileToggle); } // Handle window resize window.addEventListener('resize', () => { if (!isMobile()) { sidebar.classList.remove('mobile-open'); const overlay = document.querySelector('.sidebar-overlay'); if (overlay) overlay.remove(); } }); } // Enhanced file upload with progress function initEnhancedUpload() { const uploadElements = { area: document.getElementById('upload-area'), input: document.getElementById('file-input'), progress: document.getElementById('upload-progress'), progressBar: document.getElementById('progress-bar'), status: document.getElementById('upload-status') }; if (!uploadElements.area) return; // Enhanced upload function with progress tracking function uploadFilesWithProgress(files) { const folderPath = window.location.pathname.includes('/folder/') ? window.location.pathname.replace('/folder/', '') : ''; uploadElements.progress.classList.remove('hidden'); uploadElements.progressBar.style.width = '0%'; uploadElements.status.textContent = 'Starting upload...'; const formData = new FormData(); formData.append('path', folderPath); Array.from(files).forEach(file => { formData.append('file', file); }); const xhr = new XMLHttpRequest(); // Track upload progress xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; uploadElements.progressBar.style.width = percentComplete + '%'; uploadElements.status.textContent = `Uploading... ${Math.round(percentComplete)}%`; } }); xhr.addEventListener('load', () => { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); if (response.success) { uploadElements.progressBar.style.width = '100%'; uploadElements.status.textContent = 'Upload complete!'; showNotification('Files uploaded successfully', 'success'); setTimeout(() => window.location.reload(), 1000); } else { throw new Error(response.error || 'Upload failed'); } } catch (e) { throw new Error('Invalid server response'); } } else { throw new Error(`HTTP ${xhr.status}: ${xhr.statusText}`); } }); xhr.addEventListener('error', () => { uploadElements.status.textContent = 'Upload failed: Network error'; showNotification('Upload failed: Network error', 'error'); }); xhr.open('POST', '/upload'); xhr.send(formData); } // Replace the existing upload function if it exists if (window.uploadFiles) { window.uploadFiles = uploadFilesWithProgress; } } // Search functionality function initSearch() { // Create search input if it doesn't exist const sidebar = document.getElementById('sidebar'); if (!sidebar) return; const searchContainer = document.createElement('div'); searchContainer.className = 'p-4 border-b border-gray-700'; searchContainer.innerHTML = `
`; // Insert after the header const header = sidebar.querySelector('.p-4.border-b'); if (header) { header.after(searchContainer); } const searchInput = document.getElementById('search-input'); searchInput.addEventListener('input', debounce((e) => { const query = e.target.value.toLowerCase().trim(); const treeNodes = sidebar.querySelectorAll('.tree-node a'); treeNodes.forEach(node => { const text = node.textContent.toLowerCase(); const match = !query || text.includes(query); node.closest('.tree-node').style.display = match ? 'block' : 'none'; // Show parent folders if child matches if (match && query) { let parent = node.closest('.tree-children'); while (parent) { parent.style.display = 'block'; parent.classList.remove('hidden'); const toggle = parent.previousElementSibling; if (toggle && toggle.classList.contains('tree-toggle')) { toggle.querySelector('.tree-chevron').classList.add('rotate-90'); } parent = parent.parentElement.closest('.tree-children'); } } }); }, 300)); } // Debounce utility function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Keyboard shortcuts function initKeyboardShortcuts() { document.addEventListener('keydown', (e) => { // Global shortcuts (when not in input/textarea) if (!e.target.matches('input, textarea')) { switch(e.key) { case '/': e.preventDefault(); const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.focus(); } break; case 'n': if (e.ctrlKey || e.metaKey) { e.preventDefault(); window.location.href = '/create'; } break; case 's': if (e.ctrlKey || e.metaKey) { e.preventDefault(); window.location.href = '/settings'; } break; } } // Escape key to close modals if (e.key === 'Escape') { const modals = document.querySelectorAll('.modal-overlay'); modals.forEach(modal => { if (!modal.classList.contains('hidden')) { modal.classList.add('hidden'); } }); } }); } // Initialize all enhancements document.addEventListener('DOMContentLoaded', () => { initMobileSupport(); initEnhancedUpload(); initSearch(); initKeyboardShortcuts(); }); // Export for use in other scripts window.GobsidianUtils = { initMobileSupport, initEnhancedUpload, initSearch, initKeyboardShortcuts, debounce };