'use client'; import { memo, type MouseEvent, useCallback, useEffect, useMemo, useRef, } from 'react'; import type { ArtifactKind, UIArtifact } from './artifact'; import { FileIcon, FullscreenIcon, ImageIcon, LoaderIcon } from './icons'; import { cn, fetcher } from '@/lib/utils'; import type { Document } from '@/lib/db/schema'; import { InlineDocumentSkeleton } from './document-skeleton'; import useSWR from 'swr'; import { Editor } from './text-editor'; import { DocumentToolCall, DocumentToolResult } from './document'; import { CodeEditor } from './code-editor'; import { useArtifact } from '@/hooks/use-artifact'; import equal from 'fast-deep-equal'; import { SpreadsheetEditor } from './sheet-editor'; import { ImageEditor } from './image-editor'; interface DocumentPreviewProps { isReadonly: boolean; result?: any; args?: any; } export function DocumentPreview({ isReadonly, result, args, }: DocumentPreviewProps) { const { artifact, setArtifact } = useArtifact(); const { data: documents, isLoading: isDocumentsFetching } = useSWR< Array >(result ? `/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((artifact) => ({ ...artifact, boundingBox: { left: boundingBox.x, top: boundingBox.y, width: boundingBox.width, height: boundingBox.height, }, })); } }, [artifact.documentId, setArtifact]); if (artifact.isVisible) { if (result) { return ( ); } if (args) { return ( ); } } if (isDocumentsFetching) { return ; } 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: any; setArtifact: ( updaterFn: UIArtifact | ((currentArtifact: UIArtifact) => UIArtifact), ) => void; }) => { const handleClick = useCallback( (event: MouseEvent) => { const boundingBox = event.currentTarget.getBoundingClientRect(); setArtifact((artifact) => artifact.status === 'streaming' ? { ...artifact, isVisible: true } : { ...artifact, title: result.title, documentId: result.id, 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' ? ( ) : ( )}
{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-y-scroll border rounded-b-2xl dark:bg-muted border-t-0 dark:border-zinc-700', { 'p-4 sm:px-14 sm:py-16': document.kind === 'text', 'p-0': document.kind === 'code', }, ); const commonProps = { content: document.content ?? '', isCurrentVersion: true, currentVersionIndex: 0, status: artifact.status, saveContent: () => {}, suggestions: [], }; return (
{document.kind === 'text' ? ( {}} /> ) : document.kind === 'code' ? (
{}} />
) : document.kind === 'sheet' ? (
) : document.kind === 'image' ? ( ) : null}
); };