import { Trans, useLingui } from "@lingui/react/macro"; import { ChatMarkdown } from "@rakazo/chat-ui/web"; import { isAttachmentImageMimeType } from "@rakazo/contracts"; import type { WorkspaceArtifact } from "@rakazo/core"; import { Button } from "@rakazo/ui-web"; import { Download, X } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { useArtifactBytes } from "../hooks/useArtifactBytes"; import { type ArtifactTarget, downloadArtifactBytes } from "../lib/artifact-open"; import { parseCsvPreview } from "../lib/csv-preview"; /** A file the bot made, open beside the conversation so the user can read it while they talk. */ export function ArtifactPane({ target, artifact, onClose, }: { target: ArtifactTarget; artifact: WorkspaceArtifact; onClose: () => void; }) { const { t } = useLingui(); const loaded = useArtifactBytes(target, artifact.artifactId); const { name, mimeType } = artifact; return (

{name}

{loaded.status === "loading" ? (
Loading preview…
) : loaded.status === "error" ? (
{loaded.message}
) : isAttachmentImageMimeType(mimeType) ? ( ) : ( )}
); } function PaneImage({ bytes, mimeType, name, }: { bytes: Uint8Array; mimeType: string; name: string; }) { const [src, setSrc] = useState(null); useEffect(() => { const url = URL.createObjectURL(new Blob([new Uint8Array(bytes)], { type: mimeType })); setSrc(url); return () => URL.revokeObjectURL(url); }, [bytes, mimeType]); return src ? ( {name} ) : null; } function PaneText({ bytes, mimeType }: { bytes: Uint8Array; mimeType: string }) { const { t } = useLingui(); const text = useMemo(() => new TextDecoder("utf-8").decode(bytes), [bytes]); if (mimeType === "text/markdown") { return (
{text}
); } if (mimeType === "text/csv") { const { rows, truncated } = parseCsvPreview(text); const [head, ...body] = rows; const shownRows = body.length; return (
{head ? ( {head.map((cell, index) => ( ))} ) : null} {body.map((row, rowIndex) => ( {row.map((cell, index) => ( ))} ))}
{cell}
{cell}
{truncated ? (

{t`First ${shownRows} rows`}

) : null}
); } return (
      {mimeType === "application/json" ? prettyJson(text) : text}
    
); } function prettyJson(text: string): string { try { return JSON.stringify(JSON.parse(text), null, 2); } catch { return text; } }