import { getFileIcon, getFileEmoji, getExt, isImage, formatSize, formatDate } from '../utils/formatters.js'; export class UIRenderer { constructor(stateManager, hfService) { this.state = stateManager; this.hf = hfService; this.containers = { folders: document.getElementById('foldersContainer'), files: document.getElementById('filesContainer'), breadcrumbs: document.getElementById('breadcrumbs'), toast: document.getElementById('toastContainer'), progress: document.getElementById('uploadProgress'), progressText: document.getElementById('progressText') }; } showToast(msg, type = 'info') { const icons = { success: 'ph-fill ph-check-circle', error: 'ph-fill ph-warning-circle', info: 'ph-fill ph-info', warning: 'ph-fill ph-warning' }; const t = document.createElement('div'); t.className = `toast toast-${type}`; t.innerHTML = `${msg}`; this.containers.toast.appendChild(t); requestAnimationFrame(() => t.classList.add('show')); setTimeout(() => { t.classList.remove('show'); setTimeout(() => t.remove(), 400); }, 3500); } showProgress(msg = 'Working...') { this.containers.progressText.textContent = msg; this.containers.progress.classList.add('active'); } hideProgress() { this.containers.progress.classList.remove('active'); } renderBreadcrumbs(onCrumbClick) { this.containers.breadcrumbs.innerHTML = ''; const root = document.createElement('span'); root.className = 'breadcrumb-item' + (this.state.currentPath.length === 0 ? ' active' : ''); root.textContent = 'My Files'; root.onclick = () => onCrumbClick([]); this.containers.breadcrumbs.appendChild(root); this.state.currentPath.forEach((seg, idx) => { const sep = document.createElement('span'); sep.className = 'breadcrumb-separator'; sep.innerHTML = ''; this.containers.breadcrumbs.appendChild(sep); const crumb = document.createElement('span'); crumb.className = 'breadcrumb-item' + (idx === this.state.currentPath.length - 1 ? ' active' : ''); crumb.textContent = seg; crumb.onclick = () => onCrumbClick(this.state.currentPath.slice(0, idx + 1)); this.containers.breadcrumbs.appendChild(crumb); }); } renderFolders(folders, onFolderClick, onRename, onDelete) { this.containers.folders.innerHTML = ''; if (!folders.length) return; folders.forEach(folder => { const card = document.createElement('div'); card.className = 'folder-card'; card.innerHTML = `
${folder.name}
Folder
`; card.onclick = (e) => { e.preventDefault(); e.stopPropagation(); if (e.target.closest('.card-menu')) return; onFolderClick(folder.name); }; card.querySelector('[data-action="rename"]').onclick = (e) => { e.stopPropagation(); if (onRename) onRename(folder.path, folder.name); }; card.querySelector('[data-action="delete"]').onclick = (e) => { e.stopPropagation(); if (onDelete) onDelete(folder.path, folder.name); }; const menuBtn = card.querySelector('.card-menu-btn'); const menu = card.querySelector('.dropdown-menu'); menuBtn.onclick = (e) => { e.stopPropagation(); menu.classList.toggle('open'); }; this.containers.folders.appendChild(card); }); } renderFiles(files, actions) { this.containers.files.innerHTML = ''; const mode = this.state.viewMode; this.containers.files.className = mode === 'grid' ? 'grid-container' : 'list-container'; if (!files.length) { this.showEmpty(); return; } files.forEach(file => { const card = document.createElement('div'); card.className = mode === 'grid' ? 'file-card' : 'file-list-item'; const { icon, color } = getFileIcon(file.name); const ext = getExt(file.name).toUpperCase(); const size = formatSize(file.size); const starred = this.state.starred.includes(file.path); const url = actions.getUrl(file.path); if (mode === 'grid') { const isImg = isImage(file.name); const previewHTML = isImg ? `${file.name}` : `${getFileEmoji(ext)}`; card.innerHTML = `
${previewHTML}
${ext}

${file.name}

${size} • ${file.lastModified ? formatDate(file.lastModified) : 'Recently'}

â‹®
`; } else { card.innerHTML = `

${file.name}

${size} • ${ext}
`; } card.onclick = (e) => { if (e.target.closest('.file-actions, .quick-actions, .list-actions')) return; actions.onPreview(file); }; // Action Handlers card.addEventListener('click', (e) => { const btn = e.target.closest('[data-action]'); if (!btn) return; const action = btn.dataset.action; if (action === 'preview') actions.onPreview(file); else if (action === 'download') actions.onDownload(url, file.name); else if (action === 'rename') actions.onRename(file.path, file.name); else if (action === 'star') actions.onStar(file.path); else if (action === 'delete') actions.onDelete(file.path, file.name); else if (action === 'history') actions.onHistory(file.path, file.name); }); this.containers.files.appendChild(card); }); } showEmpty() { this.containers.files.innerHTML = `

Nothing here yet

Upload files or create folders to get started.

`; } showError(message = 'Unable to load files right now.') { this.containers.folders.innerHTML = ''; this.containers.files.innerHTML = `

Connection Problem

${message}

`; } showSkeletons(foldersCount = 3, filesCount = 6) { this.containers.folders.innerHTML = ''; this.containers.files.innerHTML = ''; for (let i = 0; i < foldersCount; i++) { const el = document.createElement('div'); el.className = 'skeleton skeleton-card'; this.containers.folders.appendChild(el); } for (let i = 0; i < filesCount; i++) { const el = document.createElement('div'); el.className = 'skeleton skeleton-card'; this.containers.files.appendChild(el); } } showHistoryModal(fileName) { const modal = document.getElementById('historyModal'); const nameEl = document.getElementById('historyFileName'); const list = document.getElementById('historyList'); nameEl.textContent = fileName; list.innerHTML = '

Fetching history...

'; modal.classList.add('active'); } renderHistory(history, onRestore) { const list = document.getElementById('historyList'); if (!history.length) { list.innerHTML = '

No version history found.

'; return; } list.innerHTML = ''; history.forEach((commit, idx) => { const item = document.createElement('div'); item.className = 'history-item'; const date = new Date(commit.timestamp).toLocaleString(); const shortId = commit.id.substring(0, 7); item.innerHTML = `
${commit.message} ${date} • ${shortId} by ${commit.author}
${idx === 0 ? 'Current' : ` `}
`; item.querySelectorAll('.restore-btn').forEach(btn => { btn.onclick = () => { const asCopy = btn.dataset.action === 'copy'; onRestore(commit.id, asCopy); }; }); list.appendChild(item); }); } }