Spaces:
Paused
Paused
Update templates/index.html
Browse files- templates/index.html +1177 -596
templates/index.html
CHANGED
|
@@ -1,251 +1,601 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
-
<html lang="
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
<title>Mission Control AI</title>
|
| 7 |
<style>
|
| 8 |
@import url('https://fonts.googleapis.com/css2?family=VT323&family=Share+Tech+Mono&display=swap');
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
--
|
| 12 |
-
--
|
| 13 |
-
--
|
| 14 |
-
--
|
| 15 |
-
--
|
| 16 |
-
--
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
.
|
| 49 |
-
.
|
| 50 |
-
.
|
| 51 |
-
.
|
| 52 |
-
.
|
| 53 |
-
.
|
| 54 |
-
.
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
/*
|
| 61 |
-
.
|
| 62 |
-
.
|
| 63 |
-
.
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
/*
|
| 67 |
-
.
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
}
|
| 92 |
-
.
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
.
|
| 101 |
-
.
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
@
|
| 106 |
-
|
| 107 |
-
/*
|
| 108 |
-
.
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
.
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
.
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
/*
|
| 135 |
-
.
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
.
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
.
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
.
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
.
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
.
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
.
|
| 194 |
-
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
/* WALK OVERLAY */
|
| 198 |
-
.walk-
|
| 199 |
-
.walk-
|
| 200 |
-
.walk-wrap{
|
| 201 |
-
.walk-lbl{font-family:'VT323',monospace;font-size:14px;color:var(--
|
| 202 |
-
.walk-log{
|
| 203 |
-
|
| 204 |
-
/*
|
| 205 |
-
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.
|
| 206 |
-
.modal-bg.show{display:flex}
|
| 207 |
-
.modal{background:
|
| 208 |
-
.modal-t{font-family:'VT323',monospace;font-size:20px;color:var(--
|
| 209 |
-
.field{margin-bottom:10px}
|
| 210 |
-
.field label{display:block;font-size:10px;color:var(--text2);letter-spacing:
|
| 211 |
-
.field input,.field select,.field textarea{width:100%;background:
|
| 212 |
-
.field input:focus,.field select:focus,.field textarea:focus{border-color:
|
| 213 |
-
.field textarea{resize:vertical;min-height:
|
| 214 |
-
.
|
| 215 |
-
.
|
| 216 |
-
.mbtn
|
| 217 |
-
.mbtn-ok
|
| 218 |
-
.mbtn-no{border-color:
|
|
|
|
| 219 |
|
| 220 |
/* NOTIFICATION */
|
| 221 |
-
.notif
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
/* SIDEBAR */
|
| 227 |
-
.sidebar{background:
|
| 228 |
-
.sb-sec{border-bottom:1px solid
|
| 229 |
-
.sb-t{font-family:'VT323',monospace;font-size:13px;
|
| 230 |
-
.act-scroll{
|
| 231 |
-
.act-item{padding:6px
|
| 232 |
-
.act-dot{width:
|
| 233 |
-
.act-msg{font-size:
|
| 234 |
-
.act-time{font-size:
|
| 235 |
-
.hist-scroll{overflow-y:auto;max-height:200px}
|
| 236 |
-
.hist-item{padding:
|
| 237 |
-
.hist-item:hover{background:
|
| 238 |
-
.hist-task{font-size:
|
| 239 |
-
.hist-meta{font-size:
|
| 240 |
-
::-webkit-scrollbar{width:4px
|
| 241 |
-
|
|
|
|
| 242 |
</style>
|
| 243 |
</head>
|
| 244 |
<body>
|
|
|
|
|
|
|
| 245 |
<header class="topbar">
|
| 246 |
<div style="display:flex;align-items:center;gap:8px">
|
| 247 |
<span class="tb-logo">MISSION CONTROL AI</span>
|
| 248 |
-
<span
|
| 249 |
</div>
|
| 250 |
<div class="tb-right">
|
| 251 |
<div class="tb-stat"><div class="sdot dg"></div><span id="top-active">0 active</span></div>
|
|
@@ -253,51 +603,123 @@ body::after{content:'';position:fixed;inset:0;background:repeating-linear-gradie
|
|
| 253 |
<div class="clock" id="clock">00:00:00</div>
|
| 254 |
</div>
|
| 255 |
</header>
|
|
|
|
| 256 |
<div class="prog" id="prog"></div>
|
|
|
|
|
|
|
| 257 |
<div class="mbar">
|
| 258 |
-
<input class="minput" id="task-input" placeholder="
|
| 259 |
<button class="launch" id="launch-btn" onclick="launchMission()">► LAUNCH</button>
|
| 260 |
<button class="add-btn" onclick="openModal()">+ Agent</button>
|
| 261 |
-
<span class="mc" id="mc-count">MISSIONS: 0</span>
|
| 262 |
</div>
|
|
|
|
|
|
|
| 263 |
<div class="main">
|
| 264 |
-
<div class="office">
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
</div>
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
</div>
|
|
|
|
|
|
|
| 271 |
<aside class="sidebar">
|
| 272 |
<div class="sb-sec"><div class="sb-t">ACTIVITY</div><div class="act-scroll" id="act-log"></div></div>
|
| 273 |
-
<div class="sb-sec"><div class="sb-t">MISSIONS</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
</aside>
|
| 275 |
</div>
|
| 276 |
|
| 277 |
<!-- WALK OVERLAY -->
|
| 278 |
-
<div class="walk-
|
| 279 |
-
<div class="walk-lbl">MANAGER
|
| 280 |
<div class="walk-wrap"><canvas id="walk-canvas"></canvas></div>
|
| 281 |
-
<div class="walk-log" id="walk-log">
|
| 282 |
</div>
|
| 283 |
|
| 284 |
<!-- LIGHTBOX -->
|
| 285 |
-
<div class="
|
| 286 |
-
<img id="
|
| 287 |
-
<div
|
| 288 |
-
<a class="
|
| 289 |
-
<div class="
|
| 290 |
</div>
|
| 291 |
</div>
|
| 292 |
|
| 293 |
<!-- ADD AGENT MODAL -->
|
| 294 |
<div class="modal-bg" id="modal-bg" onclick="if(event.target===this)closeModal()">
|
| 295 |
<div class="modal">
|
| 296 |
-
<div class="modal-t">+
|
| 297 |
-
<div class="field"><label>Key (
|
| 298 |
-
<div class="field"><label>
|
| 299 |
<div class="field"><label>Role</label><textarea id="m-role" placeholder="Especialista en..."></textarea></div>
|
| 300 |
-
<div class="field"><label>Provider</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
<div class="modal-acts">
|
| 302 |
<button class="mbtn mbtn-no" onclick="closeModal()">Cancel</button>
|
| 303 |
<button class="mbtn mbtn-ok" onclick="submitAgent()">Add Agent</button>
|
|
@@ -307,221 +729,305 @@ body::after{content:'';position:fixed;inset:0;background:repeating-linear-gradie
|
|
| 307 |
|
| 308 |
<!-- NOTIFICATION -->
|
| 309 |
<div class="notif" id="notif">
|
| 310 |
-
<div class="notif-t" id="notif-t">
|
| 311 |
<div class="notif-m" id="notif-m"></div>
|
| 312 |
</div>
|
| 313 |
|
| 314 |
<script>
|
| 315 |
-
|
| 316 |
-
const
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
}
|
| 326 |
|
| 327 |
-
|
| 328 |
-
const
|
| 329 |
-
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
-
// ββ
|
| 333 |
-
function
|
| 334 |
-
const
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
<
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
<rect x="12" y="10" width="2" height="2" fill="#111"/>
|
| 347 |
-
<rect x="18" y="10" width="2" height="2" fill="#111"/>
|
| 348 |
-
<rect x="15" y="10" width="2" height="1" fill="${color}"/>
|
| 349 |
-
<rect x="13" y="14" width="6" height="1" fill="#c88060" opacity=".5"/>
|
| 350 |
-
<rect x="9" y="17" width="14" height="11" fill="${color}" opacity=".85"/>
|
| 351 |
-
<rect x="11" y="17" width="10" height="3" fill="${color}"/>
|
| 352 |
-
<g ${a}>
|
| 353 |
-
<rect x="4" y="18" width="5" height="8" fill="${color}" opacity=".85"/>
|
| 354 |
-
<rect x="23" y="18" width="5" height="8" fill="${color}" opacity=".85"/>
|
| 355 |
-
<rect x="3" y="25" width="5" height="4" fill="#f0c8a0"/>
|
| 356 |
-
<rect x="24" y="25" width="5" height="4" fill="#f0c8a0"/>
|
| 357 |
-
</g>
|
| 358 |
-
<rect x="9" y="28" width="14" height="12" fill="#1e2535"/>
|
| 359 |
-
<rect x="13" y="40" width="5" height="7" fill="#1e2535"/>
|
| 360 |
-
<rect x="9" y="40" width="5" height="7" fill="#1e2535"/>
|
| 361 |
-
<rect x="7" y="47" width="7" height="4" fill="#111"/>
|
| 362 |
-
<rect x="18" y="47" width="7" height="4" fill="#111"/>
|
| 363 |
-
</svg>`;
|
| 364 |
-
}
|
| 365 |
-
|
| 366 |
-
function chairSVG(empty,w=22,h=26){
|
| 367 |
-
const c=empty?'#0a0f18':'#182030', s=empty?'#060b12':'#0d1a28';
|
| 368 |
-
return`<svg viewBox="0 0 26 30" width="${w}" height="${h}" style="image-rendering:pixelated;display:block">
|
| 369 |
-
<rect x="3" y="3" width="20" height="13" fill="${c}" rx="1"/>
|
| 370 |
-
<rect x="4" y="4" width="18" height="11" fill="${s}"/>
|
| 371 |
-
<rect x="3" y="16" width="20" height="4" fill="${c}"/>
|
| 372 |
-
<rect x="3" y="0" width="4" height="16" fill="${c}"/>
|
| 373 |
-
<rect x="5" y="20" width="3" height="8" fill="${c}"/>
|
| 374 |
-
<rect x="18" y="20" width="3" height="8" fill="${c}"/>
|
| 375 |
-
<rect x="3" y="27" width="5" height="3" fill="#050a10"/>
|
| 376 |
-
<rect x="18" y="27" width="5" height="3" fill="#050a10"/>
|
| 377 |
-
</svg>`;
|
| 378 |
-
}
|
| 379 |
-
|
| 380 |
-
function coffeeSVG(){
|
| 381 |
-
return`<svg viewBox="0 0 12 12" width="11" height="11" style="image-rendering:pixelated;display:block">
|
| 382 |
-
<rect x="2" y="4" width="6" height="6" fill="#5c3a1e"/>
|
| 383 |
-
<rect x="2" y="4" width="6" height="2" fill="#7a4e2a"/>
|
| 384 |
-
<rect x="8" y="5" width="2" height="3" fill="#5c3a1e"/>
|
| 385 |
-
<rect x="1" y="10" width="8" height="2" fill="#4a2e14"/>
|
| 386 |
-
<rect x="4" y="2" width="1" height="2" fill="#555" opacity=".5"/>
|
| 387 |
-
</svg>`;
|
| 388 |
-
}
|
| 389 |
-
|
| 390 |
-
function sRack(){
|
| 391 |
-
const us=[['lg','lb','1.1s','.7s',true],['la','lb','2.3s','.9s',true],['lg','lo','1.8s',null,false],['lb','lg','.7s','2.1s',true],['la','la','1.4s','1.9s',false]];
|
| 392 |
-
return us.map(([l1,l2,d1,d2,a])=>`<div class="ru"><div class="rl ${l1}" style="--d:${d1}"></div><div class="rl ${l2}" ${d2?`style="--d:${d2}"`:''} ></div><div class="rb ${a?'on':''}"></div></div>`).join('');
|
| 393 |
-
}
|
| 394 |
-
|
| 395 |
-
function screenContent(status){
|
| 396 |
-
if(status==='working') return`<div class="tdots"><div class="td"></div><div class="td"></div><div class="td"></div></div>`;
|
| 397 |
-
if(status==='active') return`<div class="scur"></div>`;
|
| 398 |
-
if(status==='resting') return`<div class="mscreen-txt" style="color:var(--Adim)">AWAY</div>`;
|
| 399 |
-
return`<div class="mscreen-txt" style="color:var(--text3)">IDLE</div>`;
|
| 400 |
-
}
|
| 401 |
-
|
| 402 |
-
// ββ BUILD ROOM βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 403 |
-
function buildRoom(agent){
|
| 404 |
-
const st=states[agent.key]||{status:'idle',message:'',model:''};
|
| 405 |
-
const status=st.status, color=COLORS[agent.key]||'#00e5a0', empty=status==='resting';
|
| 406 |
-
const badge={idle:'IDLE',working:'PROCESSING',active:'DONE',resting:'AWAY',visiting:'BRIEFING',error:'ERROR'};
|
| 407 |
-
const bcls={idle:'bi',working:'bw',active:'ba',resting:'br',visiting:'bv',error:'br'};
|
| 408 |
-
const rcls={idle:'idle',working:'working',active:'active',resting:'resting',visiting:'visiting',error:'resting'};
|
| 409 |
-
const mcls=status==='working'?'thinking':status==='active'?'done':status==='resting'?'away':'';
|
| 410 |
-
|
| 411 |
-
// Result HTML β handles images specially
|
| 412 |
-
let resultHTML='';
|
| 413 |
-
if(status==='idle'){
|
| 414 |
-
resultHTML=`<div class="rph">Waiting for mission...</div>`;
|
| 415 |
-
} else if(status==='working'||status==='visiting'){
|
| 416 |
-
resultHTML=`<div class="rph" style="color:var(--B)">Processing...</div>`;
|
| 417 |
-
} else if(status==='resting'){
|
| 418 |
-
resultHTML=`<div class="rph" style="color:var(--A)">Rate limited β back soon</div>`;
|
| 419 |
-
} else {
|
| 420 |
-
// Build download buttons per agent
|
| 421 |
-
let downloads='';
|
| 422 |
-
if(st.doc_file){
|
| 423 |
-
const label = st.doc_file.endsWith('.xlsx') ? '↓ Download .xlsx'
|
| 424 |
-
: st.doc_file.endsWith('.docx') ? '↓ Download .docx'
|
| 425 |
-
: st.doc_file.endsWith('.html') ? '↓ Download .html'
|
| 426 |
-
: st.doc_file.endsWith('.py') ? '↓ Download .py'
|
| 427 |
-
: st.doc_file.endsWith('.groovy')? '↓ Download .groovy'
|
| 428 |
-
: st.doc_file.endsWith('.jpg') ? null // images shown as thumbs
|
| 429 |
-
: '↓ Download file';
|
| 430 |
-
if(label) downloads += `<a class="dl-btn" href="/api/docs/${st.doc_file}" download>${label}</a>`;
|
| 431 |
-
}
|
| 432 |
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
|
|
|
|
|
|
|
|
|
| 442 |
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
}
|
|
|
|
|
|
|
| 448 |
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
</div>
|
| 461 |
-
<
|
| 462 |
-
|
| 463 |
-
<div class="
|
| 464 |
-
<div class="mscreen">${screenContent(status)}</div>
|
| 465 |
-
<div class="mbase"></div>
|
| 466 |
-
</div>
|
| 467 |
-
<div class="dtop"></div>
|
| 468 |
-
<div class="kbd"><div class="kk"></div><div class="kk"></div><div class="kk"></div><div class="kk"></div></div>
|
| 469 |
</div>
|
| 470 |
-
<
|
| 471 |
-
<div class="
|
| 472 |
-
|
| 473 |
-
<div class="bubble" id="bubble-${agent.key}"></div>
|
| 474 |
-
|
| 475 |
-
<
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
}
|
| 478 |
|
| 479 |
-
function
|
| 480 |
-
const
|
| 481 |
-
|
| 482 |
}
|
| 483 |
|
| 484 |
-
function
|
| 485 |
-
const
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
}
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
}
|
| 498 |
-
|
| 499 |
-
|
|
|
|
|
|
|
| 500 |
}
|
| 501 |
|
| 502 |
-
//
|
| 503 |
-
function
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
}
|
| 509 |
-
function closeLightbox(){ document.getElementById('lightbox').classList.remove('show'); }
|
| 510 |
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
}
|
| 515 |
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
b.textContent=text; b.classList.add('show');
|
| 521 |
-
bubbleT[key]=setTimeout(()=>b.classList.remove('show'),ms);
|
| 522 |
}
|
| 523 |
|
| 524 |
-
|
|
|
|
| 525 |
const n=new Date();
|
| 526 |
const t=[n.getHours(),n.getMinutes(),n.getSeconds()].map(x=>String(x).padStart(2,'0')).join(':');
|
| 527 |
activity.unshift({msg,t,color});
|
|
@@ -530,138 +1036,211 @@ function addAct(msg,color='#4a9eff'){
|
|
| 530 |
).join('');
|
| 531 |
}
|
| 532 |
|
| 533 |
-
function notify(title,msg,color='#
|
| 534 |
document.getElementById('notif-t').textContent=title;
|
| 535 |
document.getElementById('notif-t').style.color=color;
|
| 536 |
document.getElementById('notif-m').textContent=msg;
|
| 537 |
document.getElementById('notif').style.borderLeftColor=color;
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
setTimeout(()=>el.classList.remove('show'),5500);
|
| 541 |
}
|
| 542 |
|
| 543 |
function updateHistory(){
|
| 544 |
const list=document.getElementById('hist-log');
|
| 545 |
-
if(!history.length){list.innerHTML=`<div style="padding:
|
| 546 |
-
list.innerHTML=[...history].reverse().map(m=>
|
| 547 |
-
`<div class="hist-item"><div class="hist-task">${m.task}</div><div class="hist-meta"><span>${m.time}</span><span style="color:${m.ok?'var(--G)':'var(--A)'}">${m.ok?'Done':'Partial'}</span>${m.doc_file?`<span style="color:var(--P)">file</span>`:''}</div></div>`
|
| 548 |
-
).join('');
|
| 549 |
}
|
| 550 |
|
| 551 |
-
// ββ WALK ANIMATION βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 552 |
function runWalkAnimation(task, delegates, onDone){
|
| 553 |
-
const
|
| 554 |
-
const
|
| 555 |
const logEl=document.getElementById('walk-log');
|
| 556 |
-
const wrap=
|
| 557 |
-
|
| 558 |
-
const W=
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
const
|
| 563 |
-
const rW=Math.min(118,Math.floor((W-60)/Math.max(rooms.length,1)));
|
| 564 |
-
const rH=120, rY=H/2-rH/2+20;
|
| 565 |
-
const rPos=rooms.map((r,i)=>({x:30+i*(rW+12),y:rY,w:rW,h:rH,key:r.key,name:r.name,color:agentColors[r.key]||'#4a9eff'}));
|
| 566 |
const delSet=new Set(delegates);
|
| 567 |
-
const
|
| 568 |
-
|
| 569 |
-
const
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
const visited=new Set(); let curMsg='';
|
| 577 |
|
| 578 |
-
function
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
}
|
| 584 |
|
| 585 |
-
function
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
ctx.fillStyle=
|
| 590 |
-
ctx.
|
| 591 |
-
|
| 592 |
-
ctx.
|
| 593 |
-
//
|
| 594 |
-
ctx.fillStyle='#
|
| 595 |
-
ctx.fillRect(
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
const px=r.x+12, py=r.y+r.h-28;
|
| 604 |
-
ctx.fillStyle='#f0c8a0'; ctx.fillRect(px+3,py,7,7);
|
| 605 |
-
ctx.fillStyle=r.color; ctx.fillRect(px,py+7,13,7);
|
| 606 |
-
ctx.fillStyle='#1e2535'; ctx.fillRect(px+2,py+14,4,6); ctx.fillRect(px+7,py+14,4,6);
|
| 607 |
-
// LEDs
|
| 608 |
-
['#00e5a0','#4a9eff','#ffcc44'].forEach((c,i)=>{
|
| 609 |
-
ctx.beginPath(); ctx.arc(r.x+r.w-14+i*6,r.y+r.h-8,2,0,Math.PI*2);
|
| 610 |
-
ctx.fillStyle=c; ctx.globalAlpha=.3+.7*((Math.sin(frame*.09+i*1.4)+1)/2); ctx.fill();
|
| 611 |
-
}); ctx.globalAlpha=1;
|
| 612 |
-
}
|
| 613 |
-
|
| 614 |
-
function drawManager(x,y,walking){
|
| 615 |
-
const bob=walking?Math.sin(frame*.45)*2:0;
|
| 616 |
-
ctx.fillStyle='#1a1a2e'; ctx.fillRect(x+4,y,8,3);
|
| 617 |
-
ctx.fillStyle='#f0c8a0'; ctx.fillRect(x+3,y+3,10,8);
|
| 618 |
-
ctx.fillStyle=manColor+'bb'; ctx.fillRect(x,y+3,2,4); ctx.fillRect(x+13,y+3,2,4);
|
| 619 |
-
ctx.fillStyle=manColor; ctx.fillRect(x+2,y+11,12,9);
|
| 620 |
-
ctx.fillStyle='#1e2535';
|
| 621 |
-
ctx.fillRect(x+3,y+20,5,6+bob); ctx.fillRect(x+8,y+20,5,6-bob);
|
| 622 |
-
ctx.fillStyle='#111'; ctx.fillRect(x+2,y+26+bob,5,3); ctx.fillRect(x+9,y+26-bob,5,3);
|
| 623 |
}
|
| 624 |
|
| 625 |
function drawBubble(x,y,lines){
|
| 626 |
-
const mxW=Math.max(...lines.map(l=>l.length))*6+
|
| 627 |
-
const bx=Math.min(Math.max(x
|
| 628 |
-
ctx.fillStyle='
|
| 629 |
-
ctx.
|
| 630 |
-
|
| 631 |
-
|
|
|
|
|
|
|
|
|
|
| 632 |
}
|
| 633 |
|
| 634 |
function tick(){
|
| 635 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 636 |
if(stepI<steps.length){
|
| 637 |
const step=steps[stepI];
|
| 638 |
-
const dx=step.x-mx,
|
| 639 |
-
if(dist>3&&phase==='walking'){mx+=dx/dist*SPD;
|
| 640 |
else if(phase==='walking'){
|
| 641 |
-
phase='talking';
|
| 642 |
if(step.key!=='__top'&&step.key!=='__back'){
|
| 643 |
visited.add(step.key);
|
| 644 |
logEl.textContent=`Briefing ${step.key}...`;
|
| 645 |
-
addAct(`Manager β ${step.key}`,'#
|
| 646 |
} else if(step.key==='__back'){
|
| 647 |
-
logEl.textContent='Team
|
| 648 |
}
|
| 649 |
}
|
| 650 |
if(phase==='talking'){talkF++;if(talkF>TALK){phase='walking';stepI++;curMsg='';}}
|
| 651 |
} else {
|
| 652 |
-
setTimeout(()=>{
|
| 653 |
}
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
frame++; requestAnimationFrame(tick);
|
| 657 |
}
|
| 658 |
tick();
|
| 659 |
}
|
| 660 |
|
| 661 |
-
// ββ
|
| 662 |
-
function
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
async function submitAgent(){
|
| 666 |
const key=document.getElementById('m-key').value.trim();
|
| 667 |
const name=document.getElementById('m-name').value.trim()||key;
|
|
@@ -672,8 +1251,10 @@ async function submitAgent(){
|
|
| 672 |
const d=await r.json();
|
| 673 |
if(d.success){
|
| 674 |
agentDefs.push({key:d.agent.key,name:d.agent.name,role:d.agent.role});
|
| 675 |
-
|
| 676 |
-
|
|
|
|
|
|
|
| 677 |
['m-key','m-name','m-role'].forEach(id=>document.getElementById(id).value='');
|
| 678 |
}
|
| 679 |
}
|
|
@@ -687,109 +1268,109 @@ async function launchMission(){
|
|
| 687 |
document.getElementById('prog').classList.add('on');
|
| 688 |
document.getElementById('top-status').textContent='Running';
|
| 689 |
document.getElementById('top-dot').className='sdot da';
|
| 690 |
-
document.getElementById('
|
|
|
|
| 691 |
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
});
|
| 697 |
-
addAct(`Mission: "${task.substring(0,
|
| 698 |
|
| 699 |
-
// Start API call
|
| 700 |
let apiData=null;
|
| 701 |
-
const fetchP=fetch('/api/mission',{
|
| 702 |
-
|
| 703 |
-
}).then(r=>r.json()).then(d=>{apiData=d;}).catch(e=>{apiData={error:e.message};});
|
| 704 |
|
| 705 |
-
// Quick delegate guess for walk animation
|
| 706 |
const lo=task.toLowerCase();
|
| 707 |
let qd=[];
|
| 708 |
-
if(/imagen|image|foto|gato|cat|dog|perro|picture|dibuja/.test(lo)) qd.push('image_agent');
|
| 709 |
if(/informe|report|word|documento/.test(lo)){qd.push('writer');qd.push('analyst');}
|
| 710 |
if(/excel|planilla|spreadsheet|registro/.test(lo)) qd.push('backend_dev');
|
| 711 |
if(/python|script|groovy|jenkins|backend|api/.test(lo)) qd.push('backend_dev');
|
| 712 |
if(/html|css|frontend|web|interfaz/.test(lo)) qd.push('frontend_dev');
|
| 713 |
-
if(!qd.length) qd=['
|
| 714 |
|
| 715 |
runWalkAnimation(task, qd, async()=>{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 716 |
await fetchP;
|
| 717 |
|
| 718 |
if(!apiData||apiData.error){
|
| 719 |
-
agentDefs.forEach(a=>{
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
|
|
|
|
|
|
|
|
|
| 723 |
document.getElementById('top-dot').className='sdot dr';
|
| 724 |
document.getElementById('top-status').textContent='Error';
|
| 725 |
-
btn.disabled=false; document.getElementById('prog').classList.remove('on');
|
| 726 |
-
return;
|
| 727 |
}
|
| 728 |
|
| 729 |
-
let anyResting=false, hasFile=false;
|
| 730 |
-
|
| 731 |
agentDefs.forEach(a=>{
|
| 732 |
const r=apiData.results[a.key];
|
| 733 |
-
if(!r){
|
| 734 |
if(r.status==='resting') anyResting=true;
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
if(a.key==='image_agent' && r.img_base && r.img_count>0){
|
| 741 |
-
images=[];
|
| 742 |
-
for(let i=1;i<=r.img_count;i++){
|
| 743 |
-
images.push(`/api/docs/${r.img_base}_img${i}.jpg`);
|
| 744 |
-
}
|
| 745 |
-
}
|
| 746 |
-
|
| 747 |
-
states[a.key]={
|
| 748 |
-
status: r.status==='resting'?'resting': r.status==='idle'?'idle':'active',
|
| 749 |
-
message: r.message||'',
|
| 750 |
-
model: r.model||'',
|
| 751 |
-
doc_file: docFile,
|
| 752 |
-
images: images,
|
| 753 |
-
_tw: true,
|
| 754 |
};
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
if((states[a.key]||{}).status==='active'){
|
| 762 |
-
setTimeout(()=>{
|
| 763 |
-
showBubble(a.key,'Done β',2000);
|
| 764 |
-
addAct(`${a.name}: complete`,'#00e5a0');
|
| 765 |
-
},i*400);
|
| 766 |
}
|
| 767 |
});
|
| 768 |
|
| 769 |
-
|
|
|
|
|
|
|
| 770 |
mCount++;
|
| 771 |
document.getElementById('mc-count').textContent=`MISSIONS: ${mCount}`;
|
|
|
|
| 772 |
history.push({task,time:new Date().toLocaleTimeString(),ok:!anyResting,doc_file:apiData.doc_file});
|
| 773 |
updateHistory();
|
|
|
|
| 774 |
|
| 775 |
setTimeout(()=>notify(
|
| 776 |
-
anyResting?'Mission Partial':
|
| 777 |
-
anyResting?'Some agents rate-limited.':hasFile?'
|
| 778 |
-
anyResting?'#
|
| 779 |
-
),
|
| 780 |
|
| 781 |
document.getElementById('top-status').textContent=anyResting?'Partial':'Done';
|
| 782 |
document.getElementById('top-dot').className=`sdot ${anyResting?'da':'dg'}`;
|
| 783 |
-
document.getElementById('
|
| 784 |
-
btn.disabled=false;
|
| 785 |
-
document.getElementById('prog').classList.remove('on');
|
| 786 |
});
|
| 787 |
}
|
| 788 |
|
| 789 |
document.getElementById('task-input').addEventListener('keydown',e=>{if(e.key==='Enter')launchMission();});
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
</script>
|
| 794 |
</body>
|
| 795 |
-
</html>
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
+
<html lang="es">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
<title>Mission Control AI</title>
|
| 7 |
<style>
|
| 8 |
@import url('https://fonts.googleapis.com/css2?family=VT323&family=Share+Tech+Mono&display=swap');
|
| 9 |
+
|
| 10 |
+
:root {
|
| 11 |
+
--wall:#f2ede0; --wall2:#e8e2d2; --floor:#d4cdb8; --floor2:#ccc5ae;
|
| 12 |
+
--desk:#c8a96e; --desk2:#b8996a; --desk-dark:#a07840;
|
| 13 |
+
--monitor-bg:#2a3545; --monitor-screen:#1a4a2a;
|
| 14 |
+
--skin:#f5c9a0; --hair-dark:#4a2e18; --hair-med:#7a4828;
|
| 15 |
+
--chair:#8a7a6a; --chair2:#6a5a4a;
|
| 16 |
+
--accent:#2563eb; --accent2:#1d4ed8;
|
| 17 |
+
--server:#1a2035; --server2:#0d1520;
|
| 18 |
+
--green:#22c55e; --amber:#f59e0b; --red:#ef4444; --blue:#3b82f6; --purple:#a855f7;
|
| 19 |
+
--text:#1a1a2a; --text2:#4a5a6a; --text3:#8a9aaa;
|
| 20 |
+
--shadow:0 8px 32px rgba(0,0,0,.18);
|
| 21 |
+
--shadow-sm:0 2px 8px rgba(0,0,0,.12);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 25 |
+
html, body { height: 100%; overflow: hidden; }
|
| 26 |
+
|
| 27 |
+
body {
|
| 28 |
+
font-family: 'Share Tech Mono', system-ui, monospace;
|
| 29 |
+
background: var(--floor);
|
| 30 |
+
color: var(--text);
|
| 31 |
+
display: flex;
|
| 32 |
+
flex-direction: column;
|
| 33 |
+
user-select: none;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/* ββ TOPBAR ββ */
|
| 37 |
+
.topbar {
|
| 38 |
+
height: 44px;
|
| 39 |
+
background: #1e2535;
|
| 40 |
+
border-bottom: 2px solid #2a3550;
|
| 41 |
+
display: flex;
|
| 42 |
+
align-items: center;
|
| 43 |
+
justify-content: space-between;
|
| 44 |
+
padding: 0 16px;
|
| 45 |
+
flex-shrink: 0;
|
| 46 |
+
z-index: 100;
|
| 47 |
+
}
|
| 48 |
+
.tb-logo { font-family: 'VT323', monospace; font-size: 20px; color: #4ade80; letter-spacing: 3px; }
|
| 49 |
+
.tb-right { display: flex; align-items: center; gap: 14px; }
|
| 50 |
+
.tb-stat { font-size: 10px; color: #5a7a9a; display: flex; align-items: center; gap: 4px; }
|
| 51 |
+
.sdot { width: 6px; height: 6px; border-radius: 50%; }
|
| 52 |
+
.dg { background: #22c55e; animation: dg 2s ease infinite; }
|
| 53 |
+
.da { background: #f59e0b; animation: dg 1.2s ease infinite; }
|
| 54 |
+
.dr { background: #ef4444; animation: bdr .5s step-end infinite; }
|
| 55 |
+
.dd { background: #334; }
|
| 56 |
+
@keyframes dg { 0%,100%{box-shadow:0 0 0 0 currentColor}50%{box-shadow:0 0 0 3px transparent} }
|
| 57 |
+
@keyframes bdr { 0%,100%{opacity:1}50%{opacity:0} }
|
| 58 |
+
.clock { font-family: 'VT323', monospace; font-size: 20px; color: #4ade80; letter-spacing: 2px; }
|
| 59 |
+
|
| 60 |
+
/* ββ PROGRESS ββ */
|
| 61 |
+
.prog { height: 2px; background: #1e2535; overflow: hidden; display: none; flex-shrink: 0; }
|
| 62 |
+
.prog.on { display: block; }
|
| 63 |
+
.prog::after { content:''; display:block; height:100%; width:30%; background:#4ade80; animation:prun 1.4s ease-in-out infinite; }
|
| 64 |
+
@keyframes prun { 0%{margin-left:-30%}100%{margin-left:110%} }
|
| 65 |
+
|
| 66 |
+
/* ββ MISSION BAR ββ */
|
| 67 |
+
.mbar {
|
| 68 |
+
background: #1e2535;
|
| 69 |
+
border-bottom: 1px solid #2a3550;
|
| 70 |
+
padding: 7px 16px;
|
| 71 |
+
display: flex;
|
| 72 |
+
gap: 8px;
|
| 73 |
+
flex-shrink: 0;
|
| 74 |
+
flex-wrap: wrap;
|
| 75 |
+
z-index: 100;
|
| 76 |
+
}
|
| 77 |
+
.minput {
|
| 78 |
+
flex: 1; min-width: 180px; height: 30px;
|
| 79 |
+
background: #111825; border: 1px solid #2a3550;
|
| 80 |
+
color: #c8d8f0; font-family: 'Share Tech Mono', monospace; font-size: 11px;
|
| 81 |
+
padding: 0 10px; outline: none; border-radius: 2px;
|
| 82 |
+
transition: border-color .15s;
|
| 83 |
+
}
|
| 84 |
+
.minput:focus { border-color: #4ade80; }
|
| 85 |
+
.minput::placeholder { color: #2a3d58; }
|
| 86 |
+
.launch {
|
| 87 |
+
height: 30px; padding: 0 14px;
|
| 88 |
+
background: #22c55e; border: none; color: #0a1a0a;
|
| 89 |
+
font-family: 'VT323', monospace; font-size: 16px; letter-spacing: 2px;
|
| 90 |
+
cursor: pointer; border-radius: 2px; transition: background .15s; white-space: nowrap;
|
| 91 |
+
}
|
| 92 |
+
.launch:hover { background: #16a34a; }
|
| 93 |
+
.launch:disabled { background: #1a3a25; color: #2a4a35; cursor: not-allowed; }
|
| 94 |
+
.add-btn {
|
| 95 |
+
height: 30px; padding: 0 12px;
|
| 96 |
+
background: transparent; border: 1px solid #2a3550;
|
| 97 |
+
color: #5a7a9a; font-size: 10px; cursor: pointer;
|
| 98 |
+
border-radius: 2px; transition: all .15s; white-space: nowrap;
|
| 99 |
+
}
|
| 100 |
+
.add-btn:hover { border-color: #3b82f6; color: #3b82f6; }
|
| 101 |
+
.mc-count { font-family: 'VT323', monospace; font-size: 15px; color: #2a3d58; white-space: nowrap; align-self: center; }
|
| 102 |
+
|
| 103 |
+
/* ββ MAIN ββ */
|
| 104 |
+
.main { display: grid; grid-template-columns: 1fr 230px; flex: 1; min-height: 0; overflow: hidden; }
|
| 105 |
+
@media(max-width:780px){ .main{grid-template-columns:1fr} .sidebar{display:none} }
|
| 106 |
+
|
| 107 |
+
/* ββ OFFICE SCENE ββ */
|
| 108 |
+
.office {
|
| 109 |
+
position: relative;
|
| 110 |
+
overflow: hidden;
|
| 111 |
+
background: var(--floor);
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
/* WALL */
|
| 115 |
+
.wall {
|
| 116 |
+
position: absolute; top: 0; left: 0; right: 0; height: 38%;
|
| 117 |
+
background: linear-gradient(180deg, var(--wall) 0%, var(--wall2) 100%);
|
| 118 |
+
box-shadow: inset 0 -8px 20px rgba(0,0,0,.08);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/* Wall trim */
|
| 122 |
+
.wall::after {
|
| 123 |
+
content: '';
|
| 124 |
+
position: absolute; bottom: 0; left: 0; right: 0; height: 8px;
|
| 125 |
+
background: #c8bc9a;
|
| 126 |
+
box-shadow: 0 2px 6px rgba(0,0,0,.1);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/* FLOOR */
|
| 130 |
+
.floor-area {
|
| 131 |
+
position: absolute; bottom: 0; left: 0; right: 0; top: 38%;
|
| 132 |
+
background: var(--floor);
|
| 133 |
+
}
|
| 134 |
+
/* Parquet pattern */
|
| 135 |
+
.floor-area::before {
|
| 136 |
+
content: '';
|
| 137 |
+
position: absolute; inset: 0;
|
| 138 |
+
background-image:
|
| 139 |
+
linear-gradient(90deg, rgba(0,0,0,.04) 1px, transparent 1px),
|
| 140 |
+
linear-gradient(rgba(0,0,0,.04) 1px, transparent 1px);
|
| 141 |
+
background-size: 60px 40px;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
/* BIG SCREEN β back wall */
|
| 145 |
+
.big-screen {
|
| 146 |
+
position: absolute;
|
| 147 |
+
left: 50%; top: 3%;
|
| 148 |
+
transform: translateX(-50%);
|
| 149 |
+
width: min(38%, 340px);
|
| 150 |
+
aspect-ratio: 16/9;
|
| 151 |
+
background: var(--monitor-bg);
|
| 152 |
+
border: 6px solid #8a9aaa;
|
| 153 |
+
border-radius: 6px;
|
| 154 |
+
box-shadow: var(--shadow), inset 0 0 40px rgba(0,0,0,.3);
|
| 155 |
+
overflow: hidden;
|
| 156 |
+
z-index: 5;
|
| 157 |
+
}
|
| 158 |
+
.big-screen-inner {
|
| 159 |
+
width: 100%; height: 100%;
|
| 160 |
+
background: var(--monitor-screen);
|
| 161 |
+
display: flex;
|
| 162 |
+
flex-direction: column;
|
| 163 |
+
align-items: center;
|
| 164 |
+
justify-content: center;
|
| 165 |
+
gap: 4px;
|
| 166 |
+
padding: 8px;
|
| 167 |
+
position: relative;
|
| 168 |
+
overflow: hidden;
|
| 169 |
+
}
|
| 170 |
+
.bs-title {
|
| 171 |
+
font-family: 'VT323', monospace;
|
| 172 |
+
font-size: 11px;
|
| 173 |
+
color: #4ade80;
|
| 174 |
+
letter-spacing: 3px;
|
| 175 |
+
text-align: center;
|
| 176 |
+
}
|
| 177 |
+
.bs-content {
|
| 178 |
+
font-size: 9px;
|
| 179 |
+
color: #a0d4b0;
|
| 180 |
+
text-align: center;
|
| 181 |
+
line-height: 1.5;
|
| 182 |
+
padding: 0 4px;
|
| 183 |
+
word-break: break-word;
|
| 184 |
+
max-height: 60%;
|
| 185 |
+
overflow: hidden;
|
| 186 |
+
}
|
| 187 |
+
.bs-scan {
|
| 188 |
+
position: absolute; left: 0; right: 0; height: 1px;
|
| 189 |
+
background: rgba(74,222,128,.3);
|
| 190 |
+
animation: scan-line 3s linear infinite;
|
| 191 |
+
}
|
| 192 |
+
@keyframes scan-line { 0%{top:0}100%{top:100%} }
|
| 193 |
+
.bs-corner {
|
| 194 |
+
position: absolute; width: 8px; height: 8px;
|
| 195 |
+
border-color: #4ade80; border-style: solid;
|
| 196 |
+
}
|
| 197 |
+
.bs-corner.tl{top:4px;left:4px;border-width:2px 0 0 2px}
|
| 198 |
+
.bs-corner.tr{top:4px;right:4px;border-width:2px 2px 0 0}
|
| 199 |
+
.bs-corner.bl{bottom:4px;left:4px;border-width:0 0 2px 2px}
|
| 200 |
+
.bs-corner.br{bottom:4px;right:4px;border-width:0 2px 2px 0}
|
| 201 |
+
|
| 202 |
+
/* Screen stand */
|
| 203 |
+
.screen-stand {
|
| 204 |
+
position: absolute;
|
| 205 |
+
left: 50%; transform: translateX(-50%);
|
| 206 |
+
width: 60px; height: 8px;
|
| 207 |
+
background: #8a9aaa;
|
| 208 |
+
border-radius: 0 0 4px 4px;
|
| 209 |
+
z-index: 4;
|
| 210 |
+
}
|
| 211 |
+
.screen-base {
|
| 212 |
+
position: absolute;
|
| 213 |
+
left: 50%; transform: translateX(-50%);
|
| 214 |
+
width: 100px; height: 5px;
|
| 215 |
+
background: #7a8a9a;
|
| 216 |
+
border-radius: 4px;
|
| 217 |
+
z-index: 4;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
/* WINDOWS β back wall sides */
|
| 221 |
+
.window {
|
| 222 |
+
position: absolute; top: 3%; width: 10%; height: 28%;
|
| 223 |
+
background: linear-gradient(180deg, #c8e8f8 0%, #a8d4f0 60%, #e8f4ff 100%);
|
| 224 |
+
border: 4px solid #d8ceb8;
|
| 225 |
+
border-radius: 3px;
|
| 226 |
+
box-shadow: inset 0 0 20px rgba(200,230,255,.5);
|
| 227 |
+
overflow: hidden;
|
| 228 |
+
}
|
| 229 |
+
.window::before {
|
| 230 |
+
content: ''; position: absolute;
|
| 231 |
+
top: 0; left: 50%; transform: translateX(-50%);
|
| 232 |
+
width: 3px; height: 100%; background: rgba(216,206,184,.8);
|
| 233 |
+
}
|
| 234 |
+
.window::after {
|
| 235 |
+
content: ''; position: absolute;
|
| 236 |
+
left: 0; top: 50%; transform: translateY(-50%);
|
| 237 |
+
height: 3px; width: 100%; background: rgba(216,206,184,.8);
|
| 238 |
+
}
|
| 239 |
+
.window.left { left: 4%; }
|
| 240 |
+
.window.right { right: 4%; }
|
| 241 |
+
|
| 242 |
+
/* SERVER RACK */
|
| 243 |
+
.server-rack {
|
| 244 |
+
position: absolute;
|
| 245 |
+
top: 2%; right: 1%;
|
| 246 |
+
width: 52px;
|
| 247 |
+
background: var(--server);
|
| 248 |
+
border: 1px solid #2a3550;
|
| 249 |
+
border-radius: 2px;
|
| 250 |
+
padding: 5px 4px;
|
| 251 |
+
display: flex;
|
| 252 |
+
flex-direction: column;
|
| 253 |
+
gap: 2px;
|
| 254 |
+
box-shadow: var(--shadow-sm);
|
| 255 |
+
z-index: 5;
|
| 256 |
+
}
|
| 257 |
+
.rack-unit {
|
| 258 |
+
height: 8px;
|
| 259 |
+
background: var(--server2);
|
| 260 |
+
border: 1px solid #1a2a3a;
|
| 261 |
+
display: flex;
|
| 262 |
+
align-items: center;
|
| 263 |
+
gap: 2px;
|
| 264 |
+
padding: 0 3px;
|
| 265 |
+
}
|
| 266 |
+
.rack-led {
|
| 267 |
+
width: 4px; height: 4px; border-radius: 50%;
|
| 268 |
+
}
|
| 269 |
+
.rack-bar { flex: 1; height: 2px; background: #1a2a3a; }
|
| 270 |
+
|
| 271 |
+
/* DESK */
|
| 272 |
+
.desk {
|
| 273 |
+
position: absolute;
|
| 274 |
+
width: 130px; height: 72px;
|
| 275 |
+
background: var(--desk);
|
| 276 |
+
border-radius: 6px;
|
| 277 |
+
box-shadow: 0 12px 30px rgba(0,0,0,.18),
|
| 278 |
+
inset 0 3px 8px rgba(255,255,255,.22);
|
| 279 |
+
}
|
| 280 |
+
.desk::before {
|
| 281 |
+
content: '';
|
| 282 |
+
position: absolute; top: 0; left: 0; right: 0; height: 3px;
|
| 283 |
+
background: rgba(255,255,255,.28);
|
| 284 |
+
border-radius: 6px 6px 0 0;
|
| 285 |
+
}
|
| 286 |
+
/* Desk legs shadow */
|
| 287 |
+
.desk::after {
|
| 288 |
+
content: '';
|
| 289 |
+
position: absolute; bottom: -14px; left: 8px; right: 8px; height: 14px;
|
| 290 |
+
background: linear-gradient(180deg, rgba(0,0,0,.15), transparent);
|
| 291 |
+
}
|
| 292 |
+
.desk-monitor {
|
| 293 |
+
position: absolute; top: 6px; left: 10px;
|
| 294 |
+
width: 88px; height: 52px;
|
| 295 |
+
background: #2a3545;
|
| 296 |
+
border: 2px solid #1a2535;
|
| 297 |
+
border-radius: 3px;
|
| 298 |
+
overflow: hidden;
|
| 299 |
+
}
|
| 300 |
+
.desk-monitor-screen {
|
| 301 |
+
width: 100%; height: 100%;
|
| 302 |
+
background: #0a1a28;
|
| 303 |
+
display: flex; align-items: center; justify-content: center;
|
| 304 |
+
font-size: 7px; color: #4ade80; letter-spacing: 1px;
|
| 305 |
+
font-family: 'Share Tech Mono', monospace;
|
| 306 |
+
position: relative;
|
| 307 |
+
}
|
| 308 |
+
.desk-mug {
|
| 309 |
+
position: absolute; top: 10px; right: 8px;
|
| 310 |
+
width: 18px; height: 20px;
|
| 311 |
+
border-radius: 3px 3px 5px 5px;
|
| 312 |
+
box-shadow: inset 0 -4px 6px rgba(0,0,0,.2);
|
| 313 |
+
}
|
| 314 |
+
.desk-kbd {
|
| 315 |
+
position: absolute; bottom: 7px; left: 14px;
|
| 316 |
+
width: 70px; height: 8px;
|
| 317 |
+
background: #d4cdc0; border-radius: 2px;
|
| 318 |
+
box-shadow: inset 0 2px 4px rgba(0,0,0,.1);
|
| 319 |
+
}
|
| 320 |
+
.desk-paper {
|
| 321 |
+
position: absolute; bottom: 18px; right: 10px;
|
| 322 |
+
width: 28px; height: 20px;
|
| 323 |
+
background: #fafaf5;
|
| 324 |
+
border: 1px solid #ddd;
|
| 325 |
+
border-radius: 1px;
|
| 326 |
+
transform: rotate(-3deg);
|
| 327 |
+
box-shadow: 1px 1px 3px rgba(0,0,0,.1);
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
/* AGENT PERSON β CSS character */
|
| 331 |
+
.agent-person {
|
| 332 |
+
position: absolute;
|
| 333 |
+
width: 36px; height: 80px;
|
| 334 |
+
z-index: 20;
|
| 335 |
+
transition: left .8s cubic-bezier(.4,0,.2,1), bottom .8s cubic-bezier(.4,0,.2,1);
|
| 336 |
+
cursor: pointer;
|
| 337 |
+
}
|
| 338 |
+
.p-head {
|
| 339 |
+
width: 24px; height: 24px;
|
| 340 |
+
background: var(--skin);
|
| 341 |
+
border-radius: 50%;
|
| 342 |
+
position: absolute; top: 0; left: 6px;
|
| 343 |
+
box-shadow: 0 3px 8px rgba(0,0,0,.15);
|
| 344 |
+
}
|
| 345 |
+
.p-hair {
|
| 346 |
+
width: 26px; height: 16px;
|
| 347 |
+
position: absolute; top: -5px; left: 5px;
|
| 348 |
+
border-radius: 50% 50% 20% 20%;
|
| 349 |
+
}
|
| 350 |
+
.p-eyes {
|
| 351 |
+
position: absolute; top: 9px; left: 8px;
|
| 352 |
+
display: flex; gap: 6px;
|
| 353 |
+
}
|
| 354 |
+
.p-eye { width: 4px; height: 4px; background: #2a1a0a; border-radius: 50%; }
|
| 355 |
+
.p-body {
|
| 356 |
+
width: 28px; height: 38px;
|
| 357 |
+
position: absolute; top: 22px; left: 4px;
|
| 358 |
+
border-radius: 8px 8px 6px 6px;
|
| 359 |
+
box-shadow: 0 4px 12px rgba(0,0,0,.15);
|
| 360 |
+
}
|
| 361 |
+
.p-arms {
|
| 362 |
+
position: absolute; top: 26px; left: 0; right: 0; height: 24px;
|
| 363 |
+
}
|
| 364 |
+
.p-arm {
|
| 365 |
+
position: absolute; width: 8px; height: 22px;
|
| 366 |
+
border-radius: 4px;
|
| 367 |
+
top: 0;
|
| 368 |
+
}
|
| 369 |
+
.p-arm.left { left: 0; transform-origin: top center; }
|
| 370 |
+
.p-arm.right { right: 0; transform-origin: top center; }
|
| 371 |
+
.p-legs { position: absolute; top: 58px; left: 4px; right: 4px; height: 20px; display: flex; gap: 2px; }
|
| 372 |
+
.p-leg { flex: 1; background: #2a3545; border-radius: 2px 2px 4px 4px; height: 100%; }
|
| 373 |
+
.p-feet { position: absolute; top: 74px; left: 2px; right: 2px; display: flex; gap: 4px; }
|
| 374 |
+
.p-foot { flex: 1; height: 6px; background: #1a1a2a; border-radius: 0 0 3px 3px; }
|
| 375 |
+
|
| 376 |
+
/* Walking animation */
|
| 377 |
+
@keyframes walk-bob { 0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)} }
|
| 378 |
+
@keyframes arm-swing-l { 0%,100%{transform:rotate(-15deg)}50%{transform:rotate(15deg)} }
|
| 379 |
+
@keyframes arm-swing-r { 0%,100%{transform:rotate(15deg)}50%{transform:rotate(-15deg)} }
|
| 380 |
+
@keyframes work-type { 0%,100%{transform:rotate(-8deg)}50%{transform:rotate(8deg)} }
|
| 381 |
+
@keyframes breathe { 0%,100%{transform:scaleY(1)}50%{transform:scaleY(1.02)} }
|
| 382 |
+
|
| 383 |
+
.agent-person.walking { animation: walk-bob .5s ease-in-out infinite; }
|
| 384 |
+
.agent-person.walking .p-arm.left { animation: arm-swing-l .5s ease-in-out infinite; }
|
| 385 |
+
.agent-person.walking .p-arm.right { animation: arm-swing-r .5s ease-in-out infinite; }
|
| 386 |
+
.agent-person.working .p-arm.left { animation: work-type .3s ease-in-out infinite; }
|
| 387 |
+
.agent-person.working .p-arm.right { animation: work-type .3s ease-in-out infinite reverse; }
|
| 388 |
+
.agent-person.idle .p-body { animation: breathe 3s ease-in-out infinite; }
|
| 389 |
+
|
| 390 |
+
/* Agent name tag */
|
| 391 |
+
.agent-tag {
|
| 392 |
+
position: absolute;
|
| 393 |
+
bottom: -18px; left: 50%; transform: translateX(-50%);
|
| 394 |
+
font-size: 8px; white-space: nowrap;
|
| 395 |
+
font-family: 'VT323', monospace;
|
| 396 |
+
letter-spacing: 1px;
|
| 397 |
+
padding: 1px 5px;
|
| 398 |
+
border-radius: 2px;
|
| 399 |
+
opacity: .9;
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
/* Talk bubble */
|
| 403 |
+
.talk-bubble {
|
| 404 |
+
position: absolute;
|
| 405 |
+
bottom: 88px; left: 50%;
|
| 406 |
+
transform: translateX(-50%) scale(.9);
|
| 407 |
+
background: white; color: var(--text);
|
| 408 |
+
padding: 6px 10px;
|
| 409 |
+
border-radius: 10px;
|
| 410 |
+
font-size: 10px; font-family: 'Share Tech Mono', monospace;
|
| 411 |
+
white-space: nowrap;
|
| 412 |
+
box-shadow: var(--shadow-sm);
|
| 413 |
+
border: 1px solid #ddd;
|
| 414 |
+
opacity: 0;
|
| 415 |
+
transition: all .3s;
|
| 416 |
+
z-index: 30;
|
| 417 |
+
pointer-events: none;
|
| 418 |
+
max-width: 160px;
|
| 419 |
+
white-space: normal;
|
| 420 |
+
text-align: center;
|
| 421 |
+
line-height: 1.3;
|
| 422 |
+
}
|
| 423 |
+
.talk-bubble::after {
|
| 424 |
+
content: '';
|
| 425 |
+
position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
|
| 426 |
+
border: 5px solid transparent;
|
| 427 |
+
border-top-color: white;
|
| 428 |
+
}
|
| 429 |
+
.talk-bubble.show { opacity: 1; transform: translateX(-50%) scale(1); }
|
| 430 |
+
|
| 431 |
+
/* Status glow ring */
|
| 432 |
+
.agent-person.working::before {
|
| 433 |
+
content: '';
|
| 434 |
+
position: absolute; inset: -4px;
|
| 435 |
+
border-radius: 50%;
|
| 436 |
+
border: 2px solid;
|
| 437 |
+
animation: dg 1s ease infinite;
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
/* PLANT */
|
| 441 |
+
.plant {
|
| 442 |
+
position: absolute;
|
| 443 |
+
z-index: 6;
|
| 444 |
+
}
|
| 445 |
+
.plant-pot { width: 20px; height: 16px; background: #a07050; border-radius: 0 0 4px 4px; margin: 0 auto; }
|
| 446 |
+
.plant-soil { width: 24px; height: 5px; background: #5a3a20; border-radius: 2px; margin: 0 auto; }
|
| 447 |
+
.plant-leaves { display: flex; justify-content: center; gap: 3px; margin-bottom: 2px; }
|
| 448 |
+
.plant-leaf { width: 12px; height: 18px; background: #2d7a3a; border-radius: 50% 50% 40% 40%; }
|
| 449 |
+
.plant-leaf:nth-child(1) { transform: rotate(-25deg); background: #267030; }
|
| 450 |
+
.plant-leaf:nth-child(3) { transform: rotate(25deg); background: #34884a; }
|
| 451 |
+
|
| 452 |
+
/* BOOKSHELF */
|
| 453 |
+
.bookshelf {
|
| 454 |
+
position: absolute;
|
| 455 |
+
background: #b8905a;
|
| 456 |
+
border: 2px solid #9a7840;
|
| 457 |
+
border-radius: 2px;
|
| 458 |
+
display: flex;
|
| 459 |
+
flex-direction: column;
|
| 460 |
+
gap: 2px;
|
| 461 |
+
padding: 3px 4px;
|
| 462 |
+
z-index: 5;
|
| 463 |
+
}
|
| 464 |
+
.book-row { display: flex; gap: 1px; }
|
| 465 |
+
.book { width: 6px; height: 18px; border-radius: 1px 2px 2px 1px; }
|
| 466 |
+
|
| 467 |
+
/* RESULT PANEL */
|
| 468 |
+
.result-panel {
|
| 469 |
+
position: absolute; bottom: 0; left: 0; right: 0;
|
| 470 |
+
max-height: 42%;
|
| 471 |
+
background: rgba(248,249,250,.96);
|
| 472 |
+
border-top: 2px solid #ddd;
|
| 473 |
+
padding: 10px 14px;
|
| 474 |
+
overflow-y: auto;
|
| 475 |
+
z-index: 50;
|
| 476 |
+
display: none;
|
| 477 |
+
backdrop-filter: blur(8px);
|
| 478 |
+
box-shadow: 0 -8px 32px rgba(0,0,0,.12);
|
| 479 |
+
}
|
| 480 |
+
.result-panel.show { display: block; }
|
| 481 |
+
.rp-header {
|
| 482 |
+
display: flex; justify-content: space-between; align-items: center;
|
| 483 |
+
margin-bottom: 10px;
|
| 484 |
+
padding-bottom: 8px;
|
| 485 |
+
border-bottom: 1px solid #eee;
|
| 486 |
+
}
|
| 487 |
+
.rp-title { font-family: 'VT323', monospace; font-size: 16px; color: var(--text); letter-spacing: 2px; }
|
| 488 |
+
.rp-close { cursor: pointer; color: var(--text3); font-size: 16px; padding: 2px 8px; border: 1px solid #ddd; border-radius: 3px; }
|
| 489 |
+
.rp-close:hover { background: #f0f0f0; }
|
| 490 |
+
.rp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
|
| 491 |
+
.rp-card {
|
| 492 |
+
background: white; border: 1px solid #e8e8e8;
|
| 493 |
+
border-radius: 6px; padding: 10px 12px;
|
| 494 |
+
box-shadow: var(--shadow-sm);
|
| 495 |
+
}
|
| 496 |
+
.rp-name {
|
| 497 |
+
font-family: 'VT323', monospace; font-size: 14px;
|
| 498 |
+
letter-spacing: 1px; margin-bottom: 5px;
|
| 499 |
+
display: flex; align-items: center; gap: 6px;
|
| 500 |
+
}
|
| 501 |
+
.rp-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
|
| 502 |
+
.rp-text { font-size: 10px; color: var(--text2); line-height: 1.5; word-break: break-word; }
|
| 503 |
+
.rp-model { font-size: 9px; color: var(--text3); margin-top: 5px; }
|
| 504 |
+
.dl-btn {
|
| 505 |
+
display: inline-flex; align-items: center; gap: 4px;
|
| 506 |
+
margin-top: 6px; padding: 4px 10px;
|
| 507 |
+
background: #f0f9f0; border: 1px solid #4ade80;
|
| 508 |
+
color: #16a34a; font-size: 9px; cursor: pointer;
|
| 509 |
+
text-decoration: none; border-radius: 3px;
|
| 510 |
+
font-family: 'Share Tech Mono', monospace;
|
| 511 |
+
transition: background .15s;
|
| 512 |
+
}
|
| 513 |
+
.dl-btn:hover { background: #dcfce7; }
|
| 514 |
+
.img-row { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 6px; }
|
| 515 |
+
.img-th {
|
| 516 |
+
width: 64px; height: 46px; object-fit: cover;
|
| 517 |
+
border: 1px solid #ddd; border-radius: 3px;
|
| 518 |
+
cursor: pointer; transition: border-color .15s;
|
| 519 |
+
}
|
| 520 |
+
.img-th:hover { border-color: #4ade80; }
|
| 521 |
+
|
| 522 |
+
/* Orch log in result panel */
|
| 523 |
+
.orch-log { margin-top: 10px; padding-top: 8px; border-top: 1px solid #eee; }
|
| 524 |
+
.orch-title { font-family: 'VT323', monospace; font-size: 12px; color: var(--text3); letter-spacing: 2px; margin-bottom: 4px; }
|
| 525 |
+
.orch-ev { font-size: 9px; color: var(--text3); padding: 1px 0; display: flex; gap: 8px; }
|
| 526 |
+
.orch-t { color: var(--text3); flex-shrink: 0; }
|
| 527 |
+
|
| 528 |
+
/* LIGHTBOX */
|
| 529 |
+
.lbox { position: fixed; inset: 0; background: rgba(0,0,0,.88); z-index: 900; display: none; align-items: center; justify-content: center; flex-direction: column; gap: 12px; }
|
| 530 |
+
.lbox.show { display: flex; }
|
| 531 |
+
.lbox img { max-width: 85vw; max-height: 72vh; border-radius: 4px; box-shadow: var(--shadow); }
|
| 532 |
+
.lbox-btns { display: flex; gap: 10px; }
|
| 533 |
+
.lbox-dl { display:inline-flex;align-items:center;gap:4px;padding:6px 16px;background:rgba(74,222,128,.1);border:1px solid #4ade80;color:#4ade80;font-size:11px;cursor:pointer;text-decoration:none;border-radius:3px; }
|
| 534 |
+
.lbox-cl { font-size: 13px; color: #8a9aaa; cursor: pointer; padding: 6px 16px; border: 1px solid #334; border-radius: 3px; }
|
| 535 |
+
.lbox-cl:hover { color: white; }
|
| 536 |
|
| 537 |
/* WALK OVERLAY */
|
| 538 |
+
.walk-ov { position: fixed; inset: 0; background: rgba(248,249,250,.94); z-index: 400; display: none; align-items: center; justify-content: center; flex-direction: column; gap: 12px; backdrop-filter: blur(4px); }
|
| 539 |
+
.walk-ov.show { display: flex; }
|
| 540 |
+
.walk-wrap { width: min(680px,94vw); height: min(340px,50vh); background: white; border: 1px solid #ddd; border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); position: relative; }
|
| 541 |
+
.walk-lbl { font-family: 'VT323', monospace; font-size: 14px; color: var(--text); letter-spacing: 3px; }
|
| 542 |
+
.walk-log { font-size: 10px; color: var(--text2); height: 14px; font-family: 'Share Tech Mono', monospace; }
|
| 543 |
+
|
| 544 |
+
/* MODAL */
|
| 545 |
+
.modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 300; display: none; align-items: center; justify-content: center; }
|
| 546 |
+
.modal-bg.show { display: flex; }
|
| 547 |
+
.modal { background: white; border-radius: 8px; padding: 22px; width: 360px; box-shadow: var(--shadow); }
|
| 548 |
+
.modal-t { font-family: 'VT323', monospace; font-size: 20px; color: var(--text); letter-spacing: 2px; margin-bottom: 14px; }
|
| 549 |
+
.field { margin-bottom: 10px; }
|
| 550 |
+
.field label { display: block; font-size: 10px; color: var(--text2); letter-spacing: 1px; margin-bottom: 3px; }
|
| 551 |
+
.field input, .field select, .field textarea { width: 100%; background: #f8f9fa; border: 1px solid #ddd; color: var(--text); font-family: 'Share Tech Mono', monospace; font-size: 11px; padding: 6px 8px; outline: none; border-radius: 3px; transition: border-color .15s; }
|
| 552 |
+
.field input:focus, .field select:focus, .field textarea:focus { border-color: #22c55e; }
|
| 553 |
+
.field textarea { resize: vertical; min-height: 50px; }
|
| 554 |
+
.modal-acts { display: flex; gap: 7px; justify-content: flex-end; margin-top: 12px; }
|
| 555 |
+
.mbtn { padding: 6px 16px; font-size: 11px; cursor: pointer; border: 1px solid; background: transparent; border-radius: 3px; transition: all .15s; }
|
| 556 |
+
.mbtn-ok { border-color: #22c55e; color: #16a34a; }
|
| 557 |
+
.mbtn-ok:hover { background: #f0fdf4; }
|
| 558 |
+
.mbtn-no { border-color: #ddd; color: var(--text2); }
|
| 559 |
+
.mbtn-no:hover { background: #f8f9fa; }
|
| 560 |
|
| 561 |
/* NOTIFICATION */
|
| 562 |
+
.notif {
|
| 563 |
+
position: fixed; top: 52px; right: 14px;
|
| 564 |
+
background: white; border: 1px solid #ddd; border-left: 3px solid #22c55e;
|
| 565 |
+
padding: 10px 14px; max-width: 260px; z-index: 600;
|
| 566 |
+
transform: translateX(280px); transition: transform .3s ease;
|
| 567 |
+
border-radius: 6px; box-shadow: var(--shadow); font-size: 11px;
|
| 568 |
+
}
|
| 569 |
+
.notif.show { transform: translateX(0); }
|
| 570 |
+
.notif-t { font-family: 'VT323', monospace; font-size: 16px; letter-spacing: 1px; margin-bottom: 2px; }
|
| 571 |
+
.notif-m { color: var(--text2); font-size: 10px; line-height: 1.4; }
|
| 572 |
|
| 573 |
/* SIDEBAR */
|
| 574 |
+
.sidebar { background: white; border-left: 1px solid #e8e8e8; display: flex; flex-direction: column; overflow: hidden; }
|
| 575 |
+
.sb-sec { border-bottom: 1px solid #eee; flex-shrink: 0; }
|
| 576 |
+
.sb-t { font-family: 'VT323', monospace; font-size: 13px; color: var(--text2); padding: 8px 12px 6px; border-bottom: 1px solid #eee; letter-spacing: 2px; }
|
| 577 |
+
.act-scroll { overflow-y: auto; max-height: 240px; }
|
| 578 |
+
.act-item { padding: 6px 12px; border-bottom: 1px solid #f0f0f0; display: flex; gap: 6px; }
|
| 579 |
+
.act-dot { width: 4px; height: 4px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; }
|
| 580 |
+
.act-msg { font-size: 10px; color: var(--text); line-height: 1.4; }
|
| 581 |
+
.act-time { font-size: 9px; color: var(--text3); margin-top: 1px; }
|
| 582 |
+
.hist-scroll { overflow-y: auto; max-height: 200px; }
|
| 583 |
+
.hist-item { padding: 7px 12px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background .1s; }
|
| 584 |
+
.hist-item:hover { background: #f8f9fa; }
|
| 585 |
+
.hist-task { font-size: 10px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 586 |
+
.hist-meta { font-size: 9px; color: var(--text3); margin-top: 2px; display: flex; gap: 6px; }
|
| 587 |
+
::-webkit-scrollbar { width: 4px; }
|
| 588 |
+
::-webkit-scrollbar-track { background: transparent; }
|
| 589 |
+
::-webkit-scrollbar-thumb { background: #ddd; border-radius: 2px; }
|
| 590 |
</style>
|
| 591 |
</head>
|
| 592 |
<body>
|
| 593 |
+
|
| 594 |
+
<!-- TOPBAR -->
|
| 595 |
<header class="topbar">
|
| 596 |
<div style="display:flex;align-items:center;gap:8px">
|
| 597 |
<span class="tb-logo">MISSION CONTROL AI</span>
|
| 598 |
+
<span style="font-size:9px;color:#2a3d58">v7</span>
|
| 599 |
</div>
|
| 600 |
<div class="tb-right">
|
| 601 |
<div class="tb-stat"><div class="sdot dg"></div><span id="top-active">0 active</span></div>
|
|
|
|
| 603 |
<div class="clock" id="clock">00:00:00</div>
|
| 604 |
</div>
|
| 605 |
</header>
|
| 606 |
+
|
| 607 |
<div class="prog" id="prog"></div>
|
| 608 |
+
|
| 609 |
+
<!-- MISSION BAR -->
|
| 610 |
<div class="mbar">
|
| 611 |
+
<input class="minput" id="task-input" placeholder="Assign mission to agents..." autocomplete="off"/>
|
| 612 |
<button class="launch" id="launch-btn" onclick="launchMission()">► LAUNCH</button>
|
| 613 |
<button class="add-btn" onclick="openModal()">+ Agent</button>
|
| 614 |
+
<span class="mc-count" id="mc-count">MISSIONS: 0</span>
|
| 615 |
</div>
|
| 616 |
+
|
| 617 |
+
<!-- MAIN -->
|
| 618 |
<div class="main">
|
| 619 |
+
<div class="office" id="office">
|
| 620 |
+
|
| 621 |
+
<!-- WALL -->
|
| 622 |
+
<div class="wall"></div>
|
| 623 |
+
|
| 624 |
+
<!-- WINDOWS -->
|
| 625 |
+
<div class="window left"></div>
|
| 626 |
+
<div class="window right"></div>
|
| 627 |
+
|
| 628 |
+
<!-- FLOOR -->
|
| 629 |
+
<div class="floor-area"></div>
|
| 630 |
+
|
| 631 |
+
<!-- BIG SCREEN -->
|
| 632 |
+
<div class="big-screen" id="big-screen">
|
| 633 |
+
<div class="big-screen-inner">
|
| 634 |
+
<div class="bs-corner tl"></div>
|
| 635 |
+
<div class="bs-corner tr"></div>
|
| 636 |
+
<div class="bs-corner bl"></div>
|
| 637 |
+
<div class="bs-corner br"></div>
|
| 638 |
+
<div class="bs-scan"></div>
|
| 639 |
+
<div class="bs-title" id="bs-title">MISSION CONTROL AI</div>
|
| 640 |
+
<div class="bs-content" id="bs-content">Awaiting mission...</div>
|
| 641 |
+
</div>
|
| 642 |
</div>
|
| 643 |
+
|
| 644 |
+
<!-- Screen stand -->
|
| 645 |
+
<div class="screen-stand" id="screen-stand" style="top:0;"></div>
|
| 646 |
+
<div class="screen-base" id="screen-base" style="top:0;"></div>
|
| 647 |
+
|
| 648 |
+
<!-- SERVER RACK -->
|
| 649 |
+
<div class="server-rack" id="server-rack-el"></div>
|
| 650 |
+
|
| 651 |
+
<!-- PLANTS -->
|
| 652 |
+
<div class="plant" style="right:16%;bottom:32%">
|
| 653 |
+
<div class="plant-leaves"><div class="plant-leaf"></div><div class="plant-leaf"></div><div class="plant-leaf"></div></div>
|
| 654 |
+
<div class="plant-soil"></div><div class="plant-pot"></div>
|
| 655 |
+
</div>
|
| 656 |
+
<div class="plant" style="left:17%;bottom:30%">
|
| 657 |
+
<div class="plant-leaves"><div class="plant-leaf"></div><div class="plant-leaf"></div><div class="plant-leaf"></div></div>
|
| 658 |
+
<div class="plant-soil"></div><div class="plant-pot"></div>
|
| 659 |
+
</div>
|
| 660 |
+
|
| 661 |
+
<!-- BOOKSHELF -->
|
| 662 |
+
<div class="bookshelf" style="left:1%;top:10%;width:36px;height:70px" id="bookshelf-el"></div>
|
| 663 |
+
|
| 664 |
+
<!-- DESKS rendered by JS -->
|
| 665 |
+
<div id="desks-layer"></div>
|
| 666 |
+
|
| 667 |
+
<!-- AGENTS rendered by JS -->
|
| 668 |
+
<div id="agents-layer"></div>
|
| 669 |
+
|
| 670 |
+
<!-- RESULT PANEL -->
|
| 671 |
+
<div class="result-panel" id="result-panel">
|
| 672 |
+
<div class="rp-header">
|
| 673 |
+
<div class="rp-title">MISSION RESULTS</div>
|
| 674 |
+
<div class="rp-close" onclick="document.getElementById('result-panel').classList.remove('show')">✕ close</div>
|
| 675 |
+
</div>
|
| 676 |
+
<div class="rp-grid" id="rp-grid"></div>
|
| 677 |
+
<div id="orch-log-wrap"></div>
|
| 678 |
+
</div>
|
| 679 |
+
|
| 680 |
</div>
|
| 681 |
+
|
| 682 |
+
<!-- SIDEBAR -->
|
| 683 |
<aside class="sidebar">
|
| 684 |
<div class="sb-sec"><div class="sb-t">ACTIVITY</div><div class="act-scroll" id="act-log"></div></div>
|
| 685 |
+
<div class="sb-sec"><div class="sb-t">MISSIONS</div>
|
| 686 |
+
<div class="hist-scroll" id="hist-log">
|
| 687 |
+
<div style="padding:12px;font-size:10px;color:var(--text3);text-align:center">No missions yet</div>
|
| 688 |
+
</div>
|
| 689 |
+
</div>
|
| 690 |
</aside>
|
| 691 |
</div>
|
| 692 |
|
| 693 |
<!-- WALK OVERLAY -->
|
| 694 |
+
<div class="walk-ov" id="walk-ov">
|
| 695 |
+
<div class="walk-lbl" id="walk-lbl">MANAGER HEADING TO CONFERENCE ROOM</div>
|
| 696 |
<div class="walk-wrap"><canvas id="walk-canvas"></canvas></div>
|
| 697 |
+
<div class="walk-log" id="walk-log">Preparing briefing...</div>
|
| 698 |
</div>
|
| 699 |
|
| 700 |
<!-- LIGHTBOX -->
|
| 701 |
+
<div class="lbox" id="lbox" onclick="if(event.target===this)closeLbox()">
|
| 702 |
+
<img id="lbox-img" src="" alt=""/>
|
| 703 |
+
<div class="lbox-btns">
|
| 704 |
+
<a class="lbox-dl" id="lbox-dl" href="#" download>↓ Download</a>
|
| 705 |
+
<div class="lbox-cl" onclick="closeLbox()">✕ Close</div>
|
| 706 |
</div>
|
| 707 |
</div>
|
| 708 |
|
| 709 |
<!-- ADD AGENT MODAL -->
|
| 710 |
<div class="modal-bg" id="modal-bg" onclick="if(event.target===this)closeModal()">
|
| 711 |
<div class="modal">
|
| 712 |
+
<div class="modal-t">+ New Agent</div>
|
| 713 |
+
<div class="field"><label>Key (ID)</label><input id="m-key" placeholder="e.g. researcher"/></div>
|
| 714 |
+
<div class="field"><label>Name</label><input id="m-name" placeholder="e.g. Researcher"/></div>
|
| 715 |
<div class="field"><label>Role</label><textarea id="m-role" placeholder="Especialista en..."></textarea></div>
|
| 716 |
+
<div class="field"><label>Provider</label>
|
| 717 |
+
<select id="m-prov">
|
| 718 |
+
<option value="openrouter">OpenRouter (free)</option>
|
| 719 |
+
<option value="gemini">Google Gemini</option>
|
| 720 |
+
<option value="groq">Groq</option>
|
| 721 |
+
</select>
|
| 722 |
+
</div>
|
| 723 |
<div class="modal-acts">
|
| 724 |
<button class="mbtn mbtn-no" onclick="closeModal()">Cancel</button>
|
| 725 |
<button class="mbtn mbtn-ok" onclick="submitAgent()">Add Agent</button>
|
|
|
|
| 729 |
|
| 730 |
<!-- NOTIFICATION -->
|
| 731 |
<div class="notif" id="notif">
|
| 732 |
+
<div class="notif-t" id="notif-t">Done</div>
|
| 733 |
<div class="notif-m" id="notif-m"></div>
|
| 734 |
</div>
|
| 735 |
|
| 736 |
<script>
|
| 737 |
+
// ββ CONFIG ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 738 |
+
const AGENT_COLORS = {
|
| 739 |
+
manager:'#22c55e', backend_dev:'#3b82f6', frontend_dev:'#a855f7',
|
| 740 |
+
analyst:'#f59e0b', writer:'#06b6d4', image_agent:'#ec4899', developer:'#3b82f6'
|
| 741 |
+
};
|
| 742 |
+
const SHIRT_COLORS = {
|
| 743 |
+
manager:'#166534', backend_dev:'#1e3a6e', frontend_dev:'#581c87',
|
| 744 |
+
analyst:'#78350f', writer:'#164e63', image_agent:'#831843', developer:'#1e3a6e'
|
| 745 |
+
};
|
| 746 |
+
const HAIR_COLORS = { manager:'#4a2e18', backend_dev:'#1a1a2e', frontend_dev:'#2e0a4a', analyst:'#3a2010', writer:'#0a2a3a', image_agent:'#2a0a1a', developer:'#1a1a2e' };
|
| 747 |
+
const SPEECH_IDLE = { manager:['Planning next move...','Reviewing KPIs...','Coffee break?'], backend_dev:['Debugging...','Push to prod?','PR approved!'], frontend_dev:['CSS is fine...','Pixel perfect!','Dark mode ftw'], analyst:['Numbers check out','Risk: medium','ROI looks good'], writer:['Draft complete','Editing now...','Words per minute: β'], image_agent:['Searching...','Found it!','Resolution: HD'], developer:['Compiling...','Ship it!','LGTM'] };
|
| 748 |
+
const SPEECH_WORK = { manager:['Delegating now...','Team briefed!','Executing plan'], backend_dev:['Building...','Almost done...','Tests passing!'], frontend_dev:['Styling...','Responsive β','Deployed!'], analyst:['Analyzing data...','Insight found!','Report ready'], writer:['Writing report...','First draft done','Publishing...'], image_agent:['Fetching image...','Generating AI...','Found 3 images!'], developer:['Coding...','Feature done!','Bug fixed!'] };
|
| 749 |
+
const PROV_MODELS = { openrouter:['meta-llama/llama-3.3-70b-instruct:free','qwen/qwen3-4b:free'], gemini:['gemini-2.5-flash-preview-04-17','gemini-2.0-flash'], groq:['llama3-70b-8192','gemma2-9b-it'] };
|
| 750 |
+
|
| 751 |
+
let agentDefs=[], agentStates={}, agentPositions={}, agentTargets={}, agentModes={};
|
| 752 |
+
let activity=[], history=[], mCount=0, bubbleTimers={};
|
| 753 |
+
let officeW=0, officeH=0;
|
| 754 |
+
|
| 755 |
+
// ββ INIT βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 756 |
+
async function init(){
|
| 757 |
+
positionScreenElements();
|
| 758 |
+
buildServerRack();
|
| 759 |
+
buildBookshelf();
|
| 760 |
+
const r = await fetch('/api/agents'), d = await r.json();
|
| 761 |
+
agentDefs = d.agents;
|
| 762 |
+
agentDefs.forEach(a=>{
|
| 763 |
+
agentStates[a.key] = { status:'idle', message:'', model:'', doc_file:null, img_base:null, img_count:0 };
|
| 764 |
+
agentModes[a.key] = 'idle';
|
| 765 |
+
});
|
| 766 |
+
renderDesks();
|
| 767 |
+
renderAgents();
|
| 768 |
+
startIdleLife();
|
| 769 |
+
addAct('Office open β agents ready','#22c55e');
|
| 770 |
}
|
| 771 |
|
| 772 |
+
function positionScreenElements(){
|
| 773 |
+
const office = document.getElementById('office');
|
| 774 |
+
officeW = office.clientWidth; officeH = office.clientHeight;
|
| 775 |
+
const screen = document.getElementById('big-screen');
|
| 776 |
+
const sw = screen.offsetWidth, sh = screen.offsetHeight;
|
| 777 |
+
const screenBottom = officeH * 0.04 + sh;
|
| 778 |
+
document.getElementById('screen-stand').style.cssText = `top:${screenBottom}px;left:${officeW/2-30}px;`;
|
| 779 |
+
document.getElementById('screen-base').style.cssText = `top:${screenBottom+8}px;left:${officeW/2-50}px;`;
|
| 780 |
+
}
|
| 781 |
|
| 782 |
+
// ββ SERVER RACK ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 783 |
+
function buildServerRack(){
|
| 784 |
+
const el = document.getElementById('server-rack-el');
|
| 785 |
+
el.style.cssText = `position:absolute;top:3%;right:1%;width:52px;background:#1a2035;border:1px solid #2a3550;border-radius:2px;padding:5px 4px;display:flex;flex-direction:column;gap:2px;box-shadow:0 4px 12px rgba(0,0,0,.2);z-index:5`;
|
| 786 |
+
const ledColors = ['#22c55e','#3b82f6','#f59e0b','#22c55e','#ec4899','#22c55e','#3b82f6'];
|
| 787 |
+
let html = '';
|
| 788 |
+
ledColors.forEach((c,i)=>{
|
| 789 |
+
html += `<div style="height:8px;background:#0d1520;border:1px solid #1a2a3a;display:flex;align-items:center;gap:2px;padding:0 3px">
|
| 790 |
+
<div style="width:4px;height:4px;border-radius:50%;background:${c};animation:led-blink ${.8+i*.3}s ease-in-out infinite alternate" class="rack-led"></div>
|
| 791 |
+
<div style="flex:1;height:2px;background:#1a2a3a"></div>
|
| 792 |
+
</div>`;
|
| 793 |
+
});
|
| 794 |
+
el.innerHTML = html + `<div style="font-family:'VT323',monospace;font-size:8px;color:#2a3d58;text-align:center;letter-spacing:1px;margin-top:2px">SRV</div>`;
|
| 795 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
|
| 797 |
+
// ββ BOOKSHELF ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 798 |
+
function buildBookshelf(){
|
| 799 |
+
const el = document.getElementById('bookshelf-el');
|
| 800 |
+
const BOOKS = ['#e74c3c','#3498db','#2ecc71','#f39c12','#9b59b6','#1abc9c','#e67e22','#34495e','#e91e63','#00bcd4'];
|
| 801 |
+
let html = '';
|
| 802 |
+
for(let row=0;row<3;row++){
|
| 803 |
+
html += '<div class="book-row">';
|
| 804 |
+
for(let b=0;b<5;b++) html += `<div class="book" style="background:${BOOKS[(row*5+b)%BOOKS.length]}"></div>`;
|
| 805 |
+
html += '</div>';
|
| 806 |
+
}
|
| 807 |
+
el.innerHTML = html;
|
| 808 |
+
}
|
| 809 |
|
| 810 |
+
// ββ DESK POSITIONS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 811 |
+
function getDeskPositions(count){
|
| 812 |
+
const positions = [];
|
| 813 |
+
const cols = Math.ceil(count/2);
|
| 814 |
+
for(let i=0;i<count;i++){
|
| 815 |
+
const col = i % cols;
|
| 816 |
+
const row = Math.floor(i / cols);
|
| 817 |
+
const xPct = 15 + (col / Math.max(cols-1,1)) * 65;
|
| 818 |
+
const yFromBottom = 22 + row * 22; // % from bottom
|
| 819 |
+
positions.push({ xPct, yFromBottom });
|
| 820 |
}
|
| 821 |
+
return positions;
|
| 822 |
+
}
|
| 823 |
|
| 824 |
+
function renderDesks(){
|
| 825 |
+
const layer = document.getElementById('desks-layer');
|
| 826 |
+
const positions = getDeskPositions(agentDefs.length);
|
| 827 |
+
const mugColors = ['#f6eb61','#ff8a65','#4fc3f7','#a5d6a7','#ce93d8','#ffcc02'];
|
| 828 |
+
layer.innerHTML = agentDefs.map((agent,i)=>{
|
| 829 |
+
const pos = positions[i];
|
| 830 |
+
const color = AGENT_COLORS[agent.key] || '#3b82f6';
|
| 831 |
+
const status = agentStates[agent.key]?.status || 'idle';
|
| 832 |
+
const screenTxt = status==='working' ? 'ββ BUSY ββ' : status==='active' ? 'β DONE' : 'β IDLE';
|
| 833 |
+
const screenColor = status==='working' ? '#4ade80' : status==='active' ? '#22c55e' : '#334';
|
| 834 |
+
return `<div id="desk-${agent.key}" class="desk" style="left:${pos.xPct}%;bottom:${pos.yFromBottom}%;transform:translateX(-50%)">
|
| 835 |
+
<div class="desk-monitor">
|
| 836 |
+
<div class="desk-monitor-screen" id="dscreen-${agent.key}" style="color:${screenColor}">${screenTxt}</div>
|
| 837 |
+
</div>
|
| 838 |
+
<div class="desk-mug" style="background:${mugColors[i%mugColors.length]}"></div>
|
| 839 |
+
<div class="desk-kbd"></div>
|
| 840 |
+
<div class="desk-paper"></div>
|
| 841 |
+
</div>`;
|
| 842 |
+
}).join('');
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
// ββ AGENT RENDERING ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 846 |
+
function seatPosition(agentKey){
|
| 847 |
+
const i = agentDefs.findIndex(a=>a.key===agentKey);
|
| 848 |
+
if(i<0) return {x:50,y:30};
|
| 849 |
+
const positions = getDeskPositions(agentDefs.length);
|
| 850 |
+
const pos = positions[i];
|
| 851 |
+
return { x: pos.xPct, y: pos.yFromBottom + 12 };
|
| 852 |
+
}
|
| 853 |
+
|
| 854 |
+
function renderAgents(){
|
| 855 |
+
const layer = document.getElementById('agents-layer');
|
| 856 |
+
layer.innerHTML = agentDefs.map(agent=>{
|
| 857 |
+
const color = AGENT_COLORS[agent.key] || '#3b82f6';
|
| 858 |
+
const shirt = SHIRT_COLORS[agent.key] || '#1e3a6e';
|
| 859 |
+
const hair = HAIR_COLORS[agent.key] || '#1a1a2e';
|
| 860 |
+
const st = agentStates[agent.key] || {};
|
| 861 |
+
const status = st.status || 'idle';
|
| 862 |
+
const seat = seatPosition(agent.key);
|
| 863 |
+
const mode = agentModes[agent.key] || 'idle';
|
| 864 |
+
const isEmpty = status === 'resting';
|
| 865 |
+
if(!agentPositions[agent.key]) agentPositions[agent.key] = { x:seat.x, y:seat.y };
|
| 866 |
+
const pos = agentPositions[agent.key];
|
| 867 |
+
const modeClass = mode === 'walking' ? 'walking' : mode === 'working' ? 'working' : 'idle';
|
| 868 |
+
const tagColor = status==='working'?color : status==='active'?'#16a34a' : status==='resting'?'#f59e0b' : '#8a9aaa';
|
| 869 |
+
const tagBg = status==='working'?`${color}22` : 'rgba(255,255,255,.8)';
|
| 870 |
+
const tagLabel = {idle:'IDLE',working:'β',active:'β',resting:'ZZZ',error:'!'}[status]||'IDLE';
|
| 871 |
+
return `<div class="agent-person ${modeClass}" id="agent-${agent.key}"
|
| 872 |
+
style="left:${pos.x}%;bottom:${pos.y}%;transform:translateX(-50%);${isEmpty?'opacity:.3':''}"
|
| 873 |
+
onclick="onAgentClick('${agent.key}')">
|
| 874 |
+
<div class="p-hair" style="background:${hair}"></div>
|
| 875 |
+
<div class="p-head"><div class="p-eyes"><div class="p-eye"></div><div class="p-eye"></div></div></div>
|
| 876 |
+
<div class="p-body" style="background:${shirt}">
|
| 877 |
+
<div style="position:absolute;top:4px;left:50%;transform:translateX(-50%);width:10px;height:3px;background:${color};opacity:.6;border-radius:1px"></div>
|
| 878 |
</div>
|
| 879 |
+
<div class="p-arms">
|
| 880 |
+
<div class="p-arm left" style="background:${shirt}"></div>
|
| 881 |
+
<div class="p-arm right" style="background:${shirt}"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 882 |
</div>
|
| 883 |
+
<div class="p-legs"><div class="p-leg"></div><div class="p-leg"></div></div>
|
| 884 |
+
<div class="p-feet"><div class="p-foot"></div><div class="p-foot"></div></div>
|
| 885 |
+
<div class="agent-tag" style="color:${tagColor};background:${tagBg}">${tagLabel} ${agent.name}</div>
|
| 886 |
+
<div class="talk-bubble" id="bubble-${agent.key}"></div>
|
| 887 |
+
${isEmpty?`<div style="position:absolute;top:-25px;left:50%;transform:translateX(-50%);font-size:16px">π€</div>`:''}
|
| 888 |
+
</div>`;
|
| 889 |
+
}).join('');
|
| 890 |
+
}
|
| 891 |
+
|
| 892 |
+
function updateAgentEl(agentKey){
|
| 893 |
+
const el = document.getElementById(`agent-${agentKey}`);
|
| 894 |
+
if(!el) return;
|
| 895 |
+
const st = agentStates[agentKey] || {};
|
| 896 |
+
const status = st.status || 'idle';
|
| 897 |
+
const mode = agentModes[agentKey] || 'idle';
|
| 898 |
+
const color = AGENT_COLORS[agentKey] || '#3b82f6';
|
| 899 |
+
const pos = agentPositions[agentKey] || seatPosition(agentKey);
|
| 900 |
+
el.style.left = pos.x + '%';
|
| 901 |
+
el.style.bottom = pos.y + '%';
|
| 902 |
+
el.className = `agent-person ${mode}`;
|
| 903 |
+
el.style.opacity = status === 'resting' ? '.3' : '1';
|
| 904 |
+
el.style.transform = 'translateX(-50%)';
|
| 905 |
+
// Tag
|
| 906 |
+
const tag = el.querySelector('.agent-tag');
|
| 907 |
+
if(tag){
|
| 908 |
+
const tagColor = status==='working'?color : status==='active'?'#16a34a' : status==='resting'?'#f59e0b' : '#8a9aaa';
|
| 909 |
+
const tagBg = status==='working'?`${color}22` : 'rgba(255,255,255,.8)';
|
| 910 |
+
const name = agentDefs.find(a=>a.key===agentKey)?.name || agentKey;
|
| 911 |
+
tag.style.color = tagColor;
|
| 912 |
+
tag.style.background = tagBg;
|
| 913 |
+
tag.textContent = ({idle:'IDLE',working:'β',active:'β',resting:'ZZZ',error:'!'}[status]||'IDLE') + ' ' + name;
|
| 914 |
+
}
|
| 915 |
+
// Desk monitor
|
| 916 |
+
const dscreen = document.getElementById(`dscreen-${agentKey}`);
|
| 917 |
+
if(dscreen){
|
| 918 |
+
dscreen.textContent = status==='working'?'ββ BUSY ββ': status==='active'?'β DONE':'β IDLE';
|
| 919 |
+
dscreen.style.color = status==='working'?'#4ade80':status==='active'?'#22c55e':'#334';
|
| 920 |
+
}
|
| 921 |
}
|
| 922 |
|
| 923 |
+
function onAgentClick(key){
|
| 924 |
+
const st = agentStates[key];
|
| 925 |
+
if(st && st.message) showSingleResult(key);
|
| 926 |
}
|
| 927 |
|
| 928 |
+
function showSingleResult(key){
|
| 929 |
+
const st = agentStates[key];
|
| 930 |
+
if(!st || !st.message) return;
|
| 931 |
+
const agent = agentDefs.find(a=>a.key===key);
|
| 932 |
+
const color = AGENT_COLORS[key]||'#3b82f6';
|
| 933 |
+
let extra='';
|
| 934 |
+
if(st.doc_file){
|
| 935 |
+
const ext=st.doc_file.split('.').pop().toLowerCase();
|
| 936 |
+
const label={xlsx:'Excel',docx:'Word',html:'HTML',py:'Python',groovy:'Groovy',jpg:'Image'}[ext]||'File';
|
| 937 |
+
extra+=`<a class="dl-btn" href="/api/docs/${st.doc_file}" download>↓ Download ${label}</a>`;
|
| 938 |
}
|
| 939 |
+
if(st.img_base&&st.img_count>0){
|
| 940 |
+
const thumbs=Array.from({length:st.img_count},(_,i)=>`<img class="img-th" src="/api/docs/${st.img_base}_img${i+1}.jpg" onclick="openLbox('/api/docs/${st.img_base}_img${i+1}.jpg','img${i+1}.jpg')"/>`).join('');
|
| 941 |
+
extra+=`<div class="img-row">${thumbs}</div>`;
|
| 942 |
+
}
|
| 943 |
+
document.getElementById('rp-grid').innerHTML=`<div class="rp-card">
|
| 944 |
+
<div class="rp-name"><div class="rp-dot" style="background:${color}"></div><span style="color:${color}">${agent?.name||key}</span></div>
|
| 945 |
+
<div class="rp-text">${(st.message||'').substring(0,500)}</div>
|
| 946 |
+
${st.model?`<div class="rp-model">via ${st.model}</div>`:''}
|
| 947 |
+
${extra}
|
| 948 |
+
</div>`;
|
| 949 |
+
document.getElementById('result-panel').classList.add('show');
|
| 950 |
}
|
| 951 |
|
| 952 |
+
// ββ IDLE LIFE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 953 |
+
function startIdleLife(){
|
| 954 |
+
setInterval(()=>{
|
| 955 |
+
agentDefs.forEach(agent=>{
|
| 956 |
+
const st = agentStates[agent.key]?.status || 'idle';
|
| 957 |
+
if(st === 'working') return;
|
| 958 |
+
const mode = agentModes[agent.key] || 'idle';
|
| 959 |
+
if(Math.random() < .25){
|
| 960 |
+
if(mode === 'idle' || mode === 'talking'){
|
| 961 |
+
// Start walking to random spot
|
| 962 |
+
agentModes[agent.key] = 'walking';
|
| 963 |
+
const targetX = 10 + Math.random() * 75;
|
| 964 |
+
const targetY = 10 + Math.random() * 35;
|
| 965 |
+
agentTargets[agent.key] = { x: targetX, y: targetY };
|
| 966 |
+
}
|
| 967 |
+
}
|
| 968 |
+
if(Math.random() < .06){
|
| 969 |
+
const phrases = SPEECH_IDLE[agent.key] || ['Working...'];
|
| 970 |
+
showBubble(agent.key, phrases[Math.floor(Math.random()*phrases.length)], 2500);
|
| 971 |
+
}
|
| 972 |
+
});
|
| 973 |
+
}, 3000);
|
| 974 |
+
|
| 975 |
+
// Smooth movement loop
|
| 976 |
+
setInterval(()=>{
|
| 977 |
+
agentDefs.forEach(agent=>{
|
| 978 |
+
const st = agentStates[agent.key]?.status || 'idle';
|
| 979 |
+
if(st === 'working'){
|
| 980 |
+
// Stay at desk
|
| 981 |
+
const seat = seatPosition(agent.key);
|
| 982 |
+
agentPositions[agent.key] = { x: seat.x, y: seat.y };
|
| 983 |
+
agentModes[agent.key] = 'working';
|
| 984 |
+
updateAgentEl(agent.key);
|
| 985 |
+
return;
|
| 986 |
+
}
|
| 987 |
+
const target = agentTargets[agent.key];
|
| 988 |
+
if(!target) return;
|
| 989 |
+
const pos = agentPositions[agent.key] || seatPosition(agent.key);
|
| 990 |
+
const dx = target.x - pos.x, dy = target.y - pos.y;
|
| 991 |
+
const dist = Math.sqrt(dx*dx+dy*dy);
|
| 992 |
+
if(dist < 1.2){
|
| 993 |
+
agentPositions[agent.key] = { x: target.x, y: target.y };
|
| 994 |
+
agentTargets[agent.key] = null;
|
| 995 |
+
agentModes[agent.key] = 'idle';
|
| 996 |
+
} else {
|
| 997 |
+
const spd = 0.4;
|
| 998 |
+
agentPositions[agent.key] = { x: pos.x + dx/dist*spd, y: pos.y + dy/dist*spd };
|
| 999 |
+
agentModes[agent.key] = 'walking';
|
| 1000 |
+
}
|
| 1001 |
+
updateAgentEl(agent.key);
|
| 1002 |
+
});
|
| 1003 |
+
}, 80);
|
| 1004 |
}
|
|
|
|
| 1005 |
|
| 1006 |
+
// ββ CLOCK ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1007 |
+
setInterval(()=>{
|
| 1008 |
+
const n=new Date();
|
| 1009 |
+
const t=[n.getHours(),n.getMinutes(),n.getSeconds()].map(x=>String(x).padStart(2,'0')).join(':');
|
| 1010 |
+
document.getElementById('clock').textContent=t;
|
| 1011 |
+
},1000);
|
| 1012 |
+
|
| 1013 |
+
// ββ SPEECH BUBBLES βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1014 |
+
function showBubble(key, text, ms=2500){
|
| 1015 |
+
const b = document.getElementById(`bubble-${key}`);
|
| 1016 |
+
if(!b) return;
|
| 1017 |
+
if(bubbleTimers[key]) clearTimeout(bubbleTimers[key]);
|
| 1018 |
+
b.textContent = text;
|
| 1019 |
+
b.classList.add('show');
|
| 1020 |
+
bubbleTimers[key] = setTimeout(()=>b.classList.remove('show'), ms);
|
| 1021 |
}
|
| 1022 |
|
| 1023 |
+
// ββ SCREEN UPDATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1024 |
+
function updateScreen(title, content){
|
| 1025 |
+
document.getElementById('bs-title').textContent = title;
|
| 1026 |
+
document.getElementById('bs-content').textContent = content;
|
|
|
|
|
|
|
| 1027 |
}
|
| 1028 |
|
| 1029 |
+
// ββ ACTIVITY βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1030 |
+
function addAct(msg,color='#3b82f6'){
|
| 1031 |
const n=new Date();
|
| 1032 |
const t=[n.getHours(),n.getMinutes(),n.getSeconds()].map(x=>String(x).padStart(2,'0')).join(':');
|
| 1033 |
activity.unshift({msg,t,color});
|
|
|
|
| 1036 |
).join('');
|
| 1037 |
}
|
| 1038 |
|
| 1039 |
+
function notify(title,msg,color='#22c55e'){
|
| 1040 |
document.getElementById('notif-t').textContent=title;
|
| 1041 |
document.getElementById('notif-t').style.color=color;
|
| 1042 |
document.getElementById('notif-m').textContent=msg;
|
| 1043 |
document.getElementById('notif').style.borderLeftColor=color;
|
| 1044 |
+
document.getElementById('notif').classList.add('show');
|
| 1045 |
+
setTimeout(()=>document.getElementById('notif').classList.remove('show'),5500);
|
|
|
|
| 1046 |
}
|
| 1047 |
|
| 1048 |
function updateHistory(){
|
| 1049 |
const list=document.getElementById('hist-log');
|
| 1050 |
+
if(!history.length){list.innerHTML=`<div style="padding:12px;font-size:10px;color:var(--text3);text-align:center">No missions yet</div>`;return;}
|
| 1051 |
+
list.innerHTML=[...history].reverse().map(m=>`<div class="hist-item" onclick=""><div class="hist-task">${m.task}</div><div class="hist-meta"><span>${m.time}</span><span style="color:${m.ok?'#16a34a':'#f59e0b'}">${m.ok?'Done':'Partial'}</span></div></div>`).join('');
|
|
|
|
|
|
|
| 1052 |
}
|
| 1053 |
|
| 1054 |
+
// ββ WALK OVERLAY ANIMATION βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1055 |
function runWalkAnimation(task, delegates, onDone){
|
| 1056 |
+
const ov=document.getElementById('walk-ov');
|
| 1057 |
+
const wc=document.getElementById('walk-canvas');
|
| 1058 |
const logEl=document.getElementById('walk-log');
|
| 1059 |
+
const wrap=wc.parentElement;
|
| 1060 |
+
wc.width=wrap.clientWidth||640; wc.height=wrap.clientHeight||320;
|
| 1061 |
+
const W=wc.width,H=wc.height,ctx=wc.getContext('2d');
|
| 1062 |
+
ov.classList.add('show');
|
| 1063 |
+
|
| 1064 |
+
// Light office background
|
| 1065 |
+
const others=agentDefs.filter(a=>a.key!=='manager').slice(0,6);
|
|
|
|
|
|
|
|
|
|
| 1066 |
const delSet=new Set(delegates);
|
| 1067 |
+
const deskW=100,deskH=52,deskY=H*0.52;
|
| 1068 |
+
const spacing=(W-80)/(Math.max(others.length-1,1)||1);
|
| 1069 |
+
const desks=others.map((a,i)=>({
|
| 1070 |
+
x:40+i*spacing, y:deskY, key:a.key, name:a.name,
|
| 1071 |
+
color:AGENT_COLORS[a.key]||'#3b82f6',
|
| 1072 |
+
shirt:SHIRT_COLORS[a.key]||'#1e3a6e',
|
| 1073 |
+
hair:HAIR_COLORS[a.key]||'#1a1a2e',
|
| 1074 |
+
}));
|
| 1075 |
+
|
| 1076 |
+
const delSet2=new Set(delegates);
|
| 1077 |
+
const toVisit=desks.filter(d=>delSet2.has(d.key));
|
| 1078 |
+
const msgs={writer:`Write report on:\n"${task.substring(0,22)}..."`,analyst:`Analyze results\nfor this task.`,backend_dev:`Build solution:\n"${task.substring(0,22)}..."`,frontend_dev:`Design the HTML\ninterface.`,image_agent:`Find/generate\nimages now.`};
|
| 1079 |
+
const manColor='#22c55e',manShirt='#166534',manHair='#4a2e18';
|
| 1080 |
+
let mx=W/2,my=6,frame=0,stepI=0,phase='walking',talkF=0;
|
| 1081 |
+
const TALK=75,SPD=3.2;
|
| 1082 |
+
const steps=[{key:'__top',x:W/2,y:H*0.14,msg:'Team! New mission.'},{...toVisit.map(d=>({key:d.key,x:d.x,y:deskY-20,msg:msgs[d.key]||'Please handle this.'}))}.length===0?{key:'__back',x:W/2,y:H*0.14,msg:'All briefed!'}:{key:'__dummy'},...toVisit.map(d=>({key:d.key,x:d.x,y:deskY-20,msg:msgs[d.key]||'Please handle this.'})),{key:'__back',x:W/2,y:H*0.14,msg:'All briefed!'}].filter(s=>s.key!=='__dummy');
|
| 1083 |
const visited=new Set(); let curMsg='';
|
| 1084 |
|
| 1085 |
+
function drawPerson(x,y,hair,shirt,color,scale=1){
|
| 1086 |
+
const s=scale;
|
| 1087 |
+
// Hair
|
| 1088 |
+
ctx.fillStyle=hair; ctx.beginPath(); ctx.ellipse(x,y-22*s,10*s,9*s,0,0,Math.PI*2); ctx.fill();
|
| 1089 |
+
// Head
|
| 1090 |
+
ctx.fillStyle='#f5c9a0'; ctx.beginPath(); ctx.arc(x,y-16*s,9*s,0,Math.PI*2); ctx.fill();
|
| 1091 |
+
// Eyes
|
| 1092 |
+
ctx.fillStyle='#2a1a0a';
|
| 1093 |
+
ctx.beginPath(); ctx.arc(x-3*s,y-17*s,1.5*s,0,Math.PI*2); ctx.fill();
|
| 1094 |
+
ctx.beginPath(); ctx.arc(x+3*s,y-17*s,1.5*s,0,Math.PI*2); ctx.fill();
|
| 1095 |
+
// Body
|
| 1096 |
+
ctx.fillStyle=shirt;
|
| 1097 |
+
ctx.beginPath(); ctx.roundRect(x-8*s,y-7*s,16*s,20*s,4*s); ctx.fill();
|
| 1098 |
+
// Color badge
|
| 1099 |
+
ctx.fillStyle=color; ctx.globalAlpha=.6;
|
| 1100 |
+
ctx.fillRect(x-3*s,y-5*s,6*s,3*s); ctx.globalAlpha=1;
|
| 1101 |
+
// Legs
|
| 1102 |
+
ctx.fillStyle='#2a3545';
|
| 1103 |
+
ctx.fillRect(x-6*s,y+13*s,6*s,14*s); ctx.fillRect(x+1*s,y+13*s,6*s,14*s);
|
| 1104 |
+
ctx.fillStyle='#1a1a2a';
|
| 1105 |
+
ctx.fillRect(x-7*s,y+27*s,7*s,4*s); ctx.fillRect(x+1*s,y+27*s,7*s,4*s);
|
| 1106 |
}
|
| 1107 |
|
| 1108 |
+
function drawDesk(d,visited){
|
| 1109 |
+
const dw=deskW*0.8,dh=deskH*0.7;
|
| 1110 |
+
const dx=d.x-dw/2,dy=d.y;
|
| 1111 |
+
// Desk
|
| 1112 |
+
ctx.fillStyle=visited?'#d4a870':'#c8a060';
|
| 1113 |
+
ctx.beginPath(); ctx.roundRect(dx,dy,dw,dh,4); ctx.fill();
|
| 1114 |
+
ctx.fillStyle='rgba(255,255,255,.15)';
|
| 1115 |
+
ctx.fillRect(dx,dy,dw,3);
|
| 1116 |
+
// Monitor
|
| 1117 |
+
ctx.fillStyle='#2a3545';
|
| 1118 |
+
ctx.fillRect(dx+8,dy+6,dw-44,dh-14);
|
| 1119 |
+
ctx.fillStyle=visited?d.color:'#1a2535';
|
| 1120 |
+
ctx.fillRect(dx+10,dy+8,dw-48,dh-18);
|
| 1121 |
+
// Name
|
| 1122 |
+
ctx.fillStyle=visited?d.color:'#8a9aaa';
|
| 1123 |
+
ctx.font=`bold 9px "Share Tech Mono"`;
|
| 1124 |
+
ctx.textAlign='center';
|
| 1125 |
+
ctx.fillText(d.name.toUpperCase(),d.x,dy+dh+14);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1126 |
}
|
| 1127 |
|
| 1128 |
function drawBubble(x,y,lines){
|
| 1129 |
+
const mxW=Math.max(...lines.map(l=>l.length))*6.5+18,bh=lines.length*14+14;
|
| 1130 |
+
const bx=Math.min(Math.max(x-mxW/2,4),W-mxW-4),by=Math.max(y-bh-8,4);
|
| 1131 |
+
ctx.fillStyle='white'; ctx.strokeStyle='#ddd'; ctx.lineWidth=1;
|
| 1132 |
+
ctx.beginPath(); ctx.roundRect(bx,by,mxW,bh,6); ctx.fill(); ctx.stroke();
|
| 1133 |
+
// Arrow
|
| 1134 |
+
ctx.beginPath(); ctx.moveTo(x-5,by+bh); ctx.lineTo(x+5,by+bh); ctx.lineTo(x,by+bh+8); ctx.closePath();
|
| 1135 |
+
ctx.fillStyle='white'; ctx.fill(); ctx.stroke();
|
| 1136 |
+
ctx.fillStyle='#1a1a2a'; ctx.font=`10px "Share Tech Mono"`; ctx.textAlign='left';
|
| 1137 |
+
lines.forEach((l,i)=>ctx.fillText(l,bx+9,by+12+i*14+4));
|
| 1138 |
}
|
| 1139 |
|
| 1140 |
function tick(){
|
| 1141 |
+
// BG
|
| 1142 |
+
ctx.fillStyle='#f2ede0'; ctx.fillRect(0,0,W,H);
|
| 1143 |
+
ctx.fillStyle='linear-gradient';
|
| 1144 |
+
// Wall
|
| 1145 |
+
const wallH=H*0.45;
|
| 1146 |
+
ctx.fillStyle='#ece7d8'; ctx.fillRect(0,0,W,wallH);
|
| 1147 |
+
ctx.fillStyle='#c8bc9a'; ctx.fillRect(0,wallH-6,W,6);
|
| 1148 |
+
// Floor
|
| 1149 |
+
ctx.fillStyle='#d4cdb8'; ctx.fillRect(0,wallH,W,H-wallH);
|
| 1150 |
+
// Floor lines
|
| 1151 |
+
ctx.strokeStyle='rgba(0,0,0,.04)'; ctx.lineWidth=1;
|
| 1152 |
+
for(let fx=0;fx<W;fx+=60){ctx.beginPath();ctx.moveTo(fx,wallH);ctx.lineTo(fx,H);ctx.stroke();}
|
| 1153 |
+
for(let fy=wallH;fy<H;fy+=40){ctx.beginPath();ctx.moveTo(0,fy);ctx.lineTo(W,fy);ctx.stroke();}
|
| 1154 |
+
|
| 1155 |
+
// Big screen on wall
|
| 1156 |
+
const scW=200,scH=120,scX=W/2-scW/2,scY=20;
|
| 1157 |
+
ctx.fillStyle='#2a3545'; ctx.fillRect(scX-6,scY-6,scW+12,scH+12);
|
| 1158 |
+
ctx.fillStyle='#1a4a2a'; ctx.fillRect(scX,scY,scW,scH);
|
| 1159 |
+
ctx.fillStyle='#4ade80'; ctx.font=`bold 9px "Share Tech Mono"`; ctx.textAlign='center';
|
| 1160 |
+
ctx.fillText('MISSION CONTROL', W/2, scY+20);
|
| 1161 |
+
ctx.fillStyle='#a0d4b0'; ctx.font=`8px "Share Tech Mono"`;
|
| 1162 |
+
const taskShort=task.substring(0,30)+(task.length>30?'...':'');
|
| 1163 |
+
ctx.fillText(taskShort, W/2, scY+38);
|
| 1164 |
+
|
| 1165 |
+
// Desks + seated agents
|
| 1166 |
+
desks.forEach(d=>{
|
| 1167 |
+
drawDesk(d,visited.has(d.key));
|
| 1168 |
+
// Seated agent at desk
|
| 1169 |
+
drawPerson(d.x, d.y-18, d.hair, d.shirt, d.color, 0.75);
|
| 1170 |
+
});
|
| 1171 |
+
|
| 1172 |
+
// Walking manager
|
| 1173 |
+
const bob=phase==='walking'?Math.sin(frame*.4)*2:0;
|
| 1174 |
+
drawPerson(Math.round(mx),Math.round(my+bob),manHair,manShirt,manColor,1);
|
| 1175 |
+
|
| 1176 |
+
// Speech bubble
|
| 1177 |
+
if(phase==='talking'&&curMsg) drawBubble(Math.round(mx),Math.round(my-10),curMsg.split('\n'));
|
| 1178 |
+
|
| 1179 |
+
// Step logic
|
| 1180 |
if(stepI<steps.length){
|
| 1181 |
const step=steps[stepI];
|
| 1182 |
+
const dx=step.x-mx,dy=step.y-my,dist=Math.sqrt(dx*dx+dy*dy);
|
| 1183 |
+
if(dist>3&&phase==='walking'){mx+=dx/dist*SPD;my+=dy/dist*SPD;}
|
| 1184 |
else if(phase==='walking'){
|
| 1185 |
+
phase='talking';talkF=0;curMsg=step.msg;
|
| 1186 |
if(step.key!=='__top'&&step.key!=='__back'){
|
| 1187 |
visited.add(step.key);
|
| 1188 |
logEl.textContent=`Briefing ${step.key}...`;
|
| 1189 |
+
addAct(`Manager β ${step.key}`,'#3b82f6');
|
| 1190 |
} else if(step.key==='__back'){
|
| 1191 |
+
logEl.textContent='Team briefed β processing!';
|
| 1192 |
}
|
| 1193 |
}
|
| 1194 |
if(phase==='talking'){talkF++;if(talkF>TALK){phase='walking';stepI++;curMsg='';}}
|
| 1195 |
} else {
|
| 1196 |
+
setTimeout(()=>{ov.classList.remove('show');onDone();},300);return;
|
| 1197 |
}
|
| 1198 |
+
frame++;
|
| 1199 |
+
requestAnimationFrame(tick);
|
|
|
|
| 1200 |
}
|
| 1201 |
tick();
|
| 1202 |
}
|
| 1203 |
|
| 1204 |
+
// ββ RESULTS PANEL ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1205 |
+
function showAllResults(results, events){
|
| 1206 |
+
const panel=document.getElementById('result-panel');
|
| 1207 |
+
const grid=document.getElementById('rp-grid');
|
| 1208 |
+
panel.classList.add('show');
|
| 1209 |
+
grid.innerHTML=agentDefs.map(agent=>{
|
| 1210 |
+
const r=results[agent.key];
|
| 1211 |
+
if(!r||r.status==='idle') return '';
|
| 1212 |
+
const color=AGENT_COLORS[agent.key]||'#3b82f6';
|
| 1213 |
+
const msg=(r.message||'').substring(0,400)+(r.message?.length>400?'...':'');
|
| 1214 |
+
let extra='';
|
| 1215 |
+
if(r.doc_file){
|
| 1216 |
+
const ext=r.doc_file.split('.').pop().toLowerCase();
|
| 1217 |
+
const label={xlsx:'Excel',docx:'Word',html:'HTML',py:'Python',groovy:'Groovy',jpg:'Image'}[ext]||'File';
|
| 1218 |
+
extra+=`<a class="dl-btn" href="/api/docs/${r.doc_file}" download>↓ ${label}</a>`;
|
| 1219 |
+
}
|
| 1220 |
+
if(r.img_base&&r.img_count>0){
|
| 1221 |
+
const thumbs=Array.from({length:r.img_count},(_,i)=>`<img class="img-th" src="/api/docs/${r.img_base}_img${i+1}.jpg" onclick="openLbox('/api/docs/${r.img_base}_img${i+1}.jpg','img${i+1}.jpg')"/>`).join('');
|
| 1222 |
+
extra+=`<div class="img-row">${thumbs}</div>`;
|
| 1223 |
+
}
|
| 1224 |
+
return `<div class="rp-card">
|
| 1225 |
+
<div class="rp-name"><div class="rp-dot" style="background:${color}"></div><span style="color:${color}">${agent.name}</span></div>
|
| 1226 |
+
<div class="rp-text">${msg||'Completed.'}</div>
|
| 1227 |
+
${r.model?`<div class="rp-model">via ${r.model}</div>`:''}
|
| 1228 |
+
${extra}
|
| 1229 |
+
</div>`;
|
| 1230 |
+
}).filter(Boolean).join('');
|
| 1231 |
+
if(events&&events.length){
|
| 1232 |
+
const evHtml=events.map(e=>`<div class="orch-ev"><span class="orch-t">${e.time}</span><span>${e.msg}</span></div>`).join('');
|
| 1233 |
+
document.getElementById('orch-log-wrap').innerHTML=`<div class="orch-log"><div class="orch-title">LOG</div>${evHtml}</div>`;
|
| 1234 |
+
}
|
| 1235 |
+
}
|
| 1236 |
|
| 1237 |
+
// ββ LIGHTBOX βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1238 |
+
function openLbox(src,fn){document.getElementById('lbox-img').src=src;document.getElementById('lbox-dl').href=src;document.getElementById('lbox-dl').download=fn;document.getElementById('lbox').classList.add('show');}
|
| 1239 |
+
function closeLbox(){document.getElementById('lbox').classList.remove('show');}
|
| 1240 |
+
|
| 1241 |
+
// ββ MODAL ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1242 |
+
function openModal(){document.getElementById('modal-bg').classList.add('show');}
|
| 1243 |
+
function closeModal(){document.getElementById('modal-bg').classList.remove('show');}
|
| 1244 |
async function submitAgent(){
|
| 1245 |
const key=document.getElementById('m-key').value.trim();
|
| 1246 |
const name=document.getElementById('m-name').value.trim()||key;
|
|
|
|
| 1251 |
const d=await r.json();
|
| 1252 |
if(d.success){
|
| 1253 |
agentDefs.push({key:d.agent.key,name:d.agent.name,role:d.agent.role});
|
| 1254 |
+
agentStates[d.agent.key]={status:'idle',message:'',model:'',doc_file:null,img_base:null,img_count:0};
|
| 1255 |
+
agentModes[d.agent.key]='idle';
|
| 1256 |
+
renderDesks(); renderAgents(); startIdleLife();
|
| 1257 |
+
addAct(`Agent "${name}" added`,'#a855f7'); closeModal();
|
| 1258 |
['m-key','m-name','m-role'].forEach(id=>document.getElementById(id).value='');
|
| 1259 |
}
|
| 1260 |
}
|
|
|
|
| 1268 |
document.getElementById('prog').classList.add('on');
|
| 1269 |
document.getElementById('top-status').textContent='Running';
|
| 1270 |
document.getElementById('top-dot').className='sdot da';
|
| 1271 |
+
document.getElementById('result-panel').classList.remove('show');
|
| 1272 |
+
updateScreen('MISSION ACTIVE', task.substring(0,60));
|
| 1273 |
|
| 1274 |
+
// Set all working
|
| 1275 |
+
agentDefs.forEach(a=>{
|
| 1276 |
+
agentStates[a.key]={status:'working',message:'',model:'',doc_file:null,img_base:null,img_count:0};
|
| 1277 |
+
agentModes[a.key]='working';
|
| 1278 |
+
const seat=seatPosition(a.key);
|
| 1279 |
+
agentPositions[a.key]={x:seat.x,y:seat.y};
|
| 1280 |
+
agentTargets[a.key]=null;
|
| 1281 |
+
updateAgentEl(a.key);
|
| 1282 |
});
|
| 1283 |
+
addAct(`Mission: "${task.substring(0,38)}"`,'#3b82f6');
|
| 1284 |
|
|
|
|
| 1285 |
let apiData=null;
|
| 1286 |
+
const fetchP=fetch('/api/mission',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({task})})
|
| 1287 |
+
.then(r=>r.json()).then(d=>{apiData=d;}).catch(e=>{apiData={error:e.message};});
|
|
|
|
| 1288 |
|
|
|
|
| 1289 |
const lo=task.toLowerCase();
|
| 1290 |
let qd=[];
|
| 1291 |
+
if(/imagen|image|foto|gato|cat|dog|perro|picture|dibuja|genera/.test(lo)) qd.push('image_agent');
|
| 1292 |
if(/informe|report|word|documento/.test(lo)){qd.push('writer');qd.push('analyst');}
|
| 1293 |
if(/excel|planilla|spreadsheet|registro/.test(lo)) qd.push('backend_dev');
|
| 1294 |
if(/python|script|groovy|jenkins|backend|api/.test(lo)) qd.push('backend_dev');
|
| 1295 |
if(/html|css|frontend|web|interfaz/.test(lo)) qd.push('frontend_dev');
|
| 1296 |
+
if(!qd.length) qd=['analyst'];
|
| 1297 |
|
| 1298 |
runWalkAnimation(task, qd, async()=>{
|
| 1299 |
+
// Stagger speech bubbles while waiting
|
| 1300 |
+
agentDefs.forEach((a,i)=>{
|
| 1301 |
+
setTimeout(()=>{
|
| 1302 |
+
const phrases=SPEECH_WORK[a.key]||['Working...'];
|
| 1303 |
+
showBubble(a.key,phrases[Math.floor(Math.random()*phrases.length)],3000);
|
| 1304 |
+
},i*600);
|
| 1305 |
+
});
|
| 1306 |
+
|
| 1307 |
await fetchP;
|
| 1308 |
|
| 1309 |
if(!apiData||apiData.error){
|
| 1310 |
+
agentDefs.forEach(a=>{
|
| 1311 |
+
agentStates[a.key]={status:'resting',message:apiData?.error||'Error',model:'',doc_file:null,img_base:null,img_count:0};
|
| 1312 |
+
agentModes[a.key]='idle'; updateAgentEl(a.key);
|
| 1313 |
+
});
|
| 1314 |
+
updateScreen('ERROR',apiData?.error||'Mission failed');
|
| 1315 |
+
addAct(`Error: ${apiData?.error}`,'#ef4444');
|
| 1316 |
+
notify('Mission Failed',apiData?.error||'Error','#ef4444');
|
| 1317 |
document.getElementById('top-dot').className='sdot dr';
|
| 1318 |
document.getElementById('top-status').textContent='Error';
|
| 1319 |
+
btn.disabled=false; document.getElementById('prog').classList.remove('on'); return;
|
|
|
|
| 1320 |
}
|
| 1321 |
|
| 1322 |
+
let anyResting=false, hasFile=false, screenText='';
|
|
|
|
| 1323 |
agentDefs.forEach(a=>{
|
| 1324 |
const r=apiData.results[a.key];
|
| 1325 |
+
if(!r){agentStates[a.key]={status:'idle',message:'',model:'',doc_file:null,img_base:null,img_count:0};return;}
|
| 1326 |
if(r.status==='resting') anyResting=true;
|
| 1327 |
+
if(r.doc_file) hasFile=true;
|
| 1328 |
+
agentStates[a.key]={
|
| 1329 |
+
status:r.status==='resting'?'resting':r.status==='idle'?'idle':'active',
|
| 1330 |
+
message:r.message||'', model:r.model||'',
|
| 1331 |
+
doc_file:r.doc_file||null, img_base:r.img_base||null, img_count:r.img_count||0,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1332 |
};
|
| 1333 |
+
agentModes[a.key]='idle';
|
| 1334 |
+
updateAgentEl(a.key);
|
| 1335 |
+
if(r.message&&r.status!=='resting') screenText+=(agentDefs.find(x=>x.key===a.key)?.name||a.key)+': '+r.message.substring(0,60)+'\n';
|
| 1336 |
+
// Celebration bubble
|
| 1337 |
+
if(r.status!=='resting'&&r.status!=='idle'){
|
| 1338 |
+
setTimeout(()=>showBubble(a.key,'Done! β',2000),Math.random()*1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1339 |
}
|
| 1340 |
});
|
| 1341 |
|
| 1342 |
+
updateScreen('MISSION COMPLETE', screenText || 'All agents responded.');
|
| 1343 |
+
showAllResults(apiData.results, apiData.events);
|
| 1344 |
+
|
| 1345 |
mCount++;
|
| 1346 |
document.getElementById('mc-count').textContent=`MISSIONS: ${mCount}`;
|
| 1347 |
+
document.getElementById('top-active').textContent='0 active';
|
| 1348 |
history.push({task,time:new Date().toLocaleTimeString(),ok:!anyResting,doc_file:apiData.doc_file});
|
| 1349 |
updateHistory();
|
| 1350 |
+
addAct('Mission complete','#22c55e');
|
| 1351 |
|
| 1352 |
setTimeout(()=>notify(
|
| 1353 |
+
anyResting?'Mission Partial':hasFile?'Files Ready β':'Mission Complete β',
|
| 1354 |
+
anyResting?'Some agents rate-limited.':hasFile?'Click agents to see results.':'All agents responded.',
|
| 1355 |
+
anyResting?'#f59e0b':hasFile?'#a855f7':'#22c55e'
|
| 1356 |
+
),500);
|
| 1357 |
|
| 1358 |
document.getElementById('top-status').textContent=anyResting?'Partial':'Done';
|
| 1359 |
document.getElementById('top-dot').className=`sdot ${anyResting?'da':'dg'}`;
|
| 1360 |
+
btn.disabled=false; document.getElementById('prog').classList.remove('on');
|
|
|
|
|
|
|
| 1361 |
});
|
| 1362 |
}
|
| 1363 |
|
| 1364 |
document.getElementById('task-input').addEventListener('keydown',e=>{if(e.key==='Enter')launchMission();});
|
| 1365 |
+
|
| 1366 |
+
// Add CSS keyframe for rack-led
|
| 1367 |
+
const style=document.createElement('style');
|
| 1368 |
+
style.textContent=`@keyframes led-blink{0%{opacity:.25}100%{opacity:1;box-shadow:0 0 4px currentColor}}`;
|
| 1369 |
+
document.head.appendChild(style);
|
| 1370 |
+
|
| 1371 |
+
// INIT
|
| 1372 |
+
window.addEventListener('resize',positionScreenElements);
|
| 1373 |
+
init();
|
| 1374 |
</script>
|
| 1375 |
</body>
|
| 1376 |
+
</html>
|