let currentSkip = 0; const limit = 24; let currentFilters = {}; let isLoading = false; let hasMore = true; let allTags = []; let selectionMode = false; let selectedIds = new Set(); function formatSize(bytes) { if (!bytes) return '—'; if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } function formatDate(iso) { if (!iso) return ''; const d = new Date(iso); return d.toLocaleDateString('es-ES', { year: 'numeric', month: 'short', day: 'numeric' }); } function renderGrid(models, append = false) { const grid = document.getElementById('grid'); const countEl = document.getElementById('model-count'); if (!append) grid.innerHTML = ''; if (models.length === 0 && !append) { grid.innerHTML = `
`; if (countEl) countEl.textContent = '0'; return; } models.forEach((m, i) => { const card = document.createElement('div'); card.className = 'glass rounded-2xl overflow-hidden cursor-pointer card-hover animate-fade-in border border-white/5 relative group'; card.style.animationDelay = `${i * 0.05}s`; card.onclick = (e) => { if (selectionMode) { e.preventDefault(); e.stopPropagation(); toggleSelection(m.id); } else { window.location.href = '/model/' + m.id; } }; const isSelected = selectedIds.has(m.id); const tagBadges = (m.tags || []).slice(0, 3).map(t => `${t.name}` ).join(''); card.innerHTML = `${m.author || 'Autor desconocido'}
No hay tags aun
'; return; } container.innerHTML = allTags.map(t => `` ).join(''); } function filterByTag(tagName) { const tagInput = document.getElementById('tag'); if (tagInput) tagInput.value = tagName; currentSkip = 0; hasMore = true; loadModels(); } async function loadModels(append = false) { if (isLoading) return; isLoading = true; const searchEl = document.getElementById('search'); const categoryEl = document.getElementById('category'); const tagEl = document.getElementById('tag'); const sortEl = document.getElementById('sort-by'); const minFacesEl = document.getElementById('min-faces'); const maxFacesEl = document.getElementById('max-faces'); const minDimEl = document.getElementById('min-dim'); const maxDimEl = document.getElementById('max-dim'); const params = new URLSearchParams(); params.append('skip', String(currentSkip)); params.append('limit', String(limit)); if (searchEl && searchEl.value) params.append('search', searchEl.value); if (categoryEl && categoryEl.value) params.append('category', categoryEl.value); if (tagEl && tagEl.value) params.append('tag', tagEl.value); if (sortEl && sortEl.value) params.append('sort_by', sortEl.value); if (minFacesEl && minFacesEl.value) params.append('min_faces', minFacesEl.value); if (maxFacesEl && maxFacesEl.value) params.append('max_faces', maxFacesEl.value); if (minDimEl && minDimEl.value) params.append('min_width', minDimEl.value); if (maxDimEl && maxDimEl.value) params.append('max_width', maxDimEl.value); if (minDimEl && minDimEl.value) params.append('min_height', minDimEl.value); if (maxDimEl && maxDimEl.value) params.append('max_height', maxDimEl.value); if (minDimEl && minDimEl.value) params.append('min_depth', minDimEl.value); if (maxDimEl && maxDimEl.value) params.append('max_depth', maxDimEl.value); try { const models = await apiGet('/models/?' + params.toString()); if (models.length < limit) hasMore = false; renderGrid(models, append); const countEl = document.getElementById('model-count'); if (countEl && !append) countEl.textContent = models.length + (hasMore ? '+' : ''); const loadMoreBtn = document.getElementById('load-more'); if (loadMoreBtn) loadMoreBtn.style.display = hasMore ? 'flex' : 'none'; if (!append && searchEl && searchEl.value.trim()) { saveSearchHistory(searchEl.value.trim()); } } catch (e) { console.error(e); if (!append) { const grid = document.getElementById('grid'); if (grid) { grid.innerHTML = `Error al cargar modelos
${e.message}
Volumen
${data.volume_cm3} cm³
Peso
${data.grams} g
Costo estimado
$${data.cost}
Tiempo
${data.estimated_time}
Basado en $${data.price_per_kg}/kg, densidad ${data.material_density} g/cm³
`; } catch (err) { content.innerHTML = `Error: ${err.message}
`; } }; window.closeEstimator = function() { const modal = document.getElementById('estimator-modal'); if (modal) modal.classList.add('hidden'); }; document.addEventListener('DOMContentLoaded', () => { loadTags(); loadModels(); renderSearchHistory(); const searchEl = document.getElementById('search'); const categoryEl = document.getElementById('category'); const tagEl = document.getElementById('tag'); const sortEl = document.getElementById('sort-by'); const minFacesEl = document.getElementById('min-faces'); const maxFacesEl = document.getElementById('max-faces'); const minDimEl = document.getElementById('min-dim'); const maxDimEl = document.getElementById('max-dim'); if (searchEl) searchEl.addEventListener('input', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); if (categoryEl) categoryEl.addEventListener('change', () => { currentSkip = 0; hasMore = true; loadModels(); }); if (tagEl) tagEl.addEventListener('input', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); if (sortEl) sortEl.addEventListener('change', () => { currentSkip = 0; hasMore = true; loadModels(); }); if (minFacesEl) minFacesEl.addEventListener('change', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); if (maxFacesEl) maxFacesEl.addEventListener('change', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); if (minDimEl) minDimEl.addEventListener('change', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); if (maxDimEl) maxDimEl.addEventListener('change', debounce(() => { currentSkip = 0; hasMore = true; loadModels(); }, 300)); });