botserver/web/desktop/index.html
Rodrigo Rodriguez (Pragmatismo) 444b424739 Refactor chat module to use unified theme system
Rewrites chat.css to use centralized CSS variables from app.css instead
of maintaining its own theme definitions. Moves all theme variables
(colors, spacing, shadows, transitions) to app.css as the single source
of truth. Improves chat UI consistency, adds better connection status
indicators, and enhances responsive design.
2025-11-20 21:09:23 -03:00

447 lines
14 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>General Bots</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="stylesheet" href="css/app.css" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
background: var(--primary-bg);
color: var(--primary-fg);
overflow: hidden;
}
/* Floating minimal header */
.float-header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
z-index: 1000;
background: transparent;
pointer-events: none;
}
.float-header > * {
pointer-events: auto;
}
/* Left side - Logo */
.header-left {
display: flex;
align-items: center;
gap: 12px;
}
.logo-wrapper {
display: flex;
align-items: center;
cursor: pointer;
padding: 8px;
border-radius: 12px;
transition: all 0.3s;
background: var(--glass-bg);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
}
.logo-wrapper:hover {
background: var(--bg-hover);
box-shadow: var(--shadow-md);
}
.logo-icon {
width: 36px;
height: 36px;
background: url("https://pragmatismo.com.br/icons/general-bots.svg")
center/contain no-repeat;
}
.logo-text {
font-size: 18px;
font-weight: 500;
color: var(--text-primary);
}
/* Right side - Apps menu and avatar */
.header-right {
display: flex;
align-items: center;
gap: 12px;
}
/* Google-style dots menu button */
.apps-menu-btn {
width: 40px;
height: 40px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
color: var(--text-secondary);
position: relative;
background: var(--glass-bg);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
}
.apps-menu-btn:hover {
background: var(--bg-hover);
box-shadow: var(--shadow-md);
}
.apps-menu-btn:active {
background: var(--bg-active);
transform: scale(0.95);
}
/* Apps dropdown */
.apps-dropdown {
position: absolute;
top: 60px;
right: 80px;
width: 320px;
background: var(--glass-bg);
border-radius: 16px;
box-shadow: var(--shadow-xl);
padding: 20px;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1001;
}
.apps-dropdown.show {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.app-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.app-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 8px;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
color: #202124;
transition: background 0.2s;
border: 1px solid transparent;
}
.app-item:hover {
background: rgba(26, 115, 232, 0.04);
border-color: rgba(26, 115, 232, 0.2);
}
.app-item.active {
background: rgba(26, 115, 232, 0.08);
border-color: rgba(26, 115, 232, 0.3);
}
.app-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 8px;
background: #f8f9fa;
}
.app-item.active .app-icon {
background: #e8f0fe;
}
.app-item span {
font-size: 13px;
color: #5f6368;
}
/* User avatar */
.user-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: #1a73e8;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.user-avatar:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
transform: scale(1.05);
}
/* Main content area */
#main-content {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
body {
background: #202124;
color: #e8eaed;
}
.float-header {
background: transparent;
}
.logo-wrapper {
background: rgba(32, 33, 36, 0.9);
}
.logo-wrapper:hover {
background: rgba(32, 33, 36, 1);
}
.logo-text {
color: #e8eaed;
}
.apps-menu-btn {
color: #9aa0a6;
}
.apps-menu-btn {
background: rgba(32, 33, 36, 0.9);
}
.apps-menu-btn:hover {
background: rgba(32, 33, 36, 1);
}
.apps-dropdown {
background: #292a2d;
box-shadow:
0 1px 3px 1px rgba(0, 0, 0, 0.15),
0 4px 8px 3px rgba(0, 0, 0, 0.15);
}
.app-item {
color: #e8eaed;
}
.app-item:hover {
background: rgba(138, 180, 248, 0.08);
border-color: rgba(138, 180, 248, 0.2);
}
.app-icon {
background: #3c4043;
}
.app-item.active .app-icon {
background: #394457;
}
.app-item span {
color: #9aa0a6;
}
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script
defer
src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
></script>
</head>
<body>
<!-- Loading overlay -->
<div class="loading-overlay" id="loadingOverlay">
<div class="loading-spinner"></div>
</div>
<!-- Minimal floating header -->
<div class="float-header">
<!-- Left: General Bots logo -->
<div class="header-left">
<div
class="logo-wrapper"
onclick="window.location.reload()"
title="General Bots"
>
<div class="logo-icon"></div>
<span class="logo-text">General Bots</span>
</div>
</div>
<!-- Right: Theme selector, Theme toggle, Apps menu and user avatar -->
<div class="header-right">
<!-- Theme dropdown selector -->
<div id="themeSelectorContainer"></div>
<button
class="icon-button apps-button"
id="appsButton"
title="Apps"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
>
<circle cx="5" cy="5" r="2"></circle>
<circle cx="12" cy="5" r="2"></circle>
<circle cx="19" cy="5" r="2"></circle>
<circle cx="5" cy="12" r="2"></circle>
<circle cx="12" cy="12" r="2"></circle>
<circle cx="19" cy="12" r="2"></circle>
<circle cx="5" cy="19" r="2"></circle>
<circle cx="12" cy="19" r="2"></circle>
<circle cx="19" cy="19" r="2"></circle>
</svg>
</button>
<!-- Apps dropdown -->
<div class="apps-dropdown" id="appsDropdown">
<div class="apps-dropdown-title">Applications</div>
<div class="app-grid">
<a
class="app-item active"
href="#chat"
data-section="chat"
>
<div class="app-icon">💬</div>
<span>Chat</span>
</a>
<a class="app-item" href="#drive" data-section="drive">
<div class="app-icon">📁</div>
<span>Drive</span>
</a>
<a class="app-item" href="#tasks" data-section="tasks">
<div class="app-icon"></div>
<span>Tasks</span>
</a>
<a class="app-item" href="#mail" data-section="mail">
<div class="app-icon"></div>
<span>Mail</span>
</a>
</div>
</div>
<!-- User avatar -->
<div class="user-avatar" id="userAvatar" title="User Account">
<span>U</span>
</div>
</div>
</div>
<div id="main-content">
<!-- Sections will be loaded dynamically -->
</div>
<!-- Load scripts -->
<script src="js/theme-manager.js"></script>
<script src="js/layout.js"></script>
<script>
// Initialize ThemeManager and Apps menu
document.addEventListener("DOMContentLoaded", () => {
// Initialize ThemeManager
if (window.ThemeManager) {
ThemeManager.init();
}
const appsBtn = document.getElementById("appsButton");
const appsDropdown = document.getElementById("appsDropdown");
const appItems = document.querySelectorAll(".app-item");
if (!appsBtn || !appsDropdown) {
console.error("Apps button or dropdown not found");
return;
}
// Toggle apps menu
appsBtn.addEventListener("click", (e) => {
e.stopPropagation();
appsDropdown.classList.toggle("show");
});
// Close dropdown when clicking outside
document.addEventListener("click", () => {
appsDropdown.classList.remove("show");
});
// Prevent dropdown from closing when clicking inside
appsDropdown.addEventListener("click", (e) => {
e.stopPropagation();
});
// Handle app selection
appItems.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
const section = item.dataset.section;
// Update active state
appItems.forEach((i) => i.classList.remove("active"));
item.classList.add("active");
// Switch section
if (window.switchSection) {
window.switchSection(section);
}
// Close dropdown
appsDropdown.classList.remove("show");
});
});
// Hide loading overlay after layout.js initializes
setTimeout(() => {
const loadingOverlay =
document.getElementById("loadingOverlay");
if (loadingOverlay) {
loadingOverlay.classList.add("hidden");
}
}, 500);
});
</script>
</body>
</html>