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 (
);
}
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 (
{duration}
);
}
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 (
);
}
function FilePreview({ particle }: { particle: StreamParticle }) {
const data = getParticleData(particle, "file");
return (
);
}
function FolderPreview({ particle }: { particle: StreamParticle }) {
const data = getParticleData(particle, "folder");
return (
);
}
function EmptyPreview() {
return (
);
}