diff --git a/SUGESTOES_MELHORIA.md b/SUGESTOES_MELHORIA.md new file mode 100644 index 0000000..6d12120 --- /dev/null +++ b/SUGESTOES_MELHORIA.md @@ -0,0 +1,153 @@ +# Sugestões de Melhoria — Camila AI (Kemily) + +*Análise feita em 2026-05-26 — App: /root/Apps/Camila* + +--- + +## 1. UX / UI + +### 1.1 Tela de Boas-Vindas (Welcome) +- **Prompt cards genéricos.** As sugestões rápidas ("Novo chat", etc.) não refletem a persona Kemily. + - Substituir por cards relevantes à educação infantil: "Relatório semanal", "Planejamento Mind Lab", "Resumo de livro infantil", "Atividade BNCC". + - Cards com ícones temáticos e descrição curta. + +- **Cursor de digitação simples.** O cursor `|` durante streaming é funcional mas visualmente pobre. + - Substituir por uma bolinha pulsante roxa (matching com o user avatar gradient) ou uma barra de loading no estilo "thinking...". + +### 1.2 Sidebar / Menu Lateral +- **Edição de tags via `prompt()` nativo.** O uso de `prompt()` do navegador é abrupto e quebrado visualmente. + - Substituir por um modal/dropdown popover com checkboxes para selecionar tags. Mesmo visual do app. + +- **Ordem do histórico.** O chat ativo não fica visível no topo quando você volta para ele. + - Ao selecionar um chat existente, movê-lo para o topo do grupo "Hoje". + +- **Favicon genérico.** O favicon aponta para `chatgpt.com/favicon.ico`. + - Criar um favicon próprio (iniciais "K" ou "CA" com o verde brand). + +### 1.3 Input de Mensagem +- **Contador de caracteres.** Principalmente no mobile, um contador visual ajuda a perceber o tamanho da mensagem. + +- **Preview de anexo no mobile.** O preview de arquivos anexados aparece, mas no mobile pode ficar mal posicionado. + - Garantir que o preview fique visível e bem adaptado antes do textarea. + +### 1.4 Header +- **Badge do modelo hardcoded.** O header mostra "GPT-4.1 Nano" de forma estática. + - Tornar dinâmico via variável de ambiente ou `/api/status` endpoint. + +### 1.5 Acessibilidade +- **Contraste em textos.** Alguns textos em `text-muted` (#8e8e8e) sobre fundo escuro podem ter contraste insuficiente para leitura prolongada. + - Aumentar ligeiramente o peso (weight 500+) ou escurecer a cor para #9e9e9e ou #a0a0a0. + +- **Focus visible.** Botões e inputs precisam de `focus-visible` melhorado para navegação por teclado. + +--- + +## 2. Visuais + +### 2.1 Avatar do Bot ( Kemily ) +- **Confusão persona/foto real.** O avatar de IA usa a foto real da Camila. Quando o usuário互换 for "Camila AI", pode gerar confusão. + - Considerar um avatar ilustrado (estilo cartoon/feminino) ou um avatar geométrico abstrato com o verde brand. A/Kemia combina com um visual mais lúdico que combine com educação infantil. + +### 2.2 Paleta e Tema +- **Apenas tema escuro.** O app só funciona em dark mode. + - Oferecer toggle de tema claro/escuro (light mode com whites/cinzas claros, mantendo o verde brand). Persistir escolha no localStorage. + +### 2.3 Ilustrações e Decorativos +- **Falta identidade visual além das fotos.** A tela de login tem um glow verde, mas não há ilustrações, ícones decorativos ou gradientes que deem personalidade. + - Adicionar ilustrações SVG minimalistas na tela de boas-vindas (lápis, livro, jogos). + - Usar o gradiente do avatar em mais lugares (bordas, destaques). + +### 2.4 Animações +- **Transições de entrada das mensagens.** A animação `fadeInUp` é boa, mas não há animação na saída (quando uma mensagem é removida na regeneração). + - Adicionar fadeOut na remoção de mensagens. + +- **Animação de loading na sidebar.** Ao carregar ou buscar histórico, um skeleton ou shimmer melhoraria a percepção. + +### 2.5 Tipografia +- **Fira Code para código.** O CSS referencia 'Fira Code' mas não está importado no `index.html`. + - Adicionar `` para Fira Code no Google Fonts, ou usar 'JetBrains Mono' como alternativa. + +--- + +## 3. Técnicas + +### 3.1 Segurança +- **Autenticação fraca.** Apenas senha em texto no `.env` (sem hash), sem proteção contra brute force. + - Adicionar rate limiting no endpoint `/api/chat` e `/api/login`. + - Considerar usar JWT ou session-based auth com cookie seguro. + +- **Senha exposta no frontend.** O `login.js` envia a senha diretamente. Mesmo em HTTPS, credenciais ficam expostas em logs de rede. + - Implementar Challenge-Response ou hash no client antes de enviar (ex: bcrypt client-side). + +### 3.2 Armazenamento +- **localStorage sem limite.** Não há controle de espaço. Chats extensos podem atingir o limite de ~5MB do localStorage. + - Implementar quota checking antes de salvar. Alertar usuário ou auto-arquivar chats antigos para API/Backend. + +- **Sem backup/exportação.** Histórico de conversas não pode ser exportado. + - Adicionar botão "Exportar conversas" (JSON ou Markdown). + +### 3.3 TTS (Text-to-Speech) +- **Web Speech API limitada.** A seleção de voz é boa (busca por nome), mas não há controle de velocidade/profundidade e vozes pt-BR são inconsistentes entre navegadores. + - Oferecer um mini popup de configurações de voz (velocidade: 0.75x, 1x, 1.25x; pitch). + - Considerar integração com Edge TTS (Microsoft) via backend para vozes neurais consistentes em pt-BR. + +### 3.4 Streaming e Performance +- **Re-renderização total do markdown.** A cada chunk do stream, `marked.parse()` é chamado na string inteira. + - Para respostas longas, implementar parsing incremental ou renderizar apenas o delta. + +- **Sem debounce no input de busca.** A busca filtra a cada `input` event, sem debounce. + - Adicionar debounce de 300ms para searches mais pesados. + +### 3.5 Backend e Infraestrutura +- **Arquivos do Agente no filesystem.** Os MDs em `/Agente/` são lidos via código no backend. + - Se o app escalar, considerar migrar para uma estrutura de API documentada (ex: FastAPI com `/api/v1/`). + +- **Sem métricas/monitoring.** Não há logs de uso, latência, ou erros de API. + - Adicionar logging básico (quem acessou, tokens usados, latência de resposta). + +### 3.6 Testes +- **Zero testes automatizados.** + - Configurar um framework (Vitest para frontend, Jest para backend). Cover: parsing de markdown, escapeHtml, lógica de filtro, renderização de componentes. + +### 3.7 Mobile / PWA +- **Não é um PWA.** O app não tem manifest nem service worker. + - Adicionar `manifest.json` e SW para funcionar offline básico (tela de login e histórico). + - Adicionar meta tags de iOS (apple-mobile-web-app-capable). + +### 3.8 SEO e Meta +- **Meta tags limitadas.** Title e description genéricos. + - Adicionar Open Graph tags, description customizada, e meta tags para compartilhamento. + +--- + +## 4. Melhorias Estratégicas (Maior Impacto) + +| Prioridade | Sugestão | Motivo | +|------------|----------|--------| +| 🔴 Alta | Tema claro/escuro | Acessibilidade e preferência pessoal | +| 🔴 Alta | Modal de tags no lugar de `prompt()` | UX consistente e profissional | +| 🔴 Alta | Toggle de tema claro | Contraste e acessibilidade | +| 🟡 Média | Avatar ilustrado para Kemily | Identidade visual + educação infantil | +| 🟡 Média | Cards de prompt temáticos | Relevância imediata ao abrir | +| 🟡 Média | Config TTS com velocidade | Experiência de voz personalizável | +| 🟡 Média | Exportar conversas | Não perder dados | +| 🟡 Média | Fira/JetBrains Mono importado | Código com monospace melhor | +| 🟢 Baixa | PWA / Service Worker | Offline e instalação | +| 🟢 Baixa | Métricas de uso | Dados para iterar | + +--- + +## 5. Observações Positivas (Manter) + +- ✅ Design system com variáveis CSS bem organizadas +- ✅ Sidebar com busca + filtros por tags + pinned chats — muito funcional +- ✅ Streaming SSE implementado corretamente com parsing incremental +- ✅ Drag-and-drop para anexos funcionando +- ✅ Reconhecimento de voz (Web Speech API) com seleção inteligente de voz pt-BR +- ✅ Ctrl+N e Ctrl+K atalhos de teclado úteis +- ✅ Mobile-first com sidebar em overlay +- ✅ Código bem comentado e organizado em seções + +--- + +*Documento gerado por Hermes — 2026-05-26* \ No newline at end of file diff --git a/public/app.js b/public/app.js index 3f23270..2f0612e 100644 --- a/public/app.js +++ b/public/app.js @@ -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 => ` +