Fix window maximization and suggestion button visibility issues
All checks were successful
BotUI CI / build (push) Successful in 2m43s
All checks were successful
BotUI CI / build (push) Successful in 2m43s
- 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
This commit is contained in:
parent
3e81991e8b
commit
48d773c0b3
4 changed files with 431 additions and 163 deletions
|
|
@ -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% {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
<link rel="stylesheet" href="/suite/chat/chat.css?v=3" />
|
||||
<link rel="stylesheet" href="/suite/chat/chat.css?v=4" />
|
||||
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
|
||||
|
||||
<div class="chat-layout" id="chat-app">
|
||||
<!-- Connection Status -->
|
||||
<div class="connection-status connecting" id="connectionStatus" style="display: none;">
|
||||
<div
|
||||
class="connection-status connecting"
|
||||
id="connectionStatus"
|
||||
style="display: none"
|
||||
>
|
||||
<span class="connection-status-dot"></span>
|
||||
<span class="connection-text">Connecting...</span>
|
||||
</div>
|
||||
|
|
@ -14,21 +18,47 @@
|
|||
<div class="suggestions-container" id="suggestions"></div>
|
||||
<div class="mention-dropdown" id="mentionDropdown">
|
||||
<div class="mention-header">
|
||||
<span class="mention-title" data-i18n="chat-mention-title">Reference Entity</span>
|
||||
<span class="mention-title" data-i18n="chat-mention-title"
|
||||
>Reference Entity</span
|
||||
>
|
||||
</div>
|
||||
<div class="mention-results" id="mentionResults"></div>
|
||||
</div>
|
||||
<form class="input-container" id="chatForm">
|
||||
<input name="content" id="messageInput" type="text" placeholder="Message... (type @ to mention)"
|
||||
data-i18n-placeholder="chat-placeholder" autofocus autocomplete="off" />
|
||||
<button type="submit" id="sendBtn" title="Send" data-i18n-title="chat-send">
|
||||
<input
|
||||
name="content"
|
||||
id="messageInput"
|
||||
type="text"
|
||||
placeholder="Message... (type @ to mention)"
|
||||
data-i18n-placeholder="chat-placeholder"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
id="sendBtn"
|
||||
title="Send"
|
||||
data-i18n-title="chat-send"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
</form>
|
||||
</footer>
|
||||
<button class="scroll-to-bottom" id="scrollToBottom" title="Scroll to bottom">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<button
|
||||
class="scroll-to-bottom"
|
||||
id="scrollToBottom"
|
||||
title="Scroll to bottom"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -42,7 +72,11 @@
|
|||
<div class="entity-card-title"></div>
|
||||
<div class="entity-card-details"></div>
|
||||
<div class="entity-card-actions">
|
||||
<button class="entity-card-btn" data-action="view" data-i18n="action-view">
|
||||
<button
|
||||
class="entity-card-btn"
|
||||
data-action="view"
|
||||
data-i18n="action-view"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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,7 +824,8 @@
|
|||
// Check if there's an action to parse
|
||||
if (sugg.action) {
|
||||
try {
|
||||
var action = typeof sugg.action === "string"
|
||||
var action =
|
||||
typeof sugg.action === "string"
|
||||
? JSON.parse(sugg.action)
|
||||
: sugg.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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -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,7 +1024,7 @@
|
|||
|
||||
// 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");
|
||||
|
|
@ -971,14 +1035,14 @@
|
|||
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
|
||||
var botTheme = window.ThemeManager
|
||||
? // Get bot-specific theme from theme manager's mapping
|
||||
(window.ThemeManager.getAvailableThemes &&
|
||||
window.ThemeManager.getAvailableThemes().find(t => t.id === botId)) ||
|
||||
window.ThemeManager.getAvailableThemes().find(
|
||||
(t) => t.id === botId,
|
||||
)) ||
|
||||
// Fallback to localStorage
|
||||
localStorage.getItem(botThemeKey)
|
||||
) : 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:", {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
.app-icon {
|
||||
background: var(--surface-hover, #ffffff);
|
||||
border: 1px solid var(--border, #e5e7eb);
|
||||
|
|
@ -12,7 +11,9 @@
|
|||
}
|
||||
|
||||
.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-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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue