Refactor chat module to use unified theme system
Rewrites chat.css to use centralized CSS variables from app.css instead of maintaining its own theme definitions. Moves all theme variables (colors, spacing, shadows, transitions) to app.css as the single source of truth. Improves chat UI consistency, adds better connection status indicators, and enhances responsive design.
This commit is contained in:
parent
eb4084fe2d
commit
444b424739
5 changed files with 989 additions and 1040 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -16,4 +16,5 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<button class="scroll-to-bottom" id="scrollToBottom">↓</button>
|
<button class="scroll-to-bottom" id="scrollToBottom">↓</button>
|
||||||
|
<div class="flash-overlay" id="flashOverlay"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -187,11 +187,15 @@ function chatApp() {
|
||||||
connectionStatus = document.getElementById("connectionStatus");
|
connectionStatus = document.getElementById("connectionStatus");
|
||||||
flashOverlay = document.getElementById("flashOverlay");
|
flashOverlay = document.getElementById("flashOverlay");
|
||||||
suggestionsContainer = document.getElementById("suggestions");
|
suggestionsContainer = document.getElementById("suggestions");
|
||||||
floatLogo = document.getElementById("floatLogo");
|
|
||||||
sidebar = document.getElementById("sidebar");
|
|
||||||
themeBtn = document.getElementById("themeBtn");
|
|
||||||
scrollToBottomBtn = document.getElementById("scrollToBottom");
|
scrollToBottomBtn = document.getElementById("scrollToBottom");
|
||||||
sidebarTitle = document.getElementById("sidebarTitle");
|
|
||||||
|
console.log("Chat DOM elements initialized:", {
|
||||||
|
messagesDiv: !!messagesDiv,
|
||||||
|
messageInputEl: !!messageInputEl,
|
||||||
|
sendBtn: !!sendBtn,
|
||||||
|
voiceBtn: !!voiceBtn,
|
||||||
|
connectionStatus: !!connectionStatus,
|
||||||
|
});
|
||||||
|
|
||||||
// Theme initialization and focus
|
// Theme initialization and focus
|
||||||
const savedTheme = localStorage.getItem("gb-theme") || "auto";
|
const savedTheme = localStorage.getItem("gb-theme") || "auto";
|
||||||
|
|
@ -231,10 +235,15 @@ function chatApp() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sendBtn) {
|
||||||
sendBtn.onclick = () => this.sendMessage();
|
sendBtn.onclick = () => this.sendMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageInputEl) {
|
||||||
messageInputEl.addEventListener("keypress", (e) => {
|
messageInputEl.addEventListener("keypress", (e) => {
|
||||||
if (e.key === "Enter") this.sendMessage();
|
if (e.key === "Enter") this.sendMessage();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Don't auto-reconnect on focus in browser to prevent multiple connections
|
// Don't auto-reconnect on focus in browser to prevent multiple connections
|
||||||
// Tauri doesn't fire focus events the same way
|
// Tauri doesn't fire focus events the same way
|
||||||
|
|
@ -263,7 +272,14 @@ function chatApp() {
|
||||||
},
|
},
|
||||||
|
|
||||||
updateConnectionStatus(s) {
|
updateConnectionStatus(s) {
|
||||||
|
if (!connectionStatus) return;
|
||||||
connectionStatus.className = `connection-status ${s}`;
|
connectionStatus.className = `connection-status ${s}`;
|
||||||
|
const statusText = {
|
||||||
|
connected: "Connected",
|
||||||
|
connecting: "Connecting...",
|
||||||
|
disconnected: "Disconnected",
|
||||||
|
};
|
||||||
|
connectionStatus.innerHTML = `<span>${statusText[s] || s}</span>`;
|
||||||
},
|
},
|
||||||
|
|
||||||
getWebSocketUrl() {
|
getWebSocketUrl() {
|
||||||
|
|
@ -513,9 +529,6 @@ function chatApp() {
|
||||||
if (d.color2) themeColor2 = d.color2;
|
if (d.color2) themeColor2 = d.color2;
|
||||||
if (d.logo_url) customLogoUrl = d.logo_url;
|
if (d.logo_url) customLogoUrl = d.logo_url;
|
||||||
if (d.title) document.title = d.title;
|
if (d.title) document.title = d.title;
|
||||||
if (d.logo_text) {
|
|
||||||
sidebarTitle.textContent = d.logo_text;
|
|
||||||
}
|
|
||||||
this.applyTheme();
|
this.applyTheme();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -526,41 +539,29 @@ function chatApp() {
|
||||||
const t = document.createElement("div");
|
const t = document.createElement("div");
|
||||||
t.id = "thinking-indicator";
|
t.id = "thinking-indicator";
|
||||||
t.className = "message-container";
|
t.className = "message-container";
|
||||||
t.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="thinking-indicator"><div class="typing-dots"><div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div></div></div></div>`;
|
t.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="thinking-indicator"><div class="thinking-dot"></div><div class="thinking-dot"></div><div class="thinking-dot"></div></div></div>`;
|
||||||
|
if (messagesDiv) {
|
||||||
messagesDiv.appendChild(t);
|
messagesDiv.appendChild(t);
|
||||||
gsap.to(t, { opacity: 1, y: 0, duration: 0.3, ease: "power2.out" });
|
|
||||||
if (!isUserScrolling) {
|
if (!isUserScrolling) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
isThinking = true;
|
||||||
|
|
||||||
thinkingTimeout = setTimeout(() => {
|
thinkingTimeout = setTimeout(() => {
|
||||||
if (isThinking) {
|
if (isThinking) {
|
||||||
this.hideThinkingIndicator();
|
this.hideThinkingIndicator();
|
||||||
this.showWarning(
|
this.showWarning("A resposta está demorando mais que o esperado...");
|
||||||
"O servidor pode estar ocupado. A resposta está demorando demais.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, 60000);
|
}, 30000);
|
||||||
isThinking = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hideThinkingIndicator() {
|
hideThinkingIndicator() {
|
||||||
if (!isThinking) return;
|
if (!isThinking) return;
|
||||||
const t = document.getElementById("thinking-indicator");
|
const t = document.getElementById("thinking-indicator");
|
||||||
if (t) {
|
if (t && t.parentNode) {
|
||||||
gsap.to(t, {
|
|
||||||
opacity: 0,
|
|
||||||
duration: 0.2,
|
|
||||||
onComplete: () => {
|
|
||||||
if (t.parentNode) {
|
|
||||||
t.remove();
|
t.remove();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (thinkingTimeout) {
|
|
||||||
clearTimeout(thinkingTimeout);
|
|
||||||
thinkingTimeout = null;
|
|
||||||
}
|
|
||||||
isThinking = false;
|
isThinking = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -568,31 +569,29 @@ function chatApp() {
|
||||||
const w = document.createElement("div");
|
const w = document.createElement("div");
|
||||||
w.className = "warning-message";
|
w.className = "warning-message";
|
||||||
w.innerHTML = `⚠️ ${m}`;
|
w.innerHTML = `⚠️ ${m}`;
|
||||||
|
if (messagesDiv) {
|
||||||
messagesDiv.appendChild(w);
|
messagesDiv.appendChild(w);
|
||||||
gsap.from(w, { opacity: 0, y: 20, duration: 0.4, ease: "power2.out" });
|
|
||||||
if (!isUserScrolling) {
|
if (!isUserScrolling) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (w.parentNode) {
|
if (w.parentNode) {
|
||||||
gsap.to(w, {
|
w.remove();
|
||||||
opacity: 0,
|
|
||||||
duration: 0.3,
|
|
||||||
onComplete: () => w.remove(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
showContinueButton() {
|
showContinueButton() {
|
||||||
const c = document.createElement("div");
|
const c = document.createElement("div");
|
||||||
c.className = "message-container";
|
c.className = "message-container";
|
||||||
c.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="assistant-message-content"><p>A conexão foi interrompida. Clique em "Continuar" para tentar recuperar a resposta.</p><button class="continue-button" onclick="this.parentElement.parentElement.parentElement.remove();">Continuar</button></div></div>`;
|
c.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="assistant-message-content"><p>A conexão foi interrompida. Clique em "Continuar" para tentar recuperar a resposta.</p><button class="continue-button" onclick="this.parentElement.parentElement.parentElement.remove();">Continuar</button></div></div>`;
|
||||||
|
if (messagesDiv) {
|
||||||
messagesDiv.appendChild(c);
|
messagesDiv.appendChild(c);
|
||||||
gsap.to(c, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" });
|
|
||||||
if (!isUserScrolling) {
|
if (!isUserScrolling) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
continueInterruptedResponse() {
|
continueInterruptedResponse() {
|
||||||
|
|
@ -629,11 +628,12 @@ function chatApp() {
|
||||||
} else {
|
} else {
|
||||||
m.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="assistant-message-content">${content}</div></div>`;
|
m.innerHTML = `<div class="assistant-message"><div class="assistant-avatar"></div><div class="assistant-message-content">${content}</div></div>`;
|
||||||
}
|
}
|
||||||
|
if (messagesDiv) {
|
||||||
messagesDiv.appendChild(m);
|
messagesDiv.appendChild(m);
|
||||||
gsap.to(m, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" });
|
|
||||||
if (!isUserScrolling) {
|
if (!isUserScrolling) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateStreamingMessage(c) {
|
updateStreamingMessage(c) {
|
||||||
|
|
@ -680,9 +680,13 @@ function chatApp() {
|
||||||
b.className = "suggestion-button";
|
b.className = "suggestion-button";
|
||||||
b.onclick = () => {
|
b.onclick = () => {
|
||||||
this.setContext(v.context);
|
this.setContext(v.context);
|
||||||
|
if (messageInputEl) {
|
||||||
messageInputEl.value = "";
|
messageInputEl.value = "";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
if (suggestionsContainer) {
|
||||||
suggestionsContainer.appendChild(b);
|
suggestionsContainer.appendChild(b);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -16,8 +16,8 @@
|
||||||
font-family:
|
font-family:
|
||||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
Helvetica, Arial, sans-serif;
|
Helvetica, Arial, sans-serif;
|
||||||
background: #ffffff;
|
background: var(--primary-bg);
|
||||||
color: #202124;
|
color: var(--primary-fg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,14 +55,14 @@
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: var(--glass-bg);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-wrapper:hover {
|
.logo-wrapper:hover {
|
||||||
background: rgba(255, 255, 255, 1);
|
background: var(--bg-hover);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-icon {
|
.logo-icon {
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
.logo-text {
|
.logo-text {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #202124;
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Right side - Apps menu and avatar */
|
/* Right side - Apps menu and avatar */
|
||||||
|
|
@ -97,20 +97,20 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
color: #5f6368;
|
color: var(--text-secondary);
|
||||||
position: relative;
|
position: relative;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: var(--glass-bg);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.apps-menu-btn:hover {
|
.apps-menu-btn:hover {
|
||||||
background: rgba(255, 255, 255, 1);
|
background: var(--bg-hover);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.apps-menu-btn:active {
|
.apps-menu-btn:active {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: var(--bg-active);
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,11 +120,9 @@
|
||||||
top: 60px;
|
top: 60px;
|
||||||
right: 80px;
|
right: 80px;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
background: white;
|
background: var(--glass-bg);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow:
|
box-shadow: var(--shadow-xl);
|
||||||
0 1px 2px 0 rgba(60, 64, 67, 0.3),
|
|
||||||
0 2px 6px 2px rgba(60, 64, 67, 0.15);
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue