2026-11-09 06:53:55 +00:00
const CHOICE_ICONS = { post : "bi-envelope" , call : "bi-telephone" , newsletter : "bi-newspaper" , broadcast : "bi-broadcast" , "pan-pan" : "bi-exclamation-triangle" , security : "bi-shield-exclamation" , mayday : "bi-life-preserver" };
const choiceAsk = { to : new Set (), dom : null , names : "" };
async function requestChoices ( cls ) {
const direct = TO_ONE . some (([ id ]) => id === cls );
const to = direct ? [... choiceAsk . to ]. sort (( a , b ) => a . localeCompare ( b )) : [ "*" ];
if (( direct && ! to . length ) || ( cls === "mayday" && ! confirmMayday ())) {
return ;
}
const label = `Choice request ( ${ CLASSES [ cls ] } ) to ${ direct ? to . join ( ", " ) : "all" } ` ;
const signed = await signedProof ( "/api/choices/sign" , { class : cls , to }, label );
if ( ! signed ) {
return ;
}
const result = await sendJson ( "/api/choices" , { class : cls , to , proof : signed . proof });
if ( ! result . ok || ! result . data ) {
toast ( ` ${ label } failed` , fail ( result ), false );
return ;
}
toast ( ` ${ label } : ${ result . data . ok ? "sent" : "stored, sending failed" } ` , [... result . data . ids . map (( id ) => `kvb-orchestrator msg ${ id } ` ), ... runLines ( result . data )], result . data . ok );
await load ();
}
2026-11-09 22:22:46 +00:00
function choiceMenu () {
2026-11-09 06:53:55 +00:00
const picks = h ( "div" , { class : "choice-agents" , role : "group" , "aria-label" : "Post or call to" });
const count = counter ( 0 );
const button = ([ id , label ]) => h ( "button" , { type : "button" , class : "btn small" , "data-channel" : id , ... named ( label , ` ${ label } : ask for every open decision as a choice, signed with your passkey. ${ CHANNEL_HINTS [ id ] } ` ), on : { click : () => requestChoices ( id )}},
icon ( CHOICE_ICONS [ id ]), label );
const direct = TO_ONE . map ( button );
choiceAsk . dom = { picks , count , direct };
choiceAsk . names = "" ;
2026-11-09 22:22:46 +00:00
return h ( "details" , { class : "menu choice-request" , id : "choice-request" },
h ( "summary" , named ( "Request choices" , "Request choices: every open decision comes back as a choice, signed with your passkey" ), icon ( "bi-ui-checks" ), "Request choices" , caret ()),
h ( "div" , { class : "menu-box end" , role : "group" , "aria-label" : "Choice requests" },
h ( "h3" , {}, "Post or call to" , count ), picks , h ( "div" , { class : "choice-buttons" }, direct ),
h ( "h3" , {}, "To all" ), h ( "div" , { class : "choice-buttons" }, TO_ALL . map ( button ))));
2026-11-09 06:53:55 +00:00
}
function syncChoices () {
choiceAsk . dom . count . textContent = String ( choiceAsk . to . size );
for ( const button of choiceAsk . dom . direct ) {
button . disabled = ! choiceAsk . to . size ;
}
}
function pickChoice ( name , on ) {
if ( on ) {
choiceAsk . to . add ( name );
} else {
choiceAsk . to . delete ( name );
}
syncChoices ();
}
function drawChoices () {
const dom = choiceAsk . dom ;
if ( ! dom || ! dom . picks . isConnected ) {
return ;
}
const names = agentNames ();
for ( const name of [... choiceAsk . to ]) {
if ( ! names . includes ( name )) {
choiceAsk . to . delete ( name );
}
}
const sig = JSON . stringify ( names );
if ( sig !== choiceAsk . names ) {
choiceAsk . names = sig ;
dom . picks . replaceChildren (...( names . length ? names . map (( name ) => h ( "label" , { class : "check" },
h ( "input" , { type : "checkbox" , value : name , checked : choiceAsk . to . has ( name ), on : { change : ( event ) => pickChoice ( name , event . target . checked )}}), name )) : [ h ( "p" , { class : "muted" }, "No agent known yet." )]));
}
syncChoices ();
}