bosun 0.40.21 → 0.41.1
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/.env.example +8 -0
- package/README.md +20 -0
- package/agent/agent-custom-tools.mjs +23 -5
- package/agent/agent-event-bus.mjs +248 -6
- package/agent/agent-pool.mjs +131 -30
- package/agent/agent-work-analyzer.mjs +8 -16
- package/agent/primary-agent.mjs +81 -7
- package/agent/retry-queue.mjs +164 -0
- package/bench/swebench/bosun-swebench.mjs +5 -0
- package/bosun.config.example.json +25 -0
- package/bosun.schema.json +825 -183
- package/cli.mjs +267 -8
- package/config/config-doctor.mjs +51 -2
- package/config/config.mjs +232 -5
- package/github/github-auth-manager.mjs +70 -19
- package/infra/library-manager.mjs +894 -60
- package/infra/monitor.mjs +701 -69
- package/infra/runtime-accumulator.mjs +376 -84
- package/infra/session-tracker.mjs +95 -28
- package/infra/test-runtime.mjs +267 -0
- package/lib/codebase-audit.mjs +133 -18
- package/package.json +30 -8
- package/server/setup-web-server.mjs +29 -1
- package/server/ui-server.mjs +1571 -49
- package/setup.mjs +27 -24
- package/shell/codex-shell.mjs +34 -3
- package/shell/copilot-shell.mjs +50 -8
- package/task/msg-hub.mjs +193 -0
- package/task/pipeline.mjs +544 -0
- package/task/task-claims.mjs +6 -10
- package/task/task-cli.mjs +38 -2
- package/task/task-executor-pipeline.mjs +143 -0
- package/task/task-executor.mjs +36 -27
- package/telegram/get-telegram-chat-id.mjs +57 -47
- package/ui/components/chat-view.js +18 -1
- package/ui/components/workspace-switcher.js +321 -9
- package/ui/demo-defaults.js +17830 -10433
- package/ui/demo.html +9 -1
- package/ui/modules/router.js +1 -1
- package/ui/modules/settings-schema.js +2 -0
- package/ui/modules/state.js +54 -57
- package/ui/modules/voice-client-sdk.js +376 -37
- package/ui/modules/voice-client.js +173 -33
- package/ui/setup.html +68 -2
- package/ui/styles/components.css +571 -1
- package/ui/styles.css +201 -1
- package/ui/tabs/dashboard.js +74 -0
- package/ui/tabs/library.js +410 -55
- package/ui/tabs/logs.js +10 -0
- package/ui/tabs/settings.js +178 -99
- package/ui/tabs/tasks.js +1083 -507
- package/ui/tabs/telemetry.js +34 -0
- package/ui/tabs/workflow-canvas-utils.mjs +38 -1
- package/ui/tabs/workflows.js +1275 -402
- package/voice/voice-agents-sdk.mjs +2 -2
- package/voice/voice-relay.mjs +28 -20
- package/workflow/declarative-workflows.mjs +145 -0
- package/workflow/msg-hub.mjs +237 -0
- package/workflow/pipeline-workflows.mjs +287 -0
- package/workflow/pipeline.mjs +828 -315
- package/workflow/project-detection.mjs +559 -0
- package/workflow/workflow-cli.mjs +128 -0
- package/workflow/workflow-contract.mjs +433 -232
- package/workflow/workflow-engine.mjs +510 -47
- package/workflow/workflow-nodes/custom-loader.mjs +251 -0
- package/workflow/workflow-nodes.mjs +2024 -184
- package/workflow/workflow-templates.mjs +118 -24
- package/workflow-templates/agents.mjs +20 -20
- package/workflow-templates/bosun-native.mjs +212 -2
- package/workflow-templates/code-quality.mjs +20 -14
- package/workflow-templates/continuation-loop.mjs +339 -0
- package/workflow-templates/github.mjs +516 -40
- package/workflow-templates/planning.mjs +446 -17
- package/workflow-templates/reliability.mjs +65 -12
- package/workflow-templates/task-batch.mjs +27 -10
- package/workflow-templates/task-execution.mjs +752 -0
- package/workflow-templates/task-lifecycle.mjs +117 -14
- package/workspace/context-cache.mjs +66 -18
- package/workspace/workspace-manager.mjs +153 -1
- package/workflow-templates/issue-continuation.mjs +0 -243
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
List, ListItem, ListItemText, ListItemIcon,
|
|
22
22
|
ListItemSecondaryAction, Divider, Tooltip, Alert,
|
|
23
23
|
Menu, MenuItem,
|
|
24
|
+
ToggleButton, ToggleButtonGroup,
|
|
25
|
+
Slider, Collapse,
|
|
24
26
|
} from "@mui/material";
|
|
25
27
|
|
|
26
28
|
const html = htm.bind(h);
|
|
@@ -146,6 +148,35 @@ async function scanDisk() {
|
|
|
146
148
|
return res;
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
// ─── API helpers for workspace state management ────────────
|
|
152
|
+
|
|
153
|
+
async function setWorkspaceState(workspaceId, state) {
|
|
154
|
+
const res = await apiFetch("/api/workspaces/state", {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: { "Content-Type": "application/json" },
|
|
157
|
+
body: JSON.stringify({ workspaceId, state }),
|
|
158
|
+
});
|
|
159
|
+
if (res?.ok) await loadWorkspaces();
|
|
160
|
+
return res;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function setWorkspaceExecutors(workspaceId, executors) {
|
|
164
|
+
const res = await apiFetch("/api/workspaces/executors", {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify({ workspaceId, ...executors }),
|
|
168
|
+
});
|
|
169
|
+
if (res?.ok) await loadWorkspaces();
|
|
170
|
+
return res;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// State display helpers
|
|
174
|
+
const STATE_CONFIG = {
|
|
175
|
+
active: { icon: "●", color: "#10b981", label: "Active", desc: "Workflows running, executors available" },
|
|
176
|
+
paused: { icon: "◐", color: "#f59e0b", label: "Paused", desc: "In-flight tasks finish, no new starts" },
|
|
177
|
+
disabled: { icon: "○", color: "#71717a", label: "Disabled", desc: "Fully off — no workflows, no executors" },
|
|
178
|
+
};
|
|
179
|
+
|
|
149
180
|
// ─── Confirm dialog helper ─────────────────────────────────
|
|
150
181
|
function ConfirmBar({ message, onConfirm, onCancel, loading }) {
|
|
151
182
|
return html`
|
|
@@ -317,6 +348,208 @@ function AddRepoForm({ workspaceId }) {
|
|
|
317
348
|
`;
|
|
318
349
|
}
|
|
319
350
|
|
|
351
|
+
// ─── Workspace state toggle ─────────────────────────────────
|
|
352
|
+
function WorkspaceStateToggle({ ws, compact = false }) {
|
|
353
|
+
const [saving, setSaving] = useState(false);
|
|
354
|
+
const currentState = ws.state || "active";
|
|
355
|
+
|
|
356
|
+
const handleChange = useCallback(async (_e, newState) => {
|
|
357
|
+
if (!newState || newState === currentState) return;
|
|
358
|
+
setSaving(true);
|
|
359
|
+
haptic("medium");
|
|
360
|
+
try {
|
|
361
|
+
await setWorkspaceState(ws.id, newState);
|
|
362
|
+
} catch (e) {
|
|
363
|
+
console.warn("[ws-manager] state change error:", e);
|
|
364
|
+
} finally {
|
|
365
|
+
setSaving(false);
|
|
366
|
+
}
|
|
367
|
+
}, [ws.id, currentState]);
|
|
368
|
+
|
|
369
|
+
if (compact) {
|
|
370
|
+
const cfg = STATE_CONFIG[currentState];
|
|
371
|
+
return html`
|
|
372
|
+
<${Tooltip} title="${cfg.label}: ${cfg.desc}">
|
|
373
|
+
<span style="color: ${cfg.color}; font-size: 14px; cursor: default;">${cfg.icon}</span>
|
|
374
|
+
<//>
|
|
375
|
+
`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return html`
|
|
379
|
+
<${Stack} direction="row" spacing=${1} alignItems="center">
|
|
380
|
+
<${ToggleButtonGroup}
|
|
381
|
+
value=${currentState}
|
|
382
|
+
exclusive
|
|
383
|
+
onChange=${handleChange}
|
|
384
|
+
size="small"
|
|
385
|
+
disabled=${saving}
|
|
386
|
+
sx=${{ height: 30 }}
|
|
387
|
+
>
|
|
388
|
+
${Object.entries(STATE_CONFIG).map(([key, cfg]) => html`
|
|
389
|
+
<${ToggleButton}
|
|
390
|
+
key=${key}
|
|
391
|
+
value=${key}
|
|
392
|
+
sx=${{
|
|
393
|
+
px: 1.2,
|
|
394
|
+
py: 0.3,
|
|
395
|
+
fontSize: "11px",
|
|
396
|
+
textTransform: "none",
|
|
397
|
+
fontWeight: currentState === key ? 600 : 400,
|
|
398
|
+
color: currentState === key ? cfg.color : "text.secondary",
|
|
399
|
+
borderColor: currentState === key ? cfg.color : undefined,
|
|
400
|
+
"&.Mui-selected": {
|
|
401
|
+
backgroundColor: cfg.color + "18",
|
|
402
|
+
color: cfg.color,
|
|
403
|
+
borderColor: cfg.color + "60",
|
|
404
|
+
"&:hover": { backgroundColor: cfg.color + "28" },
|
|
405
|
+
},
|
|
406
|
+
}}
|
|
407
|
+
>
|
|
408
|
+
<${Tooltip} title=${cfg.desc}>
|
|
409
|
+
<span>${cfg.icon} ${cfg.label}</span>
|
|
410
|
+
<//>
|
|
411
|
+
<//>
|
|
412
|
+
`)}
|
|
413
|
+
<//>
|
|
414
|
+
${saving && html`<${CircularProgress} size=${14} />`}
|
|
415
|
+
<//>
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ─── Executor config panel (collapsible) ────────────────────
|
|
420
|
+
function ExecutorConfigPanel({ ws }) {
|
|
421
|
+
const [expanded, setExpanded] = useState(false);
|
|
422
|
+
const [saving, setSaving] = useState(false);
|
|
423
|
+
const execs = ws.executors || {};
|
|
424
|
+
const [maxConcurrent, setMaxConcurrent] = useState(execs.maxConcurrent || 3);
|
|
425
|
+
const [pool, setPool] = useState(execs.pool || "shared");
|
|
426
|
+
const [weight, setWeight] = useState(execs.weight || 1.0);
|
|
427
|
+
|
|
428
|
+
// Sync state with ws props when they change
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
const e = ws.executors || {};
|
|
431
|
+
setMaxConcurrent(e.maxConcurrent || 3);
|
|
432
|
+
setPool(e.pool || "shared");
|
|
433
|
+
setWeight(e.weight || 1.0);
|
|
434
|
+
}, [ws.executors?.maxConcurrent, ws.executors?.pool, ws.executors?.weight]);
|
|
435
|
+
|
|
436
|
+
const handleSave = useCallback(async () => {
|
|
437
|
+
setSaving(true);
|
|
438
|
+
haptic("medium");
|
|
439
|
+
try {
|
|
440
|
+
await setWorkspaceExecutors(ws.id, { maxConcurrent, pool, weight });
|
|
441
|
+
} catch (e) {
|
|
442
|
+
console.warn("[ws-manager] executor config error:", e);
|
|
443
|
+
} finally {
|
|
444
|
+
setSaving(false);
|
|
445
|
+
}
|
|
446
|
+
}, [ws.id, maxConcurrent, pool, weight]);
|
|
447
|
+
|
|
448
|
+
const hasChanges = maxConcurrent !== (execs.maxConcurrent || 3)
|
|
449
|
+
|| pool !== (execs.pool || "shared")
|
|
450
|
+
|| weight !== (execs.weight || 1.0);
|
|
451
|
+
|
|
452
|
+
return html`
|
|
453
|
+
<${Box} sx=${{ mt: 0.5 }}>
|
|
454
|
+
<${Button}
|
|
455
|
+
size="small"
|
|
456
|
+
variant="text"
|
|
457
|
+
onClick=${() => { haptic("light"); setExpanded(!expanded); }}
|
|
458
|
+
sx=${{ textTransform: "none", fontSize: "11px", color: "text.secondary", px: 0.5 }}
|
|
459
|
+
>
|
|
460
|
+
${resolveIcon("settings")} ${" "}Executors ${expanded ? "▾" : "▸"}
|
|
461
|
+
${!expanded && html`
|
|
462
|
+
<${Chip}
|
|
463
|
+
label="${execs.maxConcurrent || 3} slots · ${execs.pool || "shared"}"
|
|
464
|
+
size="small"
|
|
465
|
+
variant="outlined"
|
|
466
|
+
sx=${{ ml: 0.5, height: 18, fontSize: "10px" }}
|
|
467
|
+
/>
|
|
468
|
+
`}
|
|
469
|
+
<//>
|
|
470
|
+
|
|
471
|
+
<${Collapse} in=${expanded}>
|
|
472
|
+
<${Stack} spacing=${1.5} sx=${{ pt: 1, pb: 0.5, px: 0.5 }}>
|
|
473
|
+
|
|
474
|
+
<${Box}>
|
|
475
|
+
<${Typography} variant="caption" color="text.secondary" sx=${{ mb: 0.5, display: "block" }}>
|
|
476
|
+
Max Concurrent Executors: ${maxConcurrent}
|
|
477
|
+
<//>
|
|
478
|
+
<${Slider}
|
|
479
|
+
value=${maxConcurrent}
|
|
480
|
+
onChange=${(_e, v) => setMaxConcurrent(v)}
|
|
481
|
+
min=${1}
|
|
482
|
+
max=${10}
|
|
483
|
+
step=${1}
|
|
484
|
+
marks=${[
|
|
485
|
+
{ value: 1, label: "1" },
|
|
486
|
+
{ value: 3, label: "3" },
|
|
487
|
+
{ value: 5, label: "5" },
|
|
488
|
+
{ value: 10, label: "10" },
|
|
489
|
+
]}
|
|
490
|
+
size="small"
|
|
491
|
+
sx=${{ maxWidth: 240 }}
|
|
492
|
+
/>
|
|
493
|
+
<//>
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
<${Stack} direction="row" spacing=${1} alignItems="center">
|
|
497
|
+
<${Typography} variant="caption" color="text.secondary">Pool:<//>
|
|
498
|
+
<${ToggleButtonGroup}
|
|
499
|
+
value=${pool}
|
|
500
|
+
exclusive
|
|
501
|
+
onChange=${(_e, v) => { if (v) setPool(v); }}
|
|
502
|
+
size="small"
|
|
503
|
+
sx=${{ height: 26 }}
|
|
504
|
+
>
|
|
505
|
+
<${ToggleButton} value="shared" sx=${{ px: 1, fontSize: "11px", textTransform: "none" }}>
|
|
506
|
+
<${Tooltip} title="Shares executor capacity across workspaces">
|
|
507
|
+
<span>Shared</span>
|
|
508
|
+
<//>
|
|
509
|
+
<//>
|
|
510
|
+
<${ToggleButton} value="dedicated" sx=${{ px: 1, fontSize: "11px", textTransform: "none" }}>
|
|
511
|
+
<${Tooltip} title="Dedicated executor pool — isolated from other workspaces">
|
|
512
|
+
<span>Dedicated</span>
|
|
513
|
+
<//>
|
|
514
|
+
<//>
|
|
515
|
+
<//>
|
|
516
|
+
<//>
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
${pool === "shared" && html`
|
|
520
|
+
<${Box}>
|
|
521
|
+
<${Typography} variant="caption" color="text.secondary" sx=${{ mb: 0.5, display: "block" }}>
|
|
522
|
+
Priority Weight: ${weight.toFixed(1)}×
|
|
523
|
+
<//>
|
|
524
|
+
<${Slider}
|
|
525
|
+
value=${weight}
|
|
526
|
+
onChange=${(_e, v) => setWeight(v)}
|
|
527
|
+
min=${0.1}
|
|
528
|
+
max=${5.0}
|
|
529
|
+
step=${0.1}
|
|
530
|
+
size="small"
|
|
531
|
+
sx=${{ maxWidth: 200 }}
|
|
532
|
+
/>
|
|
533
|
+
<//>
|
|
534
|
+
`}
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
${hasChanges && html`
|
|
538
|
+
<${Button}
|
|
539
|
+
size="small"
|
|
540
|
+
variant="contained"
|
|
541
|
+
onClick=${handleSave}
|
|
542
|
+
disabled=${saving}
|
|
543
|
+
startIcon=${saving ? html`<${CircularProgress} size=${14} />` : null}
|
|
544
|
+
sx=${{ alignSelf: "flex-start", textTransform: "none", fontSize: "12px" }}
|
|
545
|
+
>${saving ? "Saving…" : "Save Executor Config"}<//>
|
|
546
|
+
`}
|
|
547
|
+
<//>
|
|
548
|
+
<//>
|
|
549
|
+
<//>
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
|
|
320
553
|
// ─── Single workspace card in the management panel ─────────
|
|
321
554
|
function WorkspaceCard({ ws }) {
|
|
322
555
|
const isActive = ws.id === activeWorkspaceId.value;
|
|
@@ -324,6 +557,8 @@ function WorkspaceCard({ ws }) {
|
|
|
324
557
|
const [delConfirm, setDelConfirm] = useState(false);
|
|
325
558
|
const [deleting, setDeleting] = useState(false);
|
|
326
559
|
const [activating, setActivating] = useState(false);
|
|
560
|
+
const wsState = ws.state || "active";
|
|
561
|
+
const stateCfg = STATE_CONFIG[wsState];
|
|
327
562
|
|
|
328
563
|
const handleSetActive = useCallback(async () => {
|
|
329
564
|
setActivating(true);
|
|
@@ -365,19 +600,43 @@ function WorkspaceCard({ ws }) {
|
|
|
365
600
|
variant="outlined"
|
|
366
601
|
sx=${{
|
|
367
602
|
mb: 2,
|
|
368
|
-
borderColor:
|
|
603
|
+
borderColor: wsState === "disabled" ? "action.disabled"
|
|
604
|
+
: isActive ? "primary.main" : "divider",
|
|
369
605
|
borderWidth: isActive ? 2 : 1,
|
|
606
|
+
opacity: wsState === "disabled" ? 0.6 : 1,
|
|
607
|
+
transition: "opacity 0.2s, border-color 0.2s",
|
|
370
608
|
}}
|
|
371
609
|
>
|
|
372
610
|
<${CardContent} sx=${{ pb: 0 }}>
|
|
373
611
|
<${Stack} direction="row" justifyContent="space-between" alignItems="center">
|
|
374
612
|
<${Stack} direction="row" spacing=${1} alignItems="center">
|
|
613
|
+
<${Tooltip} title="${stateCfg.label}: ${stateCfg.desc}">
|
|
614
|
+
<span style="color: ${stateCfg.color}; font-size: 16px;">${stateCfg.icon}</span>
|
|
615
|
+
<//>
|
|
375
616
|
<${Typography} variant="subtitle1" fontWeight="bold">${ws.name}<//>
|
|
376
617
|
${isActive && html`
|
|
377
618
|
<${Chip} label="Active" size="small" color="primary" />
|
|
378
619
|
`}
|
|
620
|
+
${wsState === "paused" && html`
|
|
621
|
+
<${Chip} label="Paused" size="small"
|
|
622
|
+
sx=${{ bgcolor: "#f59e0b22", color: "#f59e0b", fontWeight: 600, fontSize: "10px" }}
|
|
623
|
+
/>
|
|
624
|
+
`}
|
|
625
|
+
${wsState === "disabled" && html`
|
|
626
|
+
<${Chip} label="Disabled" size="small"
|
|
627
|
+
sx=${{ bgcolor: "#71717a22", color: "#71717a", fontWeight: 600, fontSize: "10px" }}
|
|
628
|
+
/>
|
|
629
|
+
`}
|
|
379
630
|
<//>
|
|
380
631
|
<//>
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
<${Box} sx=${{ mt: 1.5, mb: 0.5 }}>
|
|
635
|
+
<${WorkspaceStateToggle} ws=${ws} />
|
|
636
|
+
<//>
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
<${ExecutorConfigPanel} ws=${ws} />
|
|
381
640
|
<//>
|
|
382
641
|
|
|
383
642
|
<${CardActions} sx=${{ justifyContent: "flex-end", pt: 0.5 }}>
|
|
@@ -512,8 +771,37 @@ export function WorkspaceManager({ open, onClose }) {
|
|
|
512
771
|
const wsList = workspaces.value;
|
|
513
772
|
const loading = workspacesLoading.value;
|
|
514
773
|
|
|
774
|
+
// Compute state summary counts
|
|
775
|
+
const stateCounts = { active: 0, paused: 0, disabled: 0 };
|
|
776
|
+
wsList.forEach((ws) => {
|
|
777
|
+
const s = ws.state || "active";
|
|
778
|
+
if (stateCounts[s] !== undefined) stateCounts[s]++;
|
|
779
|
+
});
|
|
780
|
+
|
|
515
781
|
return html`
|
|
516
782
|
<${Modal} title="Manage Workspaces" open=${open} onClose=${onClose}>
|
|
783
|
+
|
|
784
|
+
${wsList.length > 0 && html`
|
|
785
|
+
<${Stack} direction="row" spacing=${1.5} sx=${{ mb: 2 }} alignItems="center">
|
|
786
|
+
${Object.entries(STATE_CONFIG).map(([key, cfg]) => html`
|
|
787
|
+
<${Chip}
|
|
788
|
+
key=${key}
|
|
789
|
+
icon=${html`<span style="color: ${cfg.color}; font-size: 12px; margin-left: 8px;">${cfg.icon}</span>`}
|
|
790
|
+
label="${stateCounts[key] || 0} ${cfg.label}"
|
|
791
|
+
size="small"
|
|
792
|
+
variant=${stateCounts[key] > 0 ? "filled" : "outlined"}
|
|
793
|
+
sx=${{
|
|
794
|
+
fontSize: "11px",
|
|
795
|
+
fontWeight: 500,
|
|
796
|
+
bgcolor: stateCounts[key] > 0 ? cfg.color + "18" : undefined,
|
|
797
|
+
borderColor: cfg.color + "40",
|
|
798
|
+
color: stateCounts[key] > 0 ? cfg.color : "text.secondary",
|
|
799
|
+
}}
|
|
800
|
+
/>
|
|
801
|
+
`)}
|
|
802
|
+
<//>
|
|
803
|
+
`}
|
|
804
|
+
|
|
517
805
|
<${Box} sx=${{ mb: 2 }}>
|
|
518
806
|
<${Button}
|
|
519
807
|
size="small"
|
|
@@ -654,7 +942,11 @@ export function WorkspaceSwitcher() {
|
|
|
654
942
|
>
|
|
655
943
|
${switchingId
|
|
656
944
|
? html`<${CircularProgress} size=${16} sx=${{ mr: 1 }} />`
|
|
657
|
-
:
|
|
945
|
+
: currentWs
|
|
946
|
+
? html`<span style="color: ${STATE_CONFIG[currentWs.state || "active"].color}; margin-right: 4px;">
|
|
947
|
+
${STATE_CONFIG[currentWs.state || "active"].icon}
|
|
948
|
+
</span>`
|
|
949
|
+
: null
|
|
658
950
|
}
|
|
659
951
|
${currentWs?.name || currentId || "Select Workspace"}
|
|
660
952
|
<//>
|
|
@@ -664,13 +956,33 @@ export function WorkspaceSwitcher() {
|
|
|
664
956
|
open=${Boolean(menuAnchor)}
|
|
665
957
|
onClose=${handleMenuClose}
|
|
666
958
|
>
|
|
667
|
-
${wsList.map((ws) =>
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
959
|
+
${wsList.map((ws) => {
|
|
960
|
+
const st = STATE_CONFIG[ws.state || "active"];
|
|
961
|
+
return html`
|
|
962
|
+
<${MenuItem}
|
|
963
|
+
key=${ws.id}
|
|
964
|
+
selected=${ws.id === currentId}
|
|
965
|
+
onClick=${() => handleSelect(ws.id)}
|
|
966
|
+
sx=${{
|
|
967
|
+
opacity: ws.state === "disabled" ? 0.5 : 1,
|
|
968
|
+
gap: 1,
|
|
969
|
+
}}
|
|
970
|
+
>
|
|
971
|
+
<span style="color: ${st.color}; font-size: 12px; width: 16px; text-align: center;">${st.icon}</span>
|
|
972
|
+
${ws.name || ws.id}
|
|
973
|
+
${ws.state === "paused" && html`
|
|
974
|
+
<${Chip} label="paused" size="small"
|
|
975
|
+
sx=${{ ml: 0.5, height: 16, fontSize: "9px", bgcolor: "#f59e0b22", color: "#f59e0b" }}
|
|
976
|
+
/>
|
|
977
|
+
`}
|
|
978
|
+
${ws.state === "disabled" && html`
|
|
979
|
+
<${Chip} label="off" size="small"
|
|
980
|
+
sx=${{ ml: 0.5, height: 16, fontSize: "9px", bgcolor: "#71717a22", color: "#71717a" }}
|
|
981
|
+
/>
|
|
982
|
+
`}
|
|
983
|
+
<//>
|
|
984
|
+
`;
|
|
985
|
+
})}
|
|
674
986
|
<${Divider} />
|
|
675
987
|
<${MenuItem} onClick=${() => handleSelect("__manage__")}>
|
|
676
988
|
Manage Workspaces
|