From 48d773c0b3beb3cccbf26b8a19cad037636de686 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Mon, 2 Mar 2026 18:05:47 -0300 Subject: [PATCH] Fix window maximization and suggestion button visibility issues - Fix window title hidden behind minibar when maximized - Adjust window top position to account for 28px minibar height - Adjust window height calculation to account for minibar - Set maximized window z-index to 9998 (below minibar's 9999) - Remove transparency from chat window - Change window-header background from rgba to solid white - Remove backdrop-filter blur effect - Add explicit opacity rules to prevent transparency - Hide background content when window is maximized - Add 'window-maximized' class to body when window is maximized - Add CSS rules to hide sidebar, desktop icons, and other background elements - Hide initial suggestion buttons when dynamic suggestions displayed - Add 'has-suggestions' class to footer when suggestions are rendered - Add CSS rule to hide .quick-action-chip buttons when footer has 'has-suggestions' class - Update CSS version parameter to force cache reload --- ui/suite/chat/chat.css | 36 +++-- ui/suite/chat/chat.html | 289 +++++++++++++++++++++++++--------- ui/suite/css/desktop.css | 253 ++++++++++++++++++++--------- ui/suite/js/window-manager.js | 16 +- 4 files changed, 431 insertions(+), 163 deletions(-) diff --git a/ui/suite/chat/chat.css b/ui/suite/chat/chat.css index 4fd66f7..c650da3 100644 --- a/ui/suite/chat/chat.css +++ b/ui/suite/chat/chat.css @@ -84,21 +84,21 @@ .chat-layout { display: flex; flex-direction: column; - position: absolute; + position: fixed; top: 0; left: 0; right: 0; bottom: 0; - max-width: 800px; - margin: 0 auto; - padding: 80px 20px 20px 20px; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; box-sizing: border-box; } /* Connection Status - use shared styles from app.css */ @keyframes pulse { - 0%, 100% { opacity: 1; @@ -116,7 +116,7 @@ flex: 1; overflow-y: auto; overflow-x: hidden; - padding: 20px 0; + padding: 16px; display: flex; flex-direction: column; gap: 16px; @@ -305,7 +305,7 @@ /* Footer */ footer { - padding: 16px 0; + padding: 16px 20px; border-top: 1px solid var(--border, var(--border-color, #2a2a2a)); background: transparent; position: relative; @@ -605,6 +605,11 @@ footer { box-shadow: 0 2px 6px rgba(59, 130, 246, 0.2); } +/* Hide initial suggestion buttons when dynamic suggestions are displayed */ +footer.has-suggestions .quick-action-chip { + display: none !important; +} + /* Input Container */ .input-container, form.input-container { @@ -620,7 +625,12 @@ form.input-container { .input-container:focus-within { border-color: var(--chat-color1, var(--accent, #3b82f6)); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--chat-color1, var(--accent, #3b82f6)) 20%, transparent); + box-shadow: 0 0 0 3px + color-mix( + in srgb, + var(--chat-color1, var(--accent, #3b82f6)) 20%, + transparent + ); } #messageInput { @@ -641,7 +651,6 @@ form.input-container { } @keyframes cursor-blink { - 0%, 50% { caret-color: var(--accent, #3b82f6); @@ -761,7 +770,7 @@ form.input-container { /* Responsive */ @media (max-width: 768px) { .chat-layout { - padding: 0 12px; + padding: 0; } .message-content { @@ -1280,7 +1289,6 @@ form.input-container { } @keyframes thinkingBounce { - 0%, 80%, 100% { @@ -1361,7 +1369,7 @@ form.input-container { } .message.bot .message-content::before { - content: ''; + content: ""; position: absolute; inset: -2px; background: var(--accent-glow, rgba(59, 130, 246, 0.1)); @@ -1410,7 +1418,6 @@ form.input-container { } @keyframes bounce { - 0%, 100% { transform: translateX(-50%) translateY(0); @@ -1449,7 +1456,6 @@ form.input-container { } @keyframes typing { - 0%, 60%, 100% { @@ -1459,4 +1465,4 @@ form.input-container { 30% { transform: translateY(-8px); } -} \ No newline at end of file +} diff --git a/ui/suite/chat/chat.html b/ui/suite/chat/chat.html index c9a42df..0a15809 100644 --- a/ui/suite/chat/chat.html +++ b/ui/suite/chat/chat.html @@ -1,9 +1,13 @@ - +
- @@ -178,7 +212,9 @@ if (!messages || !scrollBtn) return; var isNearBottom = - messages.scrollHeight - messages.scrollTop - messages.clientHeight < + messages.scrollHeight - + messages.scrollTop - + messages.clientHeight < 100; if (isNearBottom) { @@ -355,9 +391,9 @@ function fetchEntityDetails(type, name) { return fetch( "/api/search/entity?type=" + - encodeURIComponent(type) + - "&name=" + - encodeURIComponent(name), + encodeURIComponent(type) + + "&name=" + + encodeURIComponent(name), ) .then(function (r) { return r.json(); @@ -443,9 +479,9 @@ function fetchEntitiesOfType(type, searchTerm) { fetch( "/api/search/entities?type=" + - encodeURIComponent(type) + - "&q=" + - encodeURIComponent(searchTerm || ""), + encodeURIComponent(type) + + "&q=" + + encodeURIComponent(searchTerm || ""), ) .then(function (r) { return r.json(); @@ -505,8 +541,8 @@ var subtitle = item.subtitle ? '' + - escapeHtml(item.subtitle) + - "" + escapeHtml(item.subtitle) + + "" : ""; var hint = item.isTypeHint ? 'Type : to search' @@ -727,7 +763,11 @@ isStreaming = false; // Render suggestions when message is complete - if (data.suggestions && Array.isArray(data.suggestions) && data.suggestions.length > 0) { + if ( + data.suggestions && + Array.isArray(data.suggestions) && + data.suggestions.length > 0 + ) { renderSuggestions(data.suggestions); } } else { @@ -755,11 +795,23 @@ return; } + // Get the footer element + var footer = suggestionsEl.closest("footer"); + // Clear existing suggestions suggestionsEl.innerHTML = ""; console.log("Rendering " + suggestions.length + " suggestions"); + // Add or remove CSS class based on whether suggestions are displayed + if (footer) { + if (suggestions.length > 0) { + footer.classList.add("has-suggestions"); + } else { + footer.classList.remove("has-suggestions"); + } + } + suggestions.forEach(function (suggestion) { var chip = document.createElement("button"); chip.className = "suggestion-chip"; @@ -772,9 +824,10 @@ // Check if there's an action to parse if (sugg.action) { try { - var action = typeof sugg.action === "string" - ? JSON.parse(sugg.action) - : sugg.action; + var action = + typeof sugg.action === "string" + ? JSON.parse(sugg.action) + : sugg.action; console.log("Parsed action:", action); @@ -783,14 +836,20 @@ // The backend will recognize this as a tool request window.sendMessage(sugg.text); } else if (action.type === "send_message") { - window.sendMessage(action.message || sugg.text); + window.sendMessage( + action.message || sugg.text, + ); } else if (action.type === "select_context") { window.sendMessage(action.context); } else { window.sendMessage(sugg.text); } } catch (e) { - console.error("Failed to parse action:", e, "falling back to text"); + console.error( + "Failed to parse action:", + e, + "falling back to text", + ); window.sendMessage(sugg.text); } } else { @@ -852,7 +911,7 @@ currentBotId: currentBotId, currentUserId: currentUserId, currentSessionId: currentSessionId, - currentBotName: currentBotName + currentBotName: currentBotName, }; }; @@ -876,7 +935,7 @@ ws = new WebSocket(url); // Add connection timeout to detect silent failures - var connectionTimeout = setTimeout(function() { + var connectionTimeout = setTimeout(function () { if (ws.readyState !== WebSocket.OPEN) { console.error("WebSocket connection timeout"); ws.close(); @@ -947,8 +1006,13 @@ updateConnectionStatus("connecting"); setTimeout(connectWebSocket, 1000 * reconnectAttempts); } else { - console.error("Max reconnection attempts reached. Stopping reconnection."); - notify("Could not reconnect to chat server after multiple attempts", "error"); + console.error( + "Max reconnection attempts reached. Stopping reconnection.", + ); + notify( + "Could not reconnect to chat server after multiple attempts", + "error", + ); } }; @@ -960,8 +1024,8 @@ // Apply theme data from WebSocket events function getContrastYIQ(hexcolor) { - if (!hexcolor) return '#ffffff'; - + if (!hexcolor) return "#ffffff"; + // Handle named colors and variables by letting the browser resolve them var temp = document.createElement("div"); temp.style.color = hexcolor; @@ -969,16 +1033,16 @@ document.body.appendChild(temp); var style = window.getComputedStyle(temp).color; document.body.removeChild(temp); - + var rgb = style.match(/\d+/g); - if (!rgb || rgb.length < 3) return '#ffffff'; - + if (!rgb || rgb.length < 3) return "#ffffff"; + var r = parseInt(rgb[0]); var g = parseInt(rgb[1]); var b = parseInt(rgb[2]); - - var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; - return (yiq >= 128) ? '#000000' : '#ffffff'; + + var yiq = (r * 299 + g * 587 + b * 114) / 1000; + return yiq >= 128 ? "#000000" : "#ffffff"; } function applyThemeData(themeData) { @@ -987,13 +1051,23 @@ var color1 = themeData.color1 || themeData.data?.color1 || "black"; var color2 = themeData.color2 || themeData.data?.color2 || "white"; var logo = themeData.logo_url || themeData.data?.logo_url || ""; - var title = themeData.title || themeData.data?.title || window.__INITIAL_BOT_NAME__ || "Chat"; + var title = + themeData.title || + themeData.data?.title || + window.__INITIAL_BOT_NAME__ || + "Chat"; // Set CSS variables for colors on document element document.documentElement.style.setProperty("--chat-color1", color1); document.documentElement.style.setProperty("--chat-color2", color2); - document.documentElement.style.setProperty("--suggestion-color", color1); - document.documentElement.style.setProperty("--suggestion-bg", color2); + document.documentElement.style.setProperty( + "--suggestion-color", + color1, + ); + document.documentElement.style.setProperty( + "--suggestion-bg", + color2, + ); // Also set on root for better cascading document.documentElement.style.setProperty("--color1", color1); @@ -1003,10 +1077,21 @@ document.documentElement.style.setProperty("--primary", color1); document.documentElement.style.setProperty("--accent", color1); - document.documentElement.style.setProperty("--chat-fg1", getContrastYIQ(color1)); - document.documentElement.style.setProperty("--chat-fg2", getContrastYIQ(color2)); + document.documentElement.style.setProperty( + "--chat-fg1", + getContrastYIQ(color1), + ); + document.documentElement.style.setProperty( + "--chat-fg2", + getContrastYIQ(color2), + ); - console.log("Theme applied:", { color1: color1, color2: color2, logo: logo, title: title }); + console.log("Theme applied:", { + color1: color1, + color2: color2, + logo: logo, + title: title, + }); } // Load bot config and apply colors/logo @@ -1023,47 +1108,98 @@ // Get the theme manager's theme for this bot to check if user selected a different theme var botId = botName.toLowerCase(); var botThemeKey = "gb-theme-" + botId; - var botTheme = window.ThemeManager ? ( - // Get bot-specific theme from theme manager's mapping - (window.ThemeManager.getAvailableThemes && - window.ThemeManager.getAvailableThemes().find(t => t.id === botId)) || - // Fallback to localStorage - localStorage.getItem(botThemeKey) - ) : localStorage.getItem(botThemeKey); + var botTheme = window.ThemeManager + ? // Get bot-specific theme from theme manager's mapping + (window.ThemeManager.getAvailableThemes && + window.ThemeManager.getAvailableThemes().find( + (t) => t.id === botId, + )) || + // Fallback to localStorage + localStorage.getItem(botThemeKey) + : localStorage.getItem(botThemeKey); // Check if bot config has a theme-base setting - var configThemeBase = config.theme_base || config["theme-base"] || "light"; + var configThemeBase = + config.theme_base || config["theme-base"] || "light"; // Only use bot config colors if: // 1. No theme has been explicitly selected by user (localStorage empty or default) // 2. AND the bot config's theme-base matches the current theme var localStorageTheme = localStorage.getItem(botThemeKey); - var useBotConfigColors = !localStorageTheme || + var useBotConfigColors = + !localStorageTheme || localStorageTheme === "default" || localStorageTheme === configThemeBase; // Apply colors from config (API returns snake_case) - var color1 = config.theme_color1 || config["theme-color1"] || config["Theme Color"] || "#3b82f6"; - var color2 = config.theme_color2 || config["theme-color2"] || "#f5deb3"; - var title = config.theme_title || config["theme-title"] || botName; + var color1 = + config.theme_color1 || + config["theme-color1"] || + config["Theme Color"] || + "#3b82f6"; + var color2 = + config.theme_color2 || + config["theme-color2"] || + "#f5deb3"; + var title = + config.theme_title || config["theme-title"] || botName; var logo = config.theme_logo || config["theme-logo"] || ""; // Only set bot config colors if user hasn't selected a different theme if (useBotConfigColors) { - document.documentElement.setAttribute("data-has-bot-colors", "true"); - document.documentElement.style.setProperty("--chat-color1", color1); - document.documentElement.style.setProperty("--chat-color2", color2); - document.documentElement.style.setProperty("--suggestion-color", color1); - document.documentElement.style.setProperty("--suggestion-bg", color2); - document.documentElement.style.setProperty("--color1", color1); - document.documentElement.style.setProperty("--color2", color2); - document.documentElement.style.setProperty("--primary", color1); - document.documentElement.style.setProperty("--accent", color1); - document.documentElement.style.setProperty("--chat-fg1", getContrastYIQ(color1)); - document.documentElement.style.setProperty("--chat-fg2", getContrastYIQ(color2)); - console.log("Bot config colors applied:", { color1: color1, color2: color2 }); + document.documentElement.setAttribute( + "data-has-bot-colors", + "true", + ); + document.documentElement.style.setProperty( + "--chat-color1", + color1, + ); + document.documentElement.style.setProperty( + "--chat-color2", + color2, + ); + document.documentElement.style.setProperty( + "--suggestion-color", + color1, + ); + document.documentElement.style.setProperty( + "--suggestion-bg", + color2, + ); + document.documentElement.style.setProperty( + "--color1", + color1, + ); + document.documentElement.style.setProperty( + "--color2", + color2, + ); + document.documentElement.style.setProperty( + "--primary", + color1, + ); + document.documentElement.style.setProperty( + "--accent", + color1, + ); + document.documentElement.style.setProperty( + "--chat-fg1", + getContrastYIQ(color1), + ); + document.documentElement.style.setProperty( + "--chat-fg2", + getContrastYIQ(color2), + ); + console.log("Bot config colors applied:", { + color1: color1, + color2: color2, + }); } else { - console.log("Bot config colors skipped - user selected custom theme:", localStorageTheme); + console.log( + "Bot config colors skipped - user selected custom theme:", + localStorageTheme, + ); } // Update logo if provided @@ -1081,7 +1217,12 @@ } } - console.log("Bot config loaded:", { color1: color1, color2: color2, title: title, logo: logo }); + console.log("Bot config loaded:", { + color1: color1, + color2: color2, + title: title, + logo: logo, + }); }) .catch(function (e) { console.log("Could not load bot config:", e); @@ -1117,8 +1258,12 @@ .catch(function (e) { console.error("Auth failed:", e); // Proceed with anonymous connection - WebSocket handler supports it - currentUserId = crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(); - currentSessionId = crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(); + currentUserId = crypto.randomUUID + ? crypto.randomUUID() + : Date.now().toString(); + currentSessionId = crypto.randomUUID + ? crypto.randomUUID() + : Date.now().toString(); currentBotId = botName; currentBotName = botName; console.log("Anonymous chat:", { @@ -1205,4 +1350,4 @@ console.log("Chat module initialized with @ mentions support"); })(); - \ No newline at end of file + diff --git a/ui/suite/css/desktop.css b/ui/suite/css/desktop.css index 1b8aa1c..3b7a1f5 100644 --- a/ui/suite/css/desktop.css +++ b/ui/suite/css/desktop.css @@ -1,36 +1,37 @@ - - .app-icon { - background: var(--surface-hover, #ffffff); - border: 1px solid var(--border, #e5e7eb); - box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.05); - /* removed border */ - /* removed border bottom */ - } - - .workspace-bg { - background-color: var(--bg, #fafdfa); - } - - .workspace-grid { - background-image: linear-gradient(to right, var(--border, #f0fdf4) 1px, transparent 1px), linear-gradient(to bottom, var(--border, #f0fdf4) 1px, transparent 1px); - background-size: 40px 40px; - } - - /* Custom scrollbar for terminal */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - ::-webkit-scrollbar-track { - background: transparent; - } - ::-webkit-scrollbar-thumb { - background: var(--surface-active, #333); - border-radius: 4px; - } - ::-webkit-scrollbar-thumb:hover { - background: var(--border-hover, #555); - } +.app-icon { + background: var(--surface-hover, #ffffff); + border: 1px solid var(--border, #e5e7eb); + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.05); + /* removed border */ + /* removed border bottom */ +} + +.workspace-bg { + background-color: var(--bg, #fafdfa); +} + +.workspace-grid { + background-image: + linear-gradient(to right, var(--border, #f0fdf4) 1px, transparent 1px), + linear-gradient(to bottom, var(--border, #f0fdf4) 1px, transparent 1px); + background-size: 40px 40px; +} + +/* Custom scrollbar for terminal */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--surface-active, #333); + border-radius: 4px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--border-hover, #555); +} /* Window Manager Core Styles (replacing missing Tailwind classes) */ .window-element { @@ -49,8 +50,7 @@ .window-header { height: 40px; - background-color: var(--surface, rgba(255, 255, 255, 0.95)); - backdrop-filter: blur(4px); + background-color: var(--surface, #ffffff); display: flex; align-items: center; justify-content: space-between; @@ -58,10 +58,12 @@ border-bottom: 1px solid var(--border, #e5e7eb); user-select: none; cursor: move; + position: relative; + z-index: 10; } .window-header .font-mono { - font-family: 'Fira Code', monospace; + font-family: "Fira Code", monospace; font-size: 12px; font-weight: 700; color: var(--accent, #16a34a); /* brand-600 */ @@ -99,47 +101,117 @@ padding: 16px; } +.w-\[700px\] { + width: 700px; +} +.h-\[500px\] { + height: 500px; +} +.bg-white { + background-color: #fff; +} +.rounded-lg { + border-radius: 0.5rem; +} +.shadow-2xl { + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); +} +.flex { + display: flex; +} +.flex-col { + flex-direction: column; +} +.border { + border-width: 1px; + border-style: solid; +} +.border-gray-200 { + border-color: #e5e7eb; +} +.overflow-hidden { + overflow: hidden; +} +.absolute { + position: absolute; +} -.w-\[700px\] { width: 700px; } -.h-\[500px\] { height: 500px; } -.bg-white { background-color: #fff; } -.rounded-lg { border-radius: 0.5rem; } -.shadow-2xl { box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); } -.flex { display: flex; } -.flex-col { flex-direction: column; } -.border { border-width: 1px; border-style: solid; } -.border-gray-200 { border-color: #e5e7eb; } -.overflow-hidden { overflow: hidden; } -.absolute { position: absolute; } - - -.h-10 { height: 2.5rem; } -.bg-white\/95 { background-color: var(--surface, rgba(255, 255, 255, 0.95)); } -.backdrop-blur { backdrop-filter: blur(8px); } -.items-center { align-items: center; } -.justify-between { justify-content: space-between; } -.px-4 { padding-left: 1rem; padding-right: 1rem; } -.border-b { border-bottom-width: 1px; } -.select-none { user-select: none; } -.cursor-move { cursor: move; } -.font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } -.text-xs { font-size: 0.75rem; line-height: 1rem; } -.font-bold { font-weight: 700; } -.text-brand-600 { color: var(--accent, #84d669); } -.tracking-wide { letter-spacing: 0.025em; } -.space-x-3 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.75rem * var(--tw-space-x-reverse)); margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); } -.text-gray-400 { color: var(--text-muted, #9ca3af); } -.hover\:text-gray-600:hover { color: var(--text, #4b5563); } -.hover\:text-red-500:hover { color: var(--error, #ef4444); } -.relative { position: relative; } -.flex-1 { flex: 1 1 0%; } -.overflow-y-auto { overflow-y: auto; } -.bg-\[\#fafdfa\] { background-color: var(--bg, #fafdfa); } - +.h-10 { + height: 2.5rem; +} +.bg-white\/95 { + background-color: var(--surface, rgba(255, 255, 255, 0.95)); +} +.backdrop-blur { + backdrop-filter: blur(8px); +} +.items-center { + align-items: center; +} +.justify-between { + justify-content: space-between; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.border-b { + border-bottom-width: 1px; +} +.select-none { + user-select: none; +} +.cursor-move { + cursor: move; +} +.font-mono { + font-family: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} +.font-bold { + font-weight: 700; +} +.text-brand-600 { + color: var(--accent, #84d669); +} +.tracking-wide { + letter-spacing: 0.025em; +} +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} +.text-gray-400 { + color: var(--text-muted, #9ca3af); +} +.hover\:text-gray-600:hover { + color: var(--text, #4b5563); +} +.hover\:text-red-500:hover { + color: var(--error, #ef4444); +} +.relative { + position: relative; +} +.flex-1 { + flex: 1 1 0%; +} +.overflow-y-auto { + overflow-y: auto; +} +.bg-\[\#fafdfa\] { + background-color: var(--bg, #fafdfa); +} .window-header { height: 40px; - background-color: var(--surface, rgba(255, 255, 255, 0.95)); + background-color: var(--surface, #ffffff); border-bottom: 1px solid var(--border, #e5e7eb); display: flex; align-items: center; @@ -147,9 +219,11 @@ padding: 0 16px; user-select: none; cursor: move; + position: relative; + z-index: 10; } .window-header .font-mono { - font-family: 'Fira Code', monospace; + font-family: "Fira Code", monospace; font-size: 12px; font-weight: 700; color: var(--accent, #16a34a); @@ -178,3 +252,34 @@ position: relative; background-color: var(--bg, #ffffff); } + +/* Hide background content when window is maximized */ +body.window-maximized .sidebar, +body.window-maximized .sidebar-item, +body.window-maximized .desktop-icon, +body.window-maximized .panel-grid, +body.window-maximized .workspace-grid, +body.window-maximized .bg-grid, +body.window-maximized .bg-svg { + display: none !important; + visibility: hidden !important; + opacity: 0 !important; +} + +/* Ensure maximized window is below minibar (28px height) */ +body.window-maximized .window-element { + z-index: 9998 !important; /* Lower than minibar's 9999 */ + top: 28px !important; /* Below the minibar */ +} + +/* Ensure window body is opaque */ +.window-body { + background-color: var(--bg, #fafdfa) !important; + opacity: 1 !important; +} + +/* Ensure window element is fully opaque */ +.window-element { + background-color: var(--surface, #ffffff) !important; + opacity: 1 !important; +} diff --git a/ui/suite/js/window-manager.js b/ui/suite/js/window-manager.js index f752483..d3f1192 100644 --- a/ui/suite/js/window-manager.js +++ b/ui/suite/js/window-manager.js @@ -196,6 +196,12 @@ if (typeof window.WindowManager === "undefined") { windowEl.style.top = windowObj.previousState.top; windowEl.style.left = windowObj.previousState.left; windowObj.isMaximized = false; + + // Check if any other windows are still maximized + const anyMaximized = this.openWindows.some((w) => w.isMaximized); + if (!anyMaximized) { + document.body.classList.remove("window-maximized"); + } } else { // Maximize windowObj.previousState = { @@ -210,11 +216,17 @@ if (typeof window.WindowManager === "undefined") { ? document.getElementById("taskbar").offsetHeight : 0; + // Adjust for minibar height (fixed 28px at top) + const minibarHeight = 28; + windowEl.style.width = "100%"; - windowEl.style.height = `calc(100% - ${taskbarHeight}px)`; - windowEl.style.top = "0px"; + windowEl.style.height = `calc(100% - ${taskbarHeight}px - ${minibarHeight}px)`; + windowEl.style.top = `${minibarHeight}px`; windowEl.style.left = "0px"; windowObj.isMaximized = true; + + // Add CSS class to body to hide background content + document.body.classList.add("window-maximized"); } this.focus(id); }