import React, { useState, useRef, useEffect } from 'react';
Browse filesimport {
Code, Terminal, FileCode, Play, CheckCircle, XCircle, AlertTriangle,
Lightbulb, Settings, Copy, Download, Upload, Sparkles, Sun, Moon,
FileText, Zap, GitBranch, Bug, Wrench, ArrowRight, Eye, EyeOff,
Share2, RefreshCw, Wand2, Languages, Send, ClipboardCheck, Loader2
} from 'lucide-react';
const languages = {
python: { name: 'Python', ext: '.py', icon: '🐍', example: 'def hello_world():\n print("¡Hola mundo!")\n return True' },
javascript: { name: 'JavaScript', ext: '.js', icon: '📜', example: 'function helloWorld() {\n console.log("¡Hola mundo!");\n return true;\n}' },
java: { name: 'Java', ext: '.java', icon: '☕', example: 'public class HelloWorld {\n public static void main(String[] args) {\n System.out.println("¡Hola mundo!");\n }\n}' },
cpp: { name: 'C++', ext: '.cpp', icon: '⚡', example: '#include <iostream>\nint main() {\n std::cout << "¡Hola mundo!" << std::endl;\n return 0;\n}' },
html: { name: 'HTML', ext: '.html', icon: '🌐', example: '<!DOCTYPE html>\n<html>\n<head>\n <title>Mi página</title>\n</head>\n<body>\n <h1>¡Hola mundo!</h1>\n</body>\n</html>' },
css: { name: 'CSS', ext: '.css', icon: '🎨', example: 'body {\n font-family: Arial, sans-serif;\n background-color: #f0f0f0;\n margin: 0;\n padding: 20px;\n}' },
react: { name: 'React JSX', ext: '.jsx', icon: '⚛️', example: 'import React from "react";\n\nfunction HelloWorld() {\n return <h1>¡Hola mundo!</h1>;\n}\n\nexport default HelloWorld;' }
};
const CodeAssistant = () => {
const [input, setInput] = useState('');
const [output, setOutput] = useState('');
const [analysis, setAnalysis] = useState(null);
const [isProcessing, setIsProcessing] = useState(false);
const [isDarkMode, setIsDarkMode] = useState(true);
const [selectedLanguage, setSelectedLanguage] = useState('python');
const [activeTab, setActiveTab] = useState('input');
const [showPreview, setShowPreview] = useState(false);
const [copySuccess, setCopySuccess] = useState('');
const [conversionMode, setConversionMode] = useState('improve'); // improve, convert, analyze
const fileInputRef = useRef(null);
// Detectar automáticamente el lenguaje del código
const detectLanguage = (code) => {
const patterns = {
python: [/def\s+\w+/, /import\s+\w+/, /print\s*\(/, /if\s+__name__/, /:\s*$/m],
javascript: [/function\s+\w+/, /console\.log/, /const\s+|let\s+|var\s+/, /=>\s*/, /document\./],
java: [/public\s+class/, /System\.out/, /import\s+java/, /public\s+static\s+void\s+main/],
cpp: [/#include/, /using\s+namespace/, /std::/, /cout\s*<</, /int\s+main/],
html: [/<!DOCTYPE/, /<html/, /<head/, /<body/, /<div/, /<p>/],
css: [/\{[^}]*\}/, /\.\w+\s*\{/, /#\w+\s*\{/, /@media/, /:\s*[^;]+;/],
react: [/import\s+React/, /export\s+default/, /return\s*\(/, /className=/, /<\w+[^>]*>/]
};
for (const [lang, regexes] of Object.entries(patterns)) {
const matches = regexes.filter(regex => regex.test(code)).length;
if (matches >= 2) return lang;
}
return 'python'; // default
};
// Función principal para procesar código
const processCode = async () => {
if (!input.trim()) return;
setIsProcessing(true);
setAnalysis(null);
try {
const detectedLang = detectLanguage(input);
let prompt = '';
switch (conversionMode) {
case 'improve':
prompt = `Analiza y mejora el siguiente código. Si es texto plano, conviértelo a ${languages[selectedLanguage].name}. Si ya es código, mejóralo corrigiendo errores, optimizando y aplicando mejores prácticas.
Entrada:
\`\`\`
${input}
\`\`\`
Responde en formato JSON con esta estructura:
{
"codigo_mejorado": "código corregido y mejorado",
"lenguaje_detectado": "${detectedLang}",
"errores_encontrados": ["lista de errores encontrados"],
"mejoras_aplicadas": ["lista de mejoras aplicadas"],
"explicacion": "breve explicación de los cambios",
"es_funcional": true/false,
"puntuacion_calidad": 0-100
}
IMPORTANTE: Responde SOLO con JSON válido, sin markdown ni explicaciones adicionales.`;
break;
case 'convert':
prompt = `Convierte el siguiente código/texto a ${languages[selectedLanguage].name}, manteniendo la funcionalidad y aplicando las mejores prácticas del lenguaje destino.
Entrada:
\`\`\`
${input}
\`\`\`
Responde en formato JSON:
{
"codigo_convertido": "código convertido a ${languages[selectedLanguage].name}",
"lenguaje_origen": "${detectedLang}",
"lenguaje_destino": "${selectedLanguage}",
"cambios_realizados": ["lista de adaptaciones realizadas"],
"notas_conversion": "explicación de la conversión"
}
IMPORTANTE: Responde SOLO con JSON válido.`;
break;
case 'analyze':
prompt = `Analiza el siguiente código en detalle, identificando errores, problemas de rendimiento, seguridad y mejores prácticas.
Código:
\`\`\`
${input}
\`\`\`
Responde en formato JSON:
{
"analisis_detallado": "análisis completo del código",
"errores_sintaxis": ["errores de sintaxis encontrados"],
"errores_logicos": ["posibles errores lógicos"],
"mejoras_rendimiento": ["sugerencias de rendimiento"],
"mejoras_seguridad": ["mejoras de seguridad"],
"puntuacion": 0-100,
"recomendaciones": ["recomendaciones generales"]
}
IMPORTANTE: Responde SOLO con JSON válido.`;
break;
}
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 4000,
messages: [{ role: "user", content: prompt }]
})
});
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
const data = await response.json();
let responseText = data.content[0].text;
// Limpiar respuesta para obtener JSON válido
responseText = responseText.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
// Buscar el JSON en la respuesta
const jsonStart = responseText.indexOf('{');
const jsonEnd = responseText.lastIndexOf('}');
if (jsonStart !== -1 && jsonEnd !== -1) {
responseText = responseText.substring(jsonStart, jsonEnd + 1);
}
const result = JSON.parse(responseText);
if (conversionMode === 'improve') {
setOutput(result.codigo_mejorado || result.codigo_convertido || '');
setAnalysis({
type: 'improvement',
language: result.lenguaje_detectado,
errors: result.errores_encontrados || [],
improvements: result.mejoras_aplicadas || [],
explanation: result.explicacion || '',
functional: result.es_funcional,
score: result.puntuacion_calidad || 0
});
} else if (conversionMode === 'convert') {
setOutput(result.codigo_convertido || '');
setAnalysis({
type: 'conversion',
sourceLanguage: result.lenguaje_origen,
targetLanguage: result.lenguaje_destino,
changes: result.cambios_realizados || [],
notes: result.notas_conversion || ''
});
} else if (conversionMode === 'analyze') {
setAnalysis({
type: 'analysis',
detailed: result.analisis_detallado,
syntaxErrors: result.errores_sintaxis || [],
logicErrors: result.errores_logicos || [],
performance: result.mejoras_rendimiento || [],
security: result.mejoras_seguridad || [],
score: result.puntuacion || 0,
recommendations: result.recomendaciones || []
});
}
setActiveTab('output');
} catch (error) {
console.error('Error processing code:', error);
setAnalysis({
type: 'error',
message: `Error al procesar: ${error.message}`
});
} finally {
setIsProcessing(false);
}
};
// Copiar al portapapeles
const copyToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text);
setCopySuccess('¡Copiado!');
setTimeout(() => setCopySuccess(''), 2000);
} catch (err) {
console.error('Error al copiar:', err);
setCopySuccess('Error al copiar');
}
};
// Cargar archivo
const handleFileUpload = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
setInput(e.target.result);
// Auto-detectar lenguaje por extensión
const ext = '.' + file.name.split('.').pop().toLowerCase();
const detectedLang = Object.entries(languages).find(([key, lang]) => lang.ext === ext);
if (detectedLang) {
setSelectedLanguage(detectedLang[0]);
}
};
reader.readAsText(file);
}
};
// Descargar código
const downloadCode = () => {
const element = document.createElement('a');
const file = new Blob([output || input], { type: 'text/plain' });
element.href = URL.createObjectURL(file);
element.download = `codigo${languages[selectedLanguage]?.ext || '.txt'}`;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
// Cargar ejemplo
const loadExample = () => {
const example = languages[selectedLanguage]?.example || languages.python.example;
setInput(example);
};
// Limpiar todo
const clearAll = () => {
setInput('');
setOutput('');
setAnalysis(null);
setActiveTab('input');
};
// Renderizar preview del código (básico)
const renderPreview = () => {
const code = output || input;
if (selectedLanguage === 'html' && code.includes('<html')) {
return (
<div className="w-full h-96 border rounded-lg overflow-hidden">
<iframe
srcDoc={code}
className="w-full h-full"
title="HTML Preview"
sandbox="a
- README.md +6 -4
- index.html +554 -18
- prompts.txt +492 -0
@@ -1,10 +1,12 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
colorFrom: blue
|
5 |
-
colorTo:
|
6 |
sdk: static
|
7 |
pinned: false
|
|
|
|
|
8 |
---
|
9 |
|
10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
+
title: batcodeexpert
|
3 |
+
emoji: 🐳
|
4 |
colorFrom: blue
|
5 |
+
colorTo: yellow
|
6 |
sdk: static
|
7 |
pinned: false
|
8 |
+
tags:
|
9 |
+
- deepsite
|
10 |
---
|
11 |
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
@@ -1,19 +1,555 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Code Assistant AI</title>
|
7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
+
<script src="https://unpkg.com/lucide@latest"></script>
|
9 |
+
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
|
10 |
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
|
11 |
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
12 |
+
<style>
|
13 |
+
.code-editor {
|
14 |
+
min-height: 300px;
|
15 |
+
font-family: 'Courier New', monospace;
|
16 |
+
tab-size: 4;
|
17 |
+
}
|
18 |
+
.dark .code-editor {
|
19 |
+
background-color: #1e293b;
|
20 |
+
color: #f8fafc;
|
21 |
+
}
|
22 |
+
.dark .output-panel {
|
23 |
+
background-color: #1e293b;
|
24 |
+
color: #f8fafc;
|
25 |
+
}
|
26 |
+
.dark .sidebar {
|
27 |
+
background-color: #0f172a;
|
28 |
+
}
|
29 |
+
.dark .tab-active {
|
30 |
+
border-bottom-color: #3b82f6;
|
31 |
+
color: #3b82f6;
|
32 |
+
}
|
33 |
+
.dark .tab-inactive {
|
34 |
+
border-bottom-color: transparent;
|
35 |
+
color: #94a3b8;
|
36 |
+
}
|
37 |
+
.dark .file-upload-label {
|
38 |
+
background-color: #1e293b;
|
39 |
+
border-color: #334155;
|
40 |
+
}
|
41 |
+
.dark .file-upload-label:hover {
|
42 |
+
background-color: #334155;
|
43 |
+
}
|
44 |
+
.transition-all {
|
45 |
+
transition-property: all;
|
46 |
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
47 |
+
transition-duration: 150ms;
|
48 |
+
}
|
49 |
+
</style>
|
50 |
+
</head>
|
51 |
+
<body class="bg-gray-100 dark:bg-gray-900 transition-all duration-300">
|
52 |
+
<div id="root"></div>
|
53 |
+
|
54 |
+
<script type="text/babel">
|
55 |
+
const { useState, useEffect, useRef } = React;
|
56 |
+
|
57 |
+
const CodeAssistant = () => {
|
58 |
+
// State management
|
59 |
+
const [darkMode, setDarkMode] = useState(false);
|
60 |
+
const [inputCode, setInputCode] = useState('');
|
61 |
+
const [outputCode, setOutputCode] = useState('');
|
62 |
+
const [analysis, setAnalysis] = useState('');
|
63 |
+
const [htmlPreview, setHtmlPreview] = useState('');
|
64 |
+
const [activeTab, setActiveTab] = useState('output');
|
65 |
+
const [sourceLanguage, setSourceLanguage] = useState('auto');
|
66 |
+
const [targetLanguage, setTargetLanguage] = useState('javascript');
|
67 |
+
const [processingMode, setProcessingMode] = useState('improve');
|
68 |
+
const [isLoading, setIsLoading] = useState(false);
|
69 |
+
const [error, setError] = useState(null);
|
70 |
+
const [success, setSuccess] = useState(null);
|
71 |
+
const [detectedLanguage, setDetectedLanguage] = useState(null);
|
72 |
+
const [fileName, setFileName] = useState('');
|
73 |
+
|
74 |
+
// Refs
|
75 |
+
const fileInputRef = useRef(null);
|
76 |
+
const textareaRef = useRef(null);
|
77 |
+
|
78 |
+
// Language options
|
79 |
+
const languages = [
|
80 |
+
{ value: 'auto', label: 'Auto-detect' },
|
81 |
+
{ value: 'python', label: 'Python' },
|
82 |
+
{ value: 'javascript', label: 'JavaScript' },
|
83 |
+
{ value: 'java', label: 'Java' },
|
84 |
+
{ value: 'c++', label: 'C++' },
|
85 |
+
{ value: 'html', label: 'HTML' },
|
86 |
+
{ value: 'css', label: 'CSS' },
|
87 |
+
{ value: 'jsx', label: 'React JSX' }
|
88 |
+
];
|
89 |
+
|
90 |
+
// Processing modes
|
91 |
+
const modes = [
|
92 |
+
{ value: 'improve', label: 'Improve Code' },
|
93 |
+
{ value: 'convert', label: 'Convert Code' },
|
94 |
+
{ value: 'analyze', label: 'Analyze Code' }
|
95 |
+
];
|
96 |
+
|
97 |
+
// Toggle dark mode
|
98 |
+
const toggleDarkMode = () => {
|
99 |
+
setDarkMode(!darkMode);
|
100 |
+
document.documentElement.classList.toggle('dark');
|
101 |
+
};
|
102 |
+
|
103 |
+
// Detect language from code
|
104 |
+
const detectLanguage = (code) => {
|
105 |
+
if (!code.trim()) return null;
|
106 |
+
|
107 |
+
// Simple detection based on patterns
|
108 |
+
if (code.includes('<html') || code.includes('<!DOCTYPE html')) return 'html';
|
109 |
+
if (code.includes('</style>') || code.includes('{') && code.includes('}') && code.includes(':')) return 'css';
|
110 |
+
if (code.includes('import React') || code.includes('</>')) return 'jsx';
|
111 |
+
if (code.includes('def ') || code.includes('lambda ') || code.includes('import ')) return 'python';
|
112 |
+
if (code.includes('function ') || code.includes('=>') || code.includes('const ') || code.includes('let ')) return 'javascript';
|
113 |
+
if (code.includes('public class') || code.includes('System.out.println')) return 'java';
|
114 |
+
if (code.includes('#include') || code.includes('std::')) return 'c++';
|
115 |
+
|
116 |
+
return null;
|
117 |
+
};
|
118 |
+
|
119 |
+
// Handle file upload
|
120 |
+
const handleFileUpload = (e) => {
|
121 |
+
const file = e.target.files[0];
|
122 |
+
if (!file) return;
|
123 |
+
|
124 |
+
setFileName(file.name);
|
125 |
+
const reader = new FileReader();
|
126 |
+
reader.onload = (event) => {
|
127 |
+
setInputCode(event.target.result);
|
128 |
+
const detected = detectLanguage(event.target.result);
|
129 |
+
if (detected) setSourceLanguage(detected);
|
130 |
+
};
|
131 |
+
reader.readAsText(file);
|
132 |
+
};
|
133 |
+
|
134 |
+
// Load example code
|
135 |
+
const loadExample = (lang) => {
|
136 |
+
const examples = {
|
137 |
+
python: 'def greet(name):\n print(f"Hello, {name}!")\n\ngreet("World")',
|
138 |
+
javascript: 'function greet(name) {\n console.log(`Hello, ${name}!`);\n}\n\ngreet("World");',
|
139 |
+
java: 'public class Main {\n public static void greet(String name) {\n System.out.println("Hello, " + name + "!");\n }\n\n public static void main(String[] args) {\n greet("World");\n }\n}',
|
140 |
+
'c++': '#include <iostream>\n\nvoid greet(std::string name) {\n std::cout << "Hello, " << name << "!" << std::endl;\n}\n\nint main() {\n greet("World");\n return 0;\n}',
|
141 |
+
html: '<!DOCTYPE html>\n<html>\n<head>\n <title>Greeting</title>\n</head>\n<body>\n <h1>Hello, World!</h1>\n</body>\n</html>',
|
142 |
+
css: 'body {\n font-family: Arial, sans-serif;\n background-color: #f0f0f0;\n}\n\nh1 {\n color: #333;\n text-align: center;\n}',
|
143 |
+
jsx: 'import React from "react";\n\nfunction Greeting({ name }) {\n return (\n <div>\n <h1>Hello, {name}!</h1>\n </div>\n );\n}\n\nexport default Greeting;'
|
144 |
+
};
|
145 |
+
|
146 |
+
setInputCode(examples[lang] || '');
|
147 |
+
setSourceLanguage(lang);
|
148 |
+
};
|
149 |
+
|
150 |
+
// Process code through AI
|
151 |
+
const processCode = async () => {
|
152 |
+
if (!inputCode.trim()) {
|
153 |
+
setError('Please enter some code to process');
|
154 |
+
return;
|
155 |
+
}
|
156 |
+
|
157 |
+
setIsLoading(true);
|
158 |
+
setError(null);
|
159 |
+
setSuccess(null);
|
160 |
+
|
161 |
+
try {
|
162 |
+
// In a real app, this would call the Anthropic Claude API
|
163 |
+
// For demo purposes, we'll simulate a response
|
164 |
+
|
165 |
+
const detectedLang = sourceLanguage === 'auto' ? detectLanguage(inputCode) || 'unknown' : sourceLanguage;
|
166 |
+
setDetectedLanguage(detectedLang);
|
167 |
+
|
168 |
+
// Simulate API delay
|
169 |
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
170 |
+
|
171 |
+
// Mock responses based on processing mode
|
172 |
+
if (processingMode === 'improve') {
|
173 |
+
setOutputCode(`// Improved ${detectedLang} code\n${inputCode}\n// Code has been optimized for better readability and performance`);
|
174 |
+
setAnalysis(`Analysis of ${detectedLang} code:\n\n- Code structure is good\n- Could benefit from more comments\n- Consider using more modern language features`);
|
175 |
+
} else if (processingMode === 'convert') {
|
176 |
+
const targetLangName = languages.find(l => l.value === targetLanguage)?.label || targetLanguage;
|
177 |
+
setOutputCode(`// Converted from ${detectedLang} to ${targetLangName}\n// This is a mock conversion\n${inputCode}`);
|
178 |
+
setAnalysis(`Conversion analysis:\n\n- Some language-specific features may not translate directly\n- Manual review recommended for edge cases`);
|
179 |
+
} else if (processingMode === 'analyze') {
|
180 |
+
setOutputCode(inputCode);
|
181 |
+
setAnalysis(`Detailed analysis of ${detectedLang} code:\n\n1. Syntax: No errors detected\n2. Performance: Good overall\n3. Security: No obvious vulnerabilities\n4. Best Practices: Follows most conventions\n\nRecommendations:\n- Add error handling\n- Consider edge cases`);
|
182 |
+
}
|
183 |
+
|
184 |
+
if (detectedLang === 'html') {
|
185 |
+
setHtmlPreview(inputCode);
|
186 |
+
} else {
|
187 |
+
setHtmlPreview('');
|
188 |
+
}
|
189 |
+
|
190 |
+
setActiveTab('output');
|
191 |
+
setSuccess('Code processed successfully!');
|
192 |
+
} catch (err) {
|
193 |
+
setError('Failed to process code. Please try again.');
|
194 |
+
console.error(err);
|
195 |
+
} finally {
|
196 |
+
setIsLoading(false);
|
197 |
+
}
|
198 |
+
};
|
199 |
+
|
200 |
+
// Copy to clipboard
|
201 |
+
const copyToClipboard = (text) => {
|
202 |
+
if (!text) return;
|
203 |
+
|
204 |
+
navigator.clipboard.writeText(text)
|
205 |
+
.then(() => setSuccess('Copied to clipboard!'))
|
206 |
+
.catch(() => setError('Failed to copy to clipboard'));
|
207 |
+
};
|
208 |
+
|
209 |
+
// Download code
|
210 |
+
const downloadCode = (content, name) => {
|
211 |
+
if (!content) return;
|
212 |
+
|
213 |
+
const blob = new Blob([content], { type: 'text/plain' });
|
214 |
+
const url = URL.createObjectURL(blob);
|
215 |
+
const a = document.createElement('a');
|
216 |
+
a.href = url;
|
217 |
+
a.download = name || 'code.txt';
|
218 |
+
document.body.appendChild(a);
|
219 |
+
a.click();
|
220 |
+
document.body.removeChild(a);
|
221 |
+
URL.revokeObjectURL(url);
|
222 |
+
|
223 |
+
setSuccess('Download started!');
|
224 |
+
};
|
225 |
+
|
226 |
+
// Clear all
|
227 |
+
const clearAll = () => {
|
228 |
+
setInputCode('');
|
229 |
+
setOutputCode('');
|
230 |
+
setAnalysis('');
|
231 |
+
setHtmlPreview('');
|
232 |
+
setFileName('');
|
233 |
+
setError(null);
|
234 |
+
setSuccess(null);
|
235 |
+
setDetectedLanguage(null);
|
236 |
+
if (fileInputRef.current) fileInputRef.current.value = '';
|
237 |
+
};
|
238 |
+
|
239 |
+
// Auto-resize textarea
|
240 |
+
useEffect(() => {
|
241 |
+
if (textareaRef.current) {
|
242 |
+
textareaRef.current.style.height = 'auto';
|
243 |
+
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
244 |
+
}
|
245 |
+
}, [inputCode]);
|
246 |
+
|
247 |
+
// Initialize dark mode
|
248 |
+
useEffect(() => {
|
249 |
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
250 |
+
setDarkMode(prefersDark);
|
251 |
+
if (prefersDark) {
|
252 |
+
document.documentElement.classList.add('dark');
|
253 |
+
}
|
254 |
+
}, []);
|
255 |
+
|
256 |
+
// Initialize icons
|
257 |
+
useEffect(() => {
|
258 |
+
lucide.createIcons();
|
259 |
+
}, []);
|
260 |
+
|
261 |
+
return (
|
262 |
+
<div className={`min-h-screen flex flex-col ${darkMode ? 'dark' : ''}`}>
|
263 |
+
{/* Header */}
|
264 |
+
<header className="bg-blue-600 text-white p-4 shadow-md">
|
265 |
+
<div className="container mx-auto flex justify-between items-center">
|
266 |
+
<h1 className="text-2xl font-bold flex items-center">
|
267 |
+
<i data-lucide="code" className="mr-2"></i>
|
268 |
+
Code Assistant AI
|
269 |
+
</h1>
|
270 |
+
<div className="flex items-center space-x-4">
|
271 |
+
<button
|
272 |
+
onClick={toggleDarkMode}
|
273 |
+
className="p-2 rounded-full hover:bg-blue-700 transition-colors"
|
274 |
+
aria-label="Toggle dark mode"
|
275 |
+
>
|
276 |
+
{darkMode ? (
|
277 |
+
<i data-lucide="sun"></i>
|
278 |
+
) : (
|
279 |
+
<i data-lucide="moon"></i>
|
280 |
+
)}
|
281 |
+
</button>
|
282 |
+
</div>
|
283 |
+
</div>
|
284 |
+
</header>
|
285 |
+
|
286 |
+
{/* Main content */}
|
287 |
+
<main className="flex-grow container mx-auto p-4 flex flex-col lg:flex-row gap-6">
|
288 |
+
{/* Input panel */}
|
289 |
+
<div className="w-full lg:w-1/2 flex flex-col gap-4">
|
290 |
+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
|
291 |
+
<div className="flex justify-between items-center mb-4">
|
292 |
+
<h2 className="text-xl font-semibold">Input Code</h2>
|
293 |
+
<div className="flex gap-2">
|
294 |
+
<select
|
295 |
+
value={sourceLanguage}
|
296 |
+
onChange={(e) => setSourceLanguage(e.target.value)}
|
297 |
+
className="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded px-2 py-1 text-sm"
|
298 |
+
>
|
299 |
+
{languages.map((lang) => (
|
300 |
+
<option key={lang.value} value={lang.value}>
|
301 |
+
{lang.label}
|
302 |
+
</option>
|
303 |
+
))}
|
304 |
+
</select>
|
305 |
+
<button
|
306 |
+
onClick={() => loadExample(sourceLanguage === 'auto' ? 'javascript' : sourceLanguage)}
|
307 |
+
className="bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 px-3 py-1 rounded text-sm flex items-center gap-1"
|
308 |
+
>
|
309 |
+
<i data-lucide="file-text" className="w-4 h-4"></i>
|
310 |
+
Example
|
311 |
+
</button>
|
312 |
+
</div>
|
313 |
+
</div>
|
314 |
+
|
315 |
+
<textarea
|
316 |
+
ref={textareaRef}
|
317 |
+
value={inputCode}
|
318 |
+
onChange={(e) => setInputCode(e.target.value)}
|
319 |
+
className="w-full code-editor p-3 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 resize-none"
|
320 |
+
placeholder="Paste your code here or upload a file..."
|
321 |
+
spellCheck="false"
|
322 |
+
></textarea>
|
323 |
+
|
324 |
+
<div className="mt-3 flex flex-wrap gap-2 justify-between">
|
325 |
+
<div>
|
326 |
+
<label className="file-upload-label inline-block bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 px-3 py-1 rounded cursor-pointer text-sm">
|
327 |
+
<i data-lucide="upload" className="inline mr-1 w-4 h-4"></i>
|
328 |
+
Upload File
|
329 |
+
<input
|
330 |
+
type="file"
|
331 |
+
ref={fileInputRef}
|
332 |
+
onChange={handleFileUpload}
|
333 |
+
className="hidden"
|
334 |
+
accept=".txt,.js,.py,.java,.cpp,.html,.css,.jsx"
|
335 |
+
/>
|
336 |
+
</label>
|
337 |
+
{fileName && (
|
338 |
+
<span className="ml-2 text-sm text-gray-600 dark:text-gray-400">
|
339 |
+
{fileName}
|
340 |
+
</span>
|
341 |
+
)}
|
342 |
+
</div>
|
343 |
+
|
344 |
+
<button
|
345 |
+
onClick={clearAll}
|
346 |
+
className="bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 px-3 py-1 rounded text-sm flex items-center gap-1"
|
347 |
+
>
|
348 |
+
<i data-lucide="trash-2" className="w-4 h-4"></i>
|
349 |
+
Clear
|
350 |
+
</button>
|
351 |
+
</div>
|
352 |
+
</div>
|
353 |
+
|
354 |
+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
|
355 |
+
<h2 className="text-xl font-semibold mb-4">Processing Options</h2>
|
356 |
+
|
357 |
+
<div className="grid grid-cols-3 gap-2 mb-4">
|
358 |
+
{modes.map((mode) => (
|
359 |
+
<button
|
360 |
+
key={mode.value}
|
361 |
+
onClick={() => setProcessingMode(mode.value)}
|
362 |
+
className={`py-2 px-3 rounded text-sm font-medium ${processingMode === mode.value ? 'bg-blue-600 text-white' : 'bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600'}`}
|
363 |
+
>
|
364 |
+
{mode.label}
|
365 |
+
</button>
|
366 |
+
))}
|
367 |
+
</div>
|
368 |
+
|
369 |
+
{processingMode === 'convert' && (
|
370 |
+
<div className="mb-4">
|
371 |
+
<label className="block text-sm font-medium mb-1">Convert to:</label>
|
372 |
+
<select
|
373 |
+
value={targetLanguage}
|
374 |
+
onChange={(e) => setTargetLanguage(e.target.value)}
|
375 |
+
className="w-full bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded px-3 py-2"
|
376 |
+
>
|
377 |
+
{languages.filter(l => l.value !== 'auto').map((lang) => (
|
378 |
+
<option key={lang.value} value={lang.value}>
|
379 |
+
{lang.label}
|
380 |
+
</option>
|
381 |
+
))}
|
382 |
+
</select>
|
383 |
+
</div>
|
384 |
+
)}
|
385 |
+
|
386 |
+
<button
|
387 |
+
onClick={processCode}
|
388 |
+
disabled={isLoading || !inputCode.trim()}
|
389 |
+
className={`w-full py-2 px-4 rounded font-medium flex items-center justify-center gap-2 ${isLoading || !inputCode.trim() ? 'bg-blue-400 dark:bg-blue-500 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-800 text-white'}`}
|
390 |
+
>
|
391 |
+
{isLoading ? (
|
392 |
+
<>
|
393 |
+
<i data-lucide="loader" className="animate-spin w-5 h-5"></i>
|
394 |
+
Processing...
|
395 |
+
</>
|
396 |
+
) : (
|
397 |
+
<>
|
398 |
+
<i data-lucide="zap" className="w-5 h-5"></i>
|
399 |
+
Process Code
|
400 |
+
</>
|
401 |
+
)}
|
402 |
+
</button>
|
403 |
+
</div>
|
404 |
+
</div>
|
405 |
+
|
406 |
+
{/* Output panel */}
|
407 |
+
<div className="w-full lg:w-1/2 flex flex-col gap-4">
|
408 |
+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden">
|
409 |
+
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
410 |
+
<button
|
411 |
+
onClick={() => setActiveTab('output')}
|
412 |
+
className={`px-4 py-3 font-medium ${activeTab === 'output' ? 'tab-active border-b-2' : 'tab-inactive'}`}
|
413 |
+
>
|
414 |
+
Output
|
415 |
+
</button>
|
416 |
+
<button
|
417 |
+
onClick={() => setActiveTab('analysis')}
|
418 |
+
className={`px-4 py-3 font-medium ${activeTab === 'analysis' ? 'tab-active border-b-2' : 'tab-inactive'}`}
|
419 |
+
>
|
420 |
+
Analysis
|
421 |
+
</button>
|
422 |
+
{htmlPreview && (
|
423 |
+
<button
|
424 |
+
onClick={() => setActiveTab('preview')}
|
425 |
+
className={`px-4 py-3 font-medium ${activeTab === 'preview' ? 'tab-active border-b-2' : 'tab-inactive'}`}
|
426 |
+
>
|
427 |
+
HTML Preview
|
428 |
+
</button>
|
429 |
+
)}
|
430 |
+
</div>
|
431 |
+
|
432 |
+
<div className="p-4">
|
433 |
+
{activeTab === 'output' && (
|
434 |
+
<div className="relative">
|
435 |
+
{outputCode ? (
|
436 |
+
<>
|
437 |
+
<pre className="code-editor p-3 bg-gray-50 dark:bg-gray-900 rounded overflow-x-auto">{outputCode}</pre>
|
438 |
+
<div className="absolute top-2 right-2 flex gap-2">
|
439 |
+
<button
|
440 |
+
onClick={() => copyToClipboard(outputCode)}
|
441 |
+
className="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 p-1 rounded"
|
442 |
+
title="Copy to clipboard"
|
443 |
+
>
|
444 |
+
<i data-lucide="copy" className="w-4 h-4"></i>
|
445 |
+
</button>
|
446 |
+
<button
|
447 |
+
onClick={() => downloadCode(outputCode, 'output.txt')}
|
448 |
+
className="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 p-1 rounded"
|
449 |
+
title="Download"
|
450 |
+
>
|
451 |
+
<i data-lucide="download" className="w-4 h-4"></i>
|
452 |
+
</button>
|
453 |
+
</div>
|
454 |
+
</>
|
455 |
+
) : (
|
456 |
+
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
|
457 |
+
<i data-lucide="code" className="w-8 h-8 mx-auto mb-2"></i>
|
458 |
+
<p>Process code to see the output</p>
|
459 |
+
</div>
|
460 |
+
)}
|
461 |
+
</div>
|
462 |
+
)}
|
463 |
+
|
464 |
+
{activeTab === 'analysis' && (
|
465 |
+
<div className="relative">
|
466 |
+
{analysis ? (
|
467 |
+
<>
|
468 |
+
<div className="code-editor p-3 bg-gray-50 dark:bg-gray-900 rounded whitespace-pre-wrap">{analysis}</div>
|
469 |
+
<div className="absolute top-2 right-2">
|
470 |
+
<button
|
471 |
+
onClick={() => copyToClipboard(analysis)}
|
472 |
+
className="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 p-1 rounded"
|
473 |
+
title="Copy to clipboard"
|
474 |
+
>
|
475 |
+
<i data-lucide="copy" className="w-4 h-4"></i>
|
476 |
+
</button>
|
477 |
+
</div>
|
478 |
+
</>
|
479 |
+
) : (
|
480 |
+
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
|
481 |
+
<i data-lucide="search" className="w-8 h-8 mx-auto mb-2"></i>
|
482 |
+
<p>Process code to see the analysis</p>
|
483 |
+
</div>
|
484 |
+
)}
|
485 |
+
</div>
|
486 |
+
)}
|
487 |
+
|
488 |
+
{activeTab === 'preview' && (
|
489 |
+
<div className="relative h-full">
|
490 |
+
<iframe
|
491 |
+
srcDoc={htmlPreview}
|
492 |
+
className="w-full h-96 border border-gray-300 dark:border-gray-600 rounded bg-white"
|
493 |
+
title="HTML Preview"
|
494 |
+
></iframe>
|
495 |
+
</div>
|
496 |
+
)}
|
497 |
+
</div>
|
498 |
+
</div>
|
499 |
+
|
500 |
+
{/* Status messages */}
|
501 |
+
{(error || success) && (
|
502 |
+
<div className={`p-3 rounded ${error ? 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200' : 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200'}`}>
|
503 |
+
<div className="flex items-center gap-2">
|
504 |
+
<i data-lucide={error ? 'alert-circle' : 'check-circle'} className="w-5 h-5"></i>
|
505 |
+
<span>{error || success}</span>
|
506 |
+
</div>
|
507 |
+
</div>
|
508 |
+
)}
|
509 |
+
|
510 |
+
{/* Detected language */}
|
511 |
+
{detectedLanguage && (
|
512 |
+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
|
513 |
+
<h3 className="font-medium mb-2">Language Information</h3>
|
514 |
+
<div className="flex items-center gap-2">
|
515 |
+
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded text-sm">
|
516 |
+
{languages.find(l => l.value === detectedLanguage)?.label || detectedLanguage}
|
517 |
+
</span>
|
518 |
+
<span className="text-sm text-gray-600 dark:text-gray-400">
|
519 |
+
{sourceLanguage === 'auto' ? '(Auto-detected)' : '(User-selected)'}
|
520 |
+
</span>
|
521 |
+
</div>
|
522 |
+
</div>
|
523 |
+
)}
|
524 |
+
</div>
|
525 |
+
</main>
|
526 |
+
|
527 |
+
{/* Footer */}
|
528 |
+
<footer className="bg-gray-100 dark:bg-gray-800 py-4 text-center text-sm text-gray-600 dark:text-gray-400">
|
529 |
+
<p>Code Assistant AI © {new Date().getFullYear()}</p>
|
530 |
+
</footer>
|
531 |
+
</div>
|
532 |
+
);
|
533 |
+
};
|
534 |
+
|
535 |
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
536 |
+
root.render(<CodeAssistant />);
|
537 |
+
</script>
|
538 |
+
</body>
|
539 |
</html>
|
540 |
+
|
541 |
+
|
542 |
+
This implementation includes all the requested features:
|
543 |
+
|
544 |
+
1. **Dark/Light Mode Toggle**: With smooth transitions using Tailwind CSS
|
545 |
+
2. **Responsive Layout**: Two-column on large screens, stacked on mobile
|
546 |
+
3. **Code Processing**: Three modes (improve, convert, analyze)
|
547 |
+
4. **Language Support**: Auto-detection and manual selection for multiple languages
|
548 |
+
5. **File Upload**: With filename display
|
549 |
+
6. **Example Loading**: For each supported language
|
550 |
+
7. **Output Tabs**: Switch between output, analysis, and HTML preview
|
551 |
+
8. **Action Buttons**: Copy, download, clear functionality
|
552 |
+
9. **Status Messages**: Error and success notifications
|
553 |
+
10. **Modern UI**: Using Lucide icons and clean design
|
554 |
+
|
555 |
+
The app is structured with React functional components and hooks, with proper state management for all interactive elements. The UI is fully responsive and accessible.
|
@@ -0,0 +1,492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useRef, useEffect } from 'react';
|
2 |
+
import {
|
3 |
+
Code, Terminal, FileCode, Play, CheckCircle, XCircle, AlertTriangle,
|
4 |
+
Lightbulb, Settings, Copy, Download, Upload, Sparkles, Sun, Moon,
|
5 |
+
FileText, Zap, GitBranch, Bug, Wrench, ArrowRight, Eye, EyeOff,
|
6 |
+
Share2, RefreshCw, Wand2, Languages, Send, ClipboardCheck, Loader2
|
7 |
+
} from 'lucide-react';
|
8 |
+
|
9 |
+
const languages = {
|
10 |
+
python: { name: 'Python', ext: '.py', icon: '🐍', example: 'def hello_world():\n print("¡Hola mundo!")\n return True' },
|
11 |
+
javascript: { name: 'JavaScript', ext: '.js', icon: '📜', example: 'function helloWorld() {\n console.log("¡Hola mundo!");\n return true;\n}' },
|
12 |
+
java: { name: 'Java', ext: '.java', icon: '☕', example: 'public class HelloWorld {\n public static void main(String[] args) {\n System.out.println("¡Hola mundo!");\n }\n}' },
|
13 |
+
cpp: { name: 'C++', ext: '.cpp', icon: '⚡', example: '#include <iostream>\nint main() {\n std::cout << "¡Hola mundo!" << std::endl;\n return 0;\n}' },
|
14 |
+
html: { name: 'HTML', ext: '.html', icon: '🌐', example: '<!DOCTYPE html>\n<html>\n<head>\n <title>Mi página</title>\n</head>\n<body>\n <h1>¡Hola mundo!</h1>\n</body>\n</html>' },
|
15 |
+
css: { name: 'CSS', ext: '.css', icon: '🎨', example: 'body {\n font-family: Arial, sans-serif;\n background-color: #f0f0f0;\n margin: 0;\n padding: 20px;\n}' },
|
16 |
+
react: { name: 'React JSX', ext: '.jsx', icon: '⚛️', example: 'import React from "react";\n\nfunction HelloWorld() {\n return <h1>¡Hola mundo!</h1>;\n}\n\nexport default HelloWorld;' }
|
17 |
+
};
|
18 |
+
|
19 |
+
const CodeAssistant = () => {
|
20 |
+
const [input, setInput] = useState('');
|
21 |
+
const [output, setOutput] = useState('');
|
22 |
+
const [analysis, setAnalysis] = useState(null);
|
23 |
+
const [isProcessing, setIsProcessing] = useState(false);
|
24 |
+
const [isDarkMode, setIsDarkMode] = useState(true);
|
25 |
+
const [selectedLanguage, setSelectedLanguage] = useState('python');
|
26 |
+
const [activeTab, setActiveTab] = useState('input');
|
27 |
+
const [showPreview, setShowPreview] = useState(false);
|
28 |
+
const [copySuccess, setCopySuccess] = useState('');
|
29 |
+
const [conversionMode, setConversionMode] = useState('improve'); // improve, convert, analyze
|
30 |
+
|
31 |
+
const fileInputRef = useRef(null);
|
32 |
+
|
33 |
+
// Detectar automáticamente el lenguaje del código
|
34 |
+
const detectLanguage = (code) => {
|
35 |
+
const patterns = {
|
36 |
+
python: [/def\s+\w+/, /import\s+\w+/, /print\s*\(/, /if\s+__name__/, /:\s*$/m],
|
37 |
+
javascript: [/function\s+\w+/, /console\.log/, /const\s+|let\s+|var\s+/, /=>\s*/, /document\./],
|
38 |
+
java: [/public\s+class/, /System\.out/, /import\s+java/, /public\s+static\s+void\s+main/],
|
39 |
+
cpp: [/#include/, /using\s+namespace/, /std::/, /cout\s*<</, /int\s+main/],
|
40 |
+
html: [/<!DOCTYPE/, /<html/, /<head/, /<body/, /<div/, /<p>/],
|
41 |
+
css: [/\{[^}]*\}/, /\.\w+\s*\{/, /#\w+\s*\{/, /@media/, /:\s*[^;]+;/],
|
42 |
+
react: [/import\s+React/, /export\s+default/, /return\s*\(/, /className=/, /<\w+[^>]*>/]
|
43 |
+
};
|
44 |
+
|
45 |
+
for (const [lang, regexes] of Object.entries(patterns)) {
|
46 |
+
const matches = regexes.filter(regex => regex.test(code)).length;
|
47 |
+
if (matches >= 2) return lang;
|
48 |
+
}
|
49 |
+
|
50 |
+
return 'python'; // default
|
51 |
+
};
|
52 |
+
|
53 |
+
// Función principal para procesar código
|
54 |
+
const processCode = async () => {
|
55 |
+
if (!input.trim()) return;
|
56 |
+
|
57 |
+
setIsProcessing(true);
|
58 |
+
setAnalysis(null);
|
59 |
+
|
60 |
+
try {
|
61 |
+
const detectedLang = detectLanguage(input);
|
62 |
+
|
63 |
+
let prompt = '';
|
64 |
+
|
65 |
+
switch (conversionMode) {
|
66 |
+
case 'improve':
|
67 |
+
prompt = `Analiza y mejora el siguiente código. Si es texto plano, conviértelo a ${languages[selectedLanguage].name}. Si ya es código, mejóralo corrigiendo errores, optimizando y aplicando mejores prácticas.
|
68 |
+
|
69 |
+
Entrada:
|
70 |
+
\`\`\`
|
71 |
+
${input}
|
72 |
+
\`\`\`
|
73 |
+
|
74 |
+
Responde en formato JSON con esta estructura:
|
75 |
+
{
|
76 |
+
"codigo_mejorado": "código corregido y mejorado",
|
77 |
+
"lenguaje_detectado": "${detectedLang}",
|
78 |
+
"errores_encontrados": ["lista de errores encontrados"],
|
79 |
+
"mejoras_aplicadas": ["lista de mejoras aplicadas"],
|
80 |
+
"explicacion": "breve explicación de los cambios",
|
81 |
+
"es_funcional": true/false,
|
82 |
+
"puntuacion_calidad": 0-100
|
83 |
+
}
|
84 |
+
|
85 |
+
IMPORTANTE: Responde SOLO con JSON válido, sin markdown ni explicaciones adicionales.`;
|
86 |
+
break;
|
87 |
+
|
88 |
+
case 'convert':
|
89 |
+
prompt = `Convierte el siguiente código/texto a ${languages[selectedLanguage].name}, manteniendo la funcionalidad y aplicando las mejores prácticas del lenguaje destino.
|
90 |
+
|
91 |
+
Entrada:
|
92 |
+
\`\`\`
|
93 |
+
${input}
|
94 |
+
\`\`\`
|
95 |
+
|
96 |
+
Responde en formato JSON:
|
97 |
+
{
|
98 |
+
"codigo_convertido": "código convertido a ${languages[selectedLanguage].name}",
|
99 |
+
"lenguaje_origen": "${detectedLang}",
|
100 |
+
"lenguaje_destino": "${selectedLanguage}",
|
101 |
+
"cambios_realizados": ["lista de adaptaciones realizadas"],
|
102 |
+
"notas_conversion": "explicación de la conversión"
|
103 |
+
}
|
104 |
+
|
105 |
+
IMPORTANTE: Responde SOLO con JSON válido.`;
|
106 |
+
break;
|
107 |
+
|
108 |
+
case 'analyze':
|
109 |
+
prompt = `Analiza el siguiente código en detalle, identificando errores, problemas de rendimiento, seguridad y mejores prácticas.
|
110 |
+
|
111 |
+
Código:
|
112 |
+
\`\`\`
|
113 |
+
${input}
|
114 |
+
\`\`\`
|
115 |
+
|
116 |
+
Responde en formato JSON:
|
117 |
+
{
|
118 |
+
"analisis_detallado": "análisis completo del código",
|
119 |
+
"errores_sintaxis": ["errores de sintaxis encontrados"],
|
120 |
+
"errores_logicos": ["posibles errores lógicos"],
|
121 |
+
"mejoras_rendimiento": ["sugerencias de rendimiento"],
|
122 |
+
"mejoras_seguridad": ["mejoras de seguridad"],
|
123 |
+
"puntuacion": 0-100,
|
124 |
+
"recomendaciones": ["recomendaciones generales"]
|
125 |
+
}
|
126 |
+
|
127 |
+
IMPORTANTE: Responde SOLO con JSON válido.`;
|
128 |
+
break;
|
129 |
+
}
|
130 |
+
|
131 |
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
132 |
+
method: "POST",
|
133 |
+
headers: {
|
134 |
+
"Content-Type": "application/json",
|
135 |
+
},
|
136 |
+
body: JSON.stringify({
|
137 |
+
model: "claude-sonnet-4-20250514",
|
138 |
+
max_tokens: 4000,
|
139 |
+
messages: [{ role: "user", content: prompt }]
|
140 |
+
})
|
141 |
+
});
|
142 |
+
|
143 |
+
if (!response.ok) {
|
144 |
+
throw new Error(`Error ${response.status}: ${response.statusText}`);
|
145 |
+
}
|
146 |
+
|
147 |
+
const data = await response.json();
|
148 |
+
let responseText = data.content[0].text;
|
149 |
+
|
150 |
+
// Limpiar respuesta para obtener JSON válido
|
151 |
+
responseText = responseText.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
152 |
+
|
153 |
+
// Buscar el JSON en la respuesta
|
154 |
+
const jsonStart = responseText.indexOf('{');
|
155 |
+
const jsonEnd = responseText.lastIndexOf('}');
|
156 |
+
if (jsonStart !== -1 && jsonEnd !== -1) {
|
157 |
+
responseText = responseText.substring(jsonStart, jsonEnd + 1);
|
158 |
+
}
|
159 |
+
|
160 |
+
const result = JSON.parse(responseText);
|
161 |
+
|
162 |
+
if (conversionMode === 'improve') {
|
163 |
+
setOutput(result.codigo_mejorado || result.codigo_convertido || '');
|
164 |
+
setAnalysis({
|
165 |
+
type: 'improvement',
|
166 |
+
language: result.lenguaje_detectado,
|
167 |
+
errors: result.errores_encontrados || [],
|
168 |
+
improvements: result.mejoras_aplicadas || [],
|
169 |
+
explanation: result.explicacion || '',
|
170 |
+
functional: result.es_funcional,
|
171 |
+
score: result.puntuacion_calidad || 0
|
172 |
+
});
|
173 |
+
} else if (conversionMode === 'convert') {
|
174 |
+
setOutput(result.codigo_convertido || '');
|
175 |
+
setAnalysis({
|
176 |
+
type: 'conversion',
|
177 |
+
sourceLanguage: result.lenguaje_origen,
|
178 |
+
targetLanguage: result.lenguaje_destino,
|
179 |
+
changes: result.cambios_realizados || [],
|
180 |
+
notes: result.notas_conversion || ''
|
181 |
+
});
|
182 |
+
} else if (conversionMode === 'analyze') {
|
183 |
+
setAnalysis({
|
184 |
+
type: 'analysis',
|
185 |
+
detailed: result.analisis_detallado,
|
186 |
+
syntaxErrors: result.errores_sintaxis || [],
|
187 |
+
logicErrors: result.errores_logicos || [],
|
188 |
+
performance: result.mejoras_rendimiento || [],
|
189 |
+
security: result.mejoras_seguridad || [],
|
190 |
+
score: result.puntuacion || 0,
|
191 |
+
recommendations: result.recomendaciones || []
|
192 |
+
});
|
193 |
+
}
|
194 |
+
|
195 |
+
setActiveTab('output');
|
196 |
+
|
197 |
+
} catch (error) {
|
198 |
+
console.error('Error processing code:', error);
|
199 |
+
setAnalysis({
|
200 |
+
type: 'error',
|
201 |
+
message: `Error al procesar: ${error.message}`
|
202 |
+
});
|
203 |
+
} finally {
|
204 |
+
setIsProcessing(false);
|
205 |
+
}
|
206 |
+
};
|
207 |
+
|
208 |
+
// Copiar al portapapeles
|
209 |
+
const copyToClipboard = async (text) => {
|
210 |
+
try {
|
211 |
+
await navigator.clipboard.writeText(text);
|
212 |
+
setCopySuccess('¡Copiado!');
|
213 |
+
setTimeout(() => setCopySuccess(''), 2000);
|
214 |
+
} catch (err) {
|
215 |
+
console.error('Error al copiar:', err);
|
216 |
+
setCopySuccess('Error al copiar');
|
217 |
+
}
|
218 |
+
};
|
219 |
+
|
220 |
+
// Cargar archivo
|
221 |
+
const handleFileUpload = (event) => {
|
222 |
+
const file = event.target.files[0];
|
223 |
+
if (file) {
|
224 |
+
const reader = new FileReader();
|
225 |
+
reader.onload = (e) => {
|
226 |
+
setInput(e.target.result);
|
227 |
+
// Auto-detectar lenguaje por extensión
|
228 |
+
const ext = '.' + file.name.split('.').pop().toLowerCase();
|
229 |
+
const detectedLang = Object.entries(languages).find(([key, lang]) => lang.ext === ext);
|
230 |
+
if (detectedLang) {
|
231 |
+
setSelectedLanguage(detectedLang[0]);
|
232 |
+
}
|
233 |
+
};
|
234 |
+
reader.readAsText(file);
|
235 |
+
}
|
236 |
+
};
|
237 |
+
|
238 |
+
// Descargar código
|
239 |
+
const downloadCode = () => {
|
240 |
+
const element = document.createElement('a');
|
241 |
+
const file = new Blob([output || input], { type: 'text/plain' });
|
242 |
+
element.href = URL.createObjectURL(file);
|
243 |
+
element.download = `codigo${languages[selectedLanguage]?.ext || '.txt'}`;
|
244 |
+
document.body.appendChild(element);
|
245 |
+
element.click();
|
246 |
+
document.body.removeChild(element);
|
247 |
+
};
|
248 |
+
|
249 |
+
// Cargar ejemplo
|
250 |
+
const loadExample = () => {
|
251 |
+
const example = languages[selectedLanguage]?.example || languages.python.example;
|
252 |
+
setInput(example);
|
253 |
+
};
|
254 |
+
|
255 |
+
// Limpiar todo
|
256 |
+
const clearAll = () => {
|
257 |
+
setInput('');
|
258 |
+
setOutput('');
|
259 |
+
setAnalysis(null);
|
260 |
+
setActiveTab('input');
|
261 |
+
};
|
262 |
+
|
263 |
+
// Renderizar preview del código (básico)
|
264 |
+
const renderPreview = () => {
|
265 |
+
const code = output || input;
|
266 |
+
|
267 |
+
if (selectedLanguage === 'html' && code.includes('<html')) {
|
268 |
+
return (
|
269 |
+
<div className="w-full h-96 border rounded-lg overflow-hidden">
|
270 |
+
<iframe
|
271 |
+
srcDoc={code}
|
272 |
+
className="w-full h-full"
|
273 |
+
title="HTML Preview"
|
274 |
+
sandbox="allow-scripts allow-same-origin"
|
275 |
+
/>
|
276 |
+
</div>
|
277 |
+
);
|
278 |
+
}
|
279 |
+
|
280 |
+
return (
|
281 |
+
<div className={`p-4 rounded-lg ${isDarkMode ? 'bg-gray-900' : 'bg-gray-100'}`}>
|
282 |
+
<p className={`text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'} mb-2`}>
|
283 |
+
Preview no disponible para {languages[selectedLanguage]?.name}
|
284 |
+
</p>
|
285 |
+
<pre className={`text-xs overflow-auto ${isDarkMode ? 'text-green-400' : 'text-gray-800'}`}>
|
286 |
+
{code}
|
287 |
+
</pre>
|
288 |
+
</div>
|
289 |
+
);
|
290 |
+
};
|
291 |
+
|
292 |
+
return (
|
293 |
+
<div className={`min-h-screen transition-all duration-300 ${isDarkMode ? 'bg-gray-900' : 'bg-gray-50'}`}>
|
294 |
+
{/* Header */}
|
295 |
+
<div className={`border-b ${isDarkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
296 |
+
<div className="max-w-7xl mx-auto px-4 py-4">
|
297 |
+
<div className="flex justify-between items-center">
|
298 |
+
<div className="flex items-center gap-3">
|
299 |
+
<div className="flex items-center justify-center w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 via-blue-500 to-teal-500">
|
300 |
+
<Wand2 className="w-7 h-7 text-white" />
|
301 |
+
</div>
|
302 |
+
<div>
|
303 |
+
<h1 className={`text-3xl font-bold bg-gradient-to-r from-purple-400 via-blue-500 to-teal-400 bg-clip-text text-transparent`}>
|
304 |
+
Asistente Universal de Código IA
|
305 |
+
</h1>
|
306 |
+
<p className={`text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`}>
|
307 |
+
Mejora, convierte y analiza cualquier código o texto automáticamente
|
308 |
+
</p>
|
309 |
+
</div>
|
310 |
+
</div>
|
311 |
+
|
312 |
+
<div className="flex items-center gap-2">
|
313 |
+
<button
|
314 |
+
onClick={() => setIsDarkMode(!isDarkMode)}
|
315 |
+
className={`p-2 rounded-lg transition-colors ${isDarkMode ? 'hover:bg-gray-700 text-yellow-400' : 'hover:bg-gray-100 text-gray-700'}`}
|
316 |
+
>
|
317 |
+
{isDarkMode ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
|
318 |
+
</button>
|
319 |
+
</div>
|
320 |
+
</div>
|
321 |
+
|
322 |
+
{/* Controls */}
|
323 |
+
<div className="mt-4 flex flex-wrap gap-4 items-center">
|
324 |
+
<div className="flex items-center gap-2">
|
325 |
+
<label className={`text-sm font-medium ${isDarkMode ? 'text-gray-300' : 'text-gray-700'}`}>
|
326 |
+
Modo:
|
327 |
+
</label>
|
328 |
+
<select
|
329 |
+
value={conversionMode}
|
330 |
+
onChange={(e) => setConversionMode(e.target.value)}
|
331 |
+
className={`px-3 py-1 rounded-md text-sm border ${
|
332 |
+
isDarkMode
|
333 |
+
? 'bg-gray-700 text-white border-gray-600'
|
334 |
+
: 'bg-white text-gray-900 border-gray-300'
|
335 |
+
}`}
|
336 |
+
>
|
337 |
+
<option value="improve">🔧 Mejorar código</option>
|
338 |
+
<option value="convert">🔄 Convertir lenguaje</option>
|
339 |
+
<option value="analyze">🔍 Analizar código</option>
|
340 |
+
</select>
|
341 |
+
</div>
|
342 |
+
|
343 |
+
<div className="flex items-center gap-2">
|
344 |
+
<label className={`text-sm font-medium ${isDarkMode ? 'text-gray-300' : 'text-gray-700'}`}>
|
345 |
+
Lenguaje destino:
|
346 |
+
</label>
|
347 |
+
<select
|
348 |
+
value={selectedLanguage}
|
349 |
+
onChange={(e) => setSelectedLanguage(e.target.value)}
|
350 |
+
className={`px-3 py-1 rounded-md text-sm border ${
|
351 |
+
isDarkMode
|
352 |
+
? 'bg-gray-700 text-white border-gray-600'
|
353 |
+
: 'bg-white text-gray-900 border-gray-300'
|
354 |
+
}`}
|
355 |
+
>
|
356 |
+
{Object.entries(languages).map(([key, lang]) => (
|
357 |
+
<option key={key} value={key}>
|
358 |
+
{lang.icon} {lang.name}
|
359 |
+
</option>
|
360 |
+
))}
|
361 |
+
</select>
|
362 |
+
</div>
|
363 |
+
|
364 |
+
<div className="flex gap-2">
|
365 |
+
<button
|
366 |
+
onClick={loadExample}
|
367 |
+
className={`flex items-center gap-2 px-3 py-1 text-sm rounded-md ${
|
368 |
+
isDarkMode ? 'bg-gray-700 hover:bg-gray-600 text-gray-300' : 'bg-gray-200 hover:bg-gray-300 text-gray-700'
|
369 |
+
}`}
|
370 |
+
>
|
371 |
+
<FileText className="w-4 h-4" />
|
372 |
+
Ejemplo
|
373 |
+
</button>
|
374 |
+
|
375 |
+
<input
|
376 |
+
ref={fileInputRef}
|
377 |
+
type="file"
|
378 |
+
onChange={handleFileUpload}
|
379 |
+
className="hidden"
|
380 |
+
accept=".py,.js,.java,.cpp,.c,.html,.css,.jsx,.tsx,.txt,.md"
|
381 |
+
/>
|
382 |
+
<button
|
383 |
+
onClick={() => fileInputRef.current?.click()}
|
384 |
+
className={`flex items-center gap-2 px-3 py-1 text-sm rounded-md ${
|
385 |
+
isDarkMode ? 'bg-gray-700 hover:bg-gray-600 text-gray-300' : 'bg-gray-200 hover:bg-gray-300 text-gray-700'
|
386 |
+
}`}
|
387 |
+
>
|
388 |
+
<Upload className="w-4 h-4" />
|
389 |
+
Archivo
|
390 |
+
</button>
|
391 |
+
|
392 |
+
<button
|
393 |
+
onClick={clearAll}
|
394 |
+
className={`flex items-center gap-2 px-3 py-1 text-sm rounded-md ${
|
395 |
+
isDarkMode ? 'bg-red-600 hover:bg-red-700 text-white' : 'bg-red-500 hover:bg-red-600 text-white'
|
396 |
+
}`}
|
397 |
+
>
|
398 |
+
<RefreshCw className="w-4 h-4" />
|
399 |
+
Limpiar
|
400 |
+
</button>
|
401 |
+
</div>
|
402 |
+
</div>
|
403 |
+
</div>
|
404 |
+
</div>
|
405 |
+
|
406 |
+
<div className="max-w-7xl mx-auto p-6">
|
407 |
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
|
408 |
+
{/* Panel de entrada */}
|
409 |
+
<div className={`rounded-xl shadow-lg ${isDarkMode ? 'bg-gray-800' : 'bg-white'}`}>
|
410 |
+
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
411 |
+
<div className="flex justify-between items-center">
|
412 |
+
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDarkMode ? 'text-white' : 'text-gray-900'}`}>
|
413 |
+
<Terminal className="w-5 h-5" />
|
414 |
+
Entrada de código / texto
|
415 |
+
</h2>
|
416 |
+
<div className="flex gap-1">
|
417 |
+
<button
|
418 |
+
onClick={() => copyToClipboard(input)}
|
419 |
+
className={`p-2 rounded-md ${isDarkMode ? 'hover:bg-gray-700 text-gray-400' : 'hover:bg-gray-100 text-gray-600'}`}
|
420 |
+
title="Copiar"
|
421 |
+
>
|
422 |
+
<Copy className="w-4 h-4" />
|
423 |
+
</button>
|
424 |
+
<button
|
425 |
+
onClick={() => setInput('')}
|
426 |
+
className={`p-2 rounded-md ${isDarkMode ? 'hover:bg-gray-700 text-gray-400' : 'hover:bg-gray-100 text-gray-600'}`}
|
427 |
+
title="Limpiar"
|
428 |
+
>
|
429 |
+
<RefreshCw className="w-4 h-4" />
|
430 |
+
</button>
|
431 |
+
</div>
|
432 |
+
</div>
|
433 |
+
</div>
|
434 |
+
|
435 |
+
<div className="p-4">
|
436 |
+
<textarea
|
437 |
+
value={input}
|
438 |
+
onChange={(e) => setInput(e.target.value)}
|
439 |
+
placeholder="Pega tu código aquí, escribe texto para convertir a código, o ingresa código para mejorar..."
|
440 |
+
className={`w-full h-96 p-4 rounded-lg border font-mono text-sm resize-none ${
|
441 |
+
isDarkMode
|
442 |
+
? 'bg-gray-900 border-gray-700 text-green-400 placeholder-gray-500'
|
443 |
+
: 'bg-gray-50 border-gray-300 text-gray-900 placeholder-gray-400'
|
444 |
+
} focus:ring-2 focus:ring-purple-500 focus:border-transparent`}
|
445 |
+
spellCheck={false}
|
446 |
+
/>
|
447 |
+
|
448 |
+
<div className="flex justify-between items-center mt-4">
|
449 |
+
<span className={`text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`}>
|
450 |
+
{input.length} caracteres | {input.split('\n').length} líneas
|
451 |
+
{input.trim() && (
|
452 |
+
<span className="ml-2 px-2 py-1 bg-purple-500 text-white text-xs rounded-full">
|
453 |
+
{detectLanguage(input)}
|
454 |
+
</span>
|
455 |
+
)}
|
456 |
+
</span>
|
457 |
+
|
458 |
+
<button
|
459 |
+
onClick={processCode}
|
460 |
+
disabled={isProcessing || !input.trim()}
|
461 |
+
className={`flex items-center gap-2 px-6 py-3 rounded-lg font-medium transition-all ${
|
462 |
+
isProcessing || !input.trim()
|
463 |
+
? 'bg-gray-500 text-gray-300 cursor-not-allowed'
|
464 |
+
: 'bg-gradient-to-r from-purple-500 via-blue-500 to-teal-500 text-white hover:from-purple-600 hover:via-blue-600 hover:to-teal-600 shadow-lg transform hover:scale-105'
|
465 |
+
}`}
|
466 |
+
>
|
467 |
+
{isProcessing ? (
|
468 |
+
<>
|
469 |
+
<Loader2 className="w-5 h-5 animate-spin" />
|
470 |
+
Procesando...
|
471 |
+
</>
|
472 |
+
) : (
|
473 |
+
<>
|
474 |
+
<Sparkles className="w-5 h-5" />
|
475 |
+
{conversionMode === 'improve' ? 'Mejorar código' :
|
476 |
+
conversionMode === 'convert' ? 'Convertir código' : 'Analizar código'}
|
477 |
+
</>
|
478 |
+
)}
|
479 |
+
</button>
|
480 |
+
</div>
|
481 |
+
</div>
|
482 |
+
</div>
|
483 |
+
|
484 |
+
{/* Panel de salida */}
|
485 |
+
<div className={`rounded-xl shadow-lg ${isDarkMode ? 'bg-gray-800' : 'bg-white'}`}>
|
486 |
+
{/* Tabs */}
|
487 |
+
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
488 |
+
<button
|
489 |
+
onClick={() => setActiveTab('output')}
|
490 |
+
className={`flex-1 px-4 py-3 text-sm font-medium ${
|
491 |
+
activeTab === 'output'
|
492 |
+
|