import { useState, useEffect, useCallback } from 'react'; import { Trash2, FolderPlus, Download, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { toast } from '@/components/ui/toast-store'; import { useLicense } from '@/context/LicenseContext'; import { downloadStackFile } from '@/lib/stackFilesApi'; import { FileTree } from './FileTree'; import { FileViewer } from './FileViewer'; import { FileUploadDropzone } from './FileUploadDropzone'; import { NewFolderDialog } from './NewFolderDialog'; import { DeleteFileConfirm } from './DeleteFileConfirm'; import type { FileEntry } from '@/lib/stackFilesApi'; interface StackFileExplorerProps { stackName: string; canEdit: boolean; isDarkMode: boolean; onNavigateToCompose?: () => void; onNavigateToEnv?: () => void; } export function StackFileExplorer({ stackName, canEdit, isDarkMode, onNavigateToCompose, onNavigateToEnv, }: StackFileExplorerProps) { const { isPaid } = useLicense(); const [selectedPath, setSelectedPath] = useState(null); const [selectedEntry, setSelectedEntry] = useState(null); const [currentDir, setCurrentDir] = useState(''); const [refreshKey, setRefreshKey] = useState(0); const [deleteOpen, setDeleteOpen] = useState(false); const [newFolderOpen, setNewFolderOpen] = useState(false); const [isDownloading, setIsDownloading] = useState(false); useEffect(() => { setSelectedPath(null); setSelectedEntry(null); setCurrentDir(''); }, [stackName]); const refresh = useCallback(() => setRefreshKey((k) => k + 1), []); const handleSelectFile = useCallback((relPath: string, entry: FileEntry) => { setSelectedPath(relPath); setSelectedEntry(entry); const parts = relPath.split('/'); parts.pop(); setCurrentDir(parts.join('/')); }, []); const handleDeleted = useCallback(() => { setSelectedPath(null); setSelectedEntry(null); refresh(); }, [refresh]); const handleDownload = async () => { if (!selectedPath) return; setIsDownloading(true); try { const res = await downloadStackFile(stackName, selectedPath); if (!res.ok) { toast.error('Download failed.'); return; } const blob = await res.blob(); const filename = selectedPath.split('/').pop() ?? selectedPath; const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(() => URL.revokeObjectURL(url), 100); } catch (e) { toast.error(e instanceof Error ? e.message : 'Download failed.'); } finally { setIsDownloading(false); } }; return (
{/* Left pane: tree + upload + new folder */}
{isPaid && ( )}
{/* Right pane: action bar + viewer */}
{selectedPath !== null && isPaid && (
)}
); }