dashboard(swarm): themed stop confirm modal with graceful checkbox
Replace the browser-native confirm() on the agent ST0P actions (per-agent menu + bulk) with an in-theme modal (themedConfirm in common.js), carrying a 'stop gracefully' checkbox. Checked sends POST /kill/<name>?graceful=1 (the quiesce path); unchecked is today's immediate hard stop, unchanged. The modal also covers the other destructive menu actions (restart / rebuild / destroy / purge) so they no longer fall back to the OS dialog.
This commit is contained in:
parent
9a6306540b
commit
03b10d22cd
3 changed files with 153 additions and 8 deletions
|
|
@ -14,7 +14,7 @@ import { marked } from 'marked';
|
|||
import {
|
||||
$, el, esc, form,
|
||||
fmtAgeSecs,
|
||||
Panel, NOTIF,
|
||||
Panel, NOTIF, themedConfirm,
|
||||
makePathLink, appendText, appendLinkified,
|
||||
openStream, renderServerWarnings, bindAsyncForms,
|
||||
} from './common.js';
|
||||
|
|
@ -281,8 +281,8 @@ window.marked = marked;
|
|||
}, true);
|
||||
|
||||
// Single-agent POST helper shared by all menu items.
|
||||
async function agentMenuPost(actionPath, name, body) {
|
||||
const url = actionPath + encodeURIComponent(name);
|
||||
async function agentMenuPost(actionPath, name, body, graceful) {
|
||||
const url = actionPath + encodeURIComponent(name) + (graceful ? '?graceful=1' : '');
|
||||
try {
|
||||
const resp = await fetch(url, {
|
||||
method: 'POST',
|
||||
|
|
@ -323,8 +323,20 @@ window.marked = marked;
|
|||
}, label);
|
||||
item.addEventListener('click', async () => {
|
||||
closeAllAgentMenus();
|
||||
if (opts.confirm && !confirm(opts.confirm)) return;
|
||||
await agentMenuPost(opts.action, c.name, opts.body || null);
|
||||
let graceful = false;
|
||||
if (opts.confirm) {
|
||||
const r = await themedConfirm({
|
||||
message: opts.confirm,
|
||||
danger: true,
|
||||
confirmLabel: opts.confirmLabel || 'confirm',
|
||||
checkboxes: opts.graceful
|
||||
? [{ name: 'graceful', label: 'stop gracefully — let the agent finish its turn and flush state before the container stops' }]
|
||||
: [],
|
||||
});
|
||||
if (!r) return;
|
||||
graceful = !!r.graceful;
|
||||
}
|
||||
await agentMenuPost(opts.action, c.name, opts.body || null, graceful);
|
||||
});
|
||||
li.append(item);
|
||||
return li;
|
||||
|
|
@ -352,7 +364,7 @@ window.marked = marked;
|
|||
if (c.running) {
|
||||
dropdown.append(
|
||||
menuItem('↺ R3ST4RT', { action: '/restart/', confirm: `restart ${c.name}?` }),
|
||||
menuItem('■ ST0P', { action: '/kill/', confirm: `stop ${c.name}?` }),
|
||||
menuItem('■ ST0P', { action: '/kill/', confirm: `stop ${c.name}?`, confirmLabel: '■ stop', graceful: true }),
|
||||
);
|
||||
} else {
|
||||
dropdown.append(
|
||||
|
|
@ -982,6 +994,8 @@ window.marked = marked;
|
|||
addBulkButton(actions, 'btn-stop', '■ ST0P', allRunning, selected, {
|
||||
action: '/kill/',
|
||||
confirm: (names) => `stop ${names.length} agent${names.length === 1 ? '' : 's'} (${names.join(', ')})?`,
|
||||
confirmLabel: '■ stop',
|
||||
graceful: true,
|
||||
disabledTitle: why('■ ST0P', stoppedNames.map((n) => `\`${n}\` is already stopped`)),
|
||||
});
|
||||
addBulkButton(actions, 'btn-start', '▶ ST4RT', allStopped, selected, {
|
||||
|
|
@ -1145,7 +1159,19 @@ window.marked = marked;
|
|||
btn.addEventListener('click', async () => {
|
||||
if (btn.disabled) return;
|
||||
const msg = opts.confirm(names);
|
||||
if (msg && !confirm(msg)) return;
|
||||
let graceful = false;
|
||||
if (msg) {
|
||||
const r = await themedConfirm({
|
||||
message: msg,
|
||||
danger: true,
|
||||
confirmLabel: opts.confirmLabel || 'confirm',
|
||||
checkboxes: opts.graceful
|
||||
? [{ name: 'graceful', label: 'stop gracefully — let each agent finish its turn and flush state before the container stops' }]
|
||||
: [],
|
||||
});
|
||||
if (!r) return;
|
||||
graceful = !!r.graceful;
|
||||
}
|
||||
btn.disabled = true;
|
||||
const original = btn.innerHTML;
|
||||
btn.innerHTML = '<span class="spinner">◐</span> ' + label;
|
||||
|
|
@ -1168,7 +1194,7 @@ window.marked = marked;
|
|||
: new URLSearchParams(opts.body || {});
|
||||
const url = opts.perAgentBodyFor
|
||||
? opts.action
|
||||
: opts.action + encodeURIComponent(name);
|
||||
: opts.action + encodeURIComponent(name) + (graceful ? '?graceful=1' : '');
|
||||
try {
|
||||
const resp = await fetch(url, {
|
||||
method: 'POST',
|
||||
|
|
|
|||
Loading…
Reference in a new issue