// Resources Management let currentResourceTab = 'screenshots'; let selectedFiles = new Set(); // Switch between resource tabs function switchResourceTab(tab) { currentResourceTab = tab; // Clear selection when switching tabs selectedFiles.clear(); updateBulkActions(); // Update tab buttons const tabs = document.querySelectorAll('#resources .tab'); tabs.forEach(t => t.classList.remove('active')); event.target.classList.add('active'); // Show/hide content document.getElementById('screenshotsTab').style.display = tab === 'screenshots' ? 'block' : 'none'; document.getElementById('htmlTab').style.display = tab === 'html' ? 'block' : 'none'; } // Update bulk actions visibility and count function updateBulkActions() { const bulkActions = document.getElementById('bulkActions'); const selectedCount = document.getElementById('selectedCount'); if (selectedFiles.size > 0) { bulkActions.style.display = 'flex'; selectedCount.textContent = `${selectedFiles.size} selected`; } else { bulkActions.style.display = 'none'; } } // Toggle file selection function toggleFileSelection(filename, event) { event.stopPropagation(); if (selectedFiles.has(filename)) { selectedFiles.delete(filename); } else { selectedFiles.add(filename); } updateBulkActions(); updateSelectionUI(); } // Update selection UI function updateSelectionUI() { // Update screenshot cards document.querySelectorAll('.resource-card').forEach(card => { const checkbox = card.querySelector('.resource-checkbox'); if (checkbox) { const filename = checkbox.dataset.filename; checkbox.checked = selectedFiles.has(filename); if (selectedFiles.has(filename)) { card.classList.add('selected'); } else { card.classList.remove('selected'); } } }); // Update HTML list items document.querySelectorAll('.resource-list-item').forEach(item => { const checkbox = item.querySelector('.resource-list-checkbox'); if (checkbox) { const filename = checkbox.dataset.filename; checkbox.checked = selectedFiles.has(filename); if (selectedFiles.has(filename)) { item.classList.add('selected'); } else { item.classList.remove('selected'); } } }); } // Select all files function selectAll() { if (currentResourceTab === 'screenshots') { document.querySelectorAll('.resource-checkbox').forEach(checkbox => { selectedFiles.add(checkbox.dataset.filename); }); } else { document.querySelectorAll('.resource-list-checkbox').forEach(checkbox => { selectedFiles.add(checkbox.dataset.filename); }); } updateBulkActions(); updateSelectionUI(); } // Delete selected files async function deleteSelected() { if (selectedFiles.size === 0) return; if (!confirm(`Are you sure you want to delete ${selectedFiles.size} file(s)?`)) { return; } const type = currentResourceTab === 'screenshots' ? 'screenshot' : 'html'; const filesToDelete = Array.from(selectedFiles); let successCount = 0; let failCount = 0; for (const filename of filesToDelete) { try { const response = await fetch(`/delete/${type}/${filename}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { successCount++; selectedFiles.delete(filename); } else { failCount++; } } catch (err) { failCount++; } } // Show single notification for all deletions if (successCount > 0) { notificationManager.success('Deleted', `Successfully deleted ${successCount} file(s)`); } if (failCount > 0) { notificationManager.error('Error', `Failed to delete ${failCount} file(s)`); } updateBulkActions(); refreshResources(); } // Load resources when switching to resources page function loadResources() { const loading = document.getElementById('resourcesLoading'); const error = document.getElementById('resourcesError'); loading.classList.add('active'); error.style.display = 'none'; fetch('/list') .then(response => response.json()) .then(data => { displayScreenshots(data.screenshots || []); displayHtmlFiles(data.html_files || []); }) .catch(err => { error.textContent = 'Error loading resources: ' + err.message; error.style.display = 'block'; }) .finally(() => { loading.classList.remove('active'); }); } // Display screenshots function displayScreenshots(screenshots) { const container = document.getElementById('screenshotsList'); const noFiles = document.getElementById('noScreenshots'); // Remove resources-grid class from container since we inject our own grids per group container.classList.remove('resources-grid'); if (screenshots.length === 0) { container.innerHTML = ''; noFiles.style.display = 'block'; return; } noFiles.style.display = 'none'; // Group screenshots by base name (e.g., "1" from "1(1).png") const groups = {}; screenshots.forEach(filename => { let base = filename; const match = filename.match(/^(.+?)\(\d+\)\.png$/); if (match) { base = match[1]; } if (!groups[base]) groups[base] = []; groups[base].push(filename); }); // Sort bases ascending (numerically if numbers, else alphabetically) const sortedBases = Object.keys(groups).sort((a, b) => { const numA = parseInt(a); const numB = parseInt(b); if (!isNaN(numA) && !isNaN(numB)) { return numA - numB; } return a.localeCompare(b); }); // Store grouped data globally for modal navigation window.resourceGroups = groups; let html = ''; sortedBases.forEach(base => { const groupFiles = groups[base]; // Sort files within group by part number descending or ascending // e.g., 1(1).png before 1(2).png groupFiles.sort((a, b) => { const matchA = a.match(/\((\d+)\)\.png$/); const matchB = b.match(/\((\d+)\)\.png$/); if (matchA && matchB) { return parseInt(matchA[1]) - parseInt(matchB[1]); } return a.localeCompare(b); }); html += `

