import React, { useState, useMemo } from 'react'; import { FolderOpen, History, DownloadCloud, Film, Save, ChevronDown, ChevronUp, Fingerprint, Wand2, Lock, Unlock, Trash2, Check, Clock, Play, Loader, Download as DownloadIcon, Volume2, Search, X, } from 'lucide-react'; import { toast } from 'react-hot-toast'; import { API } from '../api/client'; import { clearDubHistory } from '../api/dub'; import { clearHistory as clearGenHistory } from '../api/generate'; import { Button } from '../ui'; import { useAppStore } from '../store'; import './Sidebar.css'; import { askConfirm } from '../utils/dialog'; const SIDEBAR_TABS = [ { id: 'projects', icon: FolderOpen, accent: '#b8bb26' }, { id: 'history', icon: History, accent: '#d3869b' }, { id: 'downloads', icon: DownloadCloud, accent: '#8ec07c' }, ]; function timeAgo(ms) { const diff = Date.now() - ms; if (!isFinite(diff) || diff < 0) return ''; const s = Math.floor(diff / 1000); if (s < 60) return `${s}s ago`; const m = Math.floor(s / 60); if (m < 60) return `${m}m ago`; const h = Math.floor(m / 60); if (h < 24) return `${h}h ago`; const d = Math.floor(h / 24); if (d < 7) return `${d}d ago`; return new Date(ms).toLocaleDateString([], { month: 'short', day: 'numeric' }); } export default function Sidebar(props) { const { availableTabs = ['projects', 'history', 'downloads'], isSidebarProjectsCollapsed, setIsSidebarProjectsCollapsed, sidebarTab, setSidebarTab, studioProjects, profiles, history, dubHistory, exportHistory, dubVideoFile, selectedProfile, previewLoading, saveProject, loadProject, deleteProject, handleSelectProfile, handleDeleteProfile, handleOpenVoiceProfile, handleUnlockProfile, handleLockProfile, handlePreviewVoice, onOpenVoicePreview, restoreHistory, restoreDubHistory, handleSaveHistoryAsProfile, handleNativeExport, revealInFolder, deleteHistory, loadHistory, loadDubHistory, } = props; // Phase 2.2 — read UI + dub state straight from the store. const mode = useAppStore(s => s.mode); const isSidebarCollapsed = useAppStore(s => s.isSidebarCollapsed); const dubStep = useAppStore(s => s.dubStep); const activeProjectId = useAppStore(s => s.activeProjectId); const [sbQuery, setSbQuery] = useState(''); const qLower = sbQuery.trim().toLowerCase(); const matchesSearch = (s) => !qLower || (s || '').toLowerCase().includes(qLower); const filteredProjects = useMemo(() => studioProjects.filter(p => matchesSearch(p.name) || matchesSearch(p.video_path) ), [studioProjects, qLower]); const filteredProfiles = useMemo(() => profiles.filter(p => matchesSearch(p.name) || matchesSearch(p.instruct) ), [profiles, qLower]); const filteredHistory = useMemo(() => history.filter(i => matchesSearch(i.text) || matchesSearch(i.language) || matchesSearch(String(i.seed)) ), [history, qLower]); const filteredDubHistory = useMemo(() => dubHistory.filter(i => matchesSearch(i.filename) || matchesSearch(i.language) || matchesSearch(i.language_code) ), [dubHistory, qLower]); const filteredExport = useMemo(() => exportHistory.filter(i => matchesSearch(i.filename) || matchesSearch(i.destination_path) ), [exportHistory, qLower]); const handleClearHistory = async () => { if (!(await askConfirm(`Clear all ${history.length + dubHistory.length} history items? This cannot be undone.`))) return; await clearGenHistory(); await clearDubHistory(); await loadHistory(); await loadDubHistory(); toast.success('History cleared'); }; const tabCount = { projects: mode === 'dub' ? studioProjects.length : (mode === 'clone' ? profiles.filter(p => !p.instruct).length : profiles.filter(p => !!p.instruct).length), history: history.length + dubHistory.length, downloads: exportHistory.length, }; const tabLabel = { projects: 'Drive', history: 'History', downloads: 'Exports' }; return (
{title}
{hint}