@yemi33/minions 0.1.2411 → 0.1.2412
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/js/command-center.js +24 -0
- package/dashboard/js/settings.js +31 -3
- package/dashboard/slim/body.html +22 -0
- package/dashboard/slim/js/members.js +30 -3
- package/dashboard/slim/layout.html +8 -0
- package/dashboard/slim/panel-bootstrap.js +83 -0
- package/dashboard/slim/styles.css +131 -1
- package/dashboard-build.js +29 -3
- package/package.json +1 -1
|
@@ -2388,4 +2388,28 @@ if (document.readyState === 'loading') {
|
|
|
2388
2388
|
ccInitResize();
|
|
2389
2389
|
}
|
|
2390
2390
|
|
|
2391
|
+
// W-mrz5yjdw00052b6e — Esc closes the Command Center drawer. Routes through
|
|
2392
|
+
// toggleCommandCenter() so the open/close path stays the single source of truth
|
|
2393
|
+
// (same path as the ✕ button and the overlay click). Bubble phase, and guarded
|
|
2394
|
+
// so it only fires when the drawer is actually open — Esc is not stolen when CC
|
|
2395
|
+
// is hidden. If a modal (#modal.open) or the QA review modal is layered on top,
|
|
2396
|
+
// we early-out so the topmost overlay closes first (modal.js / qa.js own that
|
|
2397
|
+
// Esc). Closing while mid-typing in cc-input IS desired, so we do NOT exempt
|
|
2398
|
+
// TEXTAREA here — but the top command bar's @/# autocomplete popup gets first
|
|
2399
|
+
// dibs on Esc (first Esc dismisses the typeahead, second closes the panel).
|
|
2400
|
+
if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') {
|
|
2401
|
+
document.addEventListener('keydown', function(ev) {
|
|
2402
|
+
if (ev.key !== 'Escape' && ev.keyCode !== 27) return;
|
|
2403
|
+
if (!_ccOpen) return;
|
|
2404
|
+
// Let a modal / QA review modal layered on top close first.
|
|
2405
|
+
if (typeof _qaModalState !== 'undefined' && _qaModalState) return;
|
|
2406
|
+
var modalEl = document.getElementById('modal');
|
|
2407
|
+
if (modalEl && modalEl.classList.contains('open')) return;
|
|
2408
|
+
// First Esc dismisses the command-bar autocomplete popup, not the drawer.
|
|
2409
|
+
var popup = document.getElementById('cmd-mention-popup');
|
|
2410
|
+
if (popup && popup.classList.contains('visible')) return;
|
|
2411
|
+
toggleCommandCenter();
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2391
2415
|
window.MinionsCC = { toggleCommandCenter, ccNewSession, ccNewTab, ccSwitchTab, ccCloseTab, ccRenderTabBar, ccRestoreMessages, ccSaveState, ccUpdateSessionIndicator, ccAddMessage, ccSend, ccAbort, ccExecuteAction, ccPrActionFollowup, ccAddImageFiles, ccRemoveAttachment, ccHandlePaste, ccHandleDragOver, ccHandleDragLeave, ccHandleDrop };
|
package/dashboard/js/settings.js
CHANGED
|
@@ -253,6 +253,29 @@ async function openSettings() {
|
|
|
253
253
|
// preferences (font-size scale today; reserved for future theme picks).
|
|
254
254
|
// Kept narrow + featured near the rail top so the setting is discoverable
|
|
255
255
|
// rather than buried under generic Engine headers.
|
|
256
|
+
// W-mryz1okr00062b6b — the slim-ux enable/disable control lives in Appearance
|
|
257
|
+
// (a dashboard-wide visual preference) rather than the generic Feature Flags
|
|
258
|
+
// list. It reuses the EXISTING toggle wiring: rendering a checkbox tagged with
|
|
259
|
+
// the data-feature-toggle attribute set to slim-ux means the delegated
|
|
260
|
+
// change-handler below (querySelectorAll('input[data-feature-toggle]'))
|
|
261
|
+
// persists it via POST /api/features/toggle — no new listener, no new
|
|
262
|
+
// endpoint. If the flag is missing from the registry response we hide the
|
|
263
|
+
// control rather than render a broken toggle.
|
|
264
|
+
const slimUxFeature = featuresList.find(function(f) { return f.id === 'slim-ux'; });
|
|
265
|
+
const slimUxEnabled = slimUxFeature
|
|
266
|
+
? (slimUxFeature.enabled != null ? !!slimUxFeature.enabled : !!slimUxFeature.default)
|
|
267
|
+
: false;
|
|
268
|
+
const slimUxControl = slimUxFeature
|
|
269
|
+
? '<div data-search="slim ux appearance experimental cockpit">' +
|
|
270
|
+
'<label style="display:flex;align-items:flex-start;gap:8px;cursor:pointer">' +
|
|
271
|
+
'<input type="checkbox" data-feature-toggle="slim-ux"' + (slimUxEnabled ? ' checked' : '') + ' style="margin-top:3px;accent-color:var(--blue);width:16px;height:16px;cursor:pointer;flex-shrink:0">' +
|
|
272
|
+
'<span style="flex:1">' +
|
|
273
|
+
'<span style="font-size:var(--text-md);color:var(--text);display:block">Slim UX</span>' +
|
|
274
|
+
'<span style="font-size:var(--text-xs);color:var(--muted);display:block;margin-top:1px">Experimental focused single-screen cockpit. Persists immediately.</span>' +
|
|
275
|
+
'</span>' +
|
|
276
|
+
'</label>' +
|
|
277
|
+
'</div>'
|
|
278
|
+
: '';
|
|
256
279
|
const paneAppearance =
|
|
257
280
|
'<h3>Appearance</h3>' +
|
|
258
281
|
'<div class="settings-pane-sub">Dashboard-wide visual preferences. Persisted in browser localStorage + server-side settings so they survive a reload from a cold cache.</div>' +
|
|
@@ -270,6 +293,7 @@ async function openSettings() {
|
|
|
270
293
|
'</select>' +
|
|
271
294
|
'<div style="font-size:var(--text-xs);color:var(--muted);margin-top:1px">Scales the whole dashboard. Persisted in browser + server.</div>' +
|
|
272
295
|
'</div>' +
|
|
296
|
+
slimUxControl +
|
|
273
297
|
'</div>';
|
|
274
298
|
|
|
275
299
|
// W-mrtdw70o000sb227 — Projects tab now shows ONE project's config at a time,
|
|
@@ -605,17 +629,21 @@ async function openSettings() {
|
|
|
605
629
|
'</div>';
|
|
606
630
|
|
|
607
631
|
// Toggles persist immediately via POST /api/features/toggle — no Save needed.
|
|
632
|
+
// W-mryz1okr00062b6b — slim-ux is surfaced in the Appearance pane instead, so
|
|
633
|
+
// exclude it here (display-only; the /api/features payload + registry are
|
|
634
|
+
// untouched). The "(N registered)" count below reflects what this list shows.
|
|
635
|
+
const displayFeatures = featuresList.filter(function(f) { return f.id !== 'slim-ux'; });
|
|
608
636
|
const paneFeatures =
|
|
609
637
|
'<h3>Feature Flags</h3>' +
|
|
610
638
|
'<div class="settings-pane-sub">In-progress UX or behavior gates. Toggles persist immediately. Registry: <code>engine/features.js</code>. Env override: <code>MINIONS_FEATURE_<NAME>=1</code>.</div>' +
|
|
611
639
|
'<details id="settings-features-details" open style="border:1px solid var(--border);border-radius:6px;padding:12px">' +
|
|
612
640
|
'<summary style="cursor:pointer;font-size:var(--text-md);color:var(--text);user-select:none;margin-bottom:8px">Show experimental flags ' +
|
|
613
|
-
'<span style="font-size:var(--text-sm);color:var(--muted)">(' +
|
|
641
|
+
'<span style="font-size:var(--text-sm);color:var(--muted)">(' + displayFeatures.length + ' registered)</span>' +
|
|
614
642
|
'</summary>' +
|
|
615
|
-
(
|
|
643
|
+
(displayFeatures.length === 0
|
|
616
644
|
? '<div style="font-size:var(--text-base);color:var(--muted);padding:12px;border:1px dashed var(--border);border-radius:4px;text-align:center">No experimental features registered. Add entries to <code>engine/features.js</code> to gate new work.</div>'
|
|
617
645
|
: '<div style="display:flex;flex-direction:column;gap:8px">' +
|
|
618
|
-
|
|
646
|
+
displayFeatures.map(function(f) {
|
|
619
647
|
const checked = f.enabled ? ' checked' : '';
|
|
620
648
|
const expiredBadge = f.expired
|
|
621
649
|
? ' <span style="font-size:var(--text-xs);padding:1px 5px;background:rgba(220,80,80,0.15);color:var(--red);border-radius:3px;margin-left:4px">EXPIRED</span>'
|
package/dashboard/slim/body.html
CHANGED
|
@@ -405,3 +405,25 @@
|
|
|
405
405
|
</div>
|
|
406
406
|
</div>
|
|
407
407
|
|
|
408
|
+
<!-- Reused CLASSIC agent detail slide-in panel (W-mrz11djx00050594). Opened by
|
|
409
|
+
clicking a Team member card; populated by the reused dashboard/js panel
|
|
410
|
+
modules (openAgentDetail → renderDetailTabs/renderDetailContent) exposed on
|
|
411
|
+
window.MinionsAgents. Structure mirrors dashboard/layout.html so the shared
|
|
412
|
+
JS + CSS resolve unchanged. -->
|
|
413
|
+
<div class="detail-overlay" id="detail-overlay" onclick="closeDetail()"></div>
|
|
414
|
+
<div class="detail-panel" id="detail-panel">
|
|
415
|
+
<div class="detail-header">
|
|
416
|
+
<h3 id="detail-agent-name">—</h3>
|
|
417
|
+
<button class="detail-close" onclick="closeDetail()">ESC / Close</button>
|
|
418
|
+
</div>
|
|
419
|
+
<div class="status-line" id="detail-status-line">—</div>
|
|
420
|
+
<div class="detail-tabs" id="detail-tabs"></div>
|
|
421
|
+
<div class="detail-body">
|
|
422
|
+
<div class="detail-content" id="detail-content"></div>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<!-- Floating toast target for the reused panel's charter-save / steering
|
|
427
|
+
feedback (showToast in panel-bootstrap.js routes here). -->
|
|
428
|
+
<div class="cmd-toast" id="cmd-toast" role="status" aria-live="polite"></div>
|
|
429
|
+
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
function renderMembers(agents) {
|
|
24
24
|
var grid = document.getElementById('member-grid');
|
|
25
25
|
if (!grid) return;
|
|
26
|
+
// Feed the reused CLASSIC detail panel (W-mrz11djx00050594): its
|
|
27
|
+
// openAgentDetail(id) does agentData.find(...), so keep the global
|
|
28
|
+
// agentData (declared in slim/panel-bootstrap.js) in sync with the live
|
|
29
|
+
// /api/status agents. Set before the unchanged-early-return so it stays
|
|
30
|
+
// current even on no-op polls.
|
|
31
|
+
try { window.agentData = agents; } catch { /* non-fatal */ }
|
|
26
32
|
var json = JSON.stringify(agents);
|
|
27
33
|
if (json === _lastAgentsJson) return;
|
|
28
34
|
_lastAgentsJson = json;
|
|
@@ -43,9 +49,9 @@
|
|
|
43
49
|
card.setAttribute('tabindex', '0');
|
|
44
50
|
card.title = (a.name || a.id) + (a.role ? ' — ' + a.role : '');
|
|
45
51
|
// The clicked agent object is captured directly — no id->lookup round-trip.
|
|
46
|
-
card.addEventListener('click', function() {
|
|
52
|
+
card.addEventListener('click', function() { openAgentDetailPanel(a); });
|
|
47
53
|
card.addEventListener('keydown', function(ev) {
|
|
48
|
-
if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault();
|
|
54
|
+
if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); openAgentDetailPanel(a); }
|
|
49
55
|
});
|
|
50
56
|
// Hover (and keyboard focus, for tabindex=0 parity) reveals the inline
|
|
51
57
|
// details bar under the grid without opening the click->modal. Listeners
|
|
@@ -219,8 +225,29 @@
|
|
|
219
225
|
body.appendChild(row);
|
|
220
226
|
}
|
|
221
227
|
|
|
228
|
+
// Route an agent-card click to the CLASSIC rich detail slide-in panel
|
|
229
|
+
// (W-mrz11djx00050594) — tabs + live-output stream + charter editor + full
|
|
230
|
+
// status — reused verbatim from the classic dashboard and exposed on
|
|
231
|
+
// window.MinionsAgents by dashboard/js/render-agents.js (injected into the
|
|
232
|
+
// slim document as a separate global <script>; see dashboard-build.js).
|
|
233
|
+
// Falls back to the slim mini-modal only if that panel bundle is genuinely
|
|
234
|
+
// unavailable, so a build/regression that drops the panel still shows detail.
|
|
235
|
+
function openAgentDetailPanel(a) {
|
|
236
|
+
if (!a || !a.id) return;
|
|
237
|
+
var MA = window.MinionsAgents;
|
|
238
|
+
if (MA && typeof MA.openAgentDetail === 'function') {
|
|
239
|
+
// openAgentDetail(id) resolves the agent via the global agentData, which
|
|
240
|
+
// renderMembers keeps in sync; pass the id (classic takes an id, not the
|
|
241
|
+
// whole object).
|
|
242
|
+
MA.openAgentDetail(a.id);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
openAgentDetail(a); // graceful fallback: slim mini-modal
|
|
246
|
+
}
|
|
247
|
+
|
|
222
248
|
// Populate + open the agent detail modal for the given agent object (passed
|
|
223
|
-
// straight from the clicked card).
|
|
249
|
+
// straight from the clicked card). Retained as the fallback for
|
|
250
|
+
// openAgentDetailPanel when the reused classic panel is unavailable.
|
|
224
251
|
function openAgentDetail(a) {
|
|
225
252
|
var modal = document.getElementById('slim-agent-modal');
|
|
226
253
|
var body = document.getElementById('slim-agent-body');
|
|
@@ -18,5 +18,13 @@
|
|
|
18
18
|
/* __JS__ */
|
|
19
19
|
})();
|
|
20
20
|
</script>
|
|
21
|
+
<!-- Reused CLASSIC agent detail slide-in panel (W-mrz11djx00050594). Runs at
|
|
22
|
+
GLOBAL, non-strict scope — the same scope the classic dashboard uses — so
|
|
23
|
+
the reused dashboard/js panel modules behave identically. Kept separate from
|
|
24
|
+
(and after) the slim IIFE above; it exposes window.MinionsAgents.openAgentDetail(id),
|
|
25
|
+
which members.js calls on card click. -->
|
|
26
|
+
<script>
|
|
27
|
+
/* __PANEL_JS__ */
|
|
28
|
+
</script>
|
|
21
29
|
</body>
|
|
22
30
|
</html>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// dashboard/slim/panel-bootstrap.js — glue for reusing the CLASSIC agent detail
|
|
2
|
+
// slide-in panel inside the Slim-UX document (W-mrz11djx00050594).
|
|
3
|
+
//
|
|
4
|
+
// The classic panel modules (utils, render-utils, charter-editor, live-stream,
|
|
5
|
+
// render-agents, detail-panel) are the SAME real files the classic dashboard
|
|
6
|
+
// ships — dashboard-build.js concatenates them after this bootstrap into one
|
|
7
|
+
// GLOBAL, non-strict <script> so they run in the exact scope they were written
|
|
8
|
+
// for (unlike slim's own strict IIFE). Those files read a handful of primitives
|
|
9
|
+
// that normally live in classic's state.js / command-parser.js, which slim does
|
|
10
|
+
// NOT bundle. Rather than fork the panel, we wire up just those primitives here:
|
|
11
|
+
//
|
|
12
|
+
// - agentData / currentAgentId / currentTab (classic panel module state)
|
|
13
|
+
// - safeFetch (state.js — API fetch w/ timeout)
|
|
14
|
+
// - showToast (command-parser.js — toast feedback)
|
|
15
|
+
// - DASHBOARD_TAB_ID (state.js — per-tab id header)
|
|
16
|
+
//
|
|
17
|
+
// Declared with `var` at the top of the global panel <script> so they become
|
|
18
|
+
// window properties; slim's IIFE (dashboard/slim/js/members.js) sets
|
|
19
|
+
// `window.agentData` before invoking `window.MinionsAgents.openAgentDetail(id)`.
|
|
20
|
+
|
|
21
|
+
// Classic panel module state (mirrors dashboard/js/state.js declarations).
|
|
22
|
+
var agentData = [];
|
|
23
|
+
var currentAgentId = null;
|
|
24
|
+
var currentTab = 'thought-process';
|
|
25
|
+
|
|
26
|
+
// Per-tab id echoed to /api/* via safeFetch, mirroring state.js. Best-effort —
|
|
27
|
+
// sessionStorage may throw in locked-down contexts, so fall back to an ephemeral id.
|
|
28
|
+
var DASHBOARD_TAB_ID = (function() {
|
|
29
|
+
try {
|
|
30
|
+
var existing = sessionStorage.getItem('minions-dashboard-tab-id');
|
|
31
|
+
if (existing) return existing;
|
|
32
|
+
var id = 'tab-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
|
|
33
|
+
sessionStorage.setItem('minions-dashboard-tab-id', id);
|
|
34
|
+
return id;
|
|
35
|
+
} catch {
|
|
36
|
+
return 'tab-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
|
|
37
|
+
}
|
|
38
|
+
})();
|
|
39
|
+
|
|
40
|
+
// Abort-on-timeout fetch with the dashboard-tab headers — copied verbatim from
|
|
41
|
+
// dashboard/js/state.js so the reused panel modules (openAgentDetail, charter
|
|
42
|
+
// save, live-stream refresh/steer) behave identically to the classic surface.
|
|
43
|
+
function safeFetch(url, opts) {
|
|
44
|
+
var timeout = (opts && opts.timeout) || 15000;
|
|
45
|
+
var controller = new AbortController();
|
|
46
|
+
var timer = setTimeout(function() { controller.abort(); }, timeout);
|
|
47
|
+
var fetchOpts = Object.assign({}, opts, { signal: controller.signal });
|
|
48
|
+
if (typeof url === 'string' && url.indexOf('/api/') === 0) {
|
|
49
|
+
var headers = Object.assign({}, fetchOpts.headers || {});
|
|
50
|
+
headers['X-Minions-Dashboard-Tab'] = DASHBOARD_TAB_ID;
|
|
51
|
+
headers['X-Minions-Dashboard-Url'] = location.pathname + location.search + location.hash;
|
|
52
|
+
headers['X-Minions-Dashboard-Visibility'] = document.visibilityState || '';
|
|
53
|
+
fetchOpts.headers = headers;
|
|
54
|
+
}
|
|
55
|
+
delete fetchOpts.timeout;
|
|
56
|
+
return fetch(url, fetchOpts).finally(function() { clearTimeout(timer); });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Minimal toast used by the reused charter-editor (save) and live-stream
|
|
60
|
+
// (steering) modules. Mirrors the graceful-degradation contract of the classic
|
|
61
|
+
// showToast (dashboard/js/command-parser.js): warn + return when the target
|
|
62
|
+
// element is absent so a missing #cmd-toast can never throw.
|
|
63
|
+
function showToast(id, msg, ok, durationMs) {
|
|
64
|
+
var el = document.getElementById(id);
|
|
65
|
+
if (!el) { try { console.warn('[showToast] no element with id', id, '— message:', msg); } catch {} return; }
|
|
66
|
+
el.classList.add('cmd-toast');
|
|
67
|
+
el.classList.remove('success', 'error');
|
|
68
|
+
el.classList.add(ok ? 'success' : 'error');
|
|
69
|
+
el.textContent = String(msg == null ? '' : msg);
|
|
70
|
+
el.classList.add('open');
|
|
71
|
+
setTimeout(function() { el.classList.remove('success', 'error', 'open'); }, durationMs || 4000);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ESC closes the slide-in panel (parity with the classic "ESC / Close" button).
|
|
75
|
+
// closeDetail() is defined later in this same global script by detail-panel.js;
|
|
76
|
+
// it is only invoked on keydown (runtime), so load-order is fine.
|
|
77
|
+
document.addEventListener('keydown', function(e) {
|
|
78
|
+
if (e.key !== 'Escape') return;
|
|
79
|
+
var panel = document.getElementById('detail-panel');
|
|
80
|
+
if (panel && panel.classList.contains('open') && typeof closeDetail === 'function') {
|
|
81
|
+
closeDetail();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
@@ -1750,4 +1750,134 @@
|
|
|
1750
1750
|
font-family: inherit;
|
|
1751
1751
|
resize: vertical;
|
|
1752
1752
|
}
|
|
1753
|
-
.modal-textarea:focus { outline: none; border-color: var(--blue); }
|
|
1753
|
+
.modal-textarea:focus { outline: none; border-color: var(--blue); }
|
|
1754
|
+
/* ===================================================================
|
|
1755
|
+
Reused CLASSIC agent detail slide-in panel (W-mrz11djx00050594)
|
|
1756
|
+
-------------------------------------------------------------------
|
|
1757
|
+
The panel markup + JS are reused verbatim from the classic dashboard
|
|
1758
|
+
(dashboard/layout.html + dashboard/js/{render-agents,detail-panel,
|
|
1759
|
+
live-stream,charter-editor,render-utils,utils}.js). Slim and classic
|
|
1760
|
+
ship SEPARATE top-level stylesheets with no shared-CSS partial, so the
|
|
1761
|
+
panel's presentation rules are mirrored here. The rule bodies are the
|
|
1762
|
+
classic rules unchanged; they reference classic design tokens
|
|
1763
|
+
(--space-N, --radius-*, --yellow, --purple, --transition-*) that slim
|
|
1764
|
+
either lacks or scales differently. Those tokens are scoped to the
|
|
1765
|
+
panel roots below so every panel descendant resolves them to the
|
|
1766
|
+
classic values WITHOUT disturbing slim's global palette.
|
|
1767
|
+
=================================================================== */
|
|
1768
|
+
#detail-panel, #detail-overlay {
|
|
1769
|
+
--yellow: #d29922; --purple: #bc8cff; --orange: #e3b341;
|
|
1770
|
+
--radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px; --radius-xl: 10px; --radius-full: 50%;
|
|
1771
|
+
--transition-fast: 0.15s; --transition-base: 0.2s; --transition-slow: 0.3s;
|
|
1772
|
+
--space-1: 2px; --space-2: 4px; --space-3: 6px; --space-4: 8px; --space-5: 10px;
|
|
1773
|
+
--space-6: 12px; --space-7: 16px; --space-8: 20px; --space-9: 24px;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
|
|
1777
|
+
.pulse { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--green); display: inline-block; margin-right: var(--space-3); animation: pulse 2s infinite; }
|
|
1778
|
+
|
|
1779
|
+
.detail-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 300; }
|
|
1780
|
+
.detail-overlay.open { display: block; }
|
|
1781
|
+
.detail-panel {
|
|
1782
|
+
position: fixed; top: 0; right: 0; width: 55%; max-width: 750px; height: 100vh;
|
|
1783
|
+
background: var(--surface); border-left: 1px solid var(--border);
|
|
1784
|
+
z-index: 301; display: flex; flex-direction: column;
|
|
1785
|
+
transform: translateX(100%); transition: transform 0.25s ease;
|
|
1786
|
+
}
|
|
1787
|
+
.detail-panel.open { transform: translateX(0); }
|
|
1788
|
+
.detail-header {
|
|
1789
|
+
padding: var(--space-7) var(--space-8); border-bottom: 1px solid var(--border);
|
|
1790
|
+
display: flex; justify-content: space-between; align-items: center; flex-shrink: 0;
|
|
1791
|
+
}
|
|
1792
|
+
.detail-header h3 { font-size: var(--text-2xl); color: var(--blue); display: flex; align-items: center; gap: var(--space-4); }
|
|
1793
|
+
.detail-close { background: none; border: 1px solid var(--border); color: var(--muted); font-size: var(--text-md); cursor: pointer; padding: var(--space-2) var(--space-6); border-radius: var(--radius-sm); }
|
|
1794
|
+
.detail-close:hover { color: var(--text); border-color: var(--text); }
|
|
1795
|
+
.detail-body { flex: 1; overflow-y: auto; padding: 0; }
|
|
1796
|
+
.detail-tabs { display: flex; border-bottom: 1px solid var(--border); flex-shrink: 0; background: var(--bg); }
|
|
1797
|
+
.detail-tab {
|
|
1798
|
+
padding: var(--space-5) var(--space-7); font-size: var(--text-md); font-weight: 500; color: var(--muted);
|
|
1799
|
+
cursor: pointer; border-bottom: 2px solid transparent; transition: all var(--transition-base);
|
|
1800
|
+
}
|
|
1801
|
+
.detail-tab:hover { color: var(--text); }
|
|
1802
|
+
.detail-tab.active { color: var(--blue); border-bottom-color: var(--blue); }
|
|
1803
|
+
.detail-content {
|
|
1804
|
+
padding: var(--space-7) var(--space-8); font-size: var(--text-md); line-height: 1.7;
|
|
1805
|
+
white-space: pre-wrap; word-wrap: break-word; font-family: Consolas, monospace; color: var(--muted);
|
|
1806
|
+
}
|
|
1807
|
+
.detail-content h4 { color: var(--text); font-size: var(--text-lg); margin: var(--space-6) 0 var(--space-3) 0; font-family: 'Segoe UI', sans-serif; }
|
|
1808
|
+
.detail-content .section { margin-bottom: var(--space-7); padding: var(--space-6); background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-md); }
|
|
1809
|
+
.status-line { display: flex; align-items: center; gap: var(--space-5); padding: var(--space-5) var(--space-7); background: var(--bg); border-bottom: 1px solid var(--border); font-size: var(--text-md); }
|
|
1810
|
+
|
|
1811
|
+
.status-badge { font-size: var(--text-sm); font-weight: 600; padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
1812
|
+
.status-badge.idle { background: var(--surface); color: var(--muted); border: 1px solid var(--border); }
|
|
1813
|
+
.status-badge.working { background: rgba(210,153,34,0.15); color: var(--yellow); border: 1px solid var(--yellow); animation: pulse 1.5s infinite; }
|
|
1814
|
+
.status-badge.done { background: rgba(63,185,80,0.15); color: var(--green); border: 1px solid var(--green); }
|
|
1815
|
+
.status-badge.error { background: rgba(248,81,73,0.15); color: var(--red); border: 1px solid var(--red); }
|
|
1816
|
+
|
|
1817
|
+
.pr-table { width: 100%; border-collapse: collapse; font-size: var(--text-md); table-layout: auto; }
|
|
1818
|
+
.pr-table th, .pr-table td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); text-align: left; }
|
|
1819
|
+
.pr-table th { color: var(--muted); font-weight: 600; font-size: var(--text-sm); text-transform: uppercase; letter-spacing: 0.4px; }
|
|
1820
|
+
|
|
1821
|
+
.dispatch-type { font-size: var(--text-sm); font-weight: 600; padding: var(--space-1) var(--space-4); border-radius: var(--radius-lg); text-transform: uppercase; white-space: nowrap; }
|
|
1822
|
+
.dispatch-type.implement { background: rgba(88,166,255,0.15); color: var(--blue); }
|
|
1823
|
+
.dispatch-type.review { background: rgba(188,140,255,0.15); color: var(--purple); }
|
|
1824
|
+
.dispatch-type.fix { background: rgba(210,153,34,0.15); color: var(--yellow); }
|
|
1825
|
+
.dispatch-type.analyze { background: rgba(63,185,80,0.15); color: var(--green); }
|
|
1826
|
+
.dispatch-type.explore { background: rgba(139,148,158,0.15); color: var(--muted); }
|
|
1827
|
+
.dispatch-type.test { background: rgba(227,179,65,0.15); color: var(--orange); }
|
|
1828
|
+
.dispatch-type.plan { background: rgba(168,85,247,0.15); color: #a855f7; }
|
|
1829
|
+
.dispatch-type.ask { background: rgba(63,185,80,0.15); color: var(--green); }
|
|
1830
|
+
.dispatch-type.verify { background: rgba(63,185,80,0.2); color: var(--green); }
|
|
1831
|
+
.dispatch-type.meeting { background: rgba(88,166,255,0.2); color: var(--blue); }
|
|
1832
|
+
.dispatch-type.decompose { background: rgba(188,140,255,0.2); color: var(--purple); }
|
|
1833
|
+
.dispatch-type.docs { background: rgba(56,189,248,0.15); color: #38bdf8; }
|
|
1834
|
+
.dispatch-type.setup { background: rgba(20,184,166,0.15); color: #14b8a6; }
|
|
1835
|
+
|
|
1836
|
+
.pr-pager-btn {
|
|
1837
|
+
background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
|
|
1838
|
+
font-size: var(--text-base); padding: var(--space-2) var(--space-5); border-radius: var(--radius-sm); cursor: pointer; transition: all var(--transition-base);
|
|
1839
|
+
}
|
|
1840
|
+
.pr-pager-btn:hover { color: var(--text); border-color: var(--text); }
|
|
1841
|
+
|
|
1842
|
+
.btn-primary-lg {
|
|
1843
|
+
background: var(--blue); color: #fff; border: none; border-radius: var(--radius-sm);
|
|
1844
|
+
padding: 6px 16px; font-size: var(--text-md); cursor: pointer; transition: opacity var(--transition-base);
|
|
1845
|
+
}
|
|
1846
|
+
.btn-primary-lg:hover { opacity: 0.9; }
|
|
1847
|
+
|
|
1848
|
+
.modal-copy { background: var(--surface2); border: 1px solid var(--border); color: var(--muted); font-size: var(--text-base); cursor: pointer; padding: var(--space-2) var(--space-5); border-radius: var(--radius-sm); display: flex; align-items: center; gap: var(--space-2); transition: all var(--transition-base); }
|
|
1849
|
+
.modal-copy:hover { color: var(--text); border-color: var(--text); }
|
|
1850
|
+
.modal-copy.is-success { color: var(--green); border-color: var(--green); }
|
|
1851
|
+
.modal-copy.is-danger { color: var(--red); border-color: var(--red); }
|
|
1852
|
+
|
|
1853
|
+
.artifact-chip {
|
|
1854
|
+
display: inline-flex; align-items: center; gap: var(--space-1);
|
|
1855
|
+
padding: var(--space-1) var(--space-4); border-radius: 10px;
|
|
1856
|
+
font-size: var(--text-sm); cursor: pointer;
|
|
1857
|
+
background: var(--surface2); border: 1px solid var(--border); color: var(--text);
|
|
1858
|
+
transition: border-color var(--transition-base), color var(--transition-base);
|
|
1859
|
+
user-select: none;
|
|
1860
|
+
}
|
|
1861
|
+
.artifact-chip:hover { border-color: var(--blue); color: var(--blue); }
|
|
1862
|
+
.artifact-chip-icon { font-size: var(--text-sm); line-height: 1; }
|
|
1863
|
+
.artifact-chip-label { font-size: var(--text-sm); }
|
|
1864
|
+
.artifact-chip.deleted { cursor: not-allowed; text-decoration: line-through; color: var(--muted); opacity: 0.6; }
|
|
1865
|
+
.artifact-chip-unknown { color: var(--muted); }
|
|
1866
|
+
|
|
1867
|
+
/* Markdown content — consistent styling everywhere renderMd() is used inside the panel */
|
|
1868
|
+
.detail-panel .md-content { font-family: 'Segoe UI', system-ui, sans-serif; font-size: var(--text-md); line-height: 1.6; white-space: normal; word-break: break-word; overflow-x: auto; }
|
|
1869
|
+
.detail-panel .md-content pre { font-family: Consolas, 'Courier New', monospace; white-space: pre-wrap; max-width: 100%; }
|
|
1870
|
+
.detail-panel .md-content code { font-family: Consolas, 'Courier New', monospace; }
|
|
1871
|
+
.detail-panel .md-content table { border-collapse: collapse; margin: 6px 0; width: max-content; min-width: 100%; }
|
|
1872
|
+
.detail-panel .md-content th, .detail-panel .md-content td { padding: 4px 8px; border: 1px solid var(--border); text-align: left; font-size: var(--text-base); white-space: nowrap; }
|
|
1873
|
+
.detail-panel .md-content td { white-space: normal; min-width: 60px; }
|
|
1874
|
+
.detail-panel .md-content th { background: var(--surface); font-weight: 600; }
|
|
1875
|
+
.detail-panel .md-content .md-p { margin: 0 0 6px; }
|
|
1876
|
+
.detail-panel .md-content .md-p:last-child { margin-bottom: 0; }
|
|
1877
|
+
.detail-panel .md-content li { margin: 1px 0; }
|
|
1878
|
+
|
|
1879
|
+
/* Floating toast target for the reused panel (charter save / steering feedback). */
|
|
1880
|
+
.cmd-toast { position: fixed; bottom: 16px; right: 16px; z-index: 500; max-width: 360px; padding: 10px 14px; border-radius: 6px; background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-size: var(--text-md); box-shadow: 0 4px 16px rgba(0,0,0,0.4); opacity: 0; pointer-events: none; transition: opacity 0.2s ease; }
|
|
1881
|
+
.cmd-toast.open { opacity: 1; }
|
|
1882
|
+
.cmd-toast.success { border-color: var(--green); }
|
|
1883
|
+
.cmd-toast.error { border-color: var(--red); }
|
package/dashboard-build.js
CHANGED
|
@@ -79,6 +79,18 @@ const SLIM_JS_ORDER = [
|
|
|
79
79
|
'command-send', 'status', 'members', 'modals-tiles', 'history', 'pinned', 'knowledge', 'plans',
|
|
80
80
|
];
|
|
81
81
|
|
|
82
|
+
// W-mrz11djx00050594 — the CLASSIC agent detail slide-in panel, reused verbatim
|
|
83
|
+
// inside Slim-UX. These are the real dashboard/js/*.js modules the classic
|
|
84
|
+
// dashboard ships (no fork); they are concatenated after slim/panel-bootstrap.js
|
|
85
|
+
// into a single GLOBAL, non-strict <script> (the /* __PANEL_JS__ */ slot) so
|
|
86
|
+
// they run in the same global scope they were written for, separate from slim's
|
|
87
|
+
// strict IIFE. Order is load-bearing: helpers before consumers, and
|
|
88
|
+
// charter-editor MUST precede detail-panel (see the "charter-editor registered
|
|
89
|
+
// before detail-panel" test).
|
|
90
|
+
const SLIM_CLASSIC_PANEL_JS = [
|
|
91
|
+
'utils', 'render-utils', 'charter-editor', 'live-stream', 'render-agents', 'detail-panel',
|
|
92
|
+
];
|
|
93
|
+
|
|
82
94
|
// Cache for the assembled slim source fragments (layout/css/body/js). Keyed on
|
|
83
95
|
// the input file mtimes so dev edits still take effect on the next request
|
|
84
96
|
// without a server restart, while production serves repeated requests without
|
|
@@ -92,8 +104,10 @@ function _slimSourcePaths(slimDir) {
|
|
|
92
104
|
path.join(slimDir, 'layout.html'),
|
|
93
105
|
path.join(slimDir, 'styles.css'),
|
|
94
106
|
path.join(slimDir, 'body.html'),
|
|
107
|
+
path.join(slimDir, 'panel-bootstrap.js'),
|
|
95
108
|
...DASHBOARD_SHARED_JS.map(f => path.join(MINIONS_DIR, 'dashboard', 'shared', f + '.js')),
|
|
96
109
|
...SLIM_JS_ORDER.map(f => path.join(slimDir, 'js', f + '.js')),
|
|
110
|
+
...SLIM_CLASSIC_PANEL_JS.map(f => path.join(MINIONS_DIR, 'dashboard', 'js', f + '.js')),
|
|
97
111
|
];
|
|
98
112
|
}
|
|
99
113
|
|
|
@@ -112,7 +126,14 @@ function buildSlimHtml(opts) {
|
|
|
112
126
|
if (!_slimPartsCache
|
|
113
127
|
|| _slimPartsCache.mtimes.length !== mtimes.length
|
|
114
128
|
|| _slimPartsCache.mtimes.some((m, i) => m !== mtimes[i])) {
|
|
115
|
-
|
|
129
|
+
// paths layout: [layout, styles, body, panel-bootstrap,
|
|
130
|
+
// ...DASHBOARD_SHARED_JS, ...SLIM_JS_ORDER, ...SLIM_CLASSIC_PANEL_JS].
|
|
131
|
+
// The slim IIFE gets shared + slim js; the classic panel gets its own
|
|
132
|
+
// global <script> (bootstrap + classic modules) — see /* __PANEL_JS__ */.
|
|
133
|
+
const [lp, cp, bp, bootstrapPath, ...restPaths] = paths;
|
|
134
|
+
const iifeCount = DASHBOARD_SHARED_JS.length + SLIM_JS_ORDER.length;
|
|
135
|
+
const iifePaths = restPaths.slice(0, iifeCount);
|
|
136
|
+
const panelPaths = restPaths.slice(iifeCount);
|
|
116
137
|
_slimPartsCache = {
|
|
117
138
|
mtimes,
|
|
118
139
|
layout: safeRead(lp),
|
|
@@ -121,10 +142,14 @@ function buildSlimHtml(opts) {
|
|
|
121
142
|
// Join with a newline so a fragment that loses its trailing newline
|
|
122
143
|
// can't glue two statements together (e.g. `}var foo`). Parts share one
|
|
123
144
|
// IIFE scope, so order is load-bearing — see SLIM_JS_ORDER.
|
|
124
|
-
js:
|
|
145
|
+
js: iifePaths.map(p => safeRead(p)).join('\n'),
|
|
146
|
+
// Reused classic agent-detail panel — bootstrap glue first, then the real
|
|
147
|
+
// classic modules. Emitted into a separate GLOBAL, non-strict <script>
|
|
148
|
+
// (the /* __PANEL_JS__ */ slot) so they run in their original scope.
|
|
149
|
+
panelJs: [bootstrapPath, ...panelPaths].map(p => safeRead(p)).join('\n'),
|
|
125
150
|
};
|
|
126
151
|
}
|
|
127
|
-
const { layout, css, body, js } = _slimPartsCache;
|
|
152
|
+
const { layout, css, body, js, panelJs } = _slimPartsCache;
|
|
128
153
|
|
|
129
154
|
// Feature-flag bootstrap (window.MINIONS_FEATURES) injected at the top of the
|
|
130
155
|
// slim IIFE. serveSlimUx passes the live flags as JSON via opts.featuresJson;
|
|
@@ -138,6 +163,7 @@ function buildSlimHtml(opts) {
|
|
|
138
163
|
// an exact '\n' match would silently no-op and ship an empty body on CRLF).
|
|
139
164
|
.replace(/<!-- __BODY__ -->\r?\n/, () => body)
|
|
140
165
|
.replace('{{minions_features}}', () => featuresJson)
|
|
166
|
+
.replace('/* __PANEL_JS__ */', () => panelJs)
|
|
141
167
|
.replace('/* __JS__ */', () => js);
|
|
142
168
|
}
|
|
143
169
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2412",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|