Reuben_OS / app /components /ClaudeIntegration.tsx
Reubencf's picture
First Push
8af739b
'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 (&lt; 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>
)
}