import { useEffect, useState } from "react"; import type { StreamParticle } from "@/api/types"; import { getParticleData } from "@/api/types"; import { apiClient } from "@/api/client"; import { Skeleton } from "@/components/ui/skeleton"; import { Video, Mic, ScrollText, BookOpen, FileIcon, FolderIcon, } from "lucide-react"; import { cn } from "@/lib/utils"; interface ParticlePreviewProps { particle: StreamParticle; } /** Dynamic text sizing for card previews — inspired by TextParticleView. */ function getPreviewTextStyle(length: number) { if (length < 30) return "text-lg font-semibold"; if (length < 80) return "text-base font-medium"; if (length < 200) return "text-sm font-normal"; return "text-xs font-normal"; } export function ParticlePreview({ particle }: ParticlePreviewProps) { switch (particle.type) { case "text": return ; case "media": return ; case "quest": return ; case "paper": return ; case "file": return ; case "folder": return ; default: return ; } } function TextPreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "text"); return (

{data.content}

); } function MediaPreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "media"); const isVideo = data.mime_type.startsWith("video"); const durationSec = Math.round(data.duration_ms / 1000); const durationLabel = `${Math.floor(durationSec / 60)}:${String(durationSec % 60).padStart(2, "0")}`; if (isVideo) { return ; } return (
{durationLabel}
); } function VideoThumbnail({ particleId, duration, }: { particleId: string; duration: string; }) { const [url, setUrl] = useState(null); const [error, setError] = useState(false); useEffect(() => { let cancelled = false; apiClient .getParticleDownloadUrl(particleId) .then((downloadUrl) => { if (!cancelled) setUrl(downloadUrl); }) .catch(() => { if (!cancelled) setError(true); }); return () => { cancelled = true; }; }, [particleId]); if (error) { return (
); } if (!url) { return ; } return (
); } function QuestPreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "quest"); return (

{data.title}

{data.status && ( {data.status} )}
); } function PaperPreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "paper"); return (

{data.title}

); } function FilePreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "file"); return (

{data.filename}

); } function FolderPreview({ particle }: { particle: StreamParticle }) { const data = getParticleData(particle, "folder"); return (

{data.name}

); } function EmptyPreview() { return (

No messages yet

); }