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.
447 lines
14 KiB
HTML
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>
|