🔧 feat: Sistema de identidade dinâmica do agente - modal de configurações, avatares customizáveis, nome dinâmico sincronizado com Hermes/Telegram
This commit is contained in:
+184
-3
@@ -94,6 +94,187 @@ const initApp = () => {
|
||||
const searchClearAll = document.getElementById('searchClearAll');
|
||||
const sidebarTags = document.getElementById('sidebarTags');
|
||||
|
||||
// Configuração de Identidade do Agente (Kemily/Camila)
|
||||
const btnSettings = document.getElementById('btnSettings');
|
||||
const settingsModal = document.getElementById('settingsModal');
|
||||
const closeSettingsModal = document.getElementById('closeSettingsModal');
|
||||
const btnSaveSettings = document.getElementById('btnSaveSettings');
|
||||
const settingAgentName = document.getElementById('settingAgentName');
|
||||
const previewKemilyAvatar = document.getElementById('previewKemilyAvatar');
|
||||
const uploadKemilyAvatar = document.getElementById('uploadKemilyAvatar');
|
||||
const previewCamilaAvatar = document.getElementById('previewCamilaAvatar');
|
||||
const uploadCamilaAvatar = document.getElementById('uploadCamilaAvatar');
|
||||
const welcomeTitle = document.getElementById('welcomeTitle');
|
||||
const welcomeAvatarImg = document.getElementById('welcomeAvatarImg');
|
||||
const sidebarAvatarImg = document.getElementById('sidebarAvatarImg');
|
||||
|
||||
window.agentConfig = {
|
||||
agentName: "Kemily",
|
||||
kemilyAvatarUrl: "assets/kemily.png",
|
||||
camilaAvatarUrl: "assets/camila_prof.png"
|
||||
};
|
||||
|
||||
// Carrega as configurações do servidor
|
||||
const loadAgentConfig = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/agent-config');
|
||||
if (res.ok) {
|
||||
window.agentConfig = await res.json();
|
||||
updateAgentIdentityUI();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erro ao carregar configurações do agente:', e);
|
||||
}
|
||||
};
|
||||
|
||||
const updateAgentIdentityUI = () => {
|
||||
if (welcomeTitle) {
|
||||
welcomeTitle.innerText = `Olá! Sou sua assistente virtual e me chamo ${window.agentConfig.agentName}. Como posso ajudar?`;
|
||||
}
|
||||
if (welcomeAvatarImg) {
|
||||
welcomeAvatarImg.src = window.agentConfig.kemilyAvatarUrl;
|
||||
}
|
||||
if (sidebarAvatarImg) {
|
||||
sidebarAvatarImg.src = window.agentConfig.camilaAvatarUrl;
|
||||
}
|
||||
if (settingAgentName) {
|
||||
settingAgentName.value = window.agentConfig.agentName;
|
||||
}
|
||||
if (previewKemilyAvatar) {
|
||||
previewKemilyAvatar.src = window.agentConfig.kemilyAvatarUrl;
|
||||
}
|
||||
if (previewCamilaAvatar) {
|
||||
previewCamilaAvatar.src = window.agentConfig.camilaAvatarUrl;
|
||||
}
|
||||
};
|
||||
|
||||
// Variáveis temporárias para avatares (em base64 ou URLs novas)
|
||||
let newKemilyAvatarData = null;
|
||||
let newCamilaAvatarData = null;
|
||||
|
||||
// Eventos do Modal
|
||||
if (btnSettings) {
|
||||
btnSettings.addEventListener('click', () => {
|
||||
newKemilyAvatarData = null;
|
||||
newCamilaAvatarData = null;
|
||||
if (settingAgentName) settingAgentName.value = window.agentConfig.agentName;
|
||||
if (previewKemilyAvatar) previewKemilyAvatar.src = window.agentConfig.kemilyAvatarUrl;
|
||||
if (previewCamilaAvatar) previewCamilaAvatar.src = window.agentConfig.camilaAvatarUrl;
|
||||
settingsModal.style.display = 'flex';
|
||||
});
|
||||
}
|
||||
|
||||
if (closeSettingsModal) {
|
||||
closeSettingsModal.addEventListener('click', () => {
|
||||
settingsModal.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Fechar clicando fora do modal-content
|
||||
if (settingsModal) {
|
||||
settingsModal.addEventListener('click', (e) => {
|
||||
if (e.target === settingsModal) {
|
||||
settingsModal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Previews de Imagem
|
||||
const handleAvatarFileSelect = (input, previewImg, callback) => {
|
||||
const file = input.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
previewImg.src = e.target.result;
|
||||
callback(e.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
if (uploadKemilyAvatar) {
|
||||
uploadKemilyAvatar.addEventListener('change', () => {
|
||||
handleAvatarFileSelect(uploadKemilyAvatar, previewKemilyAvatar, (base64) => {
|
||||
newKemilyAvatarData = base64;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (uploadCamilaAvatar) {
|
||||
uploadCamilaAvatar.addEventListener('change', () => {
|
||||
handleAvatarFileSelect(uploadCamilaAvatar, previewCamilaAvatar, (base64) => {
|
||||
newCamilaAvatarData = base64;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Salvar Configurações
|
||||
if (btnSaveSettings) {
|
||||
btnSaveSettings.addEventListener('click', async () => {
|
||||
btnSaveSettings.disabled = true;
|
||||
btnSaveSettings.innerText = 'Salvando...';
|
||||
|
||||
try {
|
||||
let kemilyUrl = window.agentConfig.kemilyAvatarUrl;
|
||||
let camilaUrl = window.agentConfig.camilaAvatarUrl;
|
||||
|
||||
// Se houver novas imagens, faz upload
|
||||
if (newKemilyAvatarData) {
|
||||
const res = await fetch('/api/upload-avatar', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ avatarType: 'kemily', base64Data: newKemilyAvatarData })
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kemilyUrl = data.url;
|
||||
}
|
||||
}
|
||||
|
||||
if (newCamilaAvatarData) {
|
||||
const res = await fetch('/api/upload-avatar', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ avatarType: 'camila', base64Data: newCamilaAvatarData })
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
camilaUrl = data.url;
|
||||
}
|
||||
}
|
||||
|
||||
// Envia as configurações gerais
|
||||
const resConfig = await fetch('/api/agent-config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
agentName: settingAgentName.value,
|
||||
kemilyAvatarUrl: kemilyUrl,
|
||||
camilaAvatarUrl: camilaUrl
|
||||
})
|
||||
});
|
||||
|
||||
if (resConfig.ok) {
|
||||
const result = await resConfig.json();
|
||||
window.agentConfig = result.config;
|
||||
updateAgentIdentityUI();
|
||||
settingsModal.style.display = 'none';
|
||||
} else {
|
||||
alert('Erro ao salvar as configurações.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Ocorreu um erro ao salvar.');
|
||||
} finally {
|
||||
btnSaveSettings.disabled = false;
|
||||
btnSaveSettings.innerText = 'Salvar Configurações';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Executa o carregamento inicial das configurações
|
||||
loadAgentConfig();
|
||||
|
||||
// Variáveis de Estado
|
||||
let chats = JSON.parse(localStorage.getItem('camila_chats')) || [];
|
||||
// Migração: adicionar campos tags e pinned a chats antigos
|
||||
@@ -918,8 +1099,8 @@ const initApp = () => {
|
||||
row.className = `message-row ${role}`;
|
||||
|
||||
const avatarHtml = isUser
|
||||
? `<div class="avatar user-avatar">U</div>`
|
||||
: `<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="assets/kemily.png" alt="Kemily AI" class="bot-avatar-img"></div>`;
|
||||
? `<div class="avatar user-avatar user-avatar-img-wrapper"><img src="${window.agentConfig?.camilaAvatarUrl || 'assets/camila_prof.png'}" alt="Camila" class="user-avatar-img"></div>`
|
||||
: `<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.kemilyAvatarUrl || 'assets/kemily.png'}" alt="${window.agentConfig?.agentName || 'Kemily'}" class="bot-avatar-img"></div>`;
|
||||
|
||||
let contentHtml = '';
|
||||
if (isUser) {
|
||||
@@ -1270,7 +1451,7 @@ const initApp = () => {
|
||||
|
||||
botRow.innerHTML = `
|
||||
<div class="message-icon">
|
||||
<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="assets/kemily.png" alt="Kemily AI" class="bot-avatar-img"></div>
|
||||
<div class="avatar bot-avatar bot-avatar-img-wrapper"><img src="${window.agentConfig?.kemilyAvatarUrl || 'assets/kemily.png'}" alt="${window.agentConfig?.agentName || 'Kemily'}" class="bot-avatar-img"></div>
|
||||
</div>
|
||||
<div class="message-content-wrapper">
|
||||
<div class="message-content">
|
||||
|
||||
+45
-3
@@ -82,12 +82,17 @@
|
||||
<div class="sidebar-footer">
|
||||
<div class="user-profile">
|
||||
<div class="avatar user-avatar sidebar-avatar-wrapper">
|
||||
<img src="assets/camila_prof.png" alt="Camila Reifonas" class="sidebar-avatar-img">
|
||||
<img src="assets/camila_prof.png" alt="Camila Reifonas" class="sidebar-avatar-img" id="sidebarAvatarImg">
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="user-name">Camila Reifonas</span>
|
||||
<span class="user-status">Online</span>
|
||||
</div>
|
||||
<button class="btn-settings" id="btnSettings" title="Configurações do Agente" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; padding: 8px; border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: color var(--transition-fast), background-color var(--transition-fast); margin-right: 4px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" style="width: 20px; height: 20px;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.43l-1.003.828c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.43l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 0 1 0-.255c.007-.378-.138-.75-.43-.991l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.645-.869L9.594 3.94ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn-logout" id="btnLogout" title="Sair">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15m-3 0-3-3m0 0 3-3m-3 3H15" />
|
||||
@@ -143,10 +148,10 @@
|
||||
<div class="welcome-container" id="welcomeContainer">
|
||||
<div class="welcome-logo">
|
||||
<div class="logo-outer welcome-avatar-wrapper">
|
||||
<img src="assets/kemily.png" alt="Kemily AI" class="welcome-avatar-img">
|
||||
<img src="assets/kemily.png" alt="Kemily AI" class="welcome-avatar-img" id="welcomeAvatarImg">
|
||||
</div>
|
||||
</div>
|
||||
<h2>Olá! Sou sua assistente virtual e me chamo Kemily. Como posso ajudar?</h2>
|
||||
<h2 id="welcomeTitle">Olá! Sou sua assistente virtual e me chamo Kemily. Como posso ajudar?</h2>
|
||||
|
||||
<div class="welcome-grid">
|
||||
<div class="suggestion-card" data-prompt="Escreva um relatório de observação semanal de um aluno de educação infantil focado em socialização e interação.">
|
||||
@@ -242,6 +247,43 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Modal de Configurações do Agente -->
|
||||
<div id="settingsModal" class="settings-modal" style="display: none;">
|
||||
<div class="settings-modal-content">
|
||||
<div class="settings-modal-header">
|
||||
<h3>Configurações da Assistente</h3>
|
||||
<button id="closeSettingsModal" class="close-settings-btn">×</button>
|
||||
</div>
|
||||
<div class="settings-modal-body">
|
||||
<div class="settings-group">
|
||||
<label for="settingAgentName">Nome da Assistente</label>
|
||||
<input type="text" id="settingAgentName" placeholder="Ex: Kemily">
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<label>Avatar da Assistente</label>
|
||||
<div class="avatar-preview-container">
|
||||
<img id="previewKemilyAvatar" src="assets/kemily.png" alt="Kemily Preview" class="preview-avatar-img">
|
||||
<input type="file" id="uploadKemilyAvatar" accept="image/*" style="display: none;">
|
||||
<button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadKemilyAvatar').click()">Escolher Imagem</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<label>Avatar da Camila (Usuária)</label>
|
||||
<div class="avatar-preview-container">
|
||||
<img id="previewCamilaAvatar" src="assets/camila_prof.png" alt="Camila Preview" class="preview-avatar-img">
|
||||
<input type="file" id="uploadCamilaAvatar" accept="image/*" style="display: none;">
|
||||
<button type="button" class="btn-change-avatar" onclick="document.getElementById('uploadCamilaAvatar').click()">Escolher Imagem</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-modal-footer">
|
||||
<button id="btnSaveSettings" class="btn-save-settings">Salvar Configurações</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bibliotecas externas para renderizar markdown e blocos de código -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
|
||||
@@ -2195,3 +2195,188 @@ code {
|
||||
background-color: rgba(16, 163, 127, 0.06);
|
||||
color: rgb(167, 243, 208);
|
||||
}
|
||||
|
||||
/* Hover no botao de configuracoes da sidebar */
|
||||
.btn-settings:hover {
|
||||
color: var(--text-primary) !important;
|
||||
background-color: var(--bg-secondary) !important;
|
||||
}
|
||||
|
||||
/* Avatar do Usuario no Chat */
|
||||
.user-avatar-img-wrapper {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.user-avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Modal de Configurações */
|
||||
.settings-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.settings-modal-content {
|
||||
background-color: var(--bg-primary, #121212);
|
||||
border: 1px solid var(--border-light, #262626);
|
||||
border-radius: 16px;
|
||||
width: 450px;
|
||||
max-width: 90%;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
animation: scaleIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.settings-modal-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--border-light, #262626);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-modal-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.15rem;
|
||||
color: var(--text-primary, #ffffff);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.close-settings-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary, #a3a3a3);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.close-settings-btn:hover {
|
||||
color: var(--text-primary, #ffffff);
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.settings-modal-body {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.settings-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.settings-group label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary, #a3a3a3);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.settings-group input[type="text"] {
|
||||
background-color: var(--bg-secondary, #1a1a1a);
|
||||
border: 1px solid var(--border-light, #262626);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
transition: border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.settings-group input[type="text"]:focus {
|
||||
border-color: var(--brand-green, #0ea5e9);
|
||||
}
|
||||
|
||||
.avatar-preview-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
background-color: var(--bg-secondary, #1a1a1a);
|
||||
border: 1px solid var(--border-light, #262626);
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.preview-avatar-img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--brand-green, #0ea5e9);
|
||||
}
|
||||
|
||||
.btn-change-avatar {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--border-light, #262626);
|
||||
color: var(--text-primary, #ffffff);
|
||||
padding: 8px 14px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn-change-avatar:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.settings-modal-footer {
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid var(--border-light, #262626);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-save-settings {
|
||||
background: linear-gradient(135deg, var(--brand-green) 0%, #0a5f49 100%);
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
padding: 10px 18px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s;
|
||||
box-shadow: 0 0 12px var(--brand-glow);
|
||||
}
|
||||
|
||||
.btn-save-settings:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user