Update chat and desktop UI components
This commit is contained in:
parent
054e57e074
commit
9a84dfad28
6 changed files with 1182 additions and 162 deletions
|
|
@ -539,11 +539,12 @@ footer {
|
||||||
/* Suggestions */
|
/* Suggestions */
|
||||||
.suggestions-container {
|
.suggestions-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: wrap;
|
||||||
overflow-x: auto;
|
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
max-height: 100px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestion-chip,
|
.suggestion-chip,
|
||||||
|
|
@ -1431,3 +1432,21 @@ form.input-container {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Hide default/initial suggestions - only show bot suggestions */
|
||||||
|
.suggestions-container:not(.has-bot-suggestions) .suggestion-chip {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show suggestions when bot has responded */
|
||||||
|
.suggestions-container.has-bot-suggestions .suggestion-chip {
|
||||||
|
display: inline-flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Hide default quick action suggestions - only show bot suggestions */
|
||||||
|
#quickActions,
|
||||||
|
.quick-actions-container {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<link rel="stylesheet" href="/suite/chat/chat.css?v=4" />
|
<link rel="stylesheet" href="/suite/chat/chat.css?v=9" />
|
||||||
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
|
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
|
||||||
|
<script src="/suite/js/vendor/marked.min.js"></script>
|
||||||
|
|
||||||
<div class="chat-layout" id="chat-app">
|
<div class="chat-layout" id="chat-app">
|
||||||
<!-- Connection Status -->
|
<!-- Connection Status -->
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
width: 51px;
|
width: 51px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--bg-secondary, #f8f8f8);
|
background: var(--bg-secondary, #f8f8f8);
|
||||||
border-right: 1px solid var(--border-color, #f0f1f2);
|
border-right: 1px solid #d1d5db;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
@ -127,11 +127,16 @@
|
||||||
/* Interactive Desktop Icons triggering HTMX */
|
/* Interactive Desktop Icons triggering HTMX */
|
||||||
.desktop-icons-container {
|
.desktop-icons-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
width: 100px;
|
justify-content: flex-end;
|
||||||
|
width: auto;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
position: absolute;
|
||||||
|
right: 33px;
|
||||||
|
top: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-icon {
|
.desktop-icon {
|
||||||
|
|
@ -236,33 +241,65 @@
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.taskbar-item:hover {
|
|
||||||
background: var(--bg-hover, #f8f8f8);
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Fullscreen Mode Styles */
|
||||||
|
body.fullscreen .sidebar {
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.taskbar-item.active {
|
body.fullscreen .toolbar {
|
||||||
border-bottom-color: var(--primary, #84d669);
|
display: none !important;
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent 50%,
|
|
||||||
var(--primary-alpha-10, rgba(132, 214, 105, 0.1)) 100%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.fullscreen .panel-section {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.fullscreen .workspace {
|
||||||
|
height: 100vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.fullscreen .main-wrapper {
|
||||||
|
height: 100vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.fullscreen .window-titlebar {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.fullscreen .window {
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide window title bar and controls in fullscreen */
|
||||||
|
body.fullscreen .window > div:first-child {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.fullscreen .window > div:nth-child(2) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Minibar Component (Phase 8) -->
|
<!-- Minibar removed - login moved to taskbar -->
|
||||||
<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"
|
||||||
|
onclick="if(window.wm) window.wm.open('home', 'Home', '<div style=\'padding:20px;text-align:center;\'><h2>Welcome to General Bots Desktop</h2></div>');"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -276,7 +313,11 @@
|
||||||
<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"
|
||||||
|
onclick="alert('Search coming soon');"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -288,7 +329,11 @@
|
||||||
<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"
|
||||||
|
onclick="fetch('/suite/terminal/terminal.html').then(r=>r.text()).then(html=>{if(window.wm)window.wm.open('terminal','Terminal',html);});"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -300,7 +345,11 @@
|
||||||
<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"
|
||||||
|
onclick="alert('User profile coming soon');"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -314,7 +363,11 @@
|
||||||
<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"
|
||||||
|
onclick="if(window.wm) window.wm.open('apps', 'Applications', '<div style=\'padding:20px;\'><h3>Installed Applications</h3><p>Click desktop icons to launch apps.</p></div>');"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -332,6 +385,7 @@
|
||||||
class="sidebar-item"
|
class="sidebar-item"
|
||||||
style="margin-top: auto"
|
style="margin-top: auto"
|
||||||
title="Settings"
|
title="Settings"
|
||||||
|
onclick="fetch('/suite/admin/organization-settings.html').then(r=>r.text()).then(html=>{if(window.wm)window.wm.open('settings','Settings',html);}).catch(e=>alert('Settings page not found'));"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="sidebar-icon"
|
class="sidebar-icon"
|
||||||
|
|
@ -539,6 +593,23 @@
|
||||||
style="display: flex; align-items: center; gap: 15px"
|
style="display: flex; align-items: center; gap: 15px"
|
||||||
>
|
>
|
||||||
<div id="themeSelectorContainer"></div>
|
<div id="themeSelectorContainer"></div>
|
||||||
|
<button
|
||||||
|
id="loginBtn"
|
||||||
|
onclick="window.location.href='/suite/auth/login.html'"
|
||||||
|
style="
|
||||||
|
background: var(--primary, #84d669);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: "Fira Code", monospace;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
<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>
|
||||||
|
|
@ -550,6 +621,16 @@
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
// Check for fullscreen mode
|
||||||
|
(function() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
if (urlParams.get('fullscreen') === 'true') {
|
||||||
|
document.body.classList.add('fullscreen');
|
||||||
|
console.log('Fullscreen mode activated');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
// Initialize WindowManager
|
// Initialize WindowManager
|
||||||
if (typeof window.WindowManager !== "undefined") {
|
if (typeof window.WindowManager !== "undefined") {
|
||||||
|
|
@ -613,6 +694,29 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update login button based on auth state
|
||||||
|
function updateLoginButton() {
|
||||||
|
var token =
|
||||||
|
localStorage.getItem("gb-access-token") ||
|
||||||
|
sessionStorage.getItem("gb-access-token");
|
||||||
|
var loginBtn = document.getElementById("loginBtn");
|
||||||
|
if (token && loginBtn) {
|
||||||
|
loginBtn.textContent = "Sign Out";
|
||||||
|
loginBtn.onclick = function () {
|
||||||
|
fetch("/api/auth/logout", { method: "POST" }).finally(
|
||||||
|
function () {
|
||||||
|
localStorage.removeItem("gb-access-token");
|
||||||
|
sessionStorage.removeItem("gb-access-token");
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check login state on load
|
||||||
|
setTimeout(updateLoginButton, 500);
|
||||||
|
|
||||||
// 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();
|
||||||
|
|
|
||||||
680
ui/suite/desktop.html.backup
Normal file
680
ui/suite/desktop.html.backup
Normal file
|
|
@ -0,0 +1,680 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>General Bots Desktop</title>
|
||||||
|
|
||||||
|
<!-- Link to the existing compiled CSS -->
|
||||||
|
<link rel="stylesheet" href="/suite/css/app.css" />
|
||||||
|
<link rel="stylesheet" href="/suite/css/base.css" />
|
||||||
|
<link rel="stylesheet" href="/suite/css/theme-sentient.css" />
|
||||||
|
<link rel="stylesheet" href="/suite/css/desktop.css" />
|
||||||
|
|
||||||
|
<!-- Local JS requirements per AGENTS.md / UI.md -->
|
||||||
|
<script src="/suite/js/vendor/htmx.min.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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Fira Code", "Fira Sans", Arial, sans-serif;
|
||||||
|
background: var(--bg, #ffffff);
|
||||||
|
color: var(--text, #333333);
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Core Layout replicating BUILD V3 screenshot styling */
|
||||||
|
.build-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background: var(--bg, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Left Sidebar */
|
||||||
|
.sidebar {
|
||||||
|
width: 51px;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-secondary, #f8f8f8);
|
||||||
|
border-right: 1px solid #d1d5db;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item {
|
||||||
|
width: 51px;
|
||||||
|
height: 50px;
|
||||||
|
border-bottom: 1px solid var(--border-color, #f0f1f2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item:hover {
|
||||||
|
background: var(--bg, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item.active {
|
||||||
|
background: var(--bg, #ffffff);
|
||||||
|
border-left: 3px solid var(--primary, #84d669);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
stroke: var(--text-secondary, #3b3b3b);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition:
|
||||||
|
stroke 0.15s ease,
|
||||||
|
opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item:hover .sidebar-icon {
|
||||||
|
opacity: 1;
|
||||||
|
stroke: var(--text, #3b3b3b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content wrapper */
|
||||||
|
.main-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top Navigation Tabs - Removed (already in Vibe) */
|
||||||
|
|
||||||
|
/* Workspace (Where windows float) */
|
||||||
|
.workspace {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--bg, #ffffff);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Panel Grid (Desktop Icons) */
|
||||||
|
.panel-section {
|
||||||
|
flex: 1;
|
||||||
|
padding: 26px 33px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 10;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive Desktop Icons triggering HTMX */
|
||||||
|
.desktop-icons-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: auto;
|
||||||
|
pointer-events: auto;
|
||||||
|
position: absolute;
|
||||||
|
right: 33px;
|
||||||
|
top: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-icon {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 80px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--surface-hover, #ffffff);
|
||||||
|
border: 1px solid var(--border, #e5e7eb);
|
||||||
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.05);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-icon:hover .app-icon {
|
||||||
|
transform: scale(1.05);
|
||||||
|
border-color: var(--accent, #84d669);
|
||||||
|
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon svg {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
stroke: var(--text, #374151);
|
||||||
|
transition: stroke 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-icon:hover .app-icon svg {
|
||||||
|
stroke: var(--accent, #84d669);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-icon-label {
|
||||||
|
font-family: "Fira Code", monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary, #374151);
|
||||||
|
background: transparent;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-grid {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-svg {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bottom Taskbar */
|
||||||
|
.toolbar {
|
||||||
|
height: 50px;
|
||||||
|
background: var(--surface);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
z-index: 100;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#taskbar-apps {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-time {
|
||||||
|
font-family: "Fira Code", monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary, #3b3b3b);
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Taskbar Items generated by WindowManager */
|
||||||
|
.taskbar-item {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Minibar removed - login moved to taskbar -->
|
||||||
|
|
||||||
|
<div class="build-container">
|
||||||
|
<!-- Left Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div
|
||||||
|
class="sidebar-item active"
|
||||||
|
title="Home"
|
||||||
|
onclick="if(window.wm) window.wm.open('home', 'Home', '<div style=\'padding:20px;text-align:center;\'><h2>Welcome to General Bots Desktop</h2></div>');"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
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>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebar-item"
|
||||||
|
title="Search"
|
||||||
|
onclick="alert('Search coming soon');"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="sidebar-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebar-item"
|
||||||
|
title="Terminal"
|
||||||
|
onclick="fetch('/suite/terminal/terminal.html').then(r=>r.text()).then(html=>{if(window.wm)window.wm.open('terminal','Terminal',html);});"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<line x1="12" y1="19" x2="20" y2="19"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebar-item"
|
||||||
|
title="User"
|
||||||
|
onclick="alert('User profile coming soon');"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
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>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebar-item"
|
||||||
|
title="Apps"
|
||||||
|
onclick="if(window.wm) window.wm.open('apps', 'Applications', '<div style=\'padding:20px;\'><h3>Installed Applications</h3><p>Click desktop icons to launch apps.</p></div>');"
|
||||||
|
>
|
||||||
|
<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="14" y="3" 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>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sidebar-item"
|
||||||
|
style="margin-top: auto"
|
||||||
|
title="Settings"
|
||||||
|
onclick="fetch('/suite/admin/organization-settings.html').then(r=>r.text()).then(html=>{if(window.wm)window.wm.open('settings','Settings',html);}).catch(e=>alert('Settings page not found'));"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="sidebar-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
<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"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Wrapper -->
|
||||||
|
<div class="main-wrapper">
|
||||||
|
<!-- Top Navigation Tabs - Removed (already in Vibe) -->
|
||||||
|
|
||||||
|
<!-- Workspace container where WindowManager operates -->
|
||||||
|
<div class="workspace" id="desktop-content">
|
||||||
|
<!-- Background Pattern -->
|
||||||
|
|
||||||
|
<div class="panel-section">
|
||||||
|
<div class="desktop-icons-container">
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="vibe"
|
||||||
|
data-app-title="Vibe"
|
||||||
|
hx-get="/suite/partials/vibe.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Vibe</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
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">
|
||||||
|
<svg
|
||||||
|
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="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Tasks</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="chat"
|
||||||
|
data-app-title="Chat"
|
||||||
|
hx-get="/suite/partials/chat.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Chat</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="terminal"
|
||||||
|
data-app-title="Terminal"
|
||||||
|
hx-get="/suite/terminal/terminal.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
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" />
|
||||||
|
<line x1="12" y1="19" x2="20" y2="19" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Terminal</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="drive"
|
||||||
|
data-app-title="Explorer"
|
||||||
|
hx-get="/suite/drive/drive.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Explorer</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="editor"
|
||||||
|
data-app-title="Editor"
|
||||||
|
hx-get="/suite/editor.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
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="8 6 2 12 8 18" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Editor</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="desktop-icon"
|
||||||
|
data-app-id="browser"
|
||||||
|
data-app-title="Browser"
|
||||||
|
hx-get="/suite/browser/browser.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<div class="app-icon">
|
||||||
|
<svg
|
||||||
|
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" />
|
||||||
|
<polygon
|
||||||
|
points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="desktop-icon-label">Browser</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Taskbar -->
|
||||||
|
<footer class="toolbar" id="taskbar">
|
||||||
|
<div id="taskbar-apps">
|
||||||
|
<!-- Taskbar items populated automatically by window-manager.js -->
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="toolbar-time"
|
||||||
|
style="display: flex; align-items: center; gap: 15px"
|
||||||
|
>
|
||||||
|
<div id="themeSelectorContainer"></div>
|
||||||
|
<button
|
||||||
|
id="loginBtn"
|
||||||
|
onclick="window.location.href='/suite/auth/login.html'"
|
||||||
|
style="
|
||||||
|
background: var(--primary, #84d669);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: "Fira Code", monospace;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<div id="clock-time">00:00</div>
|
||||||
|
<div id="clock-date">01/01/2026</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- HTMX Intercepts and WindowManager Init as described in UI.md Phase 3 -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
// Initialize WindowManager
|
||||||
|
if (typeof window.WindowManager !== "undefined") {
|
||||||
|
window.wm = window.WindowManager;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"WindowManager class not loaded from window-manager.js",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize ThemeManager
|
||||||
|
if (typeof window.ThemeManager !== "undefined") {
|
||||||
|
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
|
||||||
|
document.body.addEventListener("htmx:afterRequest", function (evt) {
|
||||||
|
const target = evt.detail.elt;
|
||||||
|
|
||||||
|
// Check if the click came from a desktop icon
|
||||||
|
if (target.classList.contains("desktop-icon")) {
|
||||||
|
const appId = target.getAttribute("data-app-id");
|
||||||
|
const title = target.getAttribute("data-app-title");
|
||||||
|
const htmlContent = evt.detail.xhr.response;
|
||||||
|
|
||||||
|
// Tell WindowManager to open it
|
||||||
|
if (window.wm) {
|
||||||
|
window.wm.open(appId, title, htmlContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Theme dropdown is re-injected if wiped
|
||||||
|
if (window.ThemeManager) {
|
||||||
|
const container = document.getElementById(
|
||||||
|
"themeSelectorContainer",
|
||||||
|
);
|
||||||
|
if (container && !container.hasChildNodes()) {
|
||||||
|
// Quick and dirty way to re-init
|
||||||
|
window.ThemeManager.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update login button based on auth state
|
||||||
|
function updateLoginButton() {
|
||||||
|
var token =
|
||||||
|
localStorage.getItem("gb-access-token") ||
|
||||||
|
sessionStorage.getItem("gb-access-token");
|
||||||
|
var loginBtn = document.getElementById("loginBtn");
|
||||||
|
if (token && loginBtn) {
|
||||||
|
loginBtn.textContent = "Sign Out";
|
||||||
|
loginBtn.onclick = function () {
|
||||||
|
fetch("/api/auth/logout", { method: "POST" }).finally(
|
||||||
|
function () {
|
||||||
|
localStorage.removeItem("gb-access-token");
|
||||||
|
sessionStorage.removeItem("gb-access-token");
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check login state on load
|
||||||
|
setTimeout(updateLoginButton, 500);
|
||||||
|
|
||||||
|
// Simple Clock implementation matching the screenshot bottom right corner
|
||||||
|
setInterval(() => {
|
||||||
|
const now = new Date();
|
||||||
|
document.getElementById("clock-time").textContent =
|
||||||
|
now.toLocaleTimeString([], {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
document.getElementById("clock-date").textContent =
|
||||||
|
now.toLocaleDateString();
|
||||||
|
}, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1009,6 +1009,16 @@
|
||||||
// Clear existing suggestions
|
// Clear existing suggestions
|
||||||
suggestionsEl.innerHTML = "";
|
suggestionsEl.innerHTML = "";
|
||||||
|
|
||||||
|
// Remove any default/quick action suggestions that might be present
|
||||||
|
var defaultSuggestions =
|
||||||
|
suggestionsEl.querySelectorAll(".suggestion-chip");
|
||||||
|
defaultSuggestions.forEach(function (chip) {
|
||||||
|
chip.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add has-bot-suggestions class to make suggestions visible
|
||||||
|
suggestionsEl.classList.add("has-bot-suggestions");
|
||||||
|
|
||||||
console.log("Rendering " + suggestions.length + " suggestions");
|
console.log("Rendering " + suggestions.length + " suggestions");
|
||||||
|
|
||||||
suggestions.forEach(function (suggestion) {
|
suggestions.forEach(function (suggestion) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
body {
|
body {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
font-family: 'Fira Code', 'Fira Sans', Arial, sans-serif !important;
|
font-family: "Fira Code", "Fira Sans", Arial, sans-serif !important;
|
||||||
background: var(--bg) !important;
|
background: var(--bg) !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
height: 100vh !important;
|
height: 100vh !important;
|
||||||
|
|
@ -79,54 +79,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Top Navigation Tabs */
|
/* Top Navigation Tabs - Removed (already in Vibe) */
|
||||||
.tabs-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: var(--surface);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs-row {
|
|
||||||
display: flex;
|
|
||||||
height: 34px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-tab {
|
|
||||||
height: 34px;
|
|
||||||
min-width: 169px;
|
|
||||||
flex: 1;
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 18px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-tab:hover {
|
|
||||||
background: var(--surface-hover, #ffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-tab.active {
|
|
||||||
background: var(--accent);
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-tab.active .main-tab-content {
|
|
||||||
color: var(--bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-tab-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
font-family: 'Fira Code', monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Workspace (Where windows float) */
|
/* Workspace (Where windows float) */
|
||||||
.workspace {
|
.workspace {
|
||||||
|
|
@ -190,20 +143,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-card-label {
|
.panel-card-label {
|
||||||
font-family: 'Fira Code', monospace;
|
font-family: "Fira Code", monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-grid { display: none; }
|
.bg-grid {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-svg { display: none !important; }
|
.bg-svg {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Bottom Taskbar */
|
/* Bottom Taskbar */
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
@ -226,7 +179,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-time {
|
.toolbar-time {
|
||||||
font-family: 'Fira Code', monospace;
|
font-family: "Fira Code", monospace;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
@ -253,7 +206,11 @@
|
||||||
|
|
||||||
.taskbar-item.active {
|
.taskbar-item.active {
|
||||||
border-bottom-color: var(--accent);
|
border-bottom-color: var(--accent);
|
||||||
background: linear-gradient(to bottom, transparent 50%, var(--accent-light, rgba(132, 214, 105, 0.1)) 100%);
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
transparent 50%,
|
||||||
|
var(--accent-light, rgba(132, 214, 105, 0.1)) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Utility */
|
/* Utility */
|
||||||
|
|
@ -266,13 +223,25 @@
|
||||||
<!-- 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="var(--text)" stroke-width="2">
|
<svg
|
||||||
|
class="sidebar-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
|
<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="Terminal">
|
<div class="sidebar-item" title="Terminal">
|
||||||
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="var(--text)" stroke-width="2">
|
<svg
|
||||||
|
class="sidebar-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--text)"
|
||||||
|
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>
|
||||||
|
|
@ -281,107 +250,312 @@
|
||||||
|
|
||||||
<!-- 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="tabs-row">
|
|
||||||
<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-inner">
|
<div class="workspace" id="desktop-content-inner">
|
||||||
|
|
||||||
<!-- Background Pattern -->
|
<!-- Background Pattern -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel-section">
|
<div class="panel-section">
|
||||||
<div class="panel-grid">
|
<div class="panel-grid">
|
||||||
|
|
||||||
<!-- HTMX Enabled Desktop Icons that WindowManager catches -->
|
<!-- HTMX Enabled Desktop Icons that WindowManager catches -->
|
||||||
<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"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="chat"
|
||||||
<circle cx="21" cy="21" r="20" stroke="var(--accent)" stroke-width="2" />
|
data-app-title="Chat"
|
||||||
<path d="M14 21h14M21 14v14" stroke="var(--accent)" stroke-width="2" />
|
hx-get="/suite/partials/chat.html"
|
||||||
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="21"
|
||||||
|
cy="21"
|
||||||
|
r="20"
|
||||||
|
stroke="var(--accent)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M14 21h14M21 14v14"
|
||||||
|
stroke="var(--accent)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Chat</div>
|
<div class="panel-card-label">Chat</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desktop-icon" data-app-id="vibe" data-app-title="Vibe"
|
<div
|
||||||
hx-get="/suite/partials/vibe.html" hx-swap="none">
|
class="desktop-icon"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="vibe"
|
||||||
<circle cx="21" cy="21" r="20" stroke="var(--accent)" stroke-width="2" />
|
data-app-title="Vibe"
|
||||||
<path d="M12 14l8 7-8 7" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"
|
hx-get="/suite/partials/vibe.html"
|
||||||
stroke-linejoin="round" />
|
hx-swap="none"
|
||||||
<line x1="22" y1="28" x2="32" y2="28" stroke="var(--accent)" stroke-width="2"
|
>
|
||||||
stroke-linecap="round" />
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="21"
|
||||||
|
cy="21"
|
||||||
|
r="20"
|
||||||
|
stroke="var(--accent)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M12 14l8 7-8 7"
|
||||||
|
stroke="var(--accent)"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="22"
|
||||||
|
y1="28"
|
||||||
|
x2="32"
|
||||||
|
y2="28"
|
||||||
|
stroke="var(--accent)"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Vibe</div>
|
<div class="panel-card-label">Vibe</div>
|
||||||
</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"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="tasks"
|
||||||
<rect x="2" y="2" width="38" height="38" rx="4" stroke="var(--text)" stroke-width="2" />
|
data-app-title="Tasks"
|
||||||
<line x1="12" y1="12" x2="30" y2="12" stroke="var(--text)" stroke-width="2" />
|
hx-get="/suite/tasks/task-window.html"
|
||||||
<line x1="12" y1="21" x2="30" y2="21" stroke="var(--text)" stroke-width="2" />
|
hx-swap="none"
|
||||||
<line x1="12" y1="30" x2="24" y2="30" stroke="var(--text)" stroke-width="2" />
|
>
|
||||||
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="2"
|
||||||
|
y="2"
|
||||||
|
width="38"
|
||||||
|
height="38"
|
||||||
|
rx="4"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="12"
|
||||||
|
x2="30"
|
||||||
|
y2="12"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="21"
|
||||||
|
x2="30"
|
||||||
|
y2="21"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="30"
|
||||||
|
x2="24"
|
||||||
|
y2="30"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Tasks</div>
|
<div class="panel-card-label">Tasks</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desktop-icon" data-app-id="terminal" data-app-title="Terminal"
|
<div
|
||||||
hx-get="/suite/partials/terminal.html" hx-swap="none">
|
class="desktop-icon"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="terminal"
|
||||||
<rect x="2" y="4" width="38" height="34" rx="4" stroke="var(--text)" stroke-width="2" />
|
data-app-title="Terminal"
|
||||||
<line x1="10" y1="14" x2="32" y2="14" stroke="var(--text)" stroke-width="2" />
|
hx-get="/suite/partials/terminal.html"
|
||||||
<line x1="10" y1="22" x2="28" y2="22" stroke="var(--text)" stroke-width="2" />
|
hx-swap="none"
|
||||||
<line x1="10" y1="30" x2="24" y2="30" stroke="var(--text)" stroke-width="2" />
|
>
|
||||||
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="2"
|
||||||
|
y="4"
|
||||||
|
width="38"
|
||||||
|
height="34"
|
||||||
|
rx="4"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="14"
|
||||||
|
x2="32"
|
||||||
|
y2="14"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="22"
|
||||||
|
x2="28"
|
||||||
|
y2="22"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="30"
|
||||||
|
x2="24"
|
||||||
|
y2="30"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Terminal</div>
|
<div class="panel-card-label">Terminal</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desktop-icon" data-app-id="explorer" data-app-title="Explorer"
|
<div
|
||||||
hx-get="/suite/partials/explorer.html" hx-swap="none">
|
class="desktop-icon"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="explorer"
|
||||||
<rect x="2" y="2" width="38" height="38" rx="4" stroke="var(--text)" stroke-width="2" />
|
data-app-title="Explorer"
|
||||||
<line x1="10" y1="10" x2="32" y2="10" stroke="var(--text)" stroke-width="2" />
|
hx-get="/suite/partials/explorer.html"
|
||||||
<line x1="10" y1="18" x2="28" y2="18" stroke="var(--text)" stroke-width="2" />
|
hx-swap="none"
|
||||||
<line x1="10" y1="26" x2="32" y2="26" stroke="var(--text)" stroke-width="2" />
|
>
|
||||||
<line x1="10" y1="34" x2="24" y2="34" stroke="var(--text)" stroke-width="2" />
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="2"
|
||||||
|
y="2"
|
||||||
|
width="38"
|
||||||
|
height="38"
|
||||||
|
rx="4"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="10"
|
||||||
|
x2="32"
|
||||||
|
y2="10"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="18"
|
||||||
|
x2="28"
|
||||||
|
y2="18"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="26"
|
||||||
|
x2="32"
|
||||||
|
y2="26"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="10"
|
||||||
|
y1="34"
|
||||||
|
x2="24"
|
||||||
|
y2="34"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Explorer</div>
|
<div class="panel-card-label">Explorer</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desktop-icon" data-app-id="editor" data-app-title="Editor"
|
<div
|
||||||
hx-get="/suite/partials/editor.html" hx-swap="none">
|
class="desktop-icon"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="editor"
|
||||||
<polyline points="4 8 12 2 20 8" stroke="var(--text)" stroke-width="2" />
|
data-app-title="Editor"
|
||||||
<line x1="12" y1="2" x2="12" y2="24" stroke="var(--text)" stroke-width="2" />
|
hx-get="/suite/partials/editor.html"
|
||||||
<polyline points="22 16 30 10 38 16" stroke="var(--text)" stroke-width="2" />
|
hx-swap="none"
|
||||||
<line x1="30" y1="10" x2="30" y2="32" stroke="var(--text)" stroke-width="2" />
|
>
|
||||||
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<polyline
|
||||||
|
points="4 8 12 2 20 8"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="2"
|
||||||
|
x2="12"
|
||||||
|
y2="24"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
points="22 16 30 10 38 16"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="30"
|
||||||
|
y1="10"
|
||||||
|
x2="30"
|
||||||
|
y2="32"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Editor</div>
|
<div class="panel-card-label">Editor</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="desktop-icon" data-app-id="browser" data-app-title="Browser"
|
<div
|
||||||
hx-get="/suite/partials/browser.html" hx-swap="none">
|
class="desktop-icon"
|
||||||
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
|
data-app-id="browser"
|
||||||
<rect x="2" y="4" width="38" height="34" rx="4" stroke="var(--text)" stroke-width="2" />
|
data-app-title="Browser"
|
||||||
<circle cx="14" cy="16" r="4" stroke="var(--text)" stroke-width="2" />
|
hx-get="/suite/partials/browser.html"
|
||||||
<path d="M6 34a6 6 0 0 1 6-6h10a6 6 0 0 1 6 6v2H6v-2z" stroke="var(--text)" stroke-width="2" />
|
hx-swap="none"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="panel-card-icon"
|
||||||
|
viewBox="0 0 42 42"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="2"
|
||||||
|
y="4"
|
||||||
|
width="38"
|
||||||
|
height="34"
|
||||||
|
rx="4"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="14"
|
||||||
|
cy="16"
|
||||||
|
r="4"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6 34a6 6 0 0 1 6-6h10a6 6 0 0 1 6 6v2H6v-2z"
|
||||||
|
stroke="var(--text)"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="panel-card-label">Browser</div>
|
<div class="panel-card-label">Browser</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -394,9 +568,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>
|
||||||
|
|
@ -407,12 +584,41 @@
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
// Auto-open Chat window maximized on desktop load
|
||||||
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
// Initialize WindowManager reference
|
||||||
|
if (typeof window.WindowManager !== "undefined") {
|
||||||
|
window.wm = window.WindowManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-open Chat window maximized
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 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();
|
||||||
const timeEl = document.getElementById('clock-time');
|
const timeEl = document.getElementById("clock-time");
|
||||||
const dateEl = document.getElementById('clock-date');
|
const dateEl = document.getElementById("clock-date");
|
||||||
if (timeEl) timeEl.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
if (timeEl)
|
||||||
|
timeEl.textContent = now.toLocaleTimeString([], {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
if (dateEl) dateEl.textContent = now.toLocaleDateString();
|
if (dateEl) dateEl.textContent = now.toLocaleDateString();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue