Spaces:
Running
Running
| 'use client' | |
| import React, { useState } from 'react' | |
| import { motion } from 'framer-motion' | |
| import { | |
| Upload, | |
| Robot, | |
| FileText, | |
| CheckCircle, | |
| Warning, | |
| Info, | |
| Copy, | |
| Download | |
| } from '@phosphor-icons/react' | |
| interface ClaudeIntegrationProps { | |
| file: File | null | |
| onClose: () => void | |
| } | |
| export function ClaudeIntegration({ file, onClose }: ClaudeIntegrationProps) { | |
| const [uploadMethod, setUploadMethod] = useState<'direct' | 'chunked' | 'reference'>('direct') | |
| const [processing, setProcessing] = useState(false) | |
| const [result, setResult] = useState<string>('') | |
| const handleProcess = async () => { | |
| if (!file) return | |
| setProcessing(true) | |
| try { | |
| if (uploadMethod === 'direct') { | |
| // Direct upload - best for small files < 1MB | |
| const content = await file.text() | |
| setResult(`File: ${file.name}\n\nContent:\n${content}`) | |
| } else if (uploadMethod === 'chunked') { | |
| // Chunked upload - for larger files, split into manageable chunks | |
| const CHUNK_SIZE = 50000 // 50KB chunks | |
| const content = await file.text() | |
| const chunks = [] | |
| for (let i = 0; i < content.length; i += CHUNK_SIZE) { | |
| chunks.push(content.slice(i, i + CHUNK_SIZE)) | |
| } | |
| setResult( | |
| `File: ${file.name}\n` + | |
| `Total chunks: ${chunks.length}\n` + | |
| `Chunk size: ${CHUNK_SIZE} characters\n\n` + | |
| `Instructions for Claude:\n` + | |
| `This file has been split into ${chunks.length} chunks.\n` + | |
| `You can process each chunk sequentially.\n\n` + | |
| `First chunk:\n${chunks[0].slice(0, 500)}...` | |
| ) | |
| } else { | |
| // Reference method - store file and provide reference | |
| setResult( | |
| `File Reference Created:\n` + | |
| `Name: ${file.name}\n` + | |
| `Size: ${formatFileSize(file.size)}\n` + | |
| `Type: ${file.type}\n\n` + | |
| `This file has been stored in the system.\n` + | |
| `You can reference it by name in your conversation with Claude.\n` + | |
| `Claude can access and analyze the file content when needed.` | |
| ) | |
| } | |
| } catch (error) { | |
| console.error('Error processing file:', error) | |
| setResult('Error processing file') | |
| } finally { | |
| setProcessing(false) | |
| } | |
| } | |
| const formatFileSize = (bytes: number) => { | |
| const units = ['B', 'KB', 'MB', 'GB'] | |
| let size = bytes | |
| let unitIndex = 0 | |
| while (size >= 1024 && unitIndex < units.length - 1) { | |
| size /= 1024 | |
| unitIndex++ | |
| } | |
| return `${size.toFixed(2)} ${units[unitIndex]}` | |
| } | |
| const copyToClipboard = () => { | |
| navigator.clipboard.writeText(result) | |
| } | |
| return ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4" | |
| onClick={onClose} | |
| > | |
| <motion.div | |
| initial={{ scale: 0.9 }} | |
| animate={{ scale: 1 }} | |
| className="bg-[#2C2C2C]/95 backdrop-blur-md rounded-xl shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden border border-white/10" | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| {/* Header */} | |
| <div className="p-6 border-b border-white/10"> | |
| <div className="flex items-center gap-3"> | |
| <Robot size={24} className="text-blue-400" /> | |
| <h2 className="text-xl font-semibold text-white">Claude Integration</h2> | |
| </div> | |
| <p className="text-sm text-gray-400 mt-2"> | |
| Optimize file upload for Claude to preserve context | |
| </p> | |
| </div> | |
| {/* Content */} | |
| <div className="p-6 space-y-4 max-h-[60vh] overflow-y-auto"> | |
| {file && ( | |
| <div className="bg-white/5 rounded-lg p-4"> | |
| <div className="flex items-center gap-3"> | |
| <FileText size={24} className="text-gray-400" /> | |
| <div> | |
| <p className="text-white font-medium">{file.name}</p> | |
| <p className="text-sm text-gray-400">{formatFileSize(file.size)}</p> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Upload Methods */} | |
| <div className="space-y-3"> | |
| <h3 className="text-sm font-medium text-gray-300 uppercase tracking-wider"> | |
| Upload Method | |
| </h3> | |
| <label className="flex items-start gap-3 p-3 rounded-lg border border-white/10 hover:border-white/20 cursor-pointer transition-colors"> | |
| <input | |
| type="radio" | |
| value="direct" | |
| checked={uploadMethod === 'direct'} | |
| onChange={(e) => setUploadMethod(e.target.value as any)} | |
| className="mt-1" | |
| /> | |
| <div> | |
| <p className="text-white font-medium">Direct Upload</p> | |
| <p className="text-xs text-gray-400"> | |
| Best for small files (< 1MB). Uploads entire content at once. | |
| </p> | |
| </div> | |
| </label> | |
| <label className="flex items-start gap-3 p-3 rounded-lg border border-white/10 hover:border-white/20 cursor-pointer transition-colors"> | |
| <input | |
| type="radio" | |
| value="chunked" | |
| checked={uploadMethod === 'chunked'} | |
| onChange={(e) => setUploadMethod(e.target.value as any)} | |
| className="mt-1" | |
| /> | |
| <div> | |
| <p className="text-white font-medium">Chunked Upload</p> | |
| <p className="text-xs text-gray-400"> | |
| For larger files. Splits content into manageable chunks. | |
| </p> | |
| </div> | |
| </label> | |
| <label className="flex items-start gap-3 p-3 rounded-lg border border-white/10 hover:border-white/20 cursor-pointer transition-colors"> | |
| <input | |
| type="radio" | |
| value="reference" | |
| checked={uploadMethod === 'reference'} | |
| onChange={(e) => setUploadMethod(e.target.value as any)} | |
| className="mt-1" | |
| /> | |
| <div> | |
| <p className="text-white font-medium">Reference Method</p> | |
| <p className="text-xs text-gray-400"> | |
| Store file and provide reference. Claude accesses when needed. | |
| </p> | |
| </div> | |
| </label> | |
| </div> | |
| {/* Info Box */} | |
| <div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4 flex gap-3"> | |
| <Info size={20} className="text-blue-400 flex-shrink-0 mt-0.5" /> | |
| <div className="text-sm text-gray-300"> | |
| <p className="font-medium mb-1">Context Preservation Tips:</p> | |
| <ul className="space-y-1 text-xs text-gray-400"> | |
| <li>• Use direct upload for code files and short documents</li> | |
| <li>• Use chunked upload for large documents or datasets</li> | |
| <li>• Use reference method for binary files or archives</li> | |
| </ul> | |
| </div> | |
| </div> | |
| {/* Result */} | |
| {result && ( | |
| <div className="bg-green-500/10 border border-green-500/20 rounded-lg p-4"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <div className="flex items-center gap-2"> | |
| <CheckCircle size={20} className="text-green-400" /> | |
| <span className="text-sm font-medium text-green-400">Ready for Claude</span> | |
| </div> | |
| <button | |
| onClick={copyToClipboard} | |
| className="text-gray-400 hover:text-white transition-colors p-1" | |
| title="Copy to clipboard" | |
| > | |
| <Copy size={18} /> | |
| </button> | |
| </div> | |
| <pre className="text-xs text-gray-300 whitespace-pre-wrap font-mono max-h-40 overflow-y-auto"> | |
| {result} | |
| </pre> | |
| </div> | |
| )} | |
| </div> | |
| {/* Footer */} | |
| <div className="p-6 pt-0 flex gap-3 justify-end"> | |
| <button | |
| onClick={onClose} | |
| className="px-4 py-2 text-gray-300 hover:text-white transition-colors" | |
| > | |
| Cancel | |
| </button> | |
| <button | |
| onClick={handleProcess} | |
| disabled={!file || processing} | |
| className="px-4 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-lg font-medium hover:from-blue-600 hover:to-purple-600 transition-all disabled:opacity-50" | |
| > | |
| {processing ? 'Processing...' : 'Process File'} | |
| </button> | |
| </div> | |
| </motion.div> | |
| </motion.div> | |
| ) | |
| } |