diff --git a/web/desktop/chat/chat.css b/web/desktop/chat/chat.css
index 3b3fccb07..715310ed0 100644
--- a/web/desktop/chat/chat.css
+++ b/web/desktop/chat/chat.css
@@ -199,7 +199,7 @@ body::before {
top: 0;
left: 50%;
transform: translateX(-50%);
- bottom: 100px;
+ bottom: 75px;
overflow-y: auto;
overflow-x: hidden;
padding: 80px 20px 40px;
@@ -208,6 +208,7 @@ body::before {
z-index: 1;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
+ height: calc(100vh - 75px);
}
.message-container {
margin-bottom: 24px;
@@ -322,7 +323,7 @@ footer {
z-index: 100;
transition: all 0.3s;
backdrop-filter: blur(20px);
- height: 90px;
+ height: 75px;
}
.suggestions-container {
display: flex;
@@ -424,7 +425,7 @@ footer {
}
.scroll-to-bottom {
position: fixed;
- bottom: 80px;
+ bottom: 85px;
right: 20px;
width: 40px;
height: 40px;
@@ -476,37 +477,6 @@ footer {
color: var(--bg);
transform: translateY(-2px);
}
-.context-indicator {
- position: fixed;
- bottom: 130px;
- right: 20px;
- width: 120px;
- border-radius: 12px;
- padding: 10px;
- font-size: 10px;
- text-align: center;
- z-index: 90;
- background: var(--bg);
- border: 1px solid var(--border);
- display: none;
- backdrop-filter: blur(10px);
-}
-.context-indicator.visible {
- display: block;
-}
-.context-progress {
- height: 3px;
- background: var(--glass);
- border-radius: 2px;
- margin-top: 6px;
- overflow: hidden;
-}
-.context-progress-bar {
- height: 100%;
- background: var(--accent);
- border-radius: 2px;
- transition: width 0.3s;
-}
.connection-status {
position: fixed;
top: 20px;
@@ -706,7 +676,8 @@ footer {
#messages {
padding: 80px 16px 40px;
top: 0;
- bottom: 100px;
+ bottom: 75px;
+ height: calc(100vh - 75px);
}
.float-menu {
left: 12px;
@@ -721,12 +692,7 @@ footer {
.scroll-to-bottom {
width: 36px;
height: 36px;
- bottom: 70px;
+ bottom: 85px;
right: 12px;
}
- .context-indicator {
- bottom: 120px;
- right: 12px;
- width: 100px;
- }
}
diff --git a/web/desktop/chat/chat.html b/web/desktop/chat/chat.html
index bfea4a1f9..43899e9c3 100644
--- a/web/desktop/chat/chat.html
+++ b/web/desktop/chat/chat.html
@@ -16,15 +16,4 @@
-
- 0%
-
-
diff --git a/web/desktop/chat/chat.js b/web/desktop/chat/chat.js
index 8861f0030..b54e5c69f 100644
--- a/web/desktop/chat/chat.js
+++ b/web/desktop/chat/chat.js
@@ -55,9 +55,6 @@ function chatApp() {
sidebar,
themeBtn,
scrollToBottomBtn,
- contextIndicator,
- contextPercentage,
- contextProgressBar,
sidebarTitle;
marked.setOptions({ breaks: true, gfm: true });
@@ -194,9 +191,6 @@ function chatApp() {
sidebar = document.getElementById("sidebar");
themeBtn = document.getElementById("themeBtn");
scrollToBottomBtn = document.getElementById("scrollToBottom");
- contextIndicator = document.getElementById("contextIndicator");
- contextPercentage = document.getElementById("contextPercentage");
- contextProgressBar = document.getElementById("contextProgressBar");
sidebarTitle = document.getElementById("sidebarTitle");
// Theme initialization and focus
@@ -250,14 +244,6 @@ function chatApp() {
});
},
- updateContextUsage(u) {
- contextUsage = u;
- const p = Math.min(100, Math.round(u * 100));
- contextPercentage.textContent = `${p}%`;
- contextProgressBar.style.width = `${p}%`;
- contextIndicator.classList.remove("visible");
- },
-
flashScreen() {
gsap.to(flashOverlay, {
opacity: 0.15,
@@ -355,7 +341,6 @@ function chatApp() {
this.loadSessions();
messagesDiv.innerHTML = "";
this.clearSuggestions();
- this.updateContextUsage(0);
if (isVoiceMode) {
await this.stopVoiceSession();
isVoiceMode = false;
@@ -469,9 +454,6 @@ function chatApp() {
return;
}
- if (r.context_usage !== undefined) {
- this.updateContextUsage(r.context_usage);
- }
if (r.suggestions && r.suggestions.length > 0) {
this.handleSuggestions(r.suggestions);
}
@@ -516,7 +498,7 @@ function chatApp() {
this.showWarning(d.message);
break;
case "context_usage":
- this.updateContextUsage(d.usage);
+ // Context usage removed
break;
case "change_theme":
if (d.color1) themeColor1 = d.color1;
@@ -632,10 +614,8 @@ function chatApp() {
m.className = "message-container";
if (role === "user") {
m.innerHTML = `${this.escapeHtml(content)}
`;
- this.updateContextUsage(contextUsage + 0.05);
} else if (role === "assistant") {
m.innerHTML = `${streaming ? "" : marked.parse(content)}
`;
- this.updateContextUsage(contextUsage + 0.03);
} else if (role === "voice") {
m.innerHTML = ``;
} else {
@@ -728,10 +708,6 @@ function chatApp() {
ws.send(JSON.stringify(s));
});
await pendingContextChange;
- const x = document.getElementById("contextIndicator");
- if (x) {
- document.getElementById("contextPercentage").textContent = c;
- }
} else {
console.warn("WebSocket não está conectado. Tentando reconectar...");
this.connectWebSocket();
@@ -948,7 +924,6 @@ function chatApp() {
toggleSidebar: toggleSidebar,
toggleTheme: toggleTheme,
applyTheme: applyTheme,
- updateContextUsage: updateContextUsage,
flashScreen: flashScreen,
updateConnectionStatus: updateConnectionStatus,
getWebSocketUrl: getWebSocketUrl,
@@ -979,6 +954,16 @@ function chatApp() {
startVoiceRecording: startVoiceRecording,
simulateVoiceTranscription: simulateVoiceTranscription,
scrollToBottom: scrollToBottom,
+ cleanup: function () {
+ // Cleanup WebSocket connection
+ if (ws) {
+ ws.close();
+ ws = null;
+ }
+ // Clear any pending timeouts/intervals
+ isConnecting = false;
+ isInitialized = false;
+ },
};
// Cache and return the singleton instance
@@ -988,3 +973,14 @@ function chatApp() {
// Initialize the app
chatApp().init();
+
+// Listen for section changes to cleanup when leaving chat
+document.addEventListener("section-hidden", function (e) {
+ if (
+ e.target.id === "section-chat" &&
+ chatAppInstance &&
+ chatAppInstance.cleanup
+ ) {
+ chatAppInstance.cleanup();
+ }
+});
diff --git a/web/desktop/index.html b/web/desktop/index.html
index f4a996437..c211e9d6b 100644
--- a/web/desktop/index.html
+++ b/web/desktop/index.html
@@ -51,9 +51,8 @@
.logo-wrapper {
display: flex;
align-items: center;
- gap: 12px;
cursor: pointer;
- padding: 8px 12px;
+ padding: 8px;
border-radius: 12px;
transition: all 0.3s;
background: rgba(255, 255, 255, 0.9);
@@ -67,8 +66,8 @@
}
.logo-icon {
- width: 32px;
- height: 32px;
+ width: 36px;
+ height: 36px;
background: url("https://pragmatismo.com.br/icons/general-bots.svg")
center/contain no-repeat;
}
@@ -298,7 +297,6 @@
@@ -344,18 +342,6 @@
✉
Mail
-
- 📅
- Calendar
-
-
- 📝
- Notes
-
diff --git a/web/desktop/js/layout.js b/web/desktop/js/layout.js
index 60cdf969a..de7342ee4 100644
--- a/web/desktop/js/layout.js
+++ b/web/desktop/js/layout.js
@@ -34,6 +34,20 @@ async function loadSectionHTML(path) {
async function switchSection(section) {
const mainContent = document.getElementById("main-content");
+ // Validate section exists
+ if (!sections[section]) {
+ console.warn(`Section "${section}" does not exist, defaulting to chat`);
+ section = "chat";
+ }
+
+ // Clean up any existing WebSocket connections from chat
+ if (
+ window.chatAppInstance &&
+ typeof window.chatAppInstance.cleanup === "function"
+ ) {
+ window.chatAppInstance.cleanup();
+ }
+
try {
const htmlPath = sections[section];
console.log("Loading section:", section, "from", htmlPath);
@@ -78,6 +92,11 @@ async function switchSection(section) {
});
targetDiv.style.display = "block";
} else {
+ // Remove any existing loading divs first
+ container.querySelectorAll(".loading").forEach((div) => {
+ div.remove();
+ });
+
// Show loading placeholder inside the container
const loadingDiv = document.createElement("div");
loadingDiv.className = "loading";
@@ -95,15 +114,22 @@ async function switchSection(section) {
// Hide any existing sections
container.querySelectorAll(".section").forEach((div) => {
div.style.display = "none";
+ // Dispatch a custom event to notify sections they're being hidden
+ div.dispatchEvent(new CustomEvent("section-hidden"));
});
- // Remove loading placeholder
- container.removeChild(loadingDiv);
+ // Remove loading placeholder if it still exists
+ if (loadingDiv && loadingDiv.parentNode) {
+ container.removeChild(loadingDiv);
+ }
// Add the new section to the container and cache it
container.appendChild(wrapper);
sectionCache[section] = wrapper;
+ // Dispatch a custom event to notify the section it's being shown
+ wrapper.dispatchEvent(new CustomEvent("section-shown"));
+
// Ensure the new section is visible with a fast GSAP fade-in
gsap.fromTo(
wrapper,
@@ -178,13 +204,27 @@ function getInitialSection() {
window.addEventListener("DOMContentLoaded", () => {
// Small delay to ensure all resources are loaded
setTimeout(() => {
- switchSection(getInitialSection());
+ const section = getInitialSection();
+ // Ensure valid section
+ if (!sections[section]) {
+ window.location.hash = "#chat";
+ switchSection("chat");
+ } else {
+ switchSection(section);
+ }
}, 50);
});
// Handle browser back/forward navigation
window.addEventListener("popstate", () => {
- switchSection(getInitialSection());
+ const section = getInitialSection();
+ // Ensure valid section
+ if (!sections[section]) {
+ window.location.hash = "#chat";
+ switchSection("chat");
+ } else {
+ switchSection(section);
+ }
});
// Make switchSection globally accessible