"use client"; import equal from "fast-deep-equal"; import { type MouseEvent, memo, useCallback, useEffect, useMemo, useRef, } from "react"; import useSWR from "swr"; import { useArtifact } from "@/hooks/use-artifact"; import type { Document } from "@/lib/db/schema"; import { cn, fetcher } from "@/lib/utils"; import type { ArtifactKind, UIArtifact } from "./artifact"; import { CodeEditor } from "./code-editor"; import { InlineDocumentSkeleton } from "./document-skeleton"; import { CodeIcon, FileIcon, FullscreenIcon, ImageIcon, LoaderIcon, } from "./icons"; import { ImageEditor } from "./image-editor"; import { SpreadsheetEditor } from "./sheet-editor"; import { Editor } from "./text-editor"; type DocumentToolOutput = { id: string; title: string; kind: ArtifactKind; content?: string; }; type DocumentPreviewProps = { isReadonly: boolean; result?: Partial; args?: Partial & { isUpdate?: boolean }; }; export function DocumentPreview({ isReadonly: _isReadonly, result, args, }: DocumentPreviewProps) { const { artifact, setArtifact } = useArtifact(); const { data: documents, isLoading: isDocumentsFetching } = useSWR< Document[] >( result ? `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${result.id}` : null, fetcher ); const previewDocument = useMemo(() => documents?.[0], [documents]); const hitboxRef = useRef(null); useEffect(() => { const boundingBox = hitboxRef.current?.getBoundingClientRect(); if (artifact.documentId && boundingBox) { setArtifact((currentArtifact) => ({ ...currentArtifact, boundingBox: { left: boundingBox.x, top: boundingBox.y, width: boundingBox.width, height: boundingBox.height, }, })); } }, [artifact.documentId, setArtifact]); if (isDocumentsFetching) { const kind = result?.kind ?? args?.kind ?? artifact.kind; const title = result?.title ?? args?.title ?? artifact.title; return (
{title ? ( ) : (
)}
); } const document: Document | null = previewDocument ? previewDocument : artifact.status === "streaming" ? { title: artifact.title, kind: artifact.kind, content: artifact.content, id: artifact.documentId, createdAt: new Date(), userId: "noop", } : null; if (!document) { return ; } return (
); } const LoadingSkeleton = ({ artifactKind }: { artifactKind: ArtifactKind }) => (
{artifactKind === "image" ? (
) : (
)}
); const PureHitboxLayer = ({ hitboxRef, result, setArtifact, }: { hitboxRef: React.RefObject; result?: Partial; setArtifact: ( updaterFn: UIArtifact | ((currentArtifact: UIArtifact) => UIArtifact) ) => void; }) => { const handleClick = useCallback( (event: MouseEvent) => { const boundingBox = event.currentTarget.getBoundingClientRect(); setArtifact((artifact) => ({ ...artifact, ...(result?.id && { documentId: result.id }), ...(result?.title && { title: result.title }), ...(result?.kind && { kind: result.kind }), isVisible: true, boundingBox: { left: boundingBox.x, top: boundingBox.y, width: boundingBox.width, height: boundingBox.height, }, })); }, [setArtifact, result] ); return ( ); }; const HitboxLayer = memo(PureHitboxLayer, (prevProps, nextProps) => { if (!equal(prevProps.result, nextProps.result)) { return false; } return true; }); const PureDocumentHeader = ({ title, kind, isStreaming, }: { title: string; kind: ArtifactKind; isStreaming: boolean; }) => (
{isStreaming ? (
) : kind === "image" ? ( ) : kind === "code" ? ( ) : ( )}
{title}
); const DocumentHeader = memo(PureDocumentHeader, (prevProps, nextProps) => { if (prevProps.title !== nextProps.title) { return false; } if (prevProps.isStreaming !== nextProps.isStreaming) { return false; } return true; }); const DocumentContent = ({ document }: { document: Document }) => { const { artifact } = useArtifact(); const containerClassName = cn( "h-[257px] overflow-hidden rounded-b-2xl border border-t-0 border-border/50 dark:bg-muted", { "p-4 sm:px-10 sm:py-10": document.kind === "text", "p-0": document.kind === "code", } ); const commonProps = { content: document.content ?? "", isCurrentVersion: true, currentVersionIndex: 0, status: artifact.status, saveContent: () => null, suggestions: [], }; const handleSaveContent = () => null; return (
{document.kind === "text" ? ( ) : document.kind === "code" ? (
) : document.kind === "sheet" ? (
) : document.kind === "image" ? ( ) : null}
{document.kind === "code" && (
)}
); };