Batch: ${base}

`; groupFiles.forEach((filename, index) => { html += `
${filename}
${filename}
`; }); html += `
`; }); container.innerHTML = html; updateSelectionUI(); } // Display HTML files function displayHtmlFiles(htmlFiles) { const container = document.getElementById('htmlFilesList'); const noFiles = document.getElementById('noHtmlFiles'); if (htmlFiles.length === 0) { container.innerHTML = ''; noFiles.style.display = 'block'; return; } noFiles.style.display = 'none'; container.innerHTML = htmlFiles.map(filename => `
${filename}
HTML File
`).join(''); updateSelectionUI(); } // Refresh resources function refreshResources() { selectedFiles.clear(); updateBulkActions(); loadResources(); } // Download file function downloadFile(url, filename) { const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // View HTML file function viewHtmlFile(filename) { window.open(`/html/${filename}`, '_blank'); } // Open image in modal mapped to global utility state function openGroupImageModal(base, index) { if (window.resourceGroups && window.resourceGroups[base]) { window.currentScreenshots = { type: 'resources', files: window.resourceGroups[base] }; // Hand off to the robust modal from utils.js that supports next/prev if (typeof openImageModalOriginal === 'function') { openImageModalOriginal(index); } } } // Delete single file function deleteFile(type, filename) { if (!confirm(`Are you sure you want to delete ${filename}?`)) { return; } fetch(`/delete/${type}/${filename}`, { method: 'DELETE' }) .then(response => response.json()) .then(data => { if (data.success) { notificationManager.success('Deleted', `${filename} has been deleted`); selectedFiles.delete(filename); updateBulkActions(); refreshResources(); } else { notificationManager.error('Delete Failed', data.error || 'Could not delete file'); } }) .catch(err => { notificationManager.error('Error', 'Failed to delete file: ' + err.message); }); } // Load resources when resources page is opened document.addEventListener('DOMContentLoaded', () => { // Override switchTool to load resources when switching to resources page const originalSwitchTool = window.switchTool; window.switchTool = function (tool) { originalSwitchTool(tool); if (tool === 'resources') { selectedFiles.clear(); updateBulkActions(); loadResources(); } }; }); // Regenerate screenshots from HTML file with custom settings function regenerateScreenshots(htmlFilename) { // Create modal for settings const modal = document.createElement('div'); modal.className = 'image-modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); } // Execute regeneration async function executeRegenerate(htmlFilename) { const modal = document.querySelector('.image-modal'); // Get settings const settings = { html_filename: htmlFilename, screenshot_name: document.getElementById('regenScreenshotName').value.trim() || 'screenshot', zoom: parseFloat(document.getElementById('regenZoom').value) || 2.5, overlap: parseInt(document.getElementById('regenOverlap').value) || 35, viewport_width: parseInt(document.getElementById('regenWidth').value) || 1920, viewport_height: parseInt(document.getElementById('regenHeight').value) || 1080, max_screenshots: parseInt(document.getElementById('regenMaxScreenshots').value) || 50 }; // Close modal modal.remove(); // Show notification notificationManager.info('Regenerating', 'Creating screenshots with new settings...'); try { const response = await fetch('/regenerate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); const data = await response.json(); if (data.error) { notificationManager.error('Regeneration Failed', data.error); } else { notificationManager.success( 'Regeneration Complete!', `Created ${data.screenshot_count} screenshot(s)` ); // Switch to screenshots tab to show new files currentResourceTab = 'screenshots'; document.querySelectorAll('#resources .tab').forEach((t, i) => { t.classList.toggle('active', i === 0); }); document.getElementById('screenshotsTab').style.display = 'block'; document.getElementById('htmlTab').style.display = 'none'; // Refresh to show new screenshots refreshResources(); } } catch (err) { notificationManager.error('Error', 'Failed to regenerate: ' + err.message); } }