Clean up mock Vibe UI

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-02-26 12:40:44 -03:00
parent 7c1deca8ae
commit afb13cb397
14 changed files with 3138 additions and 1336 deletions

View file

@ -70,20 +70,20 @@
}
</style>
</head>
<body class="h-screen w-screen overflow-hidden flex flex-col bg-white text-gray-800 font-sans selection:bg-brand-200">
<body class="h-screen w-screen overflow-hidden flex flex-col bg-[var(--bg,#ffffff)] text-[var(--text,#1f2937)] font-sans selection:bg-brand-200">
<div class="flex-1 flex overflow-hidden relative">
<!-- LEFT SIDEBAR -->
<aside class="w-14 shrink-0 bg-white border-r border-gray-100 flex flex-col items-center py-6 z-20 relative">
<div class="flex flex-col space-y-8 text-gray-500">
<button class="hover:text-brand-600 transition-colors"><i class="fa-solid fa-chevron-right"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-solid fa-house"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-solid fa-magnifying-glass"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-solid fa-border-all"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-regular fa-user"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-solid fa-layer-group"></i></button>
<button class="hover:text-brand-600 transition-colors text-xl"><i class="fa-solid fa-gear"></i></button>
<aside class="w-14 shrink-0 bg-[var(--bg-secondary,#ffffff)] border-r border-[var(--border-color,#f3f4f6)] flex flex-col items-center py-6 z-20 relative">
<div class="flex flex-col space-y-8 text-[var(--text-secondary,#6b7280)]">
<button class="hover:text-[var(--primary,#16a34a)] transition-colors"><i class="fa-solid fa-chevron-right"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-solid fa-house"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-solid fa-magnifying-glass"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-solid fa-border-all"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-regular fa-user"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-solid fa-layer-group"></i></button>
<button class="hover:text-[var(--primary,#16a34a)] transition-colors text-xl"><i class="fa-solid fa-gear"></i></button>
</div>
</aside>
@ -91,29 +91,29 @@
<main class="flex-1 flex flex-col min-w-0 relative z-10">
<!-- TOP NAVIGATION PATH -->
<header class="h-12 shrink-0 bg-white border-b border-gray-100 flex items-center px-6">
<header class="h-12 shrink-0 bg-[var(--bg,#ffffff)] border-b border-[var(--border-color,#f3f4f6)] flex items-center px-6">
<div class="font-mono text-xs font-semibold tracking-wider flex space-x-3 items-center">
<span class="text-gray-400">// DASHBOARD</span>
<span class="text-gray-300">&gt;</span>
<span class="text-gray-800">// E-COMMERCE APP DEVELOPMENT</span>
<span class="text-[var(--text-secondary,#9ca3af)]">// DASHBOARD</span>
<span class="text-[var(--text-muted,#d1d5db)]">&gt;</span>
<span class="text-[var(--text,#1f2937)]">// E-COMMERCE APP DEVELOPMENT</span>
</div>
</header>
<!-- STAGE NAVIGATION -->
<nav class="h-12 shrink-0 bg-white/90 backdrop-blur-sm border-b border-gray-100 flex items-center font-mono text-xs font-semibold z-10">
<div class="flex-1 flex justify-center border-r border-gray-100 py-3 hover:bg-gray-50 cursor-pointer text-gray-400 transition-colors">
<nav class="h-12 shrink-0 bg-[var(--bg,#ffffff)] border-b border-[var(--border-color,#f3f4f6)] flex items-center font-mono text-xs font-semibold z-10">
<div class="flex-1 flex justify-center border-r border-[var(--border-color,#f3f4f6)] py-3 hover:bg-[var(--bg-hover,#f9fafb)] cursor-pointer text-[var(--text-secondary,#9ca3af)] transition-colors">
// PLAN
</div>
<div class="flex-1 flex justify-center border-r border-gray-100 py-3 bg-brand-50 text-brand-600 cursor-pointer border-b-2 border-b-brand-500 transition-colors shadow-[inset_0_2px_4px_rgba(34,197,94,0.05)]">
<div class="flex-1 flex justify-center border-r border-[var(--border-color,#f3f4f6)] py-3 bg-[var(--primary-bg,#f0fdf4)] text-[var(--primary,#16a34a)] cursor-pointer border-b-2 border-b-[var(--primary,#22c55e)] transition-colors shadow-[inset_0_2px_4px_rgba(34,197,94,0.05)]">
// BUILD
</div>
<div class="flex-1 flex justify-center border-r border-gray-100 py-3 hover:bg-gray-50 cursor-pointer text-gray-400 transition-colors">
<div class="flex-1 flex justify-center border-r border-[var(--border-color,#f3f4f6)] py-3 hover:bg-[var(--bg-hover,#f9fafb)] cursor-pointer text-[var(--text-secondary,#9ca3af)] transition-colors">
// REVIEW
</div>
<div class="flex-1 flex justify-center border-r border-gray-100 py-3 hover:bg-gray-50 cursor-pointer text-gray-400 transition-colors">
<div class="flex-1 flex justify-center border-r border-[var(--border-color,#f3f4f6)] py-3 hover:bg-[var(--bg-hover,#f9fafb)] cursor-pointer text-[var(--text-secondary,#9ca3af)] transition-colors">
// DEPLOY
</div>
<div class="flex-1 flex justify-center py-3 hover:bg-gray-50 cursor-pointer text-gray-400 transition-colors">
<div class="flex-1 flex justify-center py-3 hover:bg-[var(--bg-hover,#f9fafb)] cursor-pointer text-[var(--text-secondary,#9ca3af)] transition-colors">
// MONITOR
</div>
</nav>

View file

