🚀 Auto-deploy: Camila atualizado em 26/05/2026 10:56:30
This commit is contained in:
+86
-22
@@ -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
|
||||
|
||||
+19
-10
@@ -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,29 +24,33 @@
|
||||
<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>
|
||||
|
||||
|
||||
<form id="loginForm" class="login-form">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="errorMessage" class="error-message"></div>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
</html>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user