Skip to content

Commit e0d6ecc

Browse files
committed
Command Center
1 parent cc9fed6 commit e0d6ecc

3 files changed

Lines changed: 247 additions & 8 deletions

File tree

public/command-center.css

Lines changed: 164 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
.cc-header {
88
display: flex;
9-
flex-direction: column;
10-
gap: 6px;
9+
flex-direction: row;
10+
align-items: flex-start;
11+
justify-content: space-between;
12+
gap: 10px;
1113
margin-bottom: 14px;
1214
}
1315

@@ -22,6 +24,84 @@
2224
font-size: 13px;
2325
}
2426

27+
.cc-mode-toggle {
28+
display: flex;
29+
align-items: center;
30+
gap: 8px;
31+
}
32+
33+
.cc-toggle-label {
34+
display: flex;
35+
flex-direction: column;
36+
gap: 2px;
37+
cursor: pointer;
38+
}
39+
40+
.cc-toggle-title {
41+
font-size: 11px;
42+
letter-spacing: 0.14em;
43+
opacity: 0.9;
44+
}
45+
46+
.cc-toggle-sub {
47+
font-size: 11px;
48+
opacity: 0.65;
49+
}
50+
51+
.cc-switch {
52+
position: relative;
53+
display: inline-flex;
54+
align-items: center;
55+
cursor: pointer;
56+
}
57+
58+
.cc-switch input {
59+
position: absolute;
60+
opacity: 0;
61+
pointer-events: none;
62+
}
63+
64+
.cc-switch-track {
65+
width: 42px;
66+
height: 22px;
67+
border-radius: 999px;
68+
background: rgba(255, 255, 255, 0.14);
69+
border: 1px solid rgba(255, 255, 255, 0.18);
70+
display: inline-flex;
71+
align-items: center;
72+
padding: 2px;
73+
box-sizing: border-box;
74+
transition: background 0.18s ease-out, border-color 0.18s ease-out;
75+
}
76+
77+
.cc-switch-thumb {
78+
width: 16px;
79+
height: 16px;
80+
border-radius: 999px;
81+
background: #ffffff;
82+
transform: translateX(0);
83+
transition: transform 0.18s ease-out, box-shadow 0.18s ease-out, background 0.18s ease-out;
84+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.55);
85+
}
86+
87+
.cc-switch input:checked + .cc-switch-track {
88+
background: radial-gradient(circle at 20% 0, #66ffe1, #1b9bff 50%, #6240ff 100%);
89+
border-color: rgba(102, 255, 225, 0.7);
90+
}
91+
92+
.cc-switch input:checked + .cc-switch-track .cc-switch-thumb {
93+
transform: translateX(18px);
94+
background: #020b16;
95+
box-shadow: 0 0 6px rgba(102, 255, 225, 0.9);
96+
}
97+
98+
@media (max-width: 900px) {
99+
.cc-header {
100+
flex-direction: column;
101+
align-items: flex-start;
102+
}
103+
}
104+
25105
.cc-compose {
26106
background: rgba(255, 255, 255, 0.03);
27107
border: 1px solid rgba(255, 255, 255, 0.08);
@@ -248,6 +328,7 @@
248328
border-radius: 10px;
249329
padding: 10px 10px;
250330
background: rgba(0, 0, 0, 0.18);
331+
transition: transform 0.16s ease-out, box-shadow 0.16s ease-out, border-color 0.16s ease-out, background 0.16s ease-out;
251332
}
252333

253334
.cc-card-title {
@@ -258,6 +339,87 @@
258339
margin-bottom: 6px;
259340
}
260341

342+
.cc-card-title-main {
343+
display: inline-flex;
344+
align-items: center;
345+
gap: 8px;
346+
}
347+
348+
.cc-avatar {
349+
display: none;
350+
width: 26px;
351+
height: 26px;
352+
border-radius: 999px;
353+
border: 1px solid rgba(255, 255, 255, 0.25);
354+
font-size: 12px;
355+
font-weight: 600;
356+
align-items: center;
357+
justify-content: center;
358+
background: radial-gradient(circle at 30% 0, #ffffff, #d0f7ff 40%, #7fd8ff 100%);
359+
color: #020b16;
360+
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
361+
}
362+
363+
.cc-avatar-role-research {
364+
background: radial-gradient(circle at 30% 0, #ffffff, #c4e0ff 40%, #4fa8ff 100%);
365+
}
366+
367+
.cc-avatar-role-coder {
368+
background: radial-gradient(circle at 30% 0, #ffffff, #d5ffe9 40%, #4fe88e 100%);
369+
}
370+
371+
.cc-avatar-role-ops {
372+
background: radial-gradient(circle at 30% 0, #ffffff, #ffe6c7 40%, #ff9a3a 100%);
373+
}
374+
375+
.cc-avatar-role-planner {
376+
background: radial-gradient(circle at 30% 0, #ffffff, #f4d6ff 40%, #c36bff 100%);
377+
}
378+
379+
.cc-avatar-role-memory_curator {
380+
background: radial-gradient(circle at 30% 0, #ffffff, #fff0d6 40%, #ffce4a 100%);
381+
}
382+
383+
.cc-avatar-role-triage {
384+
background: radial-gradient(circle at 30% 0, #ffffff, #ffd4d4 40%, #ff5c5c 100%);
385+
}
386+
387+
@keyframes cc-avatar-pulse {
388+
0% { box-shadow: 0 0 0 0 rgba(102, 255, 225, 0.65); }
389+
70% { box-shadow: 0 0 0 8px rgba(102, 255, 225, 0); }
390+
100% { box-shadow: 0 0 0 0 rgba(102, 255, 225, 0); }
391+
}
392+
393+
.cc-cinematic-on .cc-avatar {
394+
display: inline-flex;
395+
}
396+
397+
.cc-cinematic-on .cc-card.cc-card-task {
398+
border-color: rgba(102, 255, 225, 0.55);
399+
background: radial-gradient(circle at 0% 0%, rgba(102, 255, 225, 0.12), rgba(0, 0, 0, 0.2) 55%, rgba(0, 0, 0, 0.35));
400+
}
401+
402+
.cc-cinematic-on .cc-card.cc-card-task:hover {
403+
transform: translateY(-1px);
404+
box-shadow: 0 0 18px rgba(0, 0, 0, 0.7);
405+
}
406+
407+
.cc-cinematic-on .cc-card.cc-card-task-status-executing .cc-avatar,
408+
.cc-cinematic-on .cc-card.cc-card-task-status-planning .cc-avatar,
409+
.cc-cinematic-on .cc-card.cc-card-task-status-queued .cc-avatar {
410+
animation: cc-avatar-pulse 1.9s ease-out infinite;
411+
}
412+
413+
.cc-cinematic-on .cc-card.cc-card-mission {
414+
border-color: rgba(123, 179, 255, 0.55);
415+
background: radial-gradient(circle at 100% 0%, rgba(123, 179, 255, 0.16), rgba(0, 0, 0, 0.2) 55%, rgba(0, 0, 0, 0.35));
416+
}
417+
418+
.cc-cinematic-on .cc-card.cc-card-mission:hover {
419+
transform: translateY(-1px);
420+
box-shadow: 0 0 18px rgba(0, 0, 0, 0.7);
421+
}
422+
261423
.cc-pill {
262424
font-size: 11px;
263425
padding: 2px 8px;

public/command-center.html

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,22 @@
2929

3030
<main class="cc-main">
3131
<div class="cc-header">
32-
<h1 class="cc-title">COMMAND CENTER</h1>
33-
<div class="cc-subtitle">High-level instructions → routed to specialized agents (always background-run)</div>
32+
<div>
33+
<h1 class="cc-title">COMMAND CENTER</h1>
34+
<div class="cc-subtitle">High-level instructions → routed to specialized agents (always background-run)</div>
35+
</div>
36+
<div class="cc-mode-toggle">
37+
<label class="cc-toggle-label" for="ccCinematicToggle">
38+
<span class="cc-toggle-title">CINEMATIC MODE</span>
39+
<span class="cc-toggle-sub">Mission dossiers + avatars</span>
40+
</label>
41+
<label class="cc-switch">
42+
<input type="checkbox" id="ccCinematicToggle" />
43+
<span class="cc-switch-track">
44+
<span class="cc-switch-thumb"></span>
45+
</span>
46+
</label>
47+
</div>
3448
</div>
3549

3650
<section class="cc-compose">

public/command-center.js

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
const debugLogEl = document.getElementById('ccDebugLog');
77
const debugSectionEl = document.querySelector('.cc-debug');
88
const debugToggleBtn = document.getElementById('ccDebugToggle');
9+
const cinematicToggleEl = document.getElementById('ccCinematicToggle');
910

1011
function setText(el, v) {
1112
if (!el) return;
@@ -47,6 +48,35 @@
4748
});
4849
}
4950
setDebugVisible(loadDebugVisible());
51+
const CINEMATIC_KEY = 'shadow.commandcenter.cinematic';
52+
let cinematicOn = false;
53+
54+
function setCinematicMode(on) {
55+
cinematicOn = !!on;
56+
try { localStorage.setItem(CINEMATIC_KEY, cinematicOn ? '1' : '0'); } catch (_) {}
57+
if (cinematicToggleEl) cinematicToggleEl.checked = cinematicOn;
58+
if (document.body) {
59+
document.body.classList.toggle('cc-cinematic-on', cinematicOn);
60+
}
61+
}
62+
63+
function loadCinematicMode() {
64+
try {
65+
const raw = localStorage.getItem(CINEMATIC_KEY);
66+
if (raw == null) return false;
67+
return raw === '1';
68+
} catch (_) {
69+
return false;
70+
}
71+
}
72+
73+
if (cinematicToggleEl) {
74+
cinematicToggleEl.addEventListener('change', () => {
75+
setCinematicMode(!!cinematicToggleEl.checked);
76+
});
77+
}
78+
setCinematicMode(loadCinematicMode());
79+
5080
const input = document.getElementById('ccInput');
5181
const sendBtn = document.getElementById('ccSendBtn');
5282
const refreshBtn = document.getElementById('ccRefreshBtn');
@@ -137,6 +167,30 @@
137167
return esc(s);
138168
}
139169

170+
function roleToAvatarLetter(role) {
171+
const r = String(role || '').toLowerCase();
172+
if (!r) return '?';
173+
if (r.includes('research')) return 'R';
174+
if (r.includes('coder') || r.includes('dev') || r.includes('code')) return 'C';
175+
if (r.includes('ops') || r.includes('operator')) return 'O';
176+
if (r.includes('plan')) return 'P';
177+
if (r.includes('memory')) return 'M';
178+
if (r.includes('triage') || r.includes('coord')) return 'T';
179+
return r.charAt(0).toUpperCase();
180+
}
181+
182+
function roleToAvatarClass(role) {
183+
const r = String(role || '').toLowerCase().replace(/[^a-z0-9_]+/g, '_');
184+
if (!r) return '';
185+
if (r.includes('research')) return 'cc-avatar-role-research';
186+
if (r.includes('coder') || r.includes('dev') || r.includes('code')) return 'cc-avatar-role-coder';
187+
if (r.includes('ops') || r.includes('operator')) return 'cc-avatar-role-ops';
188+
if (r.includes('plan')) return 'cc-avatar-role-planner';
189+
if (r.includes('memory')) return 'cc-avatar-role-memory_curator';
190+
if (r.includes('triage') || r.includes('coord')) return 'cc-avatar-role-triage';
191+
return '';
192+
}
193+
140194
function openReportModal(mission) {
141195
if (!reportModalEl || !reportBodyEl || !mission) return;
142196
const report = mission.finalReport || {};
@@ -280,10 +334,16 @@
280334
: (t.status === 'awaiting_approval' || t.status === 'blocked'
281335
? `<div class="cc-actions-note">This task needs admin action. Open <a href="/autoagent">/autoagent</a>.</div>`
282336
: '');
337+
const avatarClass = roleToAvatarClass(t.role);
338+
const avatarLetter = roleToAvatarLetter(t.role);
339+
const statusClass = String(t.status || '').toLowerCase().replace(/[^a-z0-9_]+/g, '_');
283340
return `
284-
<div class="cc-card">
341+
<div class="cc-card cc-card-task cc-card-task-status-${statusClass}">
285342
<div class="cc-card-title">
286-
<div>${title}</div>
343+
<div class="cc-card-title-main">
344+
<span class="cc-avatar ${avatarClass}">${esc(avatarLetter)}</span>
345+
<div>${title}</div>
346+
</div>
287347
<div>${pill(status)}</div>
288348
</div>
289349
<div class="cc-card-meta">
@@ -354,9 +414,12 @@
354414
`
355415
: `<button class="btn btn-small" data-action="view-report" data-mission-id="${esc(m.id || '')}">VIEW FULL REPORT</button>`;
356416
return `
357-
<div class="cc-card">
417+
<div class="cc-card cc-card-mission">
358418
<div class="cc-card-title">
359-
<div>${esc(report.headline || m.title || m.id)}</div>
419+
<div class="cc-card-title-main">
420+
<span class="cc-avatar cc-avatar-role-planner">M</span>
421+
<div>${esc(report.headline || m.title || m.id)}</div>
422+
</div>
360423
<div>${pill(report.outcome || 'complete')}</div>
361424
</div>
362425
<div class="cc-card-meta">

0 commit comments

Comments
 (0)