🚀 Auto-deploy: Camila atualizado em 26/05/2026 10:56:30
This commit is contained in:
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.
BIN
Binary file not shown.
BIN
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
@@ -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