/** * InlineArtifactCard - Claude-style inline artifact preview card * * Displays a collapsed preview of an artifact within the chat message flow. * Clicking "Preview contents" opens the artifact in the split view/side panel. */ import type { Artifact } from '@janhq/core' import { useArtifacts } from '@/hooks/useArtifacts' import { useState } from 'react' import { cn } from '@/lib/utils' type InlineArtifactCardProps = { artifact: Artifact & { content: string } // Artifact with loaded content threadId: string isCreating?: boolean } export function InlineArtifactCard({ artifact, threadId, isCreating }: InlineArtifactCardProps) { const artifacts = useArtifacts() const [isExpanded, setIsExpanded] = useState(false) const handlePreviewClick = () => { // Open split view and set this as active artifact artifacts.setActiveArtifact(threadId, artifact.id) if (!artifacts.splitViewOpen[threadId]) { artifacts.toggleSplitView(threadId) } } const handleDownload = () => { // Create blob and download const blob = new Blob([artifact.content], { type: 'text/plain' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = artifact.file_path.split('/').pop() || 'artifact.txt' document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } const getFileExtension = () => { const ext = artifact.file_path.split('.').pop() return ext?.toUpperCase() || 'TXT' } const getArtifactType = () => { if (artifact.content_type.startsWith('text/x-')) { return 'Code' } if (artifact.content_type === 'text/markdown') { return 'Document' } return 'File' } const getContentPreview = () => { // First ~200 chars for preview const preview = artifact.content.substring(0, 200) return preview.length < artifact.content.length ? preview + '...' : preview } return (
{getContentPreview()}