🚀 Auto-deploy: Camila atualizado em 26/05/2026 10:56:30

This commit is contained in:
2026-05-26 10:56:30 +00:00
parent b02f6dcca8
commit 0cf4e42620
20 changed files with 119 additions and 32 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+86 -22
View File
@@ -491,7 +491,7 @@ document.addEventListener('DOMContentLoaded', () => {
item.dataset.id = chat.id;
const pinIcon = chat.pinned
? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="pin-icon"><path d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"/></svg>`
? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="pin-icon"><line x1="12" y1="17" x2="12" y2="22" stroke="currentColor" stroke-width="2" stroke-linecap="round"></line><path d="M5 17h14v-1.76a2 2 0 0 0-.44-1.24l-2.78-3.47A2 2 0 0 1 15 9.29V5a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4.29a2 2 0 0 1-.78 1.24L5.44 14a2 2 0 0 0-.44 1.24z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>`
: '';
const tagBadges = (chat.tags || []).map(tag =>
@@ -505,17 +505,24 @@ document.addEventListener('DOMContentLoaded', () => {
${tagBadges ? `<div class="history-item-tags">${tagBadges}</div>` : ''}
</div>
<div class="history-item-actions">
<button class="btn-history-action btn-pin-chat" aria-label="${chat.pinned ? 'Desafixar' : 'Fixar'}">
<button class="btn-history-action btn-pin-chat" title="${chat.pinned ? 'Desafixar' : 'Fixar'}">
<svg xmlns="http://www.w3.org/2000/svg" fill="${chat.pinned ? 'currentColor' : 'none'}" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="16.689 16.689L19.3 19.3a1 1 0 0 0 1.414-1.414l-1.066-1.066a7.5 7.5 0 1 0-1.66 1.114l1.004 1.004A1 1 0 0 0 19.3 17.886l-.707-.707a1 1 0 0 0-1.414 1.414l.707.707a1 1 0 0 0 1.414-1.414l-.707-.707a1 1 0 0 0-1.414 1.414l.707.707a1 1 0 0 0 1.414-1.414l-.811-.811a7.5 7.5 0 0 0 .11 2.12l.796.796a1 1 0 1 0 1.414-1.414l-.707-.707a1 1 0 0 0-1.414 1.414l.707.707a1 1 0 0 0 1.414-1.414l-.72-.72a7.5 7.5 0 0 0-.47-2.12 1 1 0 0 0-1.89.574l.707.707a1 1 0 0 0 1.414-1.414l-.707-.707a1 1 0 0 0-1.414 1.414l.707.707a1 1 0 0 0 1.414-1.414l-.707-.707a1 1 0 0 0-1.414 1.414l.5.5a1 1 0 1 0 1.414-1.414l-.5-.5a1 1 0 0 0-1.414 1.414l.707.707a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0-1.414-1.414l-1.004 1.004a1 1 0 0 0 1.89-.574l-.796-.796a7.5 7.5 0 0 0-.11-2.12l.81.811a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.72-.72a7.5 7.5 0 0 0 .47 2.12l-.5-.5a1 1 0 1 0-1.414 1.414l.5.5a1 1 0 0 0-1.414-1.414l-.5.5a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0-1.414-1.414l-1.004 1.004a7.5 7.5 0 1 0 1.66-1.114l-1.004-1.004a1 1 0 1 0-1.414 1.414l1.066 1.066a1 1 0 0 0-1.414-1.414l-1.066 1.066a1 1 0 0 0 1.414-1.414l-3-3z"/>
<line x1="12" y1="17" x2="12" y2="22"></line>
<path d="M5 17h14v-1.76a2 2 0 0 0-.44-1.24l-2.78-3.47A2 2 0 0 1 15 9.29V5a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4.29a2 2 0 0 1-.78 1.24L5.44 14a2 2 0 0 0-.44 1.24z"></path>
</svg>
</button>
<button class="btn-history-action btn-rename-chat" aria-label="Renomear">
<button class="btn-history-action btn-tag-chat" title="Tags">
<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="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581a2.25 2.25 0 0 0 3.181 0l4.318-4.318a2.25 2.25 0 0 0 0-3.181l-9.58-9.581A2.25 2.25 0 0 0 9.568 3Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6Z" />
</svg>
</button>
<button class="btn-history-action btn-rename-chat" title="Renomear">
<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="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
</svg>
</button>
<button class="btn-history-action btn-delete-chat" aria-label="Excluir">
<button class="btn-history-action btn-delete-chat" title="Excluir">
<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="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
@@ -556,7 +563,8 @@ document.addEventListener('DOMContentLoaded', () => {
item.addEventListener('click', (e) => {
if (e.target.closest('.btn-delete-chat')) { e.stopPropagation(); deleteChat(chat.id); return; }
if (e.target.closest('.btn-pin-chat')) { e.stopPropagation(); togglePin(chat.id); return; }
if (e.target.closest('.btn-rename-chat')) { e.stopPropagation(); renameChat(chat.id); return; }
if (e.target.closest('.btn-rename-chat')) { e.stopPropagation(); renameChatInline(chat.id, item); return; }
if (e.target.closest('.btn-tag-chat')) { e.stopPropagation(); editTags(chat.id); return; }
selectChat(chat.id);
toggleSidebar(false);
});
@@ -584,7 +592,8 @@ document.addEventListener('DOMContentLoaded', () => {
item.addEventListener('click', (e) => {
if (e.target.closest('.btn-delete-chat')) { e.stopPropagation(); deleteChat(chat.id); return; }
if (e.target.closest('.btn-pin-chat')) { e.stopPropagation(); togglePin(chat.id); return; }
if (e.target.closest('.btn-rename-chat')) { e.stopPropagation(); renameChat(chat.id); return; }
if (e.target.closest('.btn-rename-chat')) { e.stopPropagation(); renameChatInline(chat.id, item); return; }
if (e.target.closest('.btn-tag-chat')) { e.stopPropagation(); editTags(chat.id); return; }
selectChat(chat.id);
toggleSidebar(false);
});
@@ -621,13 +630,69 @@ document.addEventListener('DOMContentLoaded', () => {
renderHistory();
};
// Renomear chat
const renameChat = (id) => {
// Renomear chat inline
const renameChatInline = (id, itemElement) => {
const chat = chats.find(c => c.id === id);
if (!chat) return;
const newTitle = prompt('Novo nome da conversa:', chat.title);
if (newTitle && newTitle.trim()) {
chat.title = newTitle.trim();
const titleDiv = itemElement.querySelector('.history-item-title');
if (!titleDiv) return;
const originalTitle = chat.title;
const input = document.createElement('input');
input.type = 'text';
input.className = 'rename-input';
input.value = originalTitle;
// Prevenir clique e propagação para o item
input.addEventListener('click', (e) => e.stopPropagation());
const finishRename = () => {
const newTitle = input.value.trim();
if (newTitle && newTitle !== originalTitle) {
chat.title = newTitle;
chat.updatedAt = Date.now();
saveChats();
}
renderHistory();
};
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
finishRename();
} else if (e.key === 'Escape') {
e.preventDefault();
renderHistory();
}
});
input.addEventListener('blur', finishRename);
titleDiv.replaceWith(input);
input.focus();
input.select();
};
// Gerenciar tags da conversa
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));
chat.tags = newTags;
chat.updatedAt = Date.now();
saveChats();
renderHistory();
}
@@ -892,24 +957,23 @@ document.addEventListener('DOMContentLoaded', () => {
const filesToSend = files || attachedFiles;
let fullText = text || '';
// Processar anexos: imagens viram descrição em texto (o modelo não tem visão)
// Processar anexos: evitar base64 gigantesco em imagens e erro de leitura binária em PDF/DOCX
if (filesToSend.length > 0) {
for (const file of filesToSend) {
if (file.type.startsWith('image/')) {
const reader = new FileReader();
const base64 = await new Promise((resolve) => {
reader.onload = (e) => resolve(e.target.result);
reader.readAsDataURL(file);
});
fullText += `\n\n[Imagem anexada: ${file.name}]\n\`\`\`\n${base64}\n\`\`\``;
} else {
// Apenas anexa uma referência textual limpa da imagem para a IA saber que ela existe
fullText += `\n\n*[Imagem anexada: ${file.name} (${Math.round(file.size/1024)} KB)]*`;
} else if (file.name.endsWith('.txt') || file.name.endsWith('.md') || file.name.endsWith('.json') || file.name.endsWith('.csv') || file.name.endsWith('.js') || file.name.endsWith('.css') || file.name.endsWith('.html')) {
const reader = new FileReader();
const content = await new Promise((resolve) => {
reader.onload = (e) => resolve(e.target.result);
reader.readAsText(file);
});
const truncated = content.length > 2000 ? content.substring(0, 2000) + '...[arquivo truncado]' : content;
fullText += `\n\n[Documento anexo: ${file.name}]\n\`\`\`\n${truncated}\n\`\`\``;
const truncated = content.length > 3000 ? content.substring(0, 3000) + '... [conteúdo truncado]' : content;
fullText += `\n\n*[Conteúdo do arquivo "${file.name}":]*\n\`\`\`\n${truncated}\n\`\`\``;
} else {
// Para arquivos binários como PDF/DOCX que não podem ser lidos diretamente como texto cru no frontend
fullText += `\n\n*[Arquivo anexado: ${file.name} (${Math.round(file.size/1024)} KB) - formato não textual]*`;
}
}
// Limpar anexos após envio
+15 -6
View File
@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -7,10 +8,13 @@
<!-- Google Fonts Outfit & Inter -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@400;600;700&display=swap" rel="stylesheet">
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@400;600;700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="style.css">
<link rel="shortcut icon" href="https://chatgpt.com/favicon.ico" type="image/x-icon">
</head>
<body class="login-body">
<div class="login-glow"></div>
<div class="login-container">
@@ -20,7 +24,7 @@
<img src="assets/camila_prof.png" alt="Camila Reifonas" class="avatar-profile-img">
<div class="avatar-profile-pulse"></div>
</div>
<h1>Camila AI</h1>
<h1>CamiIA</h1>
<p>Insira a senha de acesso para entrar no workspace</p>
</div>
@@ -28,10 +32,13 @@
<div class="input-group">
<label for="password">Senha do Portal</label>
<div class="input-wrapper">
<input type="password" id="password" required placeholder="Digite a senha..." autocomplete="current-password">
<input type="password" id="password" required placeholder="Digite a senha..."
autocomplete="current-password">
<button type="button" id="togglePassword" class="toggle-password" aria-label="Mostrar senha">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</button>
@@ -42,7 +49,8 @@
<button type="submit" id="btnSubmit" class="btn-submit">
<span>Entrar no Workspace</span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="arrow-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
class="arrow-icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>
</button>
@@ -51,4 +59,5 @@
</div>
<script src="login.js"></script>
</body>
</html>
+14
View File
@@ -1567,3 +1567,17 @@ code {
height: 100%;
object-fit: cover;
}
/* Rename Input Inline */
.rename-input {
background-color: var(--bg-tertiary) !important;
border: 1px solid var(--border-focus) !important;
border-radius: 6px !important;
color: var(--text-primary) !important;
font-size: 13px !important;
font-family: var(--font-sans) !important;
padding: 4px 6px !important;
width: 100% !important;
outline: none !important;
box-shadow: 0 0 5px var(--brand-glow) !important;
}