2026-11-06 22:20:06 +00:00
const navButtons = new Map ();
function tabButton ( id , label , hint ) {
const badge = Object . hasOwn ( state . badges , id ) ? h ( "span" , { class : "badge" , hidden : true }) : null ;
2026-11-07 15:03:14 +00:00
const button = h ( "button" , { type : "button" , id : `tab- ${ id } ` , class : "icon-only" , "aria-controls" : "main" , ... named ( label , hint ? ` ${ label } ( ${ hint } )` : label ), on : { click : () => go ( id , null )}}, icon ( Nav . ICONS [ id ]), badge );
2026-11-06 22:20:06 +00:00
navButtons . set ( id , { button , badge });
return button ;
}
document . getElementById ( "tabs" ). append (... Nav . TABS . map (([ id , label ], index ) => h ( "div" , { class : "tab-group" },
2026-11-08 17:26:51 +00:00
tabButton ( id , label , `key ${ index + 1 } ` ),
2026-11-06 22:20:06 +00:00
Nav . SUBS [ id ] ? h ( "div" , { class : "subtabs" }, Nav . SUBS [ id ]. map (([ sub , name ]) => tabButton ( sub , name , "" ))) : null )));
function measure () {
document . documentElement . style . setProperty ( "--head" , ` ${ document . getElementById ( "head" ). offsetHeight } px` );
}
function go ( screen , view ) {
const hash = Nav . hashOf ( screen , view || ui . views [ screen ] || null );
if ( window . location . hash === hash ) {
apply ();
} else {
window . location . hash = hash ;
}
}
function apply () {
const route = Nav . route ( window . location . hash );
if ( route . redirect ) {
window . history . replaceState ( null , "" , route . redirect );
}
2026-11-10 21:10:20 +00:00
if ( route . spot ) {
openSpot ( true );
}
2026-11-06 22:20:06 +00:00
ui . screen = route . screen ;
if ( route . view ) {
ui . views [ route . screen ] = route . view ;
}
if ( document . startViewTransition && drawn . screen && ! calm . matches ) {
2026-11-12 07:53:29 +00:00
document . startViewTransition (() => render ( true )). ready . catch (( error ) => {
if ( error . name !== "AbortError" ) {
throw error ;
}
});
2026-11-06 22:20:06 +00:00
} else {
render ( true );
}
}
function signature () {
if ( ui . screen === "agents" ) {
2026-11-07 15:03:14 +00:00
return JSON . stringify ([ ui . views . agents ]);
2026-11-06 22:20:06 +00:00
}
2026-11-07 23:43:26 +00:00
return JSON . stringify ( ui . screen === "artifacts" || ui . screen === "tasks" ? [ ui . views [ ui . screen ]] : ui . screen === "rules" ? state . rules : []);
2026-11-06 22:20:06 +00:00
}
function screenOf () {
2026-11-10 21:10:20 +00:00
return { stream : streamScreen , inbox : tasksScreen , rules : rulesScreen , agents : agentsScreen , artifacts : artifactsScreen , tasks : tasksScreen }[ ui . screen ]();
2026-11-06 22:20:06 +00:00
}
function render ( force ) {
for ( const [ id , { button }] of navButtons ) {
button . setAttribute ( "aria-current" , ui . screen === id ? "page" : "false" );
}
const sig = signature ();
if ( force || drawn . screen !== ui . screen || drawn . sig !== sig ) {
graphDom = null ;
stream . dom = null ;
tree . dom = null ;
inboxDom = null ;
inboxSig = "" ;
2026-11-07 23:43:26 +00:00
taskView . dom = null ;
2026-11-06 22:20:06 +00:00
const main = document . getElementById ( "main" );
main . setAttribute ( "aria-labelledby" , `tab- ${ ui . screen } ` );
main . replaceChildren ( screenOf ());
2026-11-09 06:53:55 +00:00
document . body . classList . toggle ( "fit" , [ "tasks" , "inbox" ]. includes ( ui . screen ) || ([ "agents" , "artifacts" ]. includes ( ui . screen ) && ! [ "tree" , "hierarchy" ]. includes ( ui . views [ ui . screen ])));
2026-11-06 22:20:06 +00:00
drawn . screen = ui . screen ;
drawn . sig = sig ;
2026-11-07 15:03:14 +00:00
if ( g3 . host && ! g3 . host . isConnected ) {
dispose3d ();
}
2026-11-06 22:20:06 +00:00
}
stage ();
2026-11-07 15:03:14 +00:00
drawSpawn ();
2026-11-07 17:46:39 +00:00
drawHierarchy ( force );
2026-11-06 22:20:06 +00:00
drawToolbar ();
drawInbox ();
2026-11-07 19:59:47 +00:00
drawRadio ();
2026-11-07 23:43:26 +00:00
drawTasks ();
2026-11-09 06:53:55 +00:00
drawChoices ();
2026-11-06 22:20:06 +00:00
drawStatus ();
tick ();
2026-11-08 21:18:55 +00:00
placePopup ();
2026-11-06 22:20:06 +00:00
}
function drawStatus () {
for ( const [ id , count ] of Object . entries ( state . badges )) {
const { badge } = navButtons . get ( id );
badge . textContent = String ( count );
badge . hidden = count === 0 ;
badge . className = `badge ${ id === "inbox" && count ? " urgent" : "" } ` ;
}
const banner = document . getElementById ( "mayday" );
const silence = state . silence ;
banner . hidden = ! silence ;
2026-11-11 07:45:42 +00:00
banner . replaceChildren (...( silence ? [ h ( "p" , { class : "text" }, `MAYDAY · ${ dash ( silence . by ) } : ${ silence . text } ` ), iconBtn ( "bi-x-octagon" , "cancel" , "btn small" , () => confirmAct ( "clear-mayday" , []), "cancel: end the mayday (radio.py clear)" )] : []));
2026-11-06 22:20:06 +00:00
document . title = ` ${ state . badges . inbox ? `( ${ state . badges . inbox } ) ` : "" } Agent Orchestrator` ;
window . setTimeout ( measure , 0 );
}
function toggleLeitstand () {
const on = ! document . body . classList . contains ( "leitstand" );
document . body . classList . toggle ( "leitstand" , on );
if ( on && document . documentElement . requestFullscreen && ! document . fullscreenElement ) {
document . documentElement . requestFullscreen (). catch (() => null );
}
if ( ! on && document . fullscreenElement ) {
document . exitFullscreen (). catch (() => null );
}
window . setTimeout ( measure , 50 );
}