import { type MultipartPart, parseMultipart } from '@mjackson/multipart-parser'; import { lazy, Suspense, useMemo } from 'react'; import { languageFromContentType } from '../../lib/contentType'; import { Banner } from '../core/Banner'; import { Icon } from '../core/Icon'; import { LoadingIcon } from '../core/LoadingIcon'; import { TabContent, Tabs } from '../core/Tabs/Tabs'; import { AudioViewer } from './AudioViewer'; import { CsvViewer } from './CsvViewer'; import { ImageViewer } from './ImageViewer'; import { SvgViewer } from './SvgViewer'; import { TextViewer } from './TextViewer'; import { VideoViewer } from './VideoViewer'; import { WebPageViewer } from './WebPageViewer'; const PdfViewer = lazy(() => import('./PdfViewer').then((m) => ({ default: m.PdfViewer }))); interface Props { data: Uint8Array; boundary: string; idPrefix?: string; } export function MultipartViewer({ data, boundary, idPrefix = 'multipart' }: Props) { const parseResult = useMemo(() => { try { const maxFileSize = 1024 * 1024 * 10; // 10MB const parsed = parseMultipart(data, { boundary, maxFileSize }); const parts = Array.from(parsed); return { parts, error: null }; } catch (err) { return { parts: [], error: err instanceof Error ? err.message : String(err) }; } }, [data, boundary]); const { parts, error } = parseResult; if (error) { return ( Failed to parse multipart data: {error} ); } if (parts.length === 0) { return ( No multipart parts found ); } return ( ({ label: part.name ?? '', value: part.name ?? '', rightSlot: part.filename && part.headers.contentType.mediaType?.startsWith('image/') ? (
) : part.filename ? ( ) : null, }))} > {parts.map((part, i) => ( ))}
); } function Part({ part }: { part: MultipartPart }) { const mimeType = part.headers.contentType.mediaType ?? null; const contentTypeHeader = part.headers.get('content-type'); const { uint8Array, content, detectedLanguage } = useMemo(() => { const uint8Array = new Uint8Array(part.arrayBuffer); const content = new TextDecoder().decode(part.arrayBuffer); const detectedLanguage = languageFromContentType(contentTypeHeader, content); return { uint8Array, content, detectedLanguage }; }, [part, contentTypeHeader]); if (mimeType?.match(/^image\/svg/i)) { return ; } if (mimeType?.match(/^image/i)) { return ; } if (mimeType?.match(/^audio/i)) { return ; } if (mimeType?.match(/^video/i)) { return ; } if (mimeType?.match(/csv|tab-separated/i)) { return ; } if (mimeType?.match(/^text\/html/i) || detectedLanguage === 'html') { return ; } if (mimeType?.match(/pdf/i)) { return ( }> ); } return ; }