|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Mr. Mxyzptlk - Converse com um Modelo | Fourlabs</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"> |
|
|
<link rel="icon" type="image/x-icon" href="/static/imgs/icon.svg"> |
|
|
<link rel="stylesheet" href="/static/style/dashboard.css"> |
|
|
<link rel="stylesheet" type="text/css" href="/static/style/alert.css"> |
|
|
<script src="https://unpkg.com/feather-icons"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script> |
|
|
<script> |
|
|
tailwind.config = { |
|
|
theme: { |
|
|
extend: { |
|
|
colors: { |
|
|
primary: '#21223a', |
|
|
secondary: '#ff580f', |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</head> |
|
|
<body class="bg-gray-100"> |
|
|
<div id="alert-container" style="position: fixed; top: 80px; right: 20px; z-index: 1000; width: 100%; max-width: 500px;"></div> |
|
|
<div class="flex h-screen overflow-hidden"> |
|
|
|
|
|
<div class="sidebar bg-primary text-white w-64 md:w-20 lg:w-64 fixed h-full overflow-y-auto"> |
|
|
<div class="p-4 border-b border-gray-700"> |
|
|
<div class="w-full flex flex-col items-center justify-center"> |
|
|
<img |
|
|
alt="logo" |
|
|
loading="lazy" |
|
|
width="100" |
|
|
height="100" |
|
|
decoding="async" |
|
|
data-nimg="1" |
|
|
class="w-36" |
|
|
src="/static/imgs/logo-branco-labs.svg" |
|
|
style="color: transparent;margin-top: 8px;" |
|
|
> |
|
|
<p |
|
|
class="text-[10px] text-white mt-2 text-center" |
|
|
style="margin-top: -1px; margin-left: 12px;font-size: 9px;"> |
|
|
Inovação e experimentação |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="p-4"> |
|
|
<div class="flex items-center space-x-3 mb-6"> |
|
|
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center"> |
|
|
<span class="font-bold">MS</span> |
|
|
</div> |
|
|
<div class="lg:block hidden"> |
|
|
<h3 class="font-semibold">Marlon Sousa</h3> |
|
|
<p class="text-xs text-gray-400">Admin</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<nav> |
|
|
<ul class="space-y-2"> |
|
|
<li> |
|
|
<a href="/" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
active"> |
|
|
<i class="fa-solid fa-server text-secondary w-6 text-center"></i> |
|
|
<span class="ml-3 lg:block hidden">Instances</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
"> |
|
|
<i class="fa-solid fa-cubes text-secondary w-6 text-center"></i> |
|
|
<span class="ml-3 lg:block hidden">Moxe</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="/" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
"> |
|
|
<i class="fa-solid fa-robot text-secondary w-5 h-5 mr-3"></i> |
|
|
<span class="ml-3 lg:block hidden">Agentes</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="/" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
"> |
|
|
<i class="fa-solid fa-diagram-project text-secondary w-5 h-5 mr-3"></i> |
|
|
<span class="ml-3 lg:block hidden">Projetos</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="/" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
"> |
|
|
<i data-feather="activity" class="text-secondary w-5 h-5 mr-3"></i> |
|
|
<span class="ml-3 lg:block hidden">Atividade</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="/settings/" |
|
|
class="sidebar-item flex items-center p-3 rounded-lg |
|
|
"> |
|
|
<i class="fa-solid fa-screwdriver-wrench text-secondary w-6 text-center"></i> |
|
|
<span class="ml-3 lg:block hidden">Configurações</span> |
|
|
</a> |
|
|
</li> |
|
|
</ul> |
|
|
</nav> |
|
|
</div> |
|
|
|
|
|
<div class="absolute bottom-0 w-full p-4 border-t border-gray-700"> |
|
|
<form id="logout-form" method="POST" action="/account/logout/" class="hidden"> |
|
|
<input type="hidden" name="csrfmiddlewaretoken" value="MnYe4YaLuTb5AZgEnVFzWrZPFMBdPA80wkFGxjael4hIsejNxadyoK7h2ziCbTe5"> |
|
|
</form> |
|
|
|
|
|
<a href="#" onclick="document.getElementById('logout-form').submit(); return false;" class="sidebar-item flex items-center p-3 rounded-lg"> |
|
|
<i class="fas fa-sign-out-alt text-secondary w-6 text-center"></i> |
|
|
<span class="ml-3 lg:block hidden">Logout</span> |
|
|
</a> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex-1 ml-0 md:ml-20 lg:ml-64 overflow-y-auto"> |
|
|
|
|
|
<header class="bg-white shadow-sm"> |
|
|
<div class="flex items-center justify-between p-4"> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<button id="mobileToggleSidebar" class="text-gray-600 lg:hidden block"> |
|
|
<i class="fas fa-bars text-xl"></i> |
|
|
</button> |
|
|
<h1 class="text-xl font-bold text-primary">Converse com um Modelo</h1> |
|
|
</div> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<a href="/launch/" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"> |
|
|
<i data-feather="plus" class="mr-2 w-4 h-4"></i> |
|
|
Subir Instância |
|
|
</a> |
|
|
<div class="relative"> |
|
|
<button id="notificationsBtn" class="relative"> |
|
|
<i class="fas fa-bell text-xl text-gray-600"></i> |
|
|
<span class="absolute -top-1 -right-1 bg-secondary text-white text-xs rounded-full h-4 w-4 flex items-center justify-center">0</span> |
|
|
</button> |
|
|
<div id="notificationsDropdown" class="hidden absolute right-0 mt-2 w-80 bg-white rounded-md shadow-lg overflow-hidden z-50"> |
|
|
<div class="py-1"> |
|
|
<div class="px-4 py-2 border-b border-gray-200"> |
|
|
<h3 class="text-sm font-medium text-gray-700">Notifications</h3> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
<div class="px-4 py-2 bg-gray-50 text-center"> |
|
|
<a href="#" class="text-sm font-medium text-secondary hover:text-primary">View all notifications</a> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style> |
|
|
:root { |
|
|
--primary: #21223a; |
|
|
--secondary: #ff580f; |
|
|
} |
|
|
|
|
|
.chat-container { |
|
|
scroll-behavior: smooth; |
|
|
height: calc(100% - 28%); |
|
|
overflow-y: auto; |
|
|
} |
|
|
|
|
|
.message-bubble { |
|
|
max-width: 80%; |
|
|
animation: fadeIn 0.3s ease-in; |
|
|
} |
|
|
|
|
|
.typing-indicator { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
gap: 4px; |
|
|
} |
|
|
|
|
|
.typing-dot { |
|
|
width: 6px; |
|
|
height: 6px; |
|
|
background-color: var(--secondary); |
|
|
border-radius: 50%; |
|
|
animation: typing 1.4s infinite ease-in-out; |
|
|
} |
|
|
|
|
|
.typing-dot:nth-child(2) { animation-delay: 0.16s; } |
|
|
.typing-dot:nth-child(3) { animation-delay: 0.32s; } |
|
|
|
|
|
@keyframes typing { |
|
|
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } |
|
|
30% { transform: translateY(-10px); opacity: 1; } |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
.llm-option:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 8px 25px rgba(33, 34, 58, 0.15); |
|
|
} |
|
|
</style> |
|
|
|
|
|
<div class="bg-gray-50 border-b border-gray-200 p-3"> |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center space-x-3"> |
|
|
<span class="text-sm text-gray-600">Modelo:</span> |
|
|
<select class="w-full px-4 py-3 border border-gray-200 rounded-xl" name="model" id="modelSelect"> |
|
|
|
|
|
|
|
|
|
|
|
<option value="2c387c0e-838d-4c68-8257-296cf0b68469" selected> |
|
|
granite-code:8b |
|
|
</option> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<option value="5fef64b9-ac1d-4461-a9e9-148582e780f0" > |
|
|
deepseek-r1:7b |
|
|
</option> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<option value="95d05e19-6063-4fa2-804a-c551e37358b5" > |
|
|
deepseek-r1:1.5b |
|
|
</option> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<option value="0b685e78-a520-4d3a-8adc-f3f3305d4162" > |
|
|
llama3.2:3b |
|
|
</option> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</select> |
|
|
</div> |
|
|
|
|
|
<div class="flex items-center space-x-2"> |
|
|
<button |
|
|
id="uploadPdfBtn" |
|
|
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#ff580f]" |
|
|
> |
|
|
<i data-feather="upload" class="mr-2 w-4 h-4"></i> |
|
|
Enviar PDF |
|
|
</button> |
|
|
<button |
|
|
id="generateCodeBtn" |
|
|
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium |
|
|
rounded-md text-white bg-secondary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#ff580f]" |
|
|
> |
|
|
<i data-feather="code" class="mr-2 w-4 h-4"></i> |
|
|
Gerar Código |
|
|
</button> |
|
|
|
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="flex-1 p-6 space-y-6 chat-container overflow-y-auto" id="chatMessages"> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-white border-t border-gray-200 p-6"> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<div class="flex-1 relative"> |
|
|
<input |
|
|
type="text" |
|
|
placeholder="Digite sua mensagem..." |
|
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#ff580f] focus:border-transparent" |
|
|
id="messageInput" |
|
|
> |
|
|
<div class="absolute right-3 top-3 flex items-center space-x-2"> |
|
|
<button class="text-gray-400 hover:text-[#ff580f] transition-colors"> |
|
|
<i data-feather="paperclip"></i> |
|
|
</button> |
|
|
<button class="text-gray-400 hover:text-[#ff580f] transition-colors"> |
|
|
<i data-feather="mic"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<button |
|
|
class="bg-[#ff580f] hover:bg-[#e64a0c] text-white p-3 rounded-lg transition-colors duration-200" |
|
|
onclick="sendMessage()" |
|
|
> |
|
|
<i data-feather="send"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="mt-3 flex items-center justify-end"> |
|
|
<span class="text-sm text-gray-500">⌘K para atalhos</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="pdfUploadModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"> |
|
|
<div class="relative top-20 mx-auto p-5 border max-w-3xl shadow-lg rounded-md bg-white"> |
|
|
<div class="mt-3 text-center"> |
|
|
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-[#ff580f]"> |
|
|
<i data-feather="file-text" class="text-white"></i> |
|
|
</div> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mt-2"> |
|
|
Enviar PDF |
|
|
</h3> |
|
|
<div class="mt-2 px-7 py-3"> |
|
|
<p class="text-sm text-gray-500"> |
|
|
Selecione um arquivo PDF para enviar e processar. |
|
|
</p> |
|
|
<form class="mt-4"> |
|
|
<div class="flex items-center justify-center w-full"> |
|
|
<label for="pdfFile" class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100"> |
|
|
<div class="flex flex-col items-center justify-center pt-5 pb-6"> |
|
|
<i data-feather="upload-cloud" class="w-8 h-8 text-gray-400"></i> |
|
|
<p class="mb-2 text-sm text-gray-500"><span class="font-semibold">Clique para enviar</span> ou arraste e solte</p> |
|
|
<p class="text-xs text-gray-500">PDF (MAX. 10MB)</p> |
|
|
</div> |
|
|
<input id="pdfFile" name="pdfFile" type="file" class="hidden" accept=".pdf"> |
|
|
</label> |
|
|
</div> |
|
|
</form> |
|
|
<div class="items-center px-4 py-3 mt-4"> |
|
|
<button |
|
|
id="uploadSubmitBtn" |
|
|
class="px-4 py-2 bg-[#ff580f] text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-[#e64a0c] focus:outline-none focus:ring-2 focus:ring-[#ff580f]"> |
|
|
Enviar PDF |
|
|
</button> |
|
|
<button |
|
|
id="uploadCancelBtn" |
|
|
class="mt-3 px-4 py-2 bg-gray-300 text-gray-700 text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500"> |
|
|
Cancelar |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div id="codeModal" class="fixed inset-0 z-50 hidden flex items-center justify-center"> |
|
|
<div class="bg-white rounded-lg shadow-lg w-full max-w-5xl h-[75vh] flex flex-col p-6"> |
|
|
|
|
|
<div class="flex justify-between items-center mb-4 border-b pb-2"> |
|
|
<h2 class="text-xl font-semibold text-gray-800">Código para Ollama</h2> |
|
|
<button id="closeModal" class="text-gray-500 hover:text-gray-700 text-xl leading-none">✕</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex items-end space-x-3 mb-4"> |
|
|
<div class="flex-1"> |
|
|
<label for="languageSelect" class="block text-sm font-medium text-gray-700 mb-1">Linguagem:</label> |
|
|
<select |
|
|
id="languageSelect" |
|
|
class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-[#ff580f]" |
|
|
> |
|
|
<option value="python">Python</option> |
|
|
<option value="javascript">JavaScript</option> |
|
|
<option value="java">Java</option> |
|
|
<option value="csharp">C#</option> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
<button |
|
|
id="copyCodeBtn" |
|
|
class="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-700 bg-white hover:bg-gray-50 focus:outline-none" |
|
|
> |
|
|
<i data-feather="copy" class="mr-2 w-4 h-4"></i> |
|
|
Copiar Código |
|
|
</button> |
|
|
|
|
|
<button |
|
|
id="viewDocsBtn" |
|
|
class="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-700 bg-white hover:bg-gray-50 focus:outline-none" |
|
|
> |
|
|
<i data-feather="book-open" class="mr-2 w-4 h-4"></i> |
|
|
Ver Documentação |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="flex-1 rounded-md p-4 overflow-auto border border-gray-700" style="background-color: #21223a;"> |
|
|
<pre class="h-full"><code id="exampleCode" class="language-python"></code></pre> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<script> |
|
|
feather.replace(); |
|
|
|
|
|
|
|
|
const uploadPdfBtn = document.getElementById('uploadPdfBtn'); |
|
|
const pdfUploadModal = document.getElementById('pdfUploadModal'); |
|
|
const uploadCancelBtn = document.getElementById('uploadCancelBtn'); |
|
|
const uploadSubmitBtn = document.getElementById('uploadSubmitBtn'); |
|
|
const pdfFileInput = document.getElementById('pdfFile'); |
|
|
|
|
|
uploadPdfBtn.addEventListener('click', function() { |
|
|
pdfUploadModal.classList.remove('hidden'); |
|
|
}); |
|
|
|
|
|
uploadCancelBtn.addEventListener('click', function() { |
|
|
pdfUploadModal.classList.add('hidden'); |
|
|
pdfFileInput.value = ''; |
|
|
}); |
|
|
|
|
|
uploadSubmitBtn.addEventListener('click', function() { |
|
|
const file = pdfFileInput.files[0]; |
|
|
if (!file) { |
|
|
alert('Por favor, selecione um arquivo PDF.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (file.type !== 'application/pdf') { |
|
|
alert('Por favor, selecione apenas arquivos PDF.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (file.size > 10 * 1024 * 1024) { |
|
|
alert('O arquivo é muito grande. Tamanho máximo: 10MB.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
addMessage(`PDF enviado: ${file.name}`, 'user'); |
|
|
addMessage(`Estou processando o arquivo "${file.name}". Em breve poderei responder perguntas sobre seu conteúdo.`, 'ai'); |
|
|
|
|
|
pdfUploadModal.classList.add('hidden'); |
|
|
pdfFileInput.value = ''; |
|
|
}); |
|
|
|
|
|
|
|
|
pdfUploadModal.addEventListener('click', function(e) { |
|
|
if (e.target === pdfUploadModal) { |
|
|
pdfUploadModal.classList.add('hidden'); |
|
|
pdfFileInput.value = ''; |
|
|
} |
|
|
}); |
|
|
|
|
|
const chatMessages = document.getElementById('chatMessages'); |
|
|
const messageInput = document.getElementById('messageInput'); |
|
|
|
|
|
function sendMessage() { |
|
|
const message = messageInput.value.trim(); |
|
|
if (!message) return; |
|
|
|
|
|
const modelSelect = document.querySelector("select"); |
|
|
const selectedModelId = modelSelect.value; |
|
|
|
|
|
addMessage(message, 'user'); |
|
|
messageInput.value = ''; |
|
|
addTypingIndicator(); |
|
|
|
|
|
fetch("/chatbot/", { |
|
|
method: "POST", |
|
|
headers: { |
|
|
"Content-Type": "application/json", |
|
|
"X-CSRFToken": getCookie("csrftoken") |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
message: message, |
|
|
model_id: selectedModelId |
|
|
}) |
|
|
}) |
|
|
.then(response => { |
|
|
const reader = response.body.getReader(); |
|
|
const decoder = new TextDecoder(); |
|
|
let accumulatedText = ""; |
|
|
|
|
|
function readChunk() { |
|
|
reader.read().then(({ done, value }) => { |
|
|
if (done) { |
|
|
removeTypingIndicator(); |
|
|
addMessage(accumulatedText.trim(), 'ai'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const chunk = decoder.decode(value, { stream: true }); |
|
|
const lines = chunk.split("\n\n"); |
|
|
for (const line of lines) { |
|
|
if (line.startsWith("data: ")) { |
|
|
const data = line.replace("data: ", ""); |
|
|
if (data === "[FIM]") { |
|
|
removeTypingIndicator(); |
|
|
addMessage(accumulatedText.trim(), 'ai'); |
|
|
return; |
|
|
} else { |
|
|
console.log(data) |
|
|
accumulatedText += data; |
|
|
updateTypingIndicatorText(accumulatedText); |
|
|
} |
|
|
} |
|
|
} |
|
|
readChunk(); |
|
|
}); |
|
|
} |
|
|
|
|
|
readChunk(); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error(error); |
|
|
removeTypingIndicator(); |
|
|
addMessage("Erro ao conectar com o servidor.", 'ai'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function getCookie(name) { |
|
|
let cookieValue = null; |
|
|
if (document.cookie && document.cookie !== '') { |
|
|
const cookies = document.cookie.split(';'); |
|
|
for (let i = 0; i < cookies.length; i++) { |
|
|
const cookie = cookies[i].trim(); |
|
|
if (cookie.substring(0, name.length + 1) === (name + '=')) { |
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
return cookieValue; |
|
|
} |
|
|
|
|
|
function updateTypingIndicatorText(text) { |
|
|
const typingIndicator = document.getElementById('typingIndicator'); |
|
|
if (typingIndicator) { |
|
|
typingIndicator.innerHTML = `<p>${text}</p>`; |
|
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
} |
|
|
} |
|
|
|
|
|
function addMessage(text, sender) { |
|
|
const messageDiv = document.createElement('div'); |
|
|
messageDiv.className = `message-bubble ${sender === 'user' ? 'bg-white rounded-2xl p-4 shadow-md ml-auto' : 'bg-[#21223a] text-white rounded-2xl p-4 shadow-md'}`; |
|
|
|
|
|
const messageContent = document.createElement('p'); |
|
|
messageContent.className = sender === 'user' ? 'text-gray-800' : 'text-white'; |
|
|
messageContent.textContent = text; |
|
|
|
|
|
const timestamp = document.createElement('span'); |
|
|
timestamp.className = `text-xs block mt-2 ${sender === 'user' ? 'text-gray-500' : 'text-gray-300'}`; |
|
|
timestamp.textContent = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); |
|
|
|
|
|
messageDiv.appendChild(messageContent); |
|
|
messageDiv.appendChild(timestamp); |
|
|
chatMessages.appendChild(messageDiv); |
|
|
|
|
|
|
|
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
} |
|
|
|
|
|
function addTypingIndicator() { |
|
|
const typingDiv = document.createElement('div'); |
|
|
typingDiv.className = 'message-bubble bg-[#21223a] text-white rounded-2xl p-4 shadow-md'; |
|
|
typingDiv.id = 'typingIndicator'; |
|
|
|
|
|
const typingContent = document.createElement('div'); |
|
|
typingContent.className = 'typing-indicator'; |
|
|
|
|
|
for (let i = 0; i < 3; i++) { |
|
|
const dot = document.createElement('div'); |
|
|
dot.className = 'typing-dot'; |
|
|
typingContent.appendChild(dot); |
|
|
} |
|
|
|
|
|
typingDiv.appendChild(typingContent); |
|
|
chatMessages.appendChild(typingDiv); |
|
|
|
|
|
|
|
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
} |
|
|
|
|
|
function removeTypingIndicator() { |
|
|
const typingIndicator = document.getElementById('typingIndicator'); |
|
|
if (typingIndicator) { |
|
|
typingIndicator.remove(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
messageInput.addEventListener('keypress', function(e) { |
|
|
if (e.key === 'Enter') { |
|
|
sendMessage(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.llm-option').forEach(option => { |
|
|
option.addEventListener('click', function() { |
|
|
document.querySelectorAll('.llm-option').forEach(opt => { |
|
|
opt.classList.remove('border-[#ff580f]'); |
|
|
}); |
|
|
this.classList.add('border-[#ff580f]'); |
|
|
|
|
|
const modelName = this.querySelector('span').textContent; |
|
|
document.querySelector('.mt-3 span:first-child').textContent = `Modelo selecionado: ${modelName}`; |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism-tomorrow.css" rel="stylesheet" /> |
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs/prism.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-python.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-javascript.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-java.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-csharp.min.js"></script> |
|
|
|
|
|
<script> |
|
|
document.addEventListener("DOMContentLoaded", () => { |
|
|
feather.replace(); |
|
|
|
|
|
|
|
|
const openModal = document.getElementById("generateCodeBtn"); |
|
|
const codeModal = document.getElementById("codeModal"); |
|
|
const closeModal = document.getElementById("closeModal"); |
|
|
|
|
|
openModal.addEventListener("click", () => codeModal.classList.remove("hidden")); |
|
|
closeModal.addEventListener("click", () => codeModal.classList.add("hidden")); |
|
|
|
|
|
|
|
|
const languageSelect = document.getElementById("languageSelect"); |
|
|
const codeEl = document.getElementById("exampleCode"); |
|
|
const copyBtn = document.getElementById("copyCodeBtn"); |
|
|
const docsBtn = document.getElementById("viewDocsBtn"); |
|
|
|
|
|
|
|
|
const codeExamples = { |
|
|
python: `import ollama |
|
|
|
|
|
response = ollama.chat(model="llama3", messages=[ |
|
|
{"role": "user", "content": "Olá, mundo!"} |
|
|
]) |
|
|
|
|
|
print(response['message']['content'])`, |
|
|
javascript: `import ollama from "ollama"; |
|
|
|
|
|
const response = await ollama.chat({ |
|
|
model: "llama3", |
|
|
messages: [{ role: "user", content: "Olá, mundo!" }], |
|
|
}); |
|
|
|
|
|
console.log(response.message.content);`, |
|
|
java: `import java.util.*; |
|
|
|
|
|
public class Main { |
|
|
public static void main(String[] args) { |
|
|
System.out.println("Olá, mundo!"); |
|
|
} |
|
|
}`, |
|
|
csharp: `using System; |
|
|
|
|
|
class Program { |
|
|
static void Main() { |
|
|
Console.WriteLine("Olá, mundo!"); |
|
|
} |
|
|
}` |
|
|
}; |
|
|
|
|
|
|
|
|
languageSelect.addEventListener("change", (e) => { |
|
|
const lang = e.target.value; |
|
|
codeEl.className = "language-" + lang; |
|
|
codeEl.textContent = codeExamples[lang]; |
|
|
Prism.highlightElement(codeEl); |
|
|
}); |
|
|
|
|
|
|
|
|
copyBtn.addEventListener("click", () => { |
|
|
navigator.clipboard.writeText(codeEl.textContent); |
|
|
copyBtn.innerHTML = '<i data-feather="check" class="mr-2 w-4 h-4"></i>Copiado!'; |
|
|
feather.replace(); |
|
|
setTimeout(() => { |
|
|
copyBtn.innerHTML = '<i data-feather="copy" class="mr-2 w-4 h-4"></i>Copiar Código'; |
|
|
feather.replace(); |
|
|
}, 1500); |
|
|
}); |
|
|
|
|
|
|
|
|
const docsUrls = { |
|
|
python: "https://docs.python.org/3/", |
|
|
javascript: "https://developer.mozilla.org/pt-BR/docs/Web/JavaScript", |
|
|
java: "https://docs.oracle.com/en/java/", |
|
|
csharp: "https://learn.microsoft.com/dotnet/csharp/" |
|
|
}; |
|
|
docsBtn.addEventListener("click", () => { |
|
|
const lang = languageSelect.value; |
|
|
window.open(docsUrls[lang], "_blank"); |
|
|
}); |
|
|
|
|
|
|
|
|
Prism.highlightElement(codeEl); |
|
|
}); |
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
</div> |
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
|
|
<script> |
|
|
|
|
|
VANTA.NET({ |
|
|
el: "#vanta-bg", |
|
|
mouseControls: true, |
|
|
touchControls: true, |
|
|
gyroControls: false, |
|
|
minHeight: 200.00, |
|
|
minWidth: 200.00, |
|
|
scale: 1.00, |
|
|
scaleMobile: 1.00, |
|
|
color: 0x3b82f6, |
|
|
backgroundColor: 0xf8fafc, |
|
|
points: 12.00, |
|
|
maxDistance: 22.00, |
|
|
spacing: 18.00 |
|
|
}); |
|
|
|
|
|
feather.replace(); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.pagination a').forEach(link => { |
|
|
link.addEventListener('click', function(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
|
if(this.textContent.trim() === '2' || this.textContent.trim() === '3') { |
|
|
alert('Loading page ' + this.textContent.trim()); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
<script src="/static/script/alert.js"></script> |
|
|
|
|
|
|
|
|
</body> |
|
|
</html> |
|
|
|