gemini / index.html
KEXEL's picture
1.1
079552f verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat com Gemini AI</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.chat-message.user {
background-color: #3b82f6;
color: white;
border-radius: 18px 18px 0 18px;
}
.chat-message.ai {
background-color: #f3f4f6;
color: #1f2937;
border-radius: 18px 18px 18px 0;
}
.typing-indicator span {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #9ca3af;
margin: 0 2px;
animation: bounce 1.5s infinite ease-in-out;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
#chat-container {
height: calc(100vh - 140px);
}
@media (max-width: 640px) {
#chat-container {
height: calc(100vh - 120px);
}
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="container mx-auto max-w-4xl px-4 py-6">
<!-- Cabeçalho -->
<header class="flex items-center justify-between mb-6">
<div class="flex items-center space-x-3">
<div
class="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<i class="fas fa-robot text-white"></i>
</div>
<h1 class="text-2xl font-bold text-gray-800">Chat com Gemini AI</h1>
</div>
<div class="relative">
<button id="api-key-btn"
class="px-4 py-2 bg-white border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition">
<i class="fas fa-key mr-2"></i>Chave API
</button>
<div id="api-key-modal"
class="hidden absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-xl z-10 p-4 border border-gray-200">
<h3 class="font-medium text-gray-900 mb-2">Insira sua Chave da API Gemini</h3>
<input type="password" id="api-key-input" placeholder="Sua chave API"
class="w-full px-3 py-2 border border-gray-300 rounded-md mb-3">
<div class="flex justify-end space-x-2">
<button id="cancel-api-key"
class="px-3 py-1 text-gray-600 hover:text-gray-800">Cancelar</button>
<button id="save-api-key"
class="px-3 py-1 bg-blue-600 text-white rounded-md hover:bg-blue-700">Salvar</button>
</div>
</div>
</div>
</header>
<!-- Container do Chat -->
<div id="chat-container" class="bg-white rounded-xl shadow-md overflow-hidden flex flex-col">
<!-- Mensagens -->
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4">
<div class="chat-message ai max-w-[80%] p-4">
<div class="font-medium flex items-center mb-1">
<div
class="w-6 h-6 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center mr-2">
<i class="fas fa-robot text-white text-xs"></i>
</div>
<span>Gemini AI</span>
</div>
<p>Olá! Eu sou o Gemini, seu assistente de IA. Como posso te ajudar hoje?</p>
</div>
</div>
<!-- Área de Entrada -->
<div class="border-t border-gray-200 p-4 bg-gray-50">
<form id="message-form" class="flex space-x-2">
<input type="text" id="message-input" placeholder="Digite sua mensagem..."
class="flex-1 px-4 py-3 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
autocomplete="off">
<button type="submit"
class="w-12 h-12 rounded-full bg-blue-600 text-white flex items-center justify-center hover:bg-blue-700 transition">
<i class="fas fa-paper-plane"></i>
</button>
</form>
<div class="text-xs text-gray-500 mt-2 px-2">
O Gemini pode exibir informações imprecisas, inclusive sobre pessoas, então verifique suas
respostas.
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Elementos DOM
const chatMessages = document.getElementById('chat-messages');
const messageForm = document.getElementById('message-form');
const messageInput = document.getElementById('message-input');
const apiKeyBtn = document.getElementById('api-key-btn');
const apiKeyModal = document.getElementById('api-key-modal');
const apiKeyInput = document.getElementById('api-key-input');
const saveApiKeyBtn = document.getElementById('save-api-key');
const cancelApiKeyBtn = document.getElementById('cancel-api-key');
// Estado
let apiKey = localStorage.getItem('gemini-api-key') || '';
let isWaitingForResponse = false;
// Inicialização
if (apiKey) {
apiKeyInput.value = apiKey;
}
// Event Listeners
apiKeyBtn.addEventListener('click', toggleApiKeyModal);
saveApiKeyBtn.addEventListener('click', saveApiKey);
cancelApiKeyBtn.addEventListener('click', toggleApiKeyModal);
messageForm.addEventListener('submit', handleSubmit);
// Clicar fora do modal para fechar
document.addEventListener('click', function (e) {
if (!apiKeyModal.contains(e.target) && e.target !== apiKeyBtn) {
apiKeyModal.classList.add('hidden');
}
});
// Funções
function toggleApiKeyModal() {
apiKeyModal.classList.toggle('hidden');
}
function saveApiKey() {
apiKey = apiKeyInput.value.trim();
localStorage.setItem('gemini-api-key', apiKey);
toggleApiKeyModal();
addSystemMessage(apiKey ? 'Chave API salva com sucesso!' : 'Chave API removida.');
}
function addSystemMessage(text) {
const messageDiv = document.createElement('div');
messageDiv.className = 'text-center text-sm text-gray-500 my-2';
messageDiv.textContent = text;
chatMessages.appendChild(messageDiv);
scrollToBottom();
}
async function handleSubmit(e) {
e.preventDefault();
const message = messageInput.value.trim();
if (!message) return;
if (!apiKey) {
addSystemMessage('Por favor, configure sua chave da API Gemini primeiro.');
toggleApiKeyModal();
return;
}
if (isWaitingForResponse) {
addSystemMessage('Por favor, aguarde a resposta atual ser concluída.');
return;
}
// Adiciona mensagem do usuário
addMessage(message, 'user');
messageInput.value = '';
// Mostra indicador de digitação
showTypingIndicator();
isWaitingForResponse = true;
try {
const response = await fetchGeminiResponse(message);
removeTypingIndicator();
addMessage(response, 'ai');
} catch (error) {
removeTypingIndicator();
addSystemMessage(`Erro: ${error.message}`);
} finally {
isWaitingForResponse = false;
}
}
function addMessage(text, sender) {
const messageDiv = document.createElement('div');
messageDiv.className = `chat-message ${sender} max-w-[80%] p-4`;
const senderName = sender === 'user' ? 'Você' : 'Gemini AI';
const iconClass = sender === 'user' ? 'fas fa-user' : 'fas fa-robot';
const iconBg = sender === 'user'
? 'bg-gradient-to-r from-blue-400 to-blue-600'
: 'bg-gradient-to-r from-purple-500 to-pink-500';
messageDiv.innerHTML = `
<div class="font-medium flex items-center mb-1">
<div class="w-6 h-6 rounded-full ${iconBg} flex items-center justify-center mr-2">
<i class="${iconClass} text-white text-xs"></i>
</div>
<span>${senderName}</span>
</div>
<p>${text}</p>
`;
chatMessages.appendChild(messageDiv);
scrollToBottom();
}
function showTypingIndicator() {
const typingDiv = document.createElement('div');
typingDiv.id = 'typing-indicator';
typingDiv.className = 'chat-message ai max-w-[80%] p-4';
typingDiv.innerHTML = `
<div class="font-medium flex items-center mb-1">
<div class="w-6 h-6 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center mr-2">
<i class="fas fa-robot text-white text-xs"></i>
</div>
<span>Gemini AI</span>
</div>
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
`;
chatMessages.appendChild(typingDiv);
scrollToBottom();
}
function removeTypingIndicator() {
const typingIndicator = document.getElementById('typing-indicator');
if (typingIndicator) {
typingIndicator.remove();
}
}
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
async function fetchGeminiResponse(prompt) {
if (!apiKey) {
throw new Error('Chave API não configurada');
}
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`;
const requestBody = {
contents: [{
parts: [{
text: prompt
}]
}]
};
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'Falha ao obter resposta do Gemini');
}
const data = await response.json();
return data.candidates?.[0]?.content?.parts?.[0]?.text || "Desculpe, não consegui gerar uma resposta.";
}
});
</script>
</body>
</html>