🔴 Alta priority: tema claro/escuro, modal tags, favicon K, badge modelo dinâmico

This commit is contained in:
2026-05-26 15:39:37 +00:00
parent 36ea7cce7a
commit f42bb49360
5 changed files with 629 additions and 16 deletions
+116 -13
View File
@@ -96,6 +96,120 @@ const initApp = () => {
let recognition = null;
let isListening = false;
// ==========================================================================
// THEME TOGGLE — Claro/Escuro
// ==========================================================================
const btnThemeToggle = document.getElementById('btnThemeToggle');
const iconThemeSun = document.getElementById('iconThemeSun');
const iconThemeMoon = document.getElementById('iconThemeMoon');
const applyTheme = (theme) => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('camila_theme', theme);
if (theme === 'light') {
iconThemeSun.style.display = 'none';
iconThemeMoon.style.display = 'block';
} else {
iconThemeSun.style.display = 'block';
iconThemeMoon.style.display = 'none';
}
};
// Init theme from localStorage or system preference
const savedTheme = localStorage.getItem('camila_theme');
if (savedTheme) {
applyTheme(savedTheme);
} else {
// Default to dark
applyTheme('dark');
}
btnThemeToggle.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-theme') || 'dark';
applyTheme(current === 'dark' ? 'light' : 'dark');
});
// ==========================================================================
// BADGE DE MODELO DINÂMICO
// ==========================================================================
const modelBadgeText = document.getElementById('modelBadgeText');
const updateModelBadge = async () => {
try {
const res = await fetch('/api/status');
if (res.ok) {
const data = await res.json();
// Extrai nome abreviado do modelo (ex: "openai/gpt-4.1-nano" → "GPT-4.1 Nano")
const modelName = data.model.split('/').pop();
const formatted = modelName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
modelBadgeText.textContent = formatted;
}
} catch (e) {
// Mantém texto padrão se falhar
}
};
updateModelBadge();
// ==========================================================================
// MODAL DE TAGS
// ==========================================================================
const AVAILABLE_TAGS = ['HTPC', 'ATA', 'Planejamento', 'Formação', 'Projeto', 'Relatório', 'Sugestão', 'Dúvida'];
const modalTagOverlay = document.getElementById('modalTagOverlay');
const modalTagList = document.getElementById('modalTagList');
const modalTagClose = document.getElementById('modalTagClose');
const modalTagCancel = document.getElementById('modalTagCancel');
const modalTagConfirm = document.getElementById('modalTagConfirm');
let pendingTagAction = null; // { chatId, callback }
let selectedTagsState = [];
const openTagModal = (chatId, currentTags, callback) => {
pendingTagAction = { chatId, callback };
selectedTagsState = [...currentTags];
renderTagModalList();
modalTagOverlay.classList.add('active');
};
const closeTagModal = () => {
modalTagOverlay.classList.remove('active');
pendingTagAction = null;
};
const renderTagModalList = () => {
modalTagList.innerHTML = AVAILABLE_TAGS.map(tag => `
<div class="modal-tag-item ${selectedTagsState.includes(tag) ? 'selected' : ''}" data-tag="${tag}">
<input type="checkbox" ${selectedTagsState.includes(tag) ? 'checked' : ''}>
<span>${tag}</span>
</div>
`).join('');
modalTagList.querySelectorAll('.modal-tag-item').forEach(item => {
item.addEventListener('click', () => {
const tag = item.dataset.tag;
if (selectedTagsState.includes(tag)) {
selectedTagsState = selectedTagsState.filter(t => t !== tag);
} else {
selectedTagsState.push(tag);
}
renderTagModalList();
});
});
};
modalTagClose.addEventListener('click', closeTagModal);
modalTagCancel.addEventListener('click', closeTagModal);
modalTagOverlay.addEventListener('click', (e) => {
if (e.target === modalTagOverlay) closeTagModal();
});
modalTagConfirm.addEventListener('click', () => {
if (pendingTagAction) {
pendingTagAction.callback(selectedTagsState);
}
closeTagModal();
});
// Expose globally for inline onclick handlers
window.openTagModal = openTagModal;
// ==========================================================================
// CONTROLES DE INTERFACE & SIDEBAR
// ==========================================================================
@@ -696,24 +810,13 @@ const initApp = () => {
const editTags = (id) => {
const chat = chats.find(c => c.id === id);
if (!chat) return;
const availableTags = ['relatorios', 'planejamento', 'mindlab', 'estudos'];
const currentTags = chat.tags || [];
// Prompt amigável mostrando opções
const promptText = `Atribua tags à conversa.\nOpções válidas: relatorios, planejamento, mindlab, estudos\n(Use vírgula para separar várias tags, ou deixe em branco para limpar):\n`;
const tagsStr = prompt(promptText, currentTags.join(', '));
if (tagsStr !== null) {
const newTags = tagsStr.split(',')
.map(t => t.trim().toLowerCase())
.filter(t => availableTags.includes(t));
openTagModal(id, currentTags, (newTags) => {
chat.tags = newTags;
chat.updatedAt = Date.now();
saveChats();
renderHistory();
}
});
};
const selectChat = (id) => {