diff --git a/UX_sugestoes.md b/UX_sugestoes.md
new file mode 100644
index 0000000..4c9d5ca
--- /dev/null
+++ b/UX_sugestoes.md
@@ -0,0 +1,231 @@
+# Análise UX/UI — App Camila AI
+## Sugestões de Melhoria com Base nos Chats Modernos
+
+**Data:** 25/05/2026
+**App:** camila.reifonas.cloud
+**Stack:** Express + Vanilla JS (frontend), OpenRouter API, dark theme estilo ChatGPT
+
+---
+
+## O QUE JÁ FUNCIONA BEM ✅
+
+- Tema dark premium com transições suaves
+- Sidebar com overlay no mobile
+- Boas-vindas com sugestões de prompts rápidos
+- Streaming de respostas em tempo real (SSE)
+- Renderização de markdown com syntax highlighting
+- Auto-save no localStorage por conversa
+- Logout funcional
+- Responsivo mobile-first
+- Input com auto-resize e Enter para enviar
+
+---
+
+## SUGESTÕES PRIORITÁRIAS (impacto alto)
+
+### 1. 🔒 Fixar Conversas no Topo (Pinned Chats)
+
+**Problema atual:** TODAS as conversas ficam na mesma lista cronológica. Se a Camila usa o app todo dia, em 1 mês tem 50+ conversas e achar algo específico vira um pesadelo.
+
+**Referência:** ChatGPT, Claude — tem um conceito de "pinned" no topo, separado do restante.
+
+**Solução:**
+- Adicionar botão de 📌 (pin) em cada conversation na sidebar
+- Fixadas aparecem no topo com label "FIXADAS" (assim como "HOJE", "ONTEM", etc.)
+- Ordenação: Fixadas → Cronologia (Hoje, Ontem, Semanas)
+
+### 2. 🏷️ Tags / Pastas para Organizar Conversas (Assuntos)
+
+**Problema atual:** Sem categorização nenhuma. Tudo é uma lista linear.
+
+**Referência:** Notion AI, Linear AI — permitem organizar conversas por projeto/tema.
+
+**Solução (simples):**
+- Adicionar campo `tags: []` em cada chat no localStorage
+- Na sidebar, permitir filtrar por tag (ex: "Relatórios", "Planejamento", "Mind Lab", "Estudos de Caso")
+- Tags coloridas com bolinhas (estilo Gmail/Notion)
+
+**Solução (intermediária):**
+- Pastas reais na sidebar com collapsible groups
+- Arrastar e soltar conversas entre pastas
+
+### 3. 🔍 Busca Global no Histórico
+
+**Problema atual:** Não existe busca. Só visualiza uma lista.
+
+**Referência:** Todo chat moderno tem Cmd/Ctrl+K para buscar.
+
+**Solução:**
+- Adicionar barra de busca no topo da sidebar
+- Busca por título E conteúdo das mensagens
+- Resultados destacados com snippet da mensagem que matchou
+
+### 4. ✏️ Renomear Conversas
+
+**Problema atual:** O título da conversa é gerado automaticamente das primeiras 26 caracteres da primeira mensagem. Não editable.
+
+**Referência:** ChatGPT permite renomear.
+
+**Solução:**
+- Duplo clique no título na sidebar abre edit inline
+- Botão de ⋮ (três pontos) com opção "Renomear"
+- Nomear com algo significativo: "Relatório mensal - Maio 2026", "Planejamento BNCC - Corpo"
+
+---
+
+## SUGESTÕES MÉDIAS (impacto médio)
+
+### 5. 📱 Input por Voz (Microfone)
+
+**Problema atual:** Só texto digitado.
+
+**Referência:** ChatGPT mobile, Gemini.
+
+**Solução:**
+- Adicionar botão de 🎤 (microfone) ao lado do input
+- Usa Web Speech API (navegador) — sem custo adicional
+- Transcreve voz → texto → envia automaticamente
+
+**Por que é importante para a Camila:**
+Professora passa o dia escrevendo — ter a opção de ditado para criar relatórios ou notas seria muito útil.
+
+### 6. 📋 Exportar / Copiar Mensagem Específica
+
+**Problema atual:** Só consigo copiar o texto selecionando. Não tem botão "copiar" em mensagens individuais.
+
+**Referência:** Claude, ChatGPT.
+
+**Solução:**
+- No hover de cada mensagem do bot, aparece botão de 📋 "Copiar"
+- Dropdown com: "Copiar texto", "Copiar como Markdown"
+-opcional: "Exportar conversa como PDF/Markdown"
+
+### 7. ⚡ Editar e Reenviar Mensagem
+
+**Problema atual:** Se a Camila digita algo errado, não tem como editar. Tem que criar novo chat.
+
+**Referência:** ChatGPT (clique no ícone ✏️ na mensagem do usuário).
+
+**Solução:**
+- Na mensagem do usuário, adicionar ícone de ✏️ (editar)
+- Ao clicar, o texto volta para o input para correção
+- Reenvia a mensagem e a conversa é re-gerada com a correção
+- Mostrar visual que "mensagem foi editada" (tipo chat WhatsApp)
+
+### 8. 🔄 Regenerar Resposta
+
+**Problema atual:** Se a Kemily dá uma resposta ruim, só apagando a mensagem e repedindo.
+
+**Referência:** ChatGPT, Claude.
+
+**Solução:**
+- Botão 🔄 ao lado da resposta do bot (no header da mensagem)
+- Regenera a resposta sem perder a conversa
+- Loading state na mensagem enquanto regenera
+
+### 9. 📊 Dashboard / Visão Geral do Histórico
+
+**Problema atual:** Sem noção macro de uso.
+
+**Referência:** Linear AI, Notion AI.
+
+**Solução:**
+- Mostrar "Conversas este mês: 12", "Principais assuntos: Relatórios, Mind Lab"
+- Nuvem de tags dos assuntos mais frequentes
+- Stats simples: mensagens enviadas, dias de uso
+
+---
+
+## SUGESTÕES AVANÇADAS (impacto menor, esforço maior)
+
+### 10. ⌨️ Atalhos de Teclado
+
+- `Ctrl+N` → Novo chat
+- `Ctrl+F` → Buscar no histórico
+- `Ctrl+Shift+C` → Copiar última resposta
+- `Escape` → Fechar sidebar (mobile pattern)
+
+### 11. 🌙 Tema Claro
+
+- Toggle de dark/light no header ou perfil
+- Salvar preferência no localStorage
+
+### 12. 📎 Upload de Imagens / Documentos
+
+- Permitir anexar foto de atividade, lousa, etc
+- A Kemily analisa a imagem
+- Usa mesma interface que a Camila já usa (arrastar ou colar)
+
+### 13. 🔗 Compartilhar Conversa
+
+- Gerar link público para uma conversa (sem auth)
+- Compartilhar com gestor/coordenador um relatório gerado
+
+### 14. 📅 Lembretes / Scheduling
+
+- "A Camila me pede para lembrar de algo" → agendar notificação
+- Integração com calendário da rede municipal (futuro)
+
+---
+
+## HIERARQUIA DE IMPLEMENTAÇÃO RECOMENDADA
+
+| Prioridade | Item | Esforço | Impacto |
+|---|---|---|---|
+| 🔴 Alta | Fixar conversas | Baixo | Alto |
+| 🔴 Alta | Tags/Pastas | Médio | Alto |
+| 🔴 Alta | Busca no histórico | Médio | Alto |
+| 🔴 Alta | Renomear conversas | Baixo | Médio |
+| 🟡 Média | Input por voz | Baixo | Médio |
+| 🟡 Média | Copiar mensagem | Baixo | Médio |
+| 🟡 Média | Editar/reenviar | Médio | Médio |
+| 🟡 Média | Regenerar resposta | Médio | Médio |
+| 🟢 Baixa | Atalhos de teclado | Baixo | Baixo |
+| 🟢 Baixa | Tema claro | Médio | Baixo |
+| 🟢 Baixa | Upload imagens | Médio | Alto |
+
+---
+
+## NOTES TÉCNICAS PARA IMPLEMENTAÇÃO
+
+### Estrutura localStorage sugerida (nova)
+
+```javascript
+// Chat existente
+{
+ id: "chat_1748...",
+ title: "Relatório mensal - Maio",
+ tags: ["relatorios", "bncc"],
+ pinned: true,
+ messages: [...],
+ createdAt: 1748...,
+ updatedAt: 1748...
+}
+
+// Tags globais (saved separately)
+{
+ tags: [
+ { id: "relatorios", name: "Relatórios", color: "#10a37f" },
+ { id: "planejamento", name: "Planejamento", color: "#a855f7" }
+ ]
+}
+```
+
+### Filtros na sidebar
+-Grupo "Fixadas" (se tiver pinned:true)
+- Grupo "Hoje"
+- Grupo "Ontem"
+- Grupo "Semana Passada"
+- Grupo "Este Mês"
+- Grupo "Mais Antigas"
+-Ao selecionar tag: mostra só as com aquela tag
+
+---
+
+## INSPIRAÇÕES DIRETAS
+
+1. **ChatGPT** (OpenAI) — sidebar organization, pinned, search, regenerate
+2. **Claude** (Anthropic) — edit message, copy buttons, regenerate
+3. **Notion AI** — cmd+K search, tags/folders, clean UI
+4. **Linear AI** — dashboard stats, keyboard shortcuts
+5. **Gemini** (Google) — voice input, image upload, web search integration
diff --git a/public/app.js b/public/app.js
index 68e45a0..4384f97 100644
--- a/public/app.js
+++ b/public/app.js
@@ -513,6 +513,12 @@ document.addEventListener('DOMContentLoaded', () => {
Copiar
+
` : '';
@@ -545,15 +551,78 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
+ // Event listener para regenerar resposta
+ const regenBtn = row.querySelector('.btn-regenerate');
+ if (regenBtn) {
+ regenBtn.addEventListener('click', () => {
+ if (isGenerating) return;
+ regenerateLastResponse();
+ });
+ }
+
return row;
};
+ // Regenerar última resposta do bot
+ const regenerateLastResponse = () => {
+ const chat = chats.find(c => c.id === currentChatId);
+ if (!chat || chat.messages.length === 0) return;
+
+ // Encontrar última mensagem do bot
+ let lastBotIdx = -1;
+ for (let i = chat.messages.length - 1; i >= 0; i--) {
+ if (chat.messages[i].role === 'assistant') {
+ lastBotIdx = i;
+ break;
+ }
+ }
+ if (lastBotIdx === -1) return;
+
+ // Remover última mensagem do bot e todas as mensagens depois da última mensagem do usuário
+ let lastUserIdx = -1;
+ for (let i = lastBotIdx - 1; i >= 0; i--) {
+ if (chat.messages[i].role === 'user') {
+ lastUserIdx = i;
+ break;
+ }
+ }
+
+ // Cortar mensagens até a última do usuário
+ if (lastUserIdx >= 0) {
+ chat.messages = chat.messages.slice(0, lastUserIdx + 1);
+ } else {
+ chat.messages = [];
+ }
+
+ // Remover da UI as mensagens depois do último user
+ const rows = conversationList.querySelectorAll('.message-row');
+ for (let i = rows.length - 1; i >= 0; i--) {
+ const row = rows[i];
+ const isBot = row.classList.contains('assistant');
+ const isUser = row.classList.contains('user');
+ if (isBot) {
+ row.remove();
+ } else if (isUser) {
+ break;
+ }
+ }
+
+ saveChats();
+ scrollToBottom();
+
+ // Reenviar última mensagem do usuário para gerar nova resposta
+ const lastUserMsg = chat.messages[chat.messages.length - 1];
+ if (lastUserMsg && lastUserMsg.role === 'user') {
+ handleSendMessage(lastUserMsg.content, true);
+ }
+ };
+
const scrollToBottom = () => {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};
// Função principal de envio de mensagens
- const handleSendMessage = async (text) => {
+ const handleSendMessage = async (text, skipUserAppend = false) => {
if (!text || isGenerating) return;
isGenerating = true;
@@ -589,16 +658,16 @@ document.addEventListener('DOMContentLoaded', () => {
const chat = chats.find(c => c.id === currentChatId);
if (!chat) return;
- // Adicionar mensagem do usuário no estado e na tela
- const userMsg = { role: 'user', content: text };
- chat.messages.push(userMsg);
- chat.updatedAt = Date.now();
- appendMessageUI('user', text);
- scrollToBottom();
-
- // Salvar estado temporário do chat
- saveChats();
- renderHistory();
+ // Adicionar mensagem do usuário no estado e na tela (pula se for regeneração)
+ if (!skipUserAppend) {
+ const userMsg = { role: 'user', content: text };
+ chat.messages.push(userMsg);
+ chat.updatedAt = Date.now();
+ appendMessageUI('user', text);
+ scrollToBottom();
+ saveChats();
+ renderHistory();
+ }
// Criar container para a resposta do bot (Camila AI) na tela
const botRow = document.createElement('div');
diff --git a/public/style.css b/public/style.css
index 3ab3dc2..cc21c46 100644
--- a/public/style.css
+++ b/public/style.css
@@ -1229,6 +1229,19 @@ code {
50% { transform: scale(1.15); opacity: 0.8; }
}
+/* Botão regenerar */
+.msg-action-btn.btn-regenerate:hover {
+ color: #10b981 !important;
+}
+
+.msg-action-btn.btn-regenerate:hover svg {
+ transform: rotate(-30deg);
+}
+
+.msg-action-btn.btn-regenerate svg {
+ transition: transform 0.3s ease;
+}
+
.disclaimer {
font-size: 11px;
color: var(--text-muted);