feat: Add Studio UI, intake system, and extractor improvements
Dashboard: - Add Studio page with drag-drop model upload and Claude chat - Add intake system for study creation workflow - Improve session manager and context builder - Add intake API routes and frontend components Optimization Engine: - Add CLI module for command-line operations - Add intake module for study preprocessing - Add validation module with gate checks - Improve Zernike extractor documentation - Update spec models with better validation - Enhance solve_simulation robustness Documentation: - Add ATOMIZER_STUDIO.md planning doc - Add ATOMIZER_UX_SYSTEM.md for UX patterns - Update extractor library docs - Add study-readme-generator skill Tools: - Add test scripts for extraction validation - Add Zernike recentering test Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* StudioContextFiles - Context document upload and display
|
||||
*/
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { FileText, Upload, Trash2, Loader2 } from 'lucide-react';
|
||||
import { intakeApi } from '../../api/intake';
|
||||
|
||||
interface StudioContextFilesProps {
|
||||
draftId: string;
|
||||
files: string[];
|
||||
onUploadComplete: () => void;
|
||||
}
|
||||
|
||||
export const StudioContextFiles: React.FC<StudioContextFilesProps> = ({
|
||||
draftId,
|
||||
files,
|
||||
onUploadComplete,
|
||||
}) => {
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [deleting, setDeleting] = useState<string | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const VALID_EXTENSIONS = ['.md', '.txt', '.pdf', '.json', '.csv', '.docx'];
|
||||
|
||||
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const selectedFiles = Array.from(e.target.files || []);
|
||||
if (selectedFiles.length === 0) return;
|
||||
|
||||
e.target.value = '';
|
||||
setIsUploading(true);
|
||||
|
||||
try {
|
||||
await intakeApi.uploadContextFiles(draftId, selectedFiles);
|
||||
onUploadComplete();
|
||||
} catch (err) {
|
||||
console.error('Failed to upload context files:', err);
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteFile = async (filename: string) => {
|
||||
setDeleting(filename);
|
||||
|
||||
try {
|
||||
await intakeApi.deleteContextFile(draftId, filename);
|
||||
onUploadComplete();
|
||||
} catch (err) {
|
||||
console.error('Failed to delete context file:', err);
|
||||
} finally {
|
||||
setDeleting(null);
|
||||
}
|
||||
};
|
||||
|
||||
const getFileIcon = (_filename: string) => {
|
||||
return <FileText className="w-3.5 h-3.5 text-amber-400" />;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{/* File List */}
|
||||
{files.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
{files.map((name) => (
|
||||
<div
|
||||
key={name}
|
||||
className="flex items-center gap-2 px-2 py-1.5 rounded bg-dark-700/50 text-sm group"
|
||||
>
|
||||
{getFileIcon(name)}
|
||||
<span className="text-dark-200 truncate flex-1">{name}</span>
|
||||
<button
|
||||
onClick={() => deleteFile(name)}
|
||||
disabled={deleting === name}
|
||||
className="p-1 opacity-0 group-hover:opacity-100 hover:bg-red-500/20 rounded text-red-400 transition-all"
|
||||
>
|
||||
{deleting === name ? (
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
) : (
|
||||
<Trash2 className="w-3 h-3" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Upload Button */}
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isUploading}
|
||||
className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg
|
||||
border border-dashed border-dark-600 text-dark-400 text-sm
|
||||
hover:border-primary-400/50 hover:text-primary-400 hover:bg-primary-400/5
|
||||
disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{isUploading ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<Upload className="w-4 h-4" />
|
||||
)}
|
||||
{isUploading ? 'Uploading...' : 'Add context files'}
|
||||
</button>
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple
|
||||
accept={VALID_EXTENSIONS.join(',')}
|
||||
onChange={handleFileSelect}
|
||||
className="hidden"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StudioContextFiles;
|
||||
Reference in New Issue
Block a user