/** * FilePreview -- File preview / editor panel in the right sidebar. * * Displays content preview for selected files based on their MIME type: * - Text files: rendered as text with optional editing * - Images: rendered as preview * - PDFs: link to download * - Other: metadata display */ import React, { useState, useEffect } from 'react'; import api from '../../../api'; import type { WorkspaceFile } from './useWorkspace'; interface FilePreviewProps { instanceId: string; fileId: string | null; files: WorkspaceFile[]; } export const FilePreview: React.FC = ({ instanceId, fileId, files }) => { const [content, setContent] = useState(null); const [loading, setLoading] = useState(false); const [previewUrl, setPreviewUrl] = useState(null); const file = fileId ? files.find(f => f.id === fileId) : null; useEffect(() => { setContent(null); setPreviewUrl(null); if (!file || !instanceId) return; const isText = _isTextMime(file.mimeType); const isImage = file.mimeType.startsWith('image/'); if (isText && file.fileSize < 500_000) { setLoading(true); api.get(`/api/files/${file.id}/download`, { responseType: 'text' }) .then(res => setContent(typeof res.data === 'string' ? res.data : JSON.stringify(res.data, null, 2))) .catch(() => setContent(null)) .finally(() => setLoading(false)); } else if (isImage) { const baseUrl = api.defaults.baseURL || ''; setPreviewUrl(`${baseUrl}/api/files/${file.id}/download`); } }, [file, instanceId]); if (!file) { return (
Select a file to preview
); } return (
{/* Header */}
{file.fileName}
{file.mimeType} {_formatFileSize(file.fileSize)} {file.status && {file.status}}
{file.description && (
{file.description}
)} {file.tags && file.tags.length > 0 && (
{file.tags.map(tag => ( {tag} ))}
)}
{/* Content area */}
{loading && (
Loading...
)} {content !== null && !loading && (
            {content}
          
)} {previewUrl && (
{file.fileName} setPreviewUrl(null)} />
)} {!loading && content === null && !previewUrl && (
{file.fileSize > 500_000 ? 'File too large for inline preview' : `No preview available for ${file.mimeType}`}
)}
); }; function _isTextMime(mime: string): boolean { if (mime.startsWith('text/')) return true; const textTypes = [ 'application/json', 'application/xml', 'application/javascript', 'application/typescript', 'application/x-python', 'application/x-yaml', 'application/yaml', 'application/sql', 'application/csv', ]; return textTypes.includes(mime); } function _formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }