-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Description
import React, { useState } from 'react'; // Fixed syntax error here
const App = () => {
const [image, setImage] = useState(null); // State to store the uploaded image file
const [imageUrl, setImageUrl] = useState(null); // State to store the URL for image preview
const [analysisResult, setAnalysisResult] = useState(''); // State to store the analysis result from the AI
const [isLoading, setIsLoading] = useState(false); // State to manage loading indicator
const [error, setError] = useState(null); // State to store any errors
// Function to handle image file selection
const handleImageChange = (event) => {
const file = event.target.files[0];
if (file) {
setImage(file);
setImageUrl(URL.createObjectURL(file)); // Create a URL for image preview
setAnalysisResult(''); // Clear previous analysis
setError(null); // Clear previous errors
}
};
// Function to convert image file to Base64 string
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result.split(',')[1]); // Get only the base64 part
reader.onerror = error => reject(error);
});
};
// Function to send the image to the Gemini API for analysis
const analyzeScreenshot = async () => {
if (!image) {
setError("Por favor, selecione uma imagem primeiro.");
return;
}
setIsLoading(true);
setAnalysisResult('');
setError(null);
try {
const base64ImageData = await getBase64(image);
// Updated prompt to predict the next candle color
const prompt = "Analise esta imagem de um gráfico de velas. Com base nos padrões visíveis e na tendência recente, qual é a sua previsão para a cor da próxima vela? Responda apenas 'VERMELHA', 'VERDE' ou 'INDEFINIDO'.";
let chatHistory = [];
chatHistory.push({ role: "user", parts: [{ text: prompt }] });
const payload = {
contents: [
{
role: "user",
parts: [
{ text: prompt },
{
inlineData: {
mimeType: image.type, // Use the actual mime type of the uploaded image
data: base64ImageData
}
}
]
}
],
};
// API key is handled by the environment, leave it empty
const apiKey = "";
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Erro da API: ${errorData.error.message || response.statusText}`);
}
const result = await response.json();
if (result.candidates && result.candidates.length > 0 &&
result.candidates[0].content && result.candidates[0].content.parts &&
result.candidates[0].content.parts.length > 0) {
const text = result.candidates[0].content.parts[0].text;
setAnalysisResult(text);
} else {
setError("Não foi possível obter uma resposta da análise. Estrutura de resposta inesperada.");
}
} catch (err) {
console.error("Erro ao analisar a imagem:", err);
setError(`Erro ao analisar a imagem: ${err.message}`);
} finally {
setIsLoading(false);
}
};
return (
Analisador de Prints de Tela
<div className="mb-6">
<label htmlFor="imageUpload" className="block text-gray-700 text-sm font-bold mb-2">
Selecione um Print de Tela:
</label>
<input
type="file"
id="imageUpload"
accept="image/*"
onChange={handleImageChange}
className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 focus:outline-none file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
/>
</div>
{imageUrl && (
<div className="mb-6 text-center">
<h3 className="text-lg font-semibold text-gray-700 mb-2">Pré-visualização da Imagem:</h3>
<img src={imageUrl} alt="Pré-visualização" className="max-w-full h-auto rounded-lg border border-gray-300 mx-auto" />
</div>
)}
<button
onClick={analyzeScreenshot}
disabled={!image || isLoading}
className={`w-full py-3 px-6 rounded-lg text-white font-semibold transition duration-300 ease-in-out
${!image || isLoading ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700 shadow-lg'}
focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-75
`}
>
{isLoading ? (
<div className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Analisando...
</div>
) : (
'Analisar Print'
)}
</button>
{error && (
<div className="mt-6 p-4 bg-red-100 text-red-800 rounded-lg border border-red-200">
<p className="font-semibold">Erro:</p>
<p>{error}</p>
</div>
)}
{analysisResult && (
<div className="mt-6 p-4 bg-green-50 text-green-800 rounded-lg border border-green-200">
<h3 className="text-lg font-semibold text-gray-700 mb-2">Resultado da Análise:</h3>
<p className="whitespace-pre-wrap">{analysisResult}</p>
</div>
)}
</div>
</div>
);
};
export default App;