import { i18n } from "@lingui/core"; import { t } from "@lingui/core/macro"; import { Trans, useLingui } from "@lingui/react/macro"; import { ChatMarkdown } from "@rakazo/chat-ui/web"; import { Button, Dialog, DialogClose, DialogContent, DialogTitle } from "@rakazo/ui-web"; import { Download, FileText, X } from "lucide-react"; import { type RefObject, useMemo, useRef, useState } from "react"; import { useArtifactBytes } from "../hooks/useArtifactBytes"; import { type ArtifactTarget, downloadArtifact, downloadArtifactBytes } from "../lib/artifact-open"; type ArtifactFileCardProps = { target: ArtifactTarget; artifactId: string; name: string; mimeType: string; size: number; /** Shows the preview beside the conversation instead of in a dialog. */ onOpenBeside?: () => void; }; export function ArtifactFileCard(props: ArtifactFileCardProps) { const { t } = useLingui(); const markdown = props.mimeType === "text/markdown"; const previewButton = useRef(null); const closeButton = useRef(null); const [previewOpen, setPreviewOpen] = useState(false); const [downloadError, setDownloadError] = useState(null); async function startDownload() { setDownloadError(null); try { await downloadArtifact(props.target, props.artifactId, props.name, props.mimeType); } catch { setDownloadError(t`Could not download ${props.name}. Try again.`); } } if (!markdown) { return (
{downloadError ? : null}
); } return ( <>
{downloadError ? : null}
); } function MarkdownPreview({ target, artifactId, name, mimeType, closeButtonRef, }: ArtifactFileCardProps & { closeButtonRef: RefObject }) { const { t } = useLingui(); const [downloadError, setDownloadError] = useState(null); const loaded = useArtifactBytes(target, artifactId); const state = useMemo(() => { if (loaded.status !== "ready") return loaded; try { const markdown = new TextDecoder("utf-8", { fatal: true }).decode(loaded.bytes); return { status: "ready" as const, bytes: loaded.bytes, markdown }; } catch { return { status: "error" as const, message: t`This file is not valid UTF-8 Markdown.` }; } }, [loaded, t]); return ( <>
{name} } >
{downloadError ? (
) : null}
{state.status === "loading" ? (
Loading preview…
) : state.status === "error" ? (
{state.message}
) : ( {state.markdown} )}
); } function DownloadError({ message }: { message: string }) { return (
{message}
); } function formatBytes(size: number) { const locale = i18n.locale || "en"; if (size < 1024) return t`${size} B`; const format = (value: number) => new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 1, }).format(value); if (size < 1024 * 1024) return t`${format(size / 1024)} KB`; return t`${format(size / (1024 * 1024))} MB`; }