2026-11-06 22:20:06 +00:00
async function act ( action , label , payload ) {
ui . busy = true ;
const result = await sendJson ( `/api/board/ ${ action } ` , payload );
ui . busy = false ;
if ( result . data && result . data . runs ) {
2026-11-08 17:26:51 +00:00
toast ( ` ${ label } : ${ result . data . ok ? "done" : "failed" } ` , runLines ( result . data ), result . data . ok );
2026-11-06 22:20:06 +00:00
} else {
2026-11-08 17:26:51 +00:00
toast ( ` ${ label } : failed` , [ ` ${ result . status } ${ result . text } ` . trim ()], false );
2026-11-06 22:20:06 +00:00
}
await load ();
return result . ok && result . data && result . data . ok ;
}
2026-11-07 19:59:47 +00:00
async function confirmAct ( action ) {
2026-11-06 22:20:06 +00:00
const [, label , question ] = ACTIONS . find (([ id ]) => id === action );
2026-11-07 19:59:47 +00:00
if ( ! window . confirm ( question )) {
return ;
}
const signed = await signedProof ( "/api/board/sign" , { action }, label );
if ( signed ) {
await act ( action , label , { proof : signed . proof });
2026-11-06 22:20:06 +00:00
}
}
function choice ( name , options , picked ) {
2026-11-08 17:26:51 +00:00
return h ( "div" , { class : "classes" , role : "radiogroup" , "aria-label" : "Class" }, options . map (([ id , label ]) => h ( "label" , { class : "check" },
2026-11-06 22:20:06 +00:00
h ( "input" , { type : "radio" , name , value : id , checked : id === picked }), label )));
}
const picked = ( group ) => group . querySelector ( "input:checked" ). value ;
function toAll () {
2026-11-08 17:26:51 +00:00
const text = h ( "textarea" , { "aria-label" : "Radio to all" , placeholder : "Message to all agents …" , on : { input : ( event ) => {
2026-11-06 22:20:06 +00:00
ui . draft = event . target . value ;
}}});
text . value = ui . draft ;
const classes = choice ( "to-all" , TO_ALL , "newsletter" );
const send = async () => {
const cls = picked ( classes );
2026-11-07 19:59:47 +00:00
if ( ! ui . draft . trim () || ( cls === "mayday" && ! confirmMayday ())) {
2026-11-06 22:20:06 +00:00
return ;
}
2026-11-07 19:59:47 +00:00
if ( await radioSend ({ to : "*" , text : ui . draft , upload : "" , cls , voice : "" })) {
2026-11-06 22:20:06 +00:00
ui . draft = "" ;
text . value = "" ;
}
};
2026-11-08 17:26:51 +00:00
return h ( "details" , { class : "menu" , id : "to-all" }, h ( "summary" , named ( "Radio to all" ), icon ( "bi-broadcast-pin" ), caret ()), h ( "div" , { class : "menu-box" },
text , classes , h ( "p" , { class : "muted" }, "Newsletter arrives as post at the end of the turn; Broadcast, Pan-Pan, Security and Mayday at once. Your passkey signs every message." ),
h ( "div" , { class : "actions" }, h ( "span" , { class : "spacer" }), iconBtn ( "bi-send" , "Send" , "btn soft" , send , "Send to all" ))));
2026-11-06 22:20:06 +00:00
}
function openToAll () {
go ( "agents" , null );
window . setTimeout (() => {
const menu = document . getElementById ( "to-all" );
if ( menu ) {
menu . open = true ;
menu . querySelector ( "textarea" ). focus ();
}
}, 50 );
}
function agentsToolbar () {
2026-11-08 17:26:51 +00:00
const clear = iconBtn ( "bi-life-preserver" , "Cancel mayday" , "btn small danger" , () => confirmAct ( "clear-mayday" ));
2026-11-06 22:20:06 +00:00
const mode = h ( "span" , {});
2026-11-08 17:26:51 +00:00
const night = h ( "details" , { class : "menu" }, h ( "summary" , named ( "Day/Night" ), icon ( "bi-moon-stars" ), caret ()), h ( "div" , { class : "menu-box" }, mode ,
2026-11-08 23:18:18 +00:00
h ( "p" , { class : "muted" }, "Drain: agents finish their task and take nothing new. Suspend: they park their work and idle. Resume: all continue from the handover. Your passkey signs each." ),
2026-11-06 22:20:06 +00:00
h ( "div" , { class : "actions" }, ACTIONS . slice ( 1 ). map (([ id , label ]) => btn ( label , "btn small" , () => confirmAct ( id ))))));
2026-11-08 17:26:51 +00:00
return { bar : h ( "div" , { class : "toolbar" , role : "toolbar" , "aria-label" : "Controls" }, toAll (), clear , iconBtn ( "bi-sort-down" , "Priority" , "btn small" , openAgentOrder , "Priority: agent order in the hierarchy" ), night ), clear , mode };
2026-11-06 22:20:06 +00:00
}
function drawToolbar () {
if ( ! graphDom || ! graphDom . toolbar ) {
return ;
}
const [ mode , tone ] = NIGHT [ state . night ] || [ state . night , "neutral" ];
graphDom . toolbar . clear . disabled = ! state . silence ;
graphDom . toolbar . mode . replaceChildren ( pill ( mode , tone ));
}