import { useEffect, useMemo, useRef, useState } from 'react' import { Plus, Folder, MoreVertical, Trash2, Pencil } from 'lucide-react' import { useProjects } from '../contexts/ProjectContext' import { useView } from '../contexts/ViewContext' import { LtxLogo } from '../components/LtxLogo' import { Button } from '../components/ui/button' import { pathToFileUrl } from '../lib/file-url' import type { Project } from '../types/project-model' import { useProjectReferencesMigration } from '../hooks/useProjectReferencesMigration' function formatDate(timestamp: number): string { const date = new Date(timestamp) return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }) } function ProjectCard({ project, onOpen, onDelete, onRename }: { project: Project onOpen: () => void onDelete: () => void onRename: () => void }) { const [showMenu, setShowMenu] = useState(false) const [imgError, setImgError] = useState(false) // Keep existing representative selection logic: prefer first image, else first asset. const representativeAsset = project.assets.find(a => a.type === 'image') || project.assets[0] || null const representativeUrl = representativeAsset?.path ? pathToFileUrl(representativeAsset.path) : null const representativeBigThumbnailUrl = representativeAsset?.bigThumbnailPath ? pathToFileUrl(representativeAsset.bigThumbnailPath) : null return (
{/* Thumbnail */}
{representativeAsset && !imgError ? ( representativeAsset.type === 'video' ? ( representativeBigThumbnailUrl ? ( {project.name} setImgError(true)} /> ) : representativeUrl ? (
) } export function Home() { const { projectIds, getProject, createProject, deleteProject, renameProject } = useProjects() const { openProject } = useView() const { migrationStatus, migrateProjects } = useProjectReferencesMigration() const [isCreating, setIsCreating] = useState(false) const [newProjectName, setNewProjectName] = useState('') const [renamingId, setRenamingId] = useState(null) const [renameValue, setRenameValue] = useState('') const migrationStartedRef = useRef(false) useEffect(() => { if (migrationStatus.status !== 'needed' || migrationStartedRef.current) return migrationStartedRef.current = true void migrateProjects() }, [migrateProjects, migrationStatus.status]) const projects = useMemo(() => ( projectIds .map(projectId => getProject(projectId)) .filter((project): project is Project => project !== null) ), [getProject, projectIds]) const handleCreateProject = () => { if (newProjectName.trim()) { const project = createProject(newProjectName.trim()) setNewProjectName('') setIsCreating(false) openProject(project.id) } } const handleRenameProject = (id: string, currentName: string) => { setRenamingId(id) setRenameValue(currentName) } const submitRename = () => { if (renamingId && renameValue.trim()) { renameProject(renamingId, renameValue.trim()) } setRenamingId(null) setRenameValue('') } if (migrationStatus.status === 'needed' || migrationStatus.status === 'inProgress') { const progressPct = migrationStatus.status === 'inProgress' ? migrationStatus.ratio * 100 : 0 return (

Migrating project references...

) } return (
{/* Sidebar */} {/* Main Content */}
{/* Header Banner with video background */}
{/* Create Project Modal */} {isCreating && (

Create New Project

setNewProjectName(e.target.value)} placeholder="Project name" className="w-full px-4 py-3 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder:text-zinc-500 focus:outline-none focus:border-blue-500" autoFocus onKeyDown={(e) => e.key === 'Enter' && handleCreateProject()} />
)} {/* Rename Modal */} {renamingId && (

Rename Project

setRenameValue(e.target.value)} placeholder="Project name" className="w-full px-4 py-3 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder:text-zinc-500 focus:outline-none focus:border-blue-500" autoFocus onKeyDown={(e) => e.key === 'Enter' && submitRename()} />
)}
) }