@ -113,6 +113,7 @@ const SUITE_DIRS: &[&str] = &[
"project",
#[cfg(feature = "goals")]
"goals",
"vibe",
];
const ROOT_FILES: &[&str] = &[
@ -163,7 +164,7 @@ pub async fn index(OriginalUri(uri): OriginalUri) -> Response {
let path_parts: Vec<&str> = path.split('/').collect();
let fs_path = if path_parts.len() > 1 {
let mut start_idx = 1;
let known_dirs = ["suite", "js", "css", "vendor", "assets", "public", "partials", "settings", "auth", "about", "drive", "chat", "tasks", "admin", "mail", "calendar", "meet", "docs", "sheet", "slides", "paper", "research", "sources", "learn", "analytics", "dashboards", "monitoring", "people", "crm", "tickets", "billing", "products", "video", "player", "canvas", "social", "project", "goals", "workspace", "designer"];
let known_dirs = ["suite", "js", "css", "vendor", "assets", "public", "partials", "settings", "auth", "about", "drive", "chat", "tasks", "admin", "mail", "calendar", "meet", "docs", "sheet", "slides", "paper", "research", "sources", "learn", "analytics", "dashboards", "monitoring", "people", "crm", "tickets", "billing", "products", "video", "player", "canvas", "social", "project", "goals", "workspace", "designer", "vibe"];
// Special case: /auth/suite/* should map to suite/* (auth is a route, not a directory)
if path_parts.get(1) == Some(&"auth") && path_parts.get(2) == Some(&"suite") {

View file

@ -0,0 +1,604 @@
/* Chat Agent Mode — Z.ai toggle + OpenClaw multi-panel layout */
/* ============================================
AGENT / CHAT MODE TOGGLE (Z.ai style)
============================================ */
.chat-mode-toggle {
display: flex;
align-items: center;
gap: 2px;
background: var(--surface, #1a1a24);
border: 1px solid var(--border, #2a2a2a);
border-radius: 20px;
padding: 2px;
margin-right: 8px;
}
.chat-mode-btn {
padding: 5px 14px;
border: none;
border-radius: 18px;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
background: transparent;
color: var(--text-secondary, #888);
letter-spacing: 0.3px;
}
.chat-mode-btn.active {
background: #84d669;
color: #fff;
box-shadow: 0 2px 8px rgba(132, 214, 105, 0.3);
}
.chat-mode-btn:not(.active):hover {
color: var(--text, #fff);
background: var(--hover, rgba(255, 255, 255, 0.05));
}
/* ============================================
QUICK ACTION CHIPS (Z.ai style)
============================================ */
.quick-actions-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
justify-content: center;
animation: quickActionsIn 0.4s ease;
}
@keyframes quickActionsIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.quick-action-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border: 1px solid var(--border, #2a2a2a);
border-radius: 24px;
background: var(--surface, #1a1a24);
color: var(--text, #fff);
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
white-space: nowrap;
}
.quick-action-chip:hover {
border-color: #84d669;
background: rgba(132, 214, 105, 0.08);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(132, 214, 105, 0.15);
}
.quick-action-chip:active {
transform: translateY(0);
}
.quick-action-icon {
font-size: 14px;
}
/* ============================================
AGENT MODE MULTI-PANEL LAYOUT
============================================ */
.chat-layout.agent-mode {
max-width: none;
padding: 0;
display: grid;
grid-template-columns: 48px 1fr 1fr;
grid-template-rows: 1fr auto auto;
gap: 0;
}
/* Agent Mode Left Sidebar */
.agent-sidebar {
display: none;
grid-row: 1 / -1;
grid-column: 1;
background: #f8f8f8;
border-right: 1px solid #f0f1f2;
flex-direction: column;
z-index: 50;
}
.agent-mode .agent-sidebar {
display: flex;
}
.agent-sidebar-item {
width: 48px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
border-bottom: 1px solid #f0f1f2;
background: transparent;
border-left: none;
border-right: none;
border-top: none;
color: #666;
}
.agent-sidebar-item:hover {
background: #fff;
color: #3b3b3b;
}
.agent-sidebar-item.active {
background: #fff;
border-left: 3px solid #84d669;
color: #3b3b3b;
}
.agent-sidebar-badge {
position: absolute;
top: 4px;
right: 4px;
min-width: 16px;
height: 16px;
padding: 0 4px;
border-radius: 8px;
background: #84d669;
color: #fff;
font-size: 9px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Fira Code', monospace;
}
/* Chat Panel (in agent mode) */
.agent-mode #messages {
padding: 16px;
}
.agent-mode footer {
grid-column: 2 / -1;
padding: 8px 16px;
border-top: 1px solid #f0f1f2;
}
/* ============================================
THOUGHT PROCESS BLOCK
============================================ */
.thought-process {
margin: 12px 0;
border: 1px solid var(--border, #e0e0e0);
border-radius: 8px;
overflow: hidden;
background: var(--surface, #f8f9fa);
}
.thought-process-header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
cursor: pointer;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
color: var(--text, #3b3b3b);
background: var(--surface, #f0f1f2);
transition: background 0.15s;
border: none;
width: 100%;
text-align: left;
}
.thought-process-header:hover {
background: var(--hover, #e8e9ea);
}
.thought-process-toggle {
transition: transform 0.2s;
font-size: 10px;
}
.thought-process.expanded .thought-process-toggle {
transform: rotate(90deg);
}
.thought-process-body {
display: none;
padding: 12px 14px;
font-size: 13px;
line-height: 1.6;
color: var(--text-secondary, #666);
border-top: 1px solid var(--border, #e0e0e0);
}
.thought-process.expanded .thought-process-body {
display: block;
}
/* ============================================
BROWSER PANEL
============================================ */
.agent-browser-panel {
display: none;
grid-column: 3;
grid-row: 1;
border-left: 1px solid #f0f1f2;
flex-direction: column;
background: #fff;
}
.agent-mode .agent-browser-panel {
display: flex;
}
.browser-panel-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
color: #3b3b3b;
}
.browser-url-bar {
flex: 1;
padding: 4px 10px;
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 4px;
font-family: 'Fira Code', monospace;
font-size: 11px;
color: #666;
}
.browser-panel-content {
flex: 1;
background: #fff;
}
.browser-panel-content iframe {
width: 100%;
height: 100%;
border: none;
}
.browser-panel-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #ccc;
font-family: 'Fira Code', monospace;
font-size: 13px;
}
/* ============================================
TERMINAL PANEL
============================================ */
.agent-terminal-panel {
display: none;
grid-column: 2;
grid-row: 2;
border-top: 1px solid #f0f1f2;
flex-direction: column;
max-height: 200px;
min-height: 120px;
}
.agent-mode .agent-terminal-panel {
display: flex;
}
.terminal-panel-header {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: #1e1e1e;
border-bottom: 1px solid #333;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 600;
color: #84d669;
}
.terminal-panel-content {
flex: 1;
overflow-y: auto;
background: #1e1e1e;
padding: 8px 12px;
font-family: 'Fira Code', monospace;
font-size: 12px;
line-height: 1.5;
color: #d4d4d4;
}
.terminal-line {
white-space: pre-wrap;
word-break: break-all;
}
.terminal-line.stderr {
color: #f87171;
}
.terminal-line.stdout {
color: #d4d4d4;
}
/* ============================================
AGENT INFO CARD
============================================ */
.agent-info-card {
display: none;
grid-column: 3;
grid-row: 2;
border-top: 1px solid #f0f1f2;
border-left: 1px solid #f0f1f2;
padding: 12px 16px;
background: #f8f8f8;
flex-direction: column;
gap: 8px;
}
.agent-mode .agent-info-card {
display: flex;
}
.agent-info-name {
display: flex;
align-items: center;
gap: 8px;
font-family: 'Fira Code', monospace;
font-size: 13px;
font-weight: 600;
color: #3b3b3b;
}
.agent-info-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #84d669;
}
.agent-level-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-family: 'Fira Code', monospace;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.badge-evolved {
background: #84d669;
color: #fff;
}
.badge-bred {
background: #f59e0b;
color: #fff;
}
.badge-wild {
background: #ef4444;
color: #fff;
}
.agent-info-model {
font-family: 'Fira Code', monospace;
font-size: 11px;
color: #888;
}
.agent-info-toggles {
display: flex;
gap: 8px;
align-items: center;
}
.agent-toggle {
display: flex;
align-items: center;
gap: 4px;
font-family: 'Fira Code', monospace;
font-size: 11px;
color: #666;
}
.agent-toggle-switch {
width: 28px;
height: 16px;
border-radius: 8px;
background: #ccc;
position: relative;
cursor: pointer;
transition: background 0.2s;
border: none;
padding: 0;
}
.agent-toggle-switch.on {
background: #84d669;
}
.agent-toggle-switch::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #fff;
transition: transform 0.2s;
}
.agent-toggle-switch.on::after {
transform: translateX(12px);
}
/* ============================================
STEP COUNTER BAR
============================================ */
.agent-step-bar {
display: none;
grid-column: 1 / -1;
grid-row: 3;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
background: #f8f8f8;
border-top: 1px solid #f0f1f2;
}
.agent-mode .agent-step-bar {
display: flex;
}
.step-counter {
display: flex;
align-items: center;
gap: 12px;
font-family: 'Fira Code', monospace;
font-size: 13px;
color: #3b3b3b;
}
.step-nav-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid #e0e0e0;
background: #fff;
color: #666;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
font-size: 14px;
padding: 0;
}
.step-nav-btn:hover {
border-color: #84d669;
color: #84d669;
}
.step-action-btns {
display: flex;
gap: 6px;
}
.step-action-btn {
width: 36px;
height: 36px;
border-radius: 50%;
border: none;
background: #84d669;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
font-size: 16px;
padding: 0;
}
.step-action-btn:hover {
transform: scale(1.1);
box-shadow: 0 2px 8px rgba(132, 214, 105, 0.3);
}
/* ============================================
TODO LIST (OpenClaw style)
============================================ */
.agent-todo-list {
margin: 12px 0;
border: 1px solid var(--border, #e0e0e0);
border-radius: 8px;
overflow: hidden;
}
.agent-todo-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 14px;
background: var(--surface, #f0f1f2);
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
color: var(--text, #3b3b3b);
}
.agent-todo-count {
background: #84d669;
color: #fff;
padding: 1px 6px;
border-radius: 10px;
font-size: 10px;
}
.agent-todo-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 14px;
border-top: 1px solid var(--border, #e0e0e0);
font-size: 13px;
color: var(--text, #3b3b3b);
transition: opacity 0.2s;
}
.agent-todo-item.done {
opacity: 0.5;
text-decoration: line-through;
}
.agent-todo-check {
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.agent-todo-item.done .agent-todo-check {
background: #84d669;
border-color: #84d669;
color: #fff;
font-size: 10px;
}

View file

@ -1,26 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BUILD V3 - Web Desktop Environment</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/window-manager.js"></script>
<script src="/suite/js/theme-manager.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Fira Code', 'Fira Sans', Arial, sans-serif;
background: white;
background: var(--bg, #ffffff);
color: var(--text, #333333);
overflow: hidden;
height: 100vh;
width: 100vw;
@ -28,97 +35,321 @@
}
/* Core Layout replicating BUILD V3 screenshot styling */
.build-container { width: 100%; height: 100vh; display: flex; }
.build-container {
width: 100%;
height: 100vh;
display: flex;
}
/* Left Sidebar */
.sidebar { width: 51px; height: 100vh; background: #f8f8f8; border-right: 1px solid #f0f1f2; display: flex; flex-direction: column; z-index: 100; }
.sidebar-item { width: 51px; height: 50px; border-bottom: 1px solid #f0f1f2; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.15s ease; }
.sidebar-item:hover { background: #ffffff; }
.sidebar-item.active { background: #ffffff; border-left: 3px solid #84d669; }
.sidebar-icon { width: 30px; height: 30px; opacity: 0.6; }
.sidebar-item:hover .sidebar-icon { opacity: 1; }
.sidebar {
width: 51px;
height: 100vh;
background: var(--bg-secondary, #f8f8f8);
border-right: 1px solid var(--border-color, #f0f1f2);
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; }
.main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Top Navigation Tabs */
.tabs-container { display: flex; flex-direction: column; background: #f8f8f8; border-bottom: 1px solid #f0f1f2; z-index: 100;}
.tabs-row { display: flex; height: 34px; }
.main-tab { height: 34px; min-width: 169px; flex: 1; border-right: 1px solid #f0f1f2; display: flex; align-items: center; padding: 0 18px; cursor: pointer; }
.main-tab:hover { background: #ffffff; }
.main-tab.active { background: #84d669; }
.main-tab.active .main-tab-content { color: white; }
.main-tab-content { display: flex; align-items: center; gap: 4px; font-family: 'Fira Code', monospace; font-size: 14px; font-weight: 500; color: #3b3b3b; }
.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 { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: white; position: relative; }
.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;}
.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-direction: column; gap: 20px; align-items: center; width: 100px;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
width: 100px;
pointer-events: auto;
}
.desktop-icon {
display: flex; flex-direction: column; align-items: center; width: 80px;
cursor: pointer; position: relative; gap: 8px;
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;
width: 64px;
height: 64px;
border-radius: 16px;
background: linear-gradient(135deg, #4ade80 0%, #15803d 100%);
box-shadow: inset 0px 2px 4px rgba(255,255,255,0.4), inset 0px -3px 0 rgba(20,83,45,0.8), 0px 6px 12px rgba(0,0,0,0.15);
display: flex; align-items: center; justify-content: center;
box-shadow: inset 0px 2px 4px rgba(255, 255, 255, 0.4), inset 0px -3px 0 rgba(20, 83, 45, 0.8), 0px 6px 12px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.15s ease;
}
.desktop-icon:hover .app-icon { transform: scale(1.05); }
.app-icon svg { width: 32px; height: 32px; stroke: white; }
.desktop-icon:hover .app-icon {
transform: scale(1.05);
}
.app-icon svg {
width: 32px;
height: 32px;
stroke: white;
}
.desktop-icon-label {
font-family: 'Fira Code', monospace; font-size: 12px; font-weight: 600; color: #374151;
background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(4px);
padding: 2px 8px; border-radius: 4px; text-align: center;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
color: var(--text-secondary, #374151);
background: var(--bg-alpha-70, rgba(255, 255, 255, 0.7));
backdrop-filter: blur(4px);
padding: 2px 8px;
border-radius: 4px;
text-align: center;
}
/* The background abstract pattern from BUILD V3 */
.bg-grid { position: absolute; inset: 0; background-image: linear-gradient(to right, #f0fdf4 1px, transparent 1px), linear-gradient(to bottom, #f0fdf4 1px, transparent 1px); background-size: 40px 40px; z-index: 0; pointer-events: none; }
.bg-svg { position: absolute; top: -10%; left: -10%; width: 120%; height: 120%; z-index: 0; opacity: 0.6; pointer-events: none; }
.bg-svg path { fill: none; stroke: #e6f2eb; stroke-width: 45; stroke-linecap: round; stroke-linejoin: round; }
.bg-svg path.inner { stroke: #f7faf9; stroke-width: 41; }
.bg-grid {
position: absolute;
inset: 0;
background-image: linear-gradient(to right, var(--bg-grid, #f0fdf4) 1px, transparent 1px), linear-gradient(to bottom, var(--bg-grid, #f0fdf4) 1px, transparent 1px);
background-size: 40px 40px;
z-index: 0;
pointer-events: none;
}
.bg-svg {
position: absolute;
top: -10%;
left: -10%;
width: 120%;
height: 120%;
z-index: 0;
opacity: 0.6;
pointer-events: none;
}
.bg-svg path {
fill: none;
stroke: var(--bg-grid-stroke, #e6f2eb);
stroke-width: 45;
stroke-linecap: round;
stroke-linejoin: round;
}
.bg-svg path.inner {
stroke: var(--bg-grid-stroke-inner, #f7faf9);
stroke-width: 41;
}
/* Bottom Taskbar */
.toolbar { height: 50px; background: white; border-top: 1px solid #f0f1f2; 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: #3b3b3b; text-align: right; line-height: 1.4; padding: 0 10px; margin-left: auto; }
.toolbar {
height: 50px;
background: var(--bg-secondary, white);
border-top: 1px solid var(--border-color, #f0f1f2);
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;}
.taskbar-item:hover { background: #f8f8f8; }
.taskbar-item.active { border-bottom-color: #84d669; background: linear-gradient(to bottom, rgba(132,214,105,0) 50%, rgba(132,214,105,0.1) 100%); }
.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;
}
.taskbar-item:hover {
background: var(--bg-hover, #f8f8f8);
}
.taskbar-item.active {
border-bottom-color: var(--primary, #84d669);
background: linear-gradient(to bottom, transparent 50%, var(--primary-alpha-10, rgba(132, 214, 105, 0.1)) 100%);
}
</style>
</head>
<body>
<!-- Minibar Component (Phase 8) -->
<div hx-get="/suite/partials/minibar.html" hx-trigger="load" hx-swap="outerHTML"></div>
<div class="build-container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-item active" title="Home">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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>
<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">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<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">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" stroke-width="2"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>
<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">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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>
<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">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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>
<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">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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>
<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>
@ -126,23 +357,34 @@
<div class="main-wrapper">
<!-- Top Navigation Tabs -->
<div class="tabs-container">
<div class="breadcrumb-row" style="display: flex; height: 34px; border-bottom: 1px solid #f0f1f2; align-items: center; padding: 0 18px; font-family: 'Fira Code', monospace; font-size: 14px; color: #6b7280; gap: 8px;">
<span style="color: #374151;">// DASHBOARD</span>
<span style="color: #9ca3af;">></span>
<span style="color: #374151;">// E-COMMERCE APP DEVELOPMENT</span>
<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 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 -->
<div class="workspace" id="desktop-content">
<!-- Background Pattern -->
<div class="bg-grid"></div>
<svg class="bg-svg" preserveAspectRatio="xMidYMid slice" viewBox="0 0 1000 600">
@ -156,52 +398,85 @@
<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="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>
<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/tasks.html" hx-swap="none">
<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>
<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/chat/chat.html" hx-swap="none">
<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>
<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="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>
<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="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>
<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="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>
<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="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>
<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>
@ -235,7 +510,7 @@
} else {
console.error("WindowManager class not loaded from window-manager.js");
}
// Initialize ThemeManager
if (typeof window.ThemeManager !== 'undefined') {
window.ThemeManager.init();
@ -243,21 +518,21 @@
});
// Listen to HTMX afterRequest event
document.body.addEventListener('htmx:afterRequest', function(evt) {
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');
@ -271,9 +546,10 @@
// 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-time').textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
document.getElementById('clock-date').textContent = now.toLocaleDateString();
}, 1000);
</script>
</body>
</html>
</html>

View file

@ -0,0 +1,302 @@
/**
* Chat Agent Mode handles toggling between Agent and Chat mode,
* multi-panel layout management, and WebSocket message routing
* for thought process, terminal output, browser preview, and step tracking.
*/
(function () {
"use strict";
var agentMode = false;
var currentStep = 0;
var totalSteps = 0;
var terminalLineCount = 0;
function initAgentMode() {
setupModeToggle();
setupToggleSwitches();
setupStepNavigation();
setupQuickActions();
setupSidebarItems();
}
function setupModeToggle() {
var agentBtn = document.getElementById("modeAgentBtn");
var chatBtn = document.getElementById("modeChatBtn");
if (!agentBtn || !chatBtn) return;
agentBtn.addEventListener("click", function () {
setMode("agent");
});
chatBtn.addEventListener("click", function () {
setMode("chat");
});
}
function setMode(mode) {
var chatApp = document.getElementById("chat-app");
var agentBtn = document.getElementById("modeAgentBtn");
var chatBtn = document.getElementById("modeChatBtn");
var quickActions = document.getElementById("quickActions");
if (!chatApp || !agentBtn || !chatBtn) return;
agentMode = mode === "agent";
agentBtn.classList.toggle("active", agentMode);
chatBtn.classList.toggle("active", !agentMode);
if (agentMode) {
chatApp.classList.add("agent-mode");
if (quickActions) quickActions.style.display = "none";
} else {
chatApp.classList.remove("agent-mode");
if (quickActions) quickActions.style.display = "";
}
}
function setupToggleSwitches() {
var planToggle = document.getElementById("togglePlan");
var yoloToggle = document.getElementById("toggleYolo");
if (planToggle) {
planToggle.addEventListener("click", function () {
this.classList.toggle("on");
emitModeChange();
});
}
if (yoloToggle) {
yoloToggle.addEventListener("click", function () {
this.classList.toggle("on");
emitModeChange();
});
}
}
function emitModeChange() {
var planOn = document.getElementById("togglePlan");
var yoloOn = document.getElementById("toggleYolo");
var mode = "plan";
if (yoloOn && yoloOn.classList.contains("on")) {
mode = "yolo";
}
if (window.ws && window.ws.readyState === WebSocket.OPEN) {
window.ws.send(JSON.stringify({
type: "toggle_mode",
mode: mode
}));
}
}
function setupStepNavigation() {
var prevBtn = document.getElementById("stepPrev");
var nextBtn = document.getElementById("stepNext");
if (prevBtn) {
prevBtn.addEventListener("click", function () {
if (currentStep > 1) {
currentStep--;
updateStepCounter();
}
});
}
if (nextBtn) {
nextBtn.addEventListener("click", function () {
if (currentStep < totalSteps) {
currentStep++;
updateStepCounter();
}
});
}
}
function updateStepCounter() {
var display = document.getElementById("stepCounterText");
if (display) {
display.textContent = currentStep + " / " + totalSteps;
}
}
function setupQuickActions() {
var chips = document.querySelectorAll(".quick-action-chip");
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
var action = this.getAttribute("data-action");
var prompts = {
"full-stack": "Create a full-stack web application",
"writing": "Help me write ",
"data-insight": "Analyze data and provide insights",
"magic-design": "Design a beautiful UI for "
};
var input = document.getElementById("messageInput");
if (input && prompts[action]) {
input.value = prompts[action];
input.focus();
}
});
});
}
function setupSidebarItems() {
var items = document.querySelectorAll(".agent-sidebar-item");
items.forEach(function (item) {
item.addEventListener("click", function () {
items.forEach(function (i) { i.classList.remove("active"); });
this.classList.add("active");
});
});
}
/* ===========================================
Agent Mode WebSocket Message Handlers
=========================================== */
function handleAgentMessage(data) {
if (!agentMode) return;
switch (data.type) {
case "thought_process":
renderThoughtProcess(data.content);
break;
case "terminal_output":
appendTerminalLine(data.line, data.stream);
break;
case "browser_ready":
showBrowserPreview(data.url);
break;
case "step_progress":
currentStep = data.current;
totalSteps = data.total;
updateStepCounter();
break;
case "step_complete":
break;
case "todo_update":
renderTodoList(data.todos);
break;
case "agent_status":
updateAgentInfo(data);
break;
case "file_created":
incrementBadge("explorerBadge");
break;
}
}
function renderThoughtProcess(content) {
var messages = document.getElementById("messages");
if (!messages) return;
var block = document.createElement("div");
block.className = "thought-process";
block.innerHTML =
'<button class="thought-process-header">' +
'<span class="thought-process-toggle">▶</span>' +
'<span>Thought Process</span>' +
"</button>" +
'<div class="thought-process-body">' + escapeForHtml(content) + "</div>";
var header = block.querySelector(".thought-process-header");
header.addEventListener("click", function () {
block.classList.toggle("expanded");
});
messages.appendChild(block);
}
function appendTerminalLine(text, stream) {
var terminal = document.getElementById("terminalPanelContent");
if (!terminal) return;
var line = document.createElement("div");
line.className = "terminal-line " + (stream || "stdout");
line.textContent = text;
terminal.appendChild(line);
terminal.scrollTop = terminal.scrollHeight;
terminalLineCount++;
incrementBadge("terminalBadge");
}
function showBrowserPreview(url) {
var content = document.getElementById("browserPanelContent");
var urlBar = document.getElementById("browserUrlBar");
if (!content || !urlBar) return;
urlBar.value = url;
content.innerHTML = '<iframe src="' + url + '" sandbox="allow-scripts allow-same-origin"></iframe>';
}
function renderTodoList(todos) {
var messages = document.getElementById("messages");
if (!messages) return;
var existing = messages.querySelector(".agent-todo-list:last-child");
if (existing) existing.remove();
var list = document.createElement("div");
list.className = "agent-todo-list";
var headerHtml = '<div class="agent-todo-header">' +
'<span>📋 Todos</span>' +
'<span class="agent-todo-count">' + todos.length + "</span>" +
"</div>";
var itemsHtml = todos.map(function (todo) {
var doneClass = todo.done ? " done" : "";
var checkMark = todo.done ? "✓" : "";
return '<div class="agent-todo-item' + doneClass + '">' +
'<span class="agent-todo-check">' + checkMark + "</span>" +
'<span>' + escapeForHtml(todo.text) + "</span>" +
"</div>";
}).join("");
list.innerHTML = headerHtml + itemsHtml;
messages.appendChild(list);
}
function updateAgentInfo(data) {
var nameEl = document.getElementById("agentNameDisplay");
var levelEl = document.getElementById("agentLevelBadge");
var modelEl = document.getElementById("agentModelDisplay");
if (nameEl && data.name) nameEl.textContent = data.name;
if (levelEl && data.level) {
levelEl.textContent = data.level;
levelEl.className = "agent-level-badge badge-" + data.level.toLowerCase();
}
if (modelEl && data.model) {
modelEl.textContent = data.model + " — " + (data.usage || 0) + "%";
}
}
function incrementBadge(badgeId) {
var badge = document.getElementById(badgeId);
if (!badge) return;
var count = parseInt(badge.textContent, 10) || 0;
badge.textContent = count + 1;
badge.style.display = "";
}
function escapeForHtml(text) {
var div = document.createElement("div");
div.textContent = text || "";
return div.innerHTML;
}
/* ===========================================
Expose to global scope
=========================================== */
window.AgentMode = {
init: initAgentMode,
handleMessage: handleAgentMessage,
setMode: setMode,
isActive: function () { return agentMode; }
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAgentMode);
} else {
initAgentMode();
}
})();

View file

@ -1,5 +1,6 @@
<link rel="stylesheet" href="/suite/chat/chat.css?v=3" />
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
<link rel="stylesheet" href="/suite/css/chat-agent-mode.css" />
<div class="chat-layout" id="chat-app">
<!-- Connection Status -->
@ -8,9 +9,119 @@
<span class="connection-text">Connecting...</span>
</div>
<!-- Agent Mode: Left Sidebar -->
<aside class="agent-sidebar" id="agentSidebar">
<button class="agent-sidebar-item active" data-panel="chat" title="Chat">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<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>
</button>
<button class="agent-sidebar-item" data-panel="tasks" title="Tasks">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<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>
</button>
<button class="agent-sidebar-item" data-panel="terminal" title="Terminal">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="4 17 10 11 4 5" />
<line x1="12" y1="19" x2="20" y2="19" />
</svg>
<span class="agent-sidebar-badge" id="terminalBadge" style="display:none;">0</span>
</button>
<button class="agent-sidebar-item" data-panel="explorer" title="Explorer">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<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>
<span class="agent-sidebar-badge" id="explorerBadge" style="display:none;">0</span>
</button>
<button class="agent-sidebar-item" data-panel="editor" title="Editor">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="16 18 22 12 16 6" />
<polyline points="8 6 2 12 8 18" />
</svg>
</button>
<button class="agent-sidebar-item" data-panel="browser" title="Browser">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<line x1="2" y1="12" x2="22" y2="12" />
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
</svg>
</button>
</aside>
<main id="messages"></main>
<!-- Agent Mode: Browser Panel -->
<div class="agent-browser-panel" id="agentBrowserPanel">
<div class="browser-panel-header">
<span>// BROWSER</span>
<input type="text" class="browser-url-bar" id="browserUrlBar" value="" readonly
placeholder="No preview active" />
</div>
<div class="browser-panel-content" id="browserPanelContent">
<div class="browser-panel-empty">Waiting for app preview...</div>
</div>
</div>
<!-- Agent Mode: Terminal Panel -->
<div class="agent-terminal-panel" id="agentTerminalPanel">
<div class="terminal-panel-header">
<span>// TERMINAL</span>
</div>
<div class="terminal-panel-content" id="terminalPanelContent"></div>
</div>
<!-- Agent Mode: Agent Info Card -->
<div class="agent-info-card" id="agentInfoCard">
<div class="agent-info-name">
<span class="agent-info-dot"></span>
<span id="agentNameDisplay">Agent #1</span>
</div>
<span class="agent-level-badge badge-evolved" id="agentLevelBadge">EVOLVED</span>
<div class="agent-info-model" id="agentModelDisplay">Claude Opus 4.5 &mdash; 99%</div>
<div class="agent-info-toggles">
<div class="agent-toggle">
<span>Plan</span>
<button class="agent-toggle-switch on" id="togglePlan" type="button"></button>
</div>
<div class="agent-toggle">
<span>YOLO</span>
<button class="agent-toggle-switch" id="toggleYolo" type="button"></button>
</div>
</div>
</div>
<!-- Agent Mode: Step Counter Bar -->
<div class="agent-step-bar" id="agentStepBar">
<div class="step-counter">
<button class="step-nav-btn" id="stepPrev" type="button"></button>
<span id="stepCounterText">0 / 0</span>
<button class="step-nav-btn" id="stepNext" type="button"></button>
</div>
<div class="step-action-btns">
<button class="step-action-btn" title="Chat" type="button">💬</button>
<button class="step-action-btn" title="Edit" type="button">✏️</button>
<button class="step-action-btn" title="Code" type="button">&lt;/&gt;</button>
</div>
</div>
<footer>
<!-- Quick Action Chips (visible in chat mode) -->
<div class="quick-actions-container" id="quickActions">
<button class="quick-action-chip" type="button" data-action="full-stack">
<span class="quick-action-icon">🔧</span> Full-Stack
</button>
<button class="quick-action-chip" type="button" data-action="writing">
<span class="quick-action-icon">✏️</span> Writing
</button>
<button class="quick-action-chip" type="button" data-action="data-insight">
<span class="quick-action-icon">📊</span> Data Insight
</button>
<button class="quick-action-chip" type="button" data-action="magic-design">
<span class="quick-action-icon"></span> Magic Design
</button>
</div>
<div class="suggestions-container" id="suggestions"></div>
<div class="mention-dropdown" id="mentionDropdown">
<div class="mention-header">
@ -19,6 +130,11 @@
<div class="mention-results" id="mentionResults"></div>
</div>
<form class="input-container" id="chatForm">
<!-- Agent/Chat Mode Toggle (Z.ai style) -->
<div class="chat-mode-toggle" id="chatModeToggle">
<button type="button" class="chat-mode-btn active" data-mode="agent" id="modeAgentBtn">Agent</button>
<button type="button" class="chat-mode-btn" data-mode="chat" id="modeChatBtn">Chat</button>
</div>
<input name="content" id="messageInput" type="text" placeholder="Message... (type @ to mention)"
data-i18n-placeholder="chat-placeholder" autofocus autocomplete="off" />
<button type="submit" id="sendBtn" title="Send" data-i18n-title="chat-send">
@ -911,6 +1027,15 @@
}
}
// Route agent-type messages to AgentMode handler
if (window.AgentMode && data.type && [
"thought_process", "terminal_output", "browser_ready",
"step_progress", "step_complete", "todo_update",
"agent_status", "file_created"
].indexOf(data.type) !== -1) {
window.AgentMode.handleMessage(data);
}
// Only process bot responses
if (data.message_type === MessageType.BOT_RESPONSE) {
console.log("Processing bot response:", data);
@ -945,7 +1070,7 @@
// Apply theme data from WebSocket events
function getContrastYIQ(hexcolor) {
if (!hexcolor) return '#ffffff';
// Handle named colors and variables by letting the browser resolve them
var temp = document.createElement("div");
temp.style.color = hexcolor;
@ -953,14 +1078,14 @@
document.div.appendChild(temp);
var style = window.getComputedStyle(temp).color;
document.div.removeChild(temp);
var rgb = style.match(/\d+/g);
if (!rgb || rgb.length < 3) return '#ffffff';
var r = parseInt(rgb[0]);
var g = parseInt(rgb[1]);
var b = parseInt(rgb[2]);
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return (yiq >= 128) ? '#000000' : '#ffffff';
}
@ -1179,4 +1304,5 @@
console.log("Chat module initialized with @ mentions support");
})();
</script>
</script>
<script src="/suite/js/chat-agent-mode.js"></script>

View file

@ -1,6 +1,11 @@
<style>
/* CRITICAL: Overriding default Tailwind resets that break the layout */
* { margin: 0; padding: 0; box-sizing: border-box; }
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Fira Code', 'Fira Sans', Arial, sans-serif !important;
background: white !important;
@ -11,73 +16,291 @@
}
/* Core Layout replicating BUILD V3 screenshot styling */
.build-container { width: 100%; height: 100vh; display: flex; position: absolute; inset: 0; z-index: 1000; background: white;}
.build-container {
width: 100%;
height: 100vh;
display: flex;
position: absolute;
inset: 0;
z-index: 1000;
background: white;
}
/* Left Sidebar */
.sidebar { width: 51px; height: 100vh; background: #f8f8f8; border-right: 1px solid #f0f1f2; display: flex; flex-direction: column; z-index: 100; }
.sidebar-item { width: 51px; height: 50px; border-bottom: 1px solid #f0f1f2; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.15s ease; position: relative;}
.sidebar-item:hover { background: #ffffff; }
.sidebar-item.active { background: #ffffff; border-left: 3px solid #84d669; }
.sidebar-icon { width: 30px; height: 30px; opacity: 0.6; }
.sidebar-item:hover .sidebar-icon { opacity: 1; }
.sidebar {
width: 51px;
height: 100vh;
background: #f8f8f8;
border-right: 1px solid #f0f1f2;
display: flex;
flex-direction: column;
z-index: 100;
}
.sidebar-item {
width: 51px;
height: 50px;
border-bottom: 1px solid #f0f1f2;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
}
.sidebar-item:hover {
background: #ffffff;
}
.sidebar-item.active {
background: #ffffff;
border-left: 3px solid #84d669;
}
.sidebar-icon {
width: 30px;
height: 30px;
opacity: 0.6;
}
.sidebar-item:hover .sidebar-icon {
opacity: 1;
}
/* Main Content wrapper */
.main-wrapper { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; }
.main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
/* Top Navigation Tabs */
.tabs-container { display: flex; flex-direction: column; background: #f8f8f8; border-bottom: 1px solid #f0f1f2; z-index: 100;}
.tabs-row { display: flex; height: 34px; }
.main-tab { height: 34px; min-width: 169px; flex: 1; border-right: 1px solid #f0f1f2; display: flex; align-items: center; padding: 0 18px; cursor: pointer; position: relative; }
.main-tab:hover { background: #ffffff; }
.main-tab.active { background: #84d669; border-color: #84d669;}
.main-tab.active .main-tab-content { color: white; }
.main-tab-content { display: flex; align-items: center; gap: 4px; font-family: 'Fira Code', monospace; font-size: 14px; font-weight: 500; color: #3b3b3b; }
.tabs-container {
display: flex;
flex-direction: column;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
z-index: 100;
}
.tabs-row {
display: flex;
height: 34px;
}
.main-tab {
height: 34px;
min-width: 169px;
flex: 1;
border-right: 1px solid #f0f1f2;
display: flex;
align-items: center;
padding: 0 18px;
cursor: pointer;
position: relative;
}
.main-tab:hover {
background: #ffffff;
}
.main-tab.active {
background: #84d669;
border-color: #84d669;
}
.main-tab.active .main-tab-content {
color: white;
}
.main-tab-content {
display: flex;
align-items: center;
gap: 4px;
font-family: 'Fira Code', monospace;
font-size: 14px;
font-weight: 500;
color: #3b3b3b;
}
/* Workspace (Where windows float) */
.workspace { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: white; position: relative; z-index: 10;}
.workspace {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background: white;
position: relative;
z-index: 10;
}
/* 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;}
.panel-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; max-width: 1200px; pointer-events: auto;}
.panel-section {
flex: 1;
padding: 26px 33px;
overflow-y: auto;
z-index: 10;
position: absolute;
inset: 0;
pointer-events: none;
}
.panel-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
max-width: 1200px;
pointer-events: auto;
}
/* Interactive Desktop Icons triggering HTMX */
.desktop-icon {
background: white; border: 1px solid #f0f1f2; border-radius: 8px; height: 67px; padding: 10px;
display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start;
cursor: pointer; transition: all 0.2s ease; position: relative; width: 100%;
background: white;
border: 1px solid #f0f1f2;
border-radius: 8px;
height: 67px;
padding: 10px;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
width: 100%;
}
.desktop-icon:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
border-color: #84d669;
}
.panel-card-icon {
position: absolute;
top: -20px;
left: 10px;
width: 42px;
height: 42px;
}
.panel-card-label {
font-family: 'Fira Code', monospace;
font-size: 13px;
font-weight: 500;
color: #3b3b3b;
margin-top: auto;
}
.desktop-icon:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); transform: translateY(-2px); border-color: #84d669; }
.panel-card-icon { position: absolute; top: -20px; left: 10px; width: 42px; height: 42px; }
.panel-card-label { font-family: 'Fira Code', monospace; font-size: 13px; font-weight: 500; color: #3b3b3b; margin-top: auto; }
/* The background abstract pattern from BUILD V3 */
.bg-grid { position: absolute; inset: 0; background-image: linear-gradient(to right, #f0fdf4 1px, transparent 1px), linear-gradient(to bottom, #f0fdf4 1px, transparent 1px); background-size: 40px 40px; z-index: 0; pointer-events: none; }
.bg-svg { position: absolute; top: -10%; left: -10%; width: 120%; height: 120%; z-index: 0; opacity: 0.6; pointer-events: none; display: block !important;}
.bg-svg path { fill: none; stroke: #e6f2eb; stroke-width: 45; stroke-linecap: round; stroke-linejoin: round; }
.bg-svg path.inner { stroke: #f7faf9; stroke-width: 41; }
.bg-grid {
position: absolute;
inset: 0;
background-image: linear-gradient(to right, #f0fdf4 1px, transparent 1px), linear-gradient(to bottom, #f0fdf4 1px, transparent 1px);
background-size: 40px 40px;
z-index: 0;
pointer-events: none;
}
.bg-svg {
position: absolute;
top: -10%;
left: -10%;
width: 120%;
height: 120%;
z-index: 0;
opacity: 0.6;
pointer-events: none;
display: block !important;
}
.bg-svg path {
fill: none;
stroke: #e6f2eb;
stroke-width: 45;
stroke-linecap: round;
stroke-linejoin: round;
}
.bg-svg path.inner {
stroke: #f7faf9;
stroke-width: 41;
}
/* Bottom Taskbar */
.toolbar { height: 50px; background: white; border-top: 1px solid #f0f1f2; 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: #3b3b3b; text-align: right; line-height: 1.4; padding: 0 10px; margin-left: auto; }
.toolbar {
height: 50px;
background: white;
border-top: 1px solid #f0f1f2;
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: #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;}
.taskbar-item:hover { background: #f8f8f8; }
.taskbar-item.active { border-bottom-color: #84d669; background: linear-gradient(to bottom, rgba(132,214,105,0) 50%, rgba(132,214,105,0.1) 100%); }
.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;
}
.taskbar-item:hover {
background: #f8f8f8;
}
.taskbar-item.active {
border-bottom-color: #84d669;
background: linear-gradient(to bottom, rgba(132, 214, 105, 0) 50%, rgba(132, 214, 105, 0.1) 100%);
}
/* Utility */
svg { display: block; }
svg {
display: block;
}
</style>
<div class="build-container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-item active" title="Home">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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>
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" 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="Terminal">
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" stroke-width="2"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>
<svg class="sidebar-icon" viewBox="0 0 24 24" fill="none" stroke="#3b3b3b" stroke-width="2">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
</div>
</aside>
@ -86,16 +309,24 @@
<!-- Top Navigation Tabs -->
<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 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 -->
<div class="workspace" id="desktop-content-inner">
<!-- Background Pattern -->
<div class="bg-grid"></div>
<svg class="bg-svg" preserveAspectRatio="xMidYMid slice" viewBox="0 0 1000 600">
@ -109,62 +340,80 @@
<div class="panel-section">
<div class="panel-grid">
<!-- HTMX Enabled Desktop Icons that WindowManager catches -->
<div class="desktop-icon" data-app-id="vibe" data-app-title="Vibe" hx-get="/suite/partials/chat.html" hx-swap="none">
<div class="desktop-icon" data-app-id="chat" data-app-title="Chat"
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="#84d669" stroke-width="2"/>
<path d="M14 21h14M21 14v14" stroke="#84d669" stroke-width="2"/>
<circle cx="21" cy="21" r="20" stroke="#84d669" stroke-width="2" />
<path d="M14 21h14M21 14v14" stroke="#84d669" stroke-width="2" />
</svg>
<div class="panel-card-label">Mantis</div>
<div class="panel-card-label">Chat</div>
</div>
<div class="desktop-icon" data-app-id="tasks" data-app-title="Tasks" hx-get="/suite/partials/tasks.html" hx-swap="none">
<div class="desktop-icon" data-app-id="vibe" data-app-title="Vibe"
hx-get="/suite/partials/vibe.html" hx-swap="none">
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
<rect x="2" y="2" width="38" height="38" rx="4" stroke="#3b3b3b" stroke-width="2"/>
<line x1="12" y1="12" x2="30" y2="12" stroke="#3b3b3b" stroke-width="2"/>
<line x1="12" y1="21" x2="30" y2="21" stroke="#3b3b3b" stroke-width="2"/>
<line x1="12" y1="30" x2="24" y2="30" stroke="#3b3b3b" stroke-width="2"/>
<circle cx="21" cy="21" r="20" stroke="#84d669" stroke-width="2" />
<path d="M12 14l8 7-8 7" stroke="#84d669" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
<line x1="22" y1="28" x2="32" y2="28" stroke="#84d669" stroke-width="2"
stroke-linecap="round" />
</svg>
<div class="panel-card-label">Vibe</div>
</div>
<div class="desktop-icon" data-app-id="tasks" data-app-title="Tasks"
hx-get="/suite/tasks/task-window.html" hx-swap="none">
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
<rect x="2" y="2" width="38" height="38" rx="4" stroke="#3b3b3b" stroke-width="2" />
<line x1="12" y1="12" x2="30" y2="12" stroke="#3b3b3b" stroke-width="2" />
<line x1="12" y1="21" x2="30" y2="21" stroke="#3b3b3b" stroke-width="2" />
<line x1="12" y1="30" x2="24" y2="30" stroke="#3b3b3b" stroke-width="2" />
</svg>
<div class="panel-card-label">Tasks</div>
</div>
<div class="desktop-icon" data-app-id="terminal" data-app-title="Terminal" hx-get="/suite/partials/terminal.html" hx-swap="none">
<div class="desktop-icon" data-app-id="terminal" data-app-title="Terminal"
hx-get="/suite/partials/terminal.html" 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="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="14" x2="32" y2="14" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="22" x2="28" y2="22" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="30" x2="24" y2="30" stroke="#3b3b3b" stroke-width="2"/>
<rect x="2" y="4" width="38" height="34" rx="4" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="14" x2="32" y2="14" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="22" x2="28" y2="22" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="30" x2="24" y2="30" stroke="#3b3b3b" stroke-width="2" />
</svg>
<div class="panel-card-label">Terminal</div>
</div>
<div class="desktop-icon" data-app-id="explorer" data-app-title="Explorer" hx-get="/suite/partials/explorer.html" hx-swap="none">
<div class="desktop-icon" data-app-id="explorer" data-app-title="Explorer"
hx-get="/suite/partials/explorer.html" hx-swap="none">
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
<rect x="2" y="2" width="38" height="38" rx="4" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="10" x2="32" y2="10" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="18" x2="28" y2="18" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="26" x2="32" y2="26" stroke="#3b3b3b" stroke-width="2"/>
<line x1="10" y1="34" x2="24" y2="34" stroke="#3b3b3b" stroke-width="2"/>
<rect x="2" y="2" width="38" height="38" rx="4" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="10" x2="32" y2="10" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="18" x2="28" y2="18" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="26" x2="32" y2="26" stroke="#3b3b3b" stroke-width="2" />
<line x1="10" y1="34" x2="24" y2="34" stroke="#3b3b3b" stroke-width="2" />
</svg>
<div class="panel-card-label">Explorer</div>
</div>
<div class="desktop-icon" data-app-id="editor" data-app-title="Editor" hx-get="/suite/partials/editor.html" hx-swap="none">
<div class="desktop-icon" data-app-id="editor" data-app-title="Editor"
hx-get="/suite/partials/editor.html" hx-swap="none">
<svg class="panel-card-icon" viewBox="0 0 42 42" fill="none">
<polyline points="4 8 12 2 20 8" stroke="#3b3b3b" stroke-width="2"/>
<line x1="12" y1="2" x2="12" y2="24" stroke="#3b3b3b" stroke-width="2"/>
<polyline points="22 16 30 10 38 16" stroke="#3b3b3b" stroke-width="2"/>
<line x1="30" y1="10" x2="30" y2="32" stroke="#3b3b3b" stroke-width="2"/>
<polyline points="4 8 12 2 20 8" stroke="#3b3b3b" stroke-width="2" />
<line x1="12" y1="2" x2="12" y2="24" stroke="#3b3b3b" stroke-width="2" />
<polyline points="22 16 30 10 38 16" stroke="#3b3b3b" stroke-width="2" />
<line x1="30" y1="10" x2="30" y2="32" stroke="#3b3b3b" stroke-width="2" />
</svg>
<div class="panel-card-label">Editor</div>
</div>
<div class="desktop-icon" data-app-id="browser" data-app-title="Browser" hx-get="/suite/partials/browser.html" hx-swap="none">
<div class="desktop-icon" data-app-id="browser" data-app-title="Browser"
hx-get="/suite/partials/browser.html" 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="#3b3b3b" stroke-width="2"/>
<circle cx="14" cy="16" r="4" stroke="#3b3b3b" stroke-width="2"/>
<path d="M6 34a6 6 0 0 1 6-6h10a6 6 0 0 1 6 6v2H6v-2z" stroke="#3b3b3b" stroke-width="2"/>
<rect x="2" y="4" width="38" height="34" rx="4" stroke="#3b3b3b" stroke-width="2" />
<circle cx="14" cy="16" r="4" stroke="#3b3b3b" stroke-width="2" />
<path d="M6 34a6 6 0 0 1 6-6h10a6 6 0 0 1 6 6v2H6v-2z" stroke="#3b3b3b" stroke-width="2" />
</svg>
<div class="panel-card-label">Browser</div>
</div>
@ -195,7 +444,7 @@
const now = new Date();
const timeEl = document.getElementById('clock-time');
const dateEl = document.getElementById('clock-date');
if(timeEl) timeEl.textContent = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
if(dateEl) dateEl.textContent = now.toLocaleDateString();
if (timeEl) timeEl.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
if (dateEl) dateEl.textContent = now.toLocaleDateString();
}, 1000);
</script>

View file

@ -0,0 +1,73 @@
<!-- Minibar Component — Top-left navigation -->
<style>
.minibar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 28px;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
display: flex;
align-items: center;
padding: 0 12px;
z-index: 9999;
font-family: 'Fira Code', monospace;
}
.minibar-brand {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
font-weight: 700;
color: #3b3b3b;
}
.minibar-brand-icon {
width: 16px;
height: 16px;
}
.minibar-actions {
display: flex;
align-items: center;
gap: 4px;
margin-left: auto;
}
.minibar-btn {
width: 24px;
height: 24px;
border: none;
border-radius: 4px;
background: transparent;
color: #888;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
font-size: 13px;
padding: 0;
}
.minibar-btn:hover {
background: #e0e0e0;
color: #3b3b3b;
}
</style>
<div class="minibar" id="minibar">
<div class="minibar-brand">
<svg class="minibar-brand-icon" viewBox="0 0 24 24" fill="none" stroke="#84d669" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<path d="M8 12l3 3 5-5" />
</svg>
<span>Agent Farm</span>
</div>
<div class="minibar-actions">
<button class="minibar-btn" title="Settings" type="button"></button>
<button class="minibar-btn" title="Account" type="button">👤</button>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/* =============================================================================
AUTOTASK - SENTIENT THEME
Dark UI with Neon Yellow/Lime Accents (#CDFE00)
Pixel-perfect match to Mantis Farm Dashboard
Pixel-perfect match to Agent Dashboard
============================================================================= */
/* =============================================================================
@ -1166,10 +1166,12 @@ body:has(.autotask-container) {
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
@ -1224,10 +1226,12 @@ body:has(.autotask-container) {
}
@keyframes pulse-glow {
0%,
100% {
filter: drop-shadow(0 0 4px var(--sentient-accent-glow));
}
50% {
filter: drop-shadow(0 0 12px var(--sentient-accent));
}
@ -1335,6 +1339,7 @@ body:has(.autotask-container) {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
@ -1596,6 +1601,7 @@ body:has(.autotask-container) {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
@ -1709,4 +1715,4 @@ body:has(.autotask-container) {
width: auto;
bottom: 12px;
}
}
}

View file

@ -1,7 +1,7 @@
/* =============================================================================
AUTOTASK - SENTIENT THEME
Dark UI with Neon Yellow/Lime Accents (#CDFE00)
Pixel-perfect match to Mantis Farm Dashboard
Pixel-perfect match to Agent Dashboard
============================================================================= */
/* =============================================================================
@ -1166,10 +1166,12 @@ body:has(.autotask-container) {
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
@ -1224,10 +1226,12 @@ body:has(.autotask-container) {
}
@keyframes pulse-glow {
0%,
100% {
filter: drop-shadow(0 0 4px var(--sentient-accent-glow));
}
50% {
filter: drop-shadow(0 0 12px var(--sentient-accent));
}
@ -1335,6 +1339,7 @@ body:has(.autotask-container) {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
@ -1596,6 +1601,7 @@ body:has(.autotask-container) {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
@ -1709,4 +1715,4 @@ body:has(.autotask-container) {
width: auto;
bottom: 12px;
}
}
}

View file

@ -0,0 +1,411 @@
/* Task Window — Unified Tasks Dashboard Styles */
.tw-container {
display: flex;
flex-direction: column;
height: 100%;
background: #fff;
font-family: 'Fira Code', monospace;
}
/* ============================================
TAB HEADER
============================================ */
.tw-tabs-header {
display: flex;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
min-height: 34px;
overflow-x: auto;
}
.tw-tab {
height: 34px;
min-width: 140px;
padding: 0 18px;
border: none;
border-right: 1px solid #f0f1f2;
background: transparent;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 600;
color: #3b3b3b;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.15s;
white-space: nowrap;
}
.tw-tab:hover {
background: #fff;
}
.tw-tab.active {
background: #84d669;
color: #fff;
}
.tw-tab-prefix {
opacity: 0.7;
}
.tw-tab .tw-tab-close {
margin-left: 8px;
opacity: 0.5;
font-size: 14px;
cursor: pointer;
}
.tw-tab .tw-tab-close:hover {
opacity: 1;
}
/* ============================================
PIPELINE TABS
============================================ */
.tw-pipeline-tabs {
display: flex;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
}
.tw-pipeline-tab {
flex: 1;
height: 32px;
border: none;
border-right: 1px solid #f0f1f2;
background: transparent;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 500;
color: #888;
cursor: pointer;
transition: all 0.15s;
}
.tw-pipeline-tab:last-child {
border-right: none;
}
.tw-pipeline-tab:hover {
background: #fff;
color: #3b3b3b;
}
.tw-pipeline-tab.active {
background: #84d669;
color: #fff;
}
/* ============================================
FILTERS
============================================ */
.tw-filters {
display: flex;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid #f0f1f2;
}
.tw-filter-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 5px 12px;
border: 1px solid #e0e0e0;
border-radius: 16px;
background: #fff;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 500;
color: #666;
cursor: pointer;
transition: all 0.15s;
}
.tw-filter-chip:hover {
border-color: #84d669;
color: #3b3b3b;
}
.tw-filter-chip.active {
background: #84d669;
color: #fff;
border-color: #84d669;
}
.tw-filter-icon {
font-size: 10px;
}
/* ============================================
INTENT BAR
============================================ */
.tw-intent-bar {
display: flex;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid #f0f1f2;
}
.tw-intent-input {
flex: 1;
padding: 8px 14px;
border: 1px solid #e0e0e0;
border-radius: 6px;
font-family: 'Fira Code', monospace;
font-size: 13px;
color: #3b3b3b;
outline: none;
transition: border-color 0.15s;
background: #fff;
}
.tw-intent-input:focus {
border-color: #84d669;
box-shadow: 0 0 0 3px rgba(132, 214, 105, 0.15);
}
.tw-intent-input::placeholder {
color: #bbb;
}
.tw-intent-run {
padding: 8px 20px;
border: none;
border-radius: 6px;
background: #84d669;
color: #fff;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: all 0.15s;
letter-spacing: 0.5px;
}
.tw-intent-run:hover {
background: #72c458;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(132, 214, 105, 0.3);
}
.tw-intent-run:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* ============================================
SPLIT VIEW (task list + detail)
============================================ */
.tw-split-view {
flex: 1;
display: flex;
overflow: hidden;
}
.tw-task-list {
width: 280px;
min-width: 200px;
border-right: 1px solid #f0f1f2;
overflow-y: auto;
}
.tw-task-list-empty {
padding: 24px 16px;
text-align: center;
color: #bbb;
font-size: 13px;
}
.tw-task-detail {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.tw-task-detail-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
color: #888;
gap: 8px;
}
.tw-task-detail-empty p {
margin: 0;
font-size: 14px;
}
.tw-hint {
font-size: 12px;
color: #bbb;
}
/* ============================================
TASK LIST ITEMS
============================================ */
.tw-task-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
border-bottom: 1px solid #f0f1f2;
cursor: pointer;
transition: background 0.15s;
}
.tw-task-item:hover {
background: #f8f8f8;
}
.tw-task-item.active {
background: rgba(132, 214, 105, 0.08);
border-left: 3px solid #84d669;
}
.tw-task-status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.tw-task-status-dot.running {
background: #84d669;
}
.tw-task-status-dot.pending {
background: #f59e0b;
}
.tw-task-status-dot.completed {
background: #3b82f6;
}
.tw-task-status-dot.failed {
background: #ef4444;
}
.tw-task-status-dot.paused {
background: #888;
}
.tw-task-item-info {
flex: 1;
min-width: 0;
}
.tw-task-item-name {
font-size: 12px;
font-weight: 600;
color: #3b3b3b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tw-task-item-type {
font-size: 10px;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* ============================================
AGENT PROFILE VIEW
============================================ */
.tw-agent-profile {
padding: 20px;
}
.tw-agent-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f1f2;
}
.tw-agent-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: #84d669;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 20px;
font-weight: 700;
}
.tw-agent-meta {
flex: 1;
}
.tw-agent-title {
font-size: 16px;
font-weight: 700;
color: #3b3b3b;
margin: 0 0 4px 0;
}
.tw-agent-subtitle {
font-size: 12px;
color: #888;
}
.tw-section {
margin-bottom: 20px;
}
.tw-section-title {
font-size: 11px;
font-weight: 700;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
}
.tw-stat-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 10px;
}
.tw-stat-card {
padding: 12px;
background: #f8f8f8;
border-radius: 8px;
border: 1px solid #f0f1f2;
}
.tw-stat-value {
font-size: 18px;
font-weight: 700;
color: #3b3b3b;
}
.tw-stat-label {
font-size: 10px;
color: #888;
text-transform: uppercase;
letter-spacing: 0.3px;
margin-top: 2px;
}

View file

@ -0,0 +1,189 @@
<!-- Task Window — Unified Tasks Dashboard (Phase 2) -->
<link rel="stylesheet" href="/suite/tasks/task-window.css" />
<div class="tw-container" id="taskWindow">
<!-- Tab Header -->
<div class="tw-tabs-header" id="twTabsHeader">
<button class="tw-tab active" data-tab="dashboard" id="tabDashboard">
<span class="tw-tab-prefix">//</span> DASHBOARD
</button>
</div>
<!-- Tab Content: Dashboard -->
<div class="tw-tab-content active" id="tabContentDashboard">
<!-- Pipeline Tabs -->
<div class="tw-pipeline-tabs">
<button class="tw-pipeline-tab" data-stage="plan">// PLAN</button>
<button class="tw-pipeline-tab active" data-stage="build">// BUILD</button>
<button class="tw-pipeline-tab" data-stage="review">// REVIEW</button>
<button class="tw-pipeline-tab" data-stage="deploy">// DEPLOY</button>
<button class="tw-pipeline-tab" data-stage="monitor">// MONITOR</button>
</div>
<!-- Filter Chips -->
<div class="tw-filters">
<button class="tw-filter-chip active" data-filter="all">
<span class="tw-filter-icon"></span> All
</button>
<button class="tw-filter-chip" data-filter="active">
<span class="tw-filter-icon"></span> Active
</button>
<button class="tw-filter-chip" data-filter="completed">
<span class="tw-filter-icon"></span> Complete
</button>
<button class="tw-filter-chip" data-filter="paused">
<span class="tw-filter-icon"></span> Paused
</button>
</div>
<!-- Intent Bar -->
<div class="tw-intent-bar">
<input type="text" class="tw-intent-input" id="intentInput"
placeholder="What would you like to do? e.g. 'create a CRM'" />
<button class="tw-intent-run" id="intentRunBtn" type="button">RUN</button>
</div>
<!-- Tasks List + Detail split -->
<div class="tw-split-view">
<div class="tw-task-list" id="taskList" hx-get="/api/ui/tasks?filter=all" hx-trigger="load"
hx-swap="innerHTML">
<div class="tw-task-list-empty">Loading tasks...</div>
</div>
<div class="tw-task-detail" id="taskDetail">
<div class="tw-task-detail-empty">
<p>Select a task</p>
<p class="tw-hint">Click on a task from the list to view details.</p>
<p class="tw-hint">Bot Database: All apps share the same database tables.</p>
</div>
</div>
</div>
</div>
<!-- Tab Content: Agent Profile (dynamically created) -->
<div class="tw-tab-content" id="tabContentAgent" style="display:none;">
<div class="tw-agent-profile" id="agentProfileContent">
<!-- Loaded via HTMX when an agent tab is clicked -->
</div>
</div>
</div>
<script>
(function () {
"use strict";
function initTaskWindow() {
setupTabs();
setupPipelineTabs();
setupFilters();
setupIntentBar();
}
function setupTabs() {
var tabsHeader = document.getElementById("twTabsHeader");
if (!tabsHeader) return;
tabsHeader.addEventListener("click", function (e) {
var tab = e.target.closest(".tw-tab");
if (!tab) return;
var allTabs = tabsHeader.querySelectorAll(".tw-tab");
allTabs.forEach(function (t) { t.classList.remove("active"); });
tab.classList.add("active");
var tabId = tab.getAttribute("data-tab");
var allContent = document.querySelectorAll(".tw-tab-content");
allContent.forEach(function (c) {
c.style.display = "none";
c.classList.remove("active");
});
if (tabId === "dashboard") {
var dash = document.getElementById("tabContentDashboard");
if (dash) { dash.style.display = ""; dash.classList.add("active"); }
} else {
var agent = document.getElementById("tabContentAgent");
if (agent) { agent.style.display = ""; agent.classList.add("active"); }
}
});
}
function setupPipelineTabs() {
var container = document.querySelector(".tw-pipeline-tabs");
if (!container) return;
container.addEventListener("click", function (e) {
var tab = e.target.closest(".tw-pipeline-tab");
if (!tab) return;
container.querySelectorAll(".tw-pipeline-tab").forEach(function (t) {
t.classList.remove("active");
});
tab.classList.add("active");
});
}
function setupFilters() {
var container = document.querySelector(".tw-filters");
if (!container) return;
container.addEventListener("click", function (e) {
var chip = e.target.closest(".tw-filter-chip");
if (!chip) return;
container.querySelectorAll(".tw-filter-chip").forEach(function (c) {
c.classList.remove("active");
});
chip.classList.add("active");
});
}
function setupIntentBar() {
var input = document.getElementById("intentInput");
var runBtn = document.getElementById("intentRunBtn");
if (!input || !runBtn) return;
function submitIntent() {
var text = input.value.trim();
if (!text) return;
runBtn.disabled = true;
runBtn.textContent = "...";
fetch("/api/autotask/classify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ intent: text })
})
.then(function (r) { return r.json(); })
.then(function (data) {
input.value = "";
runBtn.disabled = false;
runBtn.textContent = "RUN";
if (window.htmx) {
htmx.trigger("#taskList", "load");
}
})
.catch(function (err) {
console.error("Intent submission failed:", err);
runBtn.disabled = false;
runBtn.textContent = "RUN";
});
}
runBtn.addEventListener("click", submitIntent);
input.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
submitIntent();
}
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initTaskWindow);
} else {
initTaskWindow();
}
})();
</script>

View file

@ -0,0 +1,494 @@
/* Agents & Workspaces Sidebar + Vibe Canvas Styles */
/* ============================================
VIBE CONTAINER
============================================ */
.vibe-container {
display: flex;
flex-direction: column;
height: 100%;
background: #fff;
font-family: 'Fira Code', monospace;
}
/* ============================================
PIPELINE TABS
============================================ */
.vibe-pipeline {
display: flex;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
}
.vibe-pipeline-tab {
flex: 1;
height: 34px;
border: none;
border-right: 1px solid #f0f1f2;
background: transparent;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 600;
color: #888;
cursor: pointer;
transition: all 0.15s;
}
.vibe-pipeline-tab:last-child {
border-right: none;
}
.vibe-pipeline-tab:hover {
background: #fff;
color: #3b3b3b;
}
.vibe-pipeline-tab.active {
background: #84d669;
color: #fff;
}
/* ============================================
VIBE BODY (Canvas + Sidebar)
============================================ */
.vibe-body {
flex: 1;
display: flex;
overflow: hidden;
}
/* ============================================
CANVAS AREA
============================================ */
.vibe-canvas {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.vibe-canvas-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 40px;
text-align: center;
}
.vibe-canvas-icon {
font-size: 48px;
}
.vibe-canvas-empty h3 {
margin: 0;
font-size: 20px;
font-weight: 700;
color: #3b3b3b;
}
.vibe-canvas-empty p {
margin: 0;
font-size: 14px;
color: #888;
max-width: 400px;
}
.vibe-canvas-prompt {
display: flex;
gap: 8px;
margin-top: 12px;
width: 100%;
max-width: 500px;
}
.vibe-prompt-input {
flex: 1;
padding: 10px 16px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-family: 'Fira Code', monospace;
font-size: 13px;
color: #3b3b3b;
outline: none;
transition: border-color 0.2s;
}
.vibe-prompt-input:focus {
border-color: #84d669;
box-shadow: 0 0 0 3px rgba(132, 214, 105, 0.15);
}
.vibe-prompt-input::placeholder {
color: #bbb;
}
.vibe-prompt-btn {
padding: 10px 24px;
border: none;
border-radius: 8px;
background: #84d669;
color: #fff;
font-family: 'Fira Code', monospace;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: all 0.15s;
}
.vibe-prompt-btn:hover {
background: #72c458;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(132, 214, 105, 0.3);
}
.vibe-prompt-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* Steps */
.vibe-steps {
padding: 16px;
}
/* Preview */
.vibe-preview {
flex: 1;
display: flex;
flex-direction: column;
border-top: 1px solid #f0f1f2;
}
.vibe-preview-header {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: #f8f8f8;
border-bottom: 1px solid #f0f1f2;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 600;
color: #3b3b3b;
}
.vibe-preview-url {
flex: 1;
padding: 3px 10px;
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 4px;
font-family: 'Fira Code', monospace;
font-size: 11px;
color: #666;
}
.vibe-preview-content {
flex: 1;
}
.vibe-preview-content iframe {
width: 100%;
height: 100%;
border: none;
}
/* ============================================
AGENTS & WORKSPACES SIDEBAR
============================================ */
.agents-sidebar {
width: 260px;
min-width: 260px;
background: #f8f8f8;
border-left: 1px solid #f0f1f2;
overflow-y: auto;
transition: width 0.25s, min-width 0.25s;
}
.agents-sidebar.collapsed {
width: 0;
min-width: 0;
overflow: hidden;
}
/* Section */
.as-section {
border-bottom: 1px solid #f0f1f2;
}
.as-section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px 8px 14px;
}
.as-section-header h3 {
margin: 0;
font-size: 11px;
font-weight: 700;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.as-collapse-btn {
width: 20px;
height: 20px;
border: none;
background: transparent;
color: #888;
cursor: pointer;
font-size: 10px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.15s;
}
.as-collapse-btn:hover {
background: #e0e0e0;
color: #3b3b3b;
}
/* ============================================
AGENT CARDS
============================================ */
.as-agent-list {
padding: 0 8px;
}
.as-agent-card {
background: #fff;
border: 1px solid #f0f1f2;
border-radius: 8px;
padding: 10px 12px;
margin-bottom: 6px;
cursor: grab;
transition: all 0.15s;
}
.as-agent-card:hover {
border-color: #84d669;
box-shadow: 0 2px 8px rgba(132, 214, 105, 0.1);
}
.as-agent-card.dragging {
opacity: 0.5;
transform: scale(0.95);
}
.as-agent-header {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 6px;
}
.as-status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.as-status-dot.green {
background: #84d669;
}
.as-status-dot.yellow {
background: #f59e0b;
}
.as-status-dot.red {
background: #ef4444;
}
.as-status-dot.gray {
background: #ccc;
}
.as-agent-name {
flex: 1;
font-size: 12px;
font-weight: 600;
color: #3b3b3b;
}
.as-drag-handle {
color: #ccc;
font-size: 14px;
cursor: grab;
}
.as-agent-body {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.as-agent-icons {
font-size: 12px;
letter-spacing: 2px;
}
.as-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 3px;
font-size: 9px;
font-weight: 700;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.badge-evolved {
background: #84d669;
color: #fff;
}
.badge-bred {
background: #f59e0b;
color: #fff;
}
.badge-wild {
background: #ef4444;
color: #fff;
}
/* Quota Bar */
.as-agent-bar {
height: 3px;
background: #f0f1f2;
border-radius: 2px;
overflow: hidden;
}
.as-bar-fill {
height: 100%;
background: #84d669;
border-radius: 2px;
transition: width 0.3s;
}
.as-bar-fill.bred {
background: #f59e0b;
}
.as-bar-fill.wild {
background: #ef4444;
}
/* Create Agent Button */
.as-create-btn {
width: calc(100% - 16px);
margin: 8px 8px;
padding: 8px;
border: 2px dashed #e0e0e0;
border-radius: 8px;
background: transparent;
font-family: 'Fira Code', monospace;
font-size: 11px;
font-weight: 500;
color: #888;
cursor: pointer;
transition: all 0.15s;
}
.as-create-btn:hover {
border-color: #84d669;
color: #84d669;
background: rgba(132, 214, 105, 0.05);
}
/* ============================================
WORKSPACES
============================================ */
.as-workspace-list {
padding: 0 8px 8px 8px;
}
.as-workspace-item {
margin-bottom: 4px;
}
.as-workspace-toggle {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding: 8px 10px;
border: none;
border-radius: 6px;
background: transparent;
font-family: 'Fira Code', monospace;
font-size: 12px;
font-weight: 500;
color: #3b3b3b;
cursor: pointer;
transition: background 0.15s;
text-align: left;
}
.as-workspace-toggle:hover {
background: #fff;
}
.as-workspace-arrow {
font-size: 8px;
color: #888;
width: 8px;
transition: transform 0.15s;
}
.as-workspace-count {
margin-left: auto;
font-size: 10px;
background: #e0e0e0;
color: #666;
padding: 1px 6px;
border-radius: 8px;
}
.as-workspace-body {
padding: 4px 0 4px 20px;
}
.as-workspace-agent {
padding: 4px 10px;
font-size: 11px;
color: #666;
border-left: 2px solid #f0f1f2;
}
.as-workspace-dropzone {
margin-top: 4px;
padding: 10px;
border: 2px dashed #e0e0e0;
border-radius: 6px;
text-align: center;
font-size: 11px;
color: #ccc;
transition: all 0.2s;
}
.as-workspace-dropzone.drag-over {
border-color: #84d669;
background: rgba(132, 214, 105, 0.05);
color: #84d669;
}