Compare commits

..

3 commits

Author SHA1 Message Date
61d90da409 feat: add dynamic window title updates
All checks were successful
BotUI CI / build (push) Successful in 2m48s
- Window title now shows current window name followed by 'General Bots'
- Title format: '{Window Name} - General Bots' (e.g., 'Chat - General Bots')
- Resets to 'General Bots Desktop' when all windows are closed
- Updated default title from 'BUILD V3 - Web Desktop Environment' to 'General Bots Desktop'
- Added cache-busting parameter to window-manager.js to ensure browser reloads updated code
2026-03-03 09:33:38 -03:00
84b7cb63f9 fix: Change desktop title from 'Agent Farm' to 'General Bots'
- Update minibar brand title to use product name from .product configuration
- Replace hardcoded 'Agent Farm' text with 'General Bots'
- Improves brand consistency across the UI
2026-03-03 08:42:10 -03:00
e2f1efef8c Fix window transparency and theme dropdown background issues
- Fixed chat footer background to use var(--surface) CSS variable instead of hardcoded colors, ensuring proper theme adaptation
- Fixed theme dropdown background by changing from var(--input-bg) to var(--surface) in theme-sentient.css
- Added CSS override rules in desktop.css to ensure dropdown uses correct background
- Added JavaScript fix in theme-manager.js to automatically apply correct dropdown background on theme load

This resolves issues where theme dropdown and chat footer had black backgrounds in light themes.
2026-03-03 08:27:17 -03:00
7 changed files with 1249 additions and 1048 deletions

File diff suppressed because it is too large Load diff

View file

@ -283,3 +283,10 @@ body.window-maximized .window-element {
background-color: var(--surface, #ffffff) !important; background-color: var(--surface, #ffffff) !important;
opacity: 1 !important; opacity: 1 !important;
} }
/* Fix theme dropdown background - use surface color to adapt to theme */
/* This overrides hardcoded black background from other CSS files */
#themeDropdown,
select.theme-dropdown {
background: var(--surface) !important;
}

View file

@ -876,7 +876,7 @@
[data-theme="sentient"] input, [data-theme="sentient"] input,
[data-theme="sentient"] textarea, [data-theme="sentient"] textarea,
[data-theme="sentient"] select { [data-theme="sentient"] select {
background: var(--input-bg); background: var(--surface);
border: 1px solid var(--input-border); border: 1px solid var(--input-border);
color: var(--text); color: var(--text);
border-radius: 8px; border-radius: 8px;
@ -884,6 +884,12 @@
transition: all 0.2s ease; transition: all 0.2s ease;
} }
/* Ensure theme dropdown uses surface color in all themes */
[data-theme="sentient"] select.theme-dropdown,
[data-theme="sentient"] #themeDropdown {
background: var(--surface) !important;
}
[data-theme="sentient"] input:focus, [data-theme="sentient"] input:focus,
[data-theme="sentient"] textarea:focus, [data-theme="sentient"] textarea:focus,
[data-theme="sentient"] select:focus { [data-theme="sentient"] select:focus {

View file

@ -1,10 +1,9 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BUILD V3 - Web Desktop Environment</title> <title>General Bots Desktop</title>
<!-- Link to the existing compiled CSS --> <!-- Link to the existing compiled CSS -->
<link rel="stylesheet" href="/suite/css/app.css" /> <link rel="stylesheet" href="/suite/css/app.css" />
@ -14,7 +13,8 @@
<!-- Local JS requirements per AGENTS.md / UI.md --> <!-- Local JS requirements per AGENTS.md / UI.md -->
<script src="/suite/js/vendor/htmx.min.js"></script> <script src="/suite/js/vendor/htmx.min.js"></script>
<script src="/suite/js/window-manager.js"></script> <script src="/suite/js/vendor/marked.min.js"></script>
<script src="/suite/js/window-manager.js?v=2"></script>
<script src="/suite/js/theme-manager.js"></script> <script src="/suite/js/theme-manager.js"></script>
<style> <style>
@ -25,7 +25,7 @@
} }
body { body {
font-family: 'Fira Code', 'Fira Sans', Arial, sans-serif; font-family: "Fira Code", "Fira Sans", Arial, sans-serif;
background: var(--bg, #ffffff); background: var(--bg, #ffffff);
color: var(--text, #333333); color: var(--text, #333333);
overflow: hidden; overflow: hidden;
@ -83,7 +83,9 @@
height: 30px; height: 30px;
stroke: var(--text-secondary, #3b3b3b); stroke: var(--text-secondary, #3b3b3b);
opacity: 0.6; opacity: 0.6;
transition: stroke 0.15s ease, opacity 0.15s ease; transition:
stroke 0.15s ease,
opacity 0.15s ease;
} }
.sidebar-item:hover .sidebar-icon { .sidebar-item:hover .sidebar-icon {
@ -99,54 +101,7 @@
overflow: hidden; overflow: hidden;
} }
/* Top Navigation Tabs */ /* Top Navigation Tabs - Removed (already in Vibe) */
.tabs-container {
display: flex;
flex-direction: column;
background: var(--bg-secondary, #f8f8f8);
border-bottom: 1px solid var(--border-color, #f0f1f2);
z-index: 100;
}
.tabs-row {
display: flex;
height: 34px;
}
.main-tab {
height: 34px;
min-width: 169px;
flex: 1;
border-right: 1px solid var(--border-color, #f0f1f2);
display: flex;
align-items: center;
padding: 0 18px;
cursor: pointer;
transition: background 0.15s ease;
}
.main-tab:hover {
background: var(--bg, #ffffff);
}
.main-tab.active {
background: var(--primary, #84d669);
}
.main-tab.active .main-tab-content {
color: var(--bg, #ffffff);
}
.main-tab-content {
display: flex;
align-items: center;
gap: 4px;
font-family: 'Fira Code', monospace;
font-size: 14px;
font-weight: 500;
color: var(--text-secondary, #3b3b3b);
transition: color 0.15s ease;
}
/* Workspace (Where windows float) */ /* Workspace (Where windows float) */
.workspace { .workspace {
@ -220,7 +175,7 @@
} }
.desktop-icon-label { .desktop-icon-label {
font-family: 'Fira Code', monospace; font-family: "Fira Code", monospace;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: var(--text-secondary, #374151); color: var(--text-secondary, #374151);
@ -231,13 +186,13 @@
text-align: center; text-align: center;
} }
.bg-grid { display: none; } .bg-grid {
display: none;
.bg-svg { display: none !important; } }
.bg-svg {
display: none !important;
}
/* Bottom Taskbar */ /* Bottom Taskbar */
.toolbar { .toolbar {
@ -260,7 +215,7 @@
} }
.toolbar-time { .toolbar-time {
font-family: 'Fira Code', monospace; font-family: "Fira Code", monospace;
font-size: 14px; font-size: 14px;
color: var(--text-secondary, #3b3b3b); color: var(--text-secondary, #3b3b3b);
text-align: right; text-align: right;
@ -287,138 +242,211 @@
.taskbar-item.active { .taskbar-item.active {
border-bottom-color: var(--primary, #84d669); border-bottom-color: var(--primary, #84d669);
background: linear-gradient(to bottom, transparent 50%, var(--primary-alpha-10, rgba(132, 214, 105, 0.1)) 100%); background: linear-gradient(
to bottom,
transparent 50%,
var(--primary-alpha-10, rgba(132, 214, 105, 0.1)) 100%
);
} }
</style> </style>
</head> </head>
<body> <body>
<!-- Minibar Component (Phase 8) --> <!-- Minibar Component (Phase 8) -->
<div hx-get="/suite/partials/minibar.html" hx-trigger="load" hx-swap="outerHTML"></div> <div
hx-get="/suite/partials/minibar.html"
hx-trigger="load"
hx-swap="outerHTML"
></div>
<div class="build-container"> <div class="build-container">
<!-- Left Sidebar --> <!-- Left Sidebar -->
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-item active" title="Home"> <div class="sidebar-item active" title="Home">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline> <polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg> </svg>
</div> </div>
<div class="sidebar-item" title="Search"> <div class="sidebar-item" title="Search">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle> <circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line> <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg> </svg>
</div> </div>
<div class="sidebar-item" title="Terminal"> <div class="sidebar-item" title="Terminal">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<polyline points="4 17 10 11 4 5"></polyline> <polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line> <line x1="12" y1="19" x2="20" y2="19"></line>
</svg> </svg>
</div> </div>
<div class="sidebar-item" title="User"> <div class="sidebar-item" title="User">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path> class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"
></path>
<circle cx="12" cy="7" r="4"></circle> <circle cx="12" cy="7" r="4"></circle>
</svg> </svg>
</div> </div>
<div class="sidebar-item" title="Apps"> <div class="sidebar-item" title="Apps">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="3" width="7" height="7"></rect> <rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect> <rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect> <rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect> <rect x="3" y="14" width="7" height="7"></rect>
</svg> </svg>
</div> </div>
<div class="sidebar-item" style="margin-top: auto;" title="Settings"> <div
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> class="sidebar-item"
style="margin-top: auto"
title="Settings"
>
<svg
class="sidebar-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="3"></circle> <circle cx="12" cy="12" r="3"></circle>
<path <path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"> d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
</path> ></path>
</svg> </svg>
</div> </div>
</aside> </aside>
<!-- Main Wrapper --> <!-- Main Wrapper -->
<div class="main-wrapper"> <div class="main-wrapper">
<!-- Top Navigation Tabs --> <!-- Top Navigation Tabs - Removed (already in Vibe) -->
<div class="tabs-container">
<div class="breadcrumb-row"
style="display: flex; height: 34px; border-bottom: 1px solid var(--border-color, #f0f1f2); align-items: center; padding: 0 18px; font-family: 'Fira Code', monospace; font-size: 14px; color: var(--text-muted, #6b7280); gap: 8px;">
<span style="color: var(--text-secondary, #374151);">// DASHBOARD</span>
<span style="color: var(--text-muted, #9ca3af);">></span>
<span style="color: var(--text, #374151); font-weight: 600;">// E-COMMERCE APP DEVELOPMENT</span>
</div>
<div class="tabs-row">
<div class="main-tab">
<div class="main-tab-content"><span>//</span><span>PLAN</span></div>
</div>
<div class="main-tab active">
<div class="main-tab-content"><span>//</span><span>BUILD</span></div>
</div>
<div class="main-tab">
<div class="main-tab-content"><span>//</span><span>REVIEW</span></div>
</div>
<div class="main-tab">
<div class="main-tab-content"><span>//</span><span>DEPLOY</span></div>
</div>
<div class="main-tab">
<div class="main-tab-content"><span>//</span><span>MONITOR</span></div>
</div>
</div>
</div>
<!-- Workspace container where WindowManager operates --> <!-- Workspace container where WindowManager operates -->
<div class="workspace" id="desktop-content"> <div class="workspace" id="desktop-content">
<!-- Background Pattern --> <!-- Background Pattern -->
<div class="panel-section"> <div class="panel-section">
<div class="desktop-icons-container"> <div class="desktop-icons-container">
<div
<div class="desktop-icon" data-app-id="vibe" data-app-title="Vibe" class="desktop-icon"
hx-get="/suite/partials/vibe.html" hx-swap="none"> data-app-id="vibe"
data-app-title="Vibe"
hx-get="/suite/partials/vibe.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" /> fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"
/>
</svg> </svg>
</div> </div>
<span class="desktop-icon-label">Vibe</span> <span class="desktop-icon-label">Vibe</span>
</div> </div>
<div class="desktop-icon" data-app-id="tasks" data-app-title="Tasks" <div
hx-get="/suite/tasks/task-window.html" hx-swap="none"> class="desktop-icon"
data-app-id="tasks"
data-app-title="Tasks"
hx-get="/suite/tasks/task-window.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 11l3 3L22 4" /> <path d="M9 11l3 3L22 4" />
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" /> <path
d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"
/>
</svg> </svg>
</div> </div>
<span class="desktop-icon-label">Tasks</span> <span class="desktop-icon-label">Tasks</span>
</div> </div>
<div class="desktop-icon" data-app-id="chat" data-app-title="Chat" <div
hx-get="/suite/partials/chat.html" hx-swap="none"> class="desktop-icon"
data-app-id="chat"
data-app-title="Chat"
hx-get="/suite/partials/chat.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /> fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
/>
</svg> </svg>
</div> </div>
<span class="desktop-icon-label">Chat</span> <span class="desktop-icon-label">Chat</span>
</div> </div>
<div class="desktop-icon" data-app-id="terminal" data-app-title="Terminal" <div
hx-get="/suite/terminal/terminal.html" hx-swap="none"> class="desktop-icon"
data-app-id="terminal"
data-app-title="Terminal"
hx-get="/suite/terminal/terminal.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="4 17 10 11 4 5" /> <polyline points="4 17 10 11 4 5" />
<line x1="12" y1="19" x2="20" y2="19" /> <line x1="12" y1="19" x2="20" y2="19" />
</svg> </svg>
@ -426,23 +454,46 @@
<span class="desktop-icon-label">Terminal</span> <span class="desktop-icon-label">Terminal</span>
</div> </div>
<div class="desktop-icon" data-app-id="drive" data-app-title="Explorer" <div
hx-get="/suite/drive/drive.html" hx-swap="none"> class="desktop-icon"
data-app-id="drive"
data-app-title="Explorer"
hx-get="/suite/drive/drive.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path <path
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" /> d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
/>
</svg> </svg>
</div> </div>
<span class="desktop-icon-label">Explorer</span> <span class="desktop-icon-label">Explorer</span>
</div> </div>
<div class="desktop-icon" data-app-id="editor" data-app-title="Editor" <div
hx-get="/suite/editor.html" hx-swap="none"> class="desktop-icon"
data-app-id="editor"
data-app-title="Editor"
hx-get="/suite/editor.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="16 18 22 12 16 6" /> <polyline points="16 18 22 12 16 6" />
<polyline points="8 6 2 12 8 18" /> <polyline points="8 6 2 12 8 18" />
</svg> </svg>
@ -450,18 +501,30 @@
<span class="desktop-icon-label">Editor</span> <span class="desktop-icon-label">Editor</span>
</div> </div>
<div class="desktop-icon" data-app-id="browser" data-app-title="Browser" <div
hx-get="/suite/browser/browser.html" hx-swap="none"> class="desktop-icon"
data-app-id="browser"
data-app-title="Browser"
hx-get="/suite/browser/browser.html"
hx-swap="none"
>
<div class="app-icon"> <div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg
stroke-linecap="round" stroke-linejoin="round"> viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" /> <polygon
points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"
/>
</svg> </svg>
</div> </div>
<span class="desktop-icon-label">Browser</span> <span class="desktop-icon-label">Browser</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -471,9 +534,12 @@
<div id="taskbar-apps"> <div id="taskbar-apps">
<!-- Taskbar items populated automatically by window-manager.js --> <!-- Taskbar items populated automatically by window-manager.js -->
</div> </div>
<div class="toolbar-time" style="display: flex; align-items: center; gap: 15px;"> <div
class="toolbar-time"
style="display: flex; align-items: center; gap: 15px"
>
<div id="themeSelectorContainer"></div> <div id="themeSelectorContainer"></div>
<div style="text-align: right;"> <div style="text-align: right">
<div id="clock-time">00:00</div> <div id="clock-time">00:00</div>
<div id="clock-date">01/01/2026</div> <div id="clock-date">01/01/2026</div>
</div> </div>
@ -484,28 +550,49 @@
<!-- HTMX Intercepts and WindowManager Init as described in UI.md Phase 3 --> <!-- HTMX Intercepts and WindowManager Init as described in UI.md Phase 3 -->
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener("DOMContentLoaded", async () => {
// Initialize WindowManager // Initialize WindowManager
if (typeof window.WindowManager !== 'undefined') { if (typeof window.WindowManager !== "undefined") {
window.wm = window.WindowManager; window.wm = window.WindowManager;
} else { } else {
console.error("WindowManager class not loaded from window-manager.js"); console.error(
"WindowManager class not loaded from window-manager.js",
);
} }
// Initialize ThemeManager // Initialize ThemeManager
if (typeof window.ThemeManager !== 'undefined') { if (typeof window.ThemeManager !== "undefined") {
window.ThemeManager.init(); window.ThemeManager.init();
} }
// Auto-open Chat window maximized on desktop load
if (window.wm) {
try {
const response = await fetch(
"/suite/partials/chat.html",
);
if (response.ok) {
const htmlContent = await response.text();
window.wm.open("chat", "Chat", htmlContent);
// Maximize the chat window after opening
setTimeout(() => {
window.wm.toggleMaximize("chat");
}, 100);
}
} catch (err) {
console.error("Failed to auto-open chat:", err);
}
}
}); });
// Listen to HTMX afterRequest event // Listen to HTMX afterRequest event
document.body.addEventListener('htmx:afterRequest', function (evt) { document.body.addEventListener("htmx:afterRequest", function (evt) {
const target = evt.detail.elt; const target = evt.detail.elt;
// Check if the click came from a desktop icon // Check if the click came from a desktop icon
if (target.classList.contains('desktop-icon')) { if (target.classList.contains("desktop-icon")) {
const appId = target.getAttribute('data-app-id'); const appId = target.getAttribute("data-app-id");
const title = target.getAttribute('data-app-title'); const title = target.getAttribute("data-app-title");
const htmlContent = evt.detail.xhr.response; const htmlContent = evt.detail.xhr.response;
// Tell WindowManager to open it // Tell WindowManager to open it
@ -516,7 +603,9 @@
// Ensure Theme dropdown is re-injected if wiped // Ensure Theme dropdown is re-injected if wiped
if (window.ThemeManager) { if (window.ThemeManager) {
const container = document.getElementById('themeSelectorContainer'); const container = document.getElementById(
"themeSelectorContainer",
);
if (container && !container.hasChildNodes()) { if (container && !container.hasChildNodes()) {
// Quick and dirty way to re-init // Quick and dirty way to re-init
window.ThemeManager.init(); window.ThemeManager.init();
@ -527,10 +616,14 @@
// Simple Clock implementation matching the screenshot bottom right corner // Simple Clock implementation matching the screenshot bottom right corner
setInterval(() => { setInterval(() => {
const now = new Date(); const now = new Date();
document.getElementById('clock-time').textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); document.getElementById("clock-time").textContent =
document.getElementById('clock-date').textContent = now.toLocaleDateString(); now.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
});
document.getElementById("clock-date").textContent =
now.toLocaleDateString();
}, 1000); }, 1000);
</script> </script>
</body> </body>
</html> </html>

View file

@ -263,6 +263,22 @@ const ThemeManager = (() => {
primary: primary, primary: primary,
}); });
updateDropdown(); updateDropdown();
// Fix theme dropdown background to use surface color
const themeDropdown = document.getElementById("themeDropdown");
if (themeDropdown) {
const surfaceColor = getComputedStyle(document.documentElement)
.getPropertyValue("--surface")
.trim();
if (surfaceColor) {
themeDropdown.style.setProperty(
"background",
surfaceColor,
"important",
);
}
}
subscribers.forEach((cb) => cb({ themeId: id, themeName: theme.name })); subscribers.forEach((cb) => cb({ themeId: id, themeName: theme.name }));
}, 50); }, 50);
}; };

View file

@ -131,6 +131,12 @@ if (typeof window.WindowManager === "undefined") {
windowEl.style.zIndex = this.zIndexCounter++; windowEl.style.zIndex = this.zIndexCounter++;
} }
// Update document title
const windowObj = this.openWindows.find((w) => w.id === id);
if (windowObj) {
document.title = `${windowObj.title} - General Bots`;
}
// Highlight taskbar icon // Highlight taskbar icon
if (this.taskbarApps) { if (this.taskbarApps) {
const icons = this.taskbarApps.querySelectorAll(".taskbar-icon"); const icons = this.taskbarApps.querySelectorAll(".taskbar-icon");
@ -156,7 +162,10 @@ if (typeof window.WindowManager === "undefined") {
this.openWindows = this.openWindows.filter((w) => w.id !== id); this.openWindows = this.openWindows.filter((w) => w.id !== id);
if (this.activeWindowId === id) { if (this.activeWindowId === id) {
this.activeWindowId = null; this.activeWindowId = null;
// Optionally focus the next highest z-index window // Reset title to default when all windows are closed
if (this.openWindows.length === 0) {
document.title = "General Bots Desktop";
}
} }
} }
@ -220,7 +229,7 @@ if (typeof window.WindowManager === "undefined") {
const minibarHeight = 28; const minibarHeight = 28;
windowEl.style.width = "100%"; windowEl.style.width = "100%";
windowEl.style.height = `calc(100% - ${taskbarHeight}px - ${minibarHeight}px)`; windowEl.style.height = `calc(100% - ${minibarHeight}px)`;
windowEl.style.top = `${minibarHeight}px`; windowEl.style.top = `${minibarHeight}px`;
windowEl.style.left = "0px"; windowEl.style.left = "0px";
windowObj.isMaximized = true; windowObj.isMaximized = true;

View file

@ -12,7 +12,7 @@
align-items: center; align-items: center;
padding: 0 12px; padding: 0 12px;
z-index: 9999; z-index: 9999;
font-family: 'Fira Code', monospace; font-family: "Fira Code", monospace;
} }
.minibar-brand { .minibar-brand {
@ -60,29 +60,113 @@
<div class="minibar" id="minibar"> <div class="minibar" id="minibar">
<div class="minibar-brand"> <div class="minibar-brand">
<svg class="minibar-brand-icon" viewBox="0 0 24 24" fill="none" stroke="#84d669" stroke-width="2"> <svg
class="minibar-brand-icon"
viewBox="0 0 24 24"
fill="none"
stroke="#84d669"
stroke-width="2"
>
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />
<path d="M8 12l3 3 5-5" /> <path d="M8 12l3 3 5-5" />
</svg> </svg>
<span>Agent Farm</span> <span>General Bots</span>
</div> </div>
<div class="minibar-actions" style="position: relative;"> <div class="minibar-actions" style="position: relative">
<button class="minibar-btn" title="Settings" type="button"></button> <button class="minibar-btn" title="Settings" type="button"></button>
<div class="user-menu-container" style="position: relative;"> <div class="user-menu-container" style="position: relative">
<button class="minibar-btn" id="userAvatar" title="Account" type="button" <button
style="border-radius: 50%; width: 22px; height: 22px; background: #ddd; color: #555; font-size: 11px; font-weight: bold; overflow: hidden; display: flex; align-items: center; justify-content: center; margin-left: 4px;"><span>U</span></button> class="minibar-btn"
<div id="userDropdown" id="userAvatar"
style="display: none; position: absolute; right: 0; top: 100%; margin-top: 4px; background: white; border: 1px solid #f0f1f2; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); width: 220px; padding: 12px; z-index: 10000; flex-direction: column; gap: 8px;"> title="Account"
type="button"
style="
border-radius: 50%;
width: 22px;
height: 22px;
background: #ddd;
color: #555;
font-size: 11px;
font-weight: bold;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
margin-left: 4px;
"
>
<span>U</span>
</button>
<div <div
style="display: flex; flex-direction: column; border-bottom: 1px solid #f0f1f2; padding-bottom: 8px;"> id="userDropdown"
<span id="userName" style="font-weight: 600; color: #3b3b3b; font-size: 13px;">User</span> style="
<span id="userEmail" style="color: #888; font-size: 11px;">user@example.com</span> display: none;
position: absolute;
right: 0;
top: 100%;
margin-top: 4px;
background: white;
border: 1px solid #f0f1f2;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 220px;
padding: 12px;
z-index: 10000;
flex-direction: column;
gap: 8px;
"
>
<div
style="
display: flex;
flex-direction: column;
border-bottom: 1px solid #f0f1f2;
padding-bottom: 8px;
"
>
<span
id="userName"
style="
font-weight: 600;
color: #3b3b3b;
font-size: 13px;
"
>User</span
>
<span id="userEmail" style="color: #888; font-size: 11px"
>user@example.com</span
>
</div> </div>
<a id="authAction" href="/auth/login.html" <a
style="display: flex; align-items: center; gap: 6px; text-decoration: none; padding: 6px; border-radius: 4px; font-size: 12px; transition: background 0.15s; cursor: pointer; color: var(--primary, #84d669);"> id="authAction"
<svg id="authIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" href="/auth/login.html"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> style="
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path> display: flex;
align-items: center;
gap: 6px;
text-decoration: none;
padding: 6px;
border-radius: 4px;
font-size: 12px;
transition: background 0.15s;
cursor: pointer;
color: var(--primary, #84d669);
"
>
<svg
id="authIcon"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"
></path>
<polyline points="10 17 15 12 21 12"></polyline> <polyline points="10 17 15 12 21 12"></polyline>
<line x1="15" y1="12" x2="3" y2="12"></line> <line x1="15" y1="12" x2="3" y2="12"></line>
</svg> </svg>
@ -95,47 +179,68 @@
<script> <script>
(function () { (function () {
var avatar = document.getElementById('userAvatar'); var avatar = document.getElementById("userAvatar");
var dropdown = document.getElementById('userDropdown'); var dropdown = document.getElementById("userDropdown");
avatar.addEventListener('click', function (e) { avatar.addEventListener("click", function (e) {
e.stopPropagation(); e.stopPropagation();
dropdown.style.display = dropdown.style.display === 'none' ? 'flex' : 'none'; dropdown.style.display =
dropdown.style.display === "none" ? "flex" : "none";
}); });
document.addEventListener('click', function (e) { document.addEventListener("click", function (e) {
if (!e.target.closest('.user-menu-container')) { if (!e.target.closest(".user-menu-container")) {
dropdown.style.display = 'none'; dropdown.style.display = "none";
} }
}); });
// Trigger manual load or UI sync since minibar loads asynchronously via HTMX // Trigger manual load or UI sync since minibar loads asynchronously via HTMX
setTimeout(function () { setTimeout(function () {
var token = localStorage.getItem("gb-access-token") || sessionStorage.getItem("gb-access-token"); var token =
localStorage.getItem("gb-access-token") ||
sessionStorage.getItem("gb-access-token");
if (token) { if (token) {
fetch("/api/auth/me", { headers: { Authorization: "Bearer " + token } }) fetch("/api/auth/me", {
.then(res => res.json()) headers: { Authorization: "Bearer " + token },
.then(user => { })
document.getElementById('userName').textContent = user.display_name || user.first_name || user.username || "User"; .then((res) => res.json())
document.getElementById('userEmail').textContent = user.email || ""; .then((user) => {
document.getElementById("userName").textContent =
user.display_name ||
user.first_name ||
user.username ||
"User";
document.getElementById("userEmail").textContent =
user.email || "";
var btn = document.getElementById('authAction'); var btn = document.getElementById("authAction");
btn.href = "#"; btn.href = "#";
btn.style.color = "var(--error, #ef4444)"; btn.style.color = "var(--error, #ef4444)";
document.getElementById('authText').textContent = "Sign out"; document.getElementById("authText").textContent =
document.getElementById('authIcon').innerHTML = '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line>'; "Sign out";
document.getElementById("authIcon").innerHTML =
'<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line>';
btn.onclick = function (e) { btn.onclick = function (e) {
e.preventDefault(); e.preventDefault();
fetch("/api/auth/logout", { method: "POST" }).finally(function () { fetch("/api/auth/logout", {
method: "POST",
}).finally(function () {
localStorage.removeItem("gb-access-token"); localStorage.removeItem("gb-access-token");
sessionStorage.removeItem("gb-access-token"); sessionStorage.removeItem("gb-access-token");
window.location.href = "/auth/login.html"; window.location.href = "/auth/login.html";
}); });
}; };
var initial = (user.display_name || user.username || "U").charAt(0).toUpperCase(); var initial = (
document.getElementById('userAvatar').innerHTML = '<span>' + initial + '</span>'; user.display_name ||
user.username ||
"U"
)
.charAt(0)
.toUpperCase();
document.getElementById("userAvatar").innerHTML =
"<span>" + initial + "</span>";
}) })
.catch(console.error); .catch(console.error);
} }