@@ -25,6 +25,7 @@ import type { ThemeModeChangeDetail } from "../components/theme-mode-toggle.ts";
2525import { t } from "../i18n/index.ts" ;
2626import { copyToClipboard } from "../lib/clipboard.ts" ;
2727import { isGatewayMethodAdvertised } from "../lib/gateway-methods.ts" ;
28+ import { isWorkboardEnabledInConfigSnapshot } from "../lib/plugin-activation.ts" ;
2829import { searchForSession } from "../lib/sessions/index.ts" ;
2930import { resolveAgentIdFromSessionKey } from "../lib/sessions/session-key.ts" ;
3031import { normalizeLowercaseStringOrEmpty , normalizeOptionalString } from "../lib/string-coerce.ts" ;
@@ -45,6 +46,10 @@ type ShellRouteState = {
4546 location ?: RouteLocation ;
4647} ;
4748
49+ // Stable references so the sidebar's enabledRouteIds property does not churn
50+ // on every shell render.
51+ const ROUTE_IDS_WITHOUT_WORKBOARD = APP_ROUTE_IDS . filter ( ( routeId ) => routeId !== "workboard" ) ;
52+
4853function selectShellRouteState ( routerState : RouterState < RouteId > ) : ShellRouteState {
4954 const match = selectRenderedRouteMatch ( routerState . matches [ 0 ] , routerState . pendingMatches [ 0 ] ) ;
5055 return match
@@ -360,6 +365,7 @@ class OpenClawShell extends LitElement {
360365 private stopNavigationSubscription : ( ( ) => void ) | undefined ;
361366 private stopRouteSubscription : ( ( ) => void ) | undefined ;
362367 private stopOverlaySubscription : ( ( ) => void ) | undefined ;
368+ private stopRuntimeConfigSubscription : ( ( ) => void ) | undefined ;
363369 private stopThemeSubscription : ( ( ) => void ) | undefined ;
364370
365371 override createRenderRoot ( ) {
@@ -388,6 +394,7 @@ class OpenClawShell extends LitElement {
388394 this . stopNavigationSubscription ||
389395 this . stopRouteSubscription ||
390396 this . stopOverlaySubscription ||
397+ this . stopRuntimeConfigSubscription ||
391398 this . stopThemeSubscription
392399 ) {
393400 return ;
@@ -400,12 +407,14 @@ class OpenClawShell extends LitElement {
400407 this . updateGatewayStatus ( context . gateway . snapshot ) ;
401408 this . updateTerminalSurface ( context . gateway . snapshot ) ;
402409 this . updateAgentLabel ( ) ;
410+ this . ensureRuntimeConfig ( context . gateway . snapshot ) ;
403411 this . stopGatewaySubscription = context . gateway . subscribe ( ( snapshot ) => {
404412 this . updateGatewaySessionKey ( snapshot ) ;
405413 this . updateGatewayStatus ( snapshot ) ;
406414 this . updateTerminalSurface ( snapshot ) ;
407415 this . updateAgentLabel ( ) ;
408416 this . ensureAgentsList ( snapshot ) ;
417+ this . ensureRuntimeConfig ( snapshot ) ;
409418 } ) ;
410419 this . stopConfigSubscription = context . config . subscribe ( ( ) => {
411420 this . updateTerminalSurface ( context . gateway . snapshot ) ;
@@ -426,6 +435,10 @@ class OpenClawShell extends LitElement {
426435 this . stopOverlaySubscription = context . overlays . subscribe ( ( snapshot ) => {
427436 this . overlaySnapshot = snapshot ;
428437 } ) ;
438+ this . stopRuntimeConfigSubscription = context . runtimeConfig . subscribe ( ( ) => {
439+ // Route enablement (e.g. Workboard) derives from the config snapshot.
440+ this . requestUpdate ( ) ;
441+ } ) ;
429442 }
430443
431444 override disconnectedCallback ( ) {
@@ -442,6 +455,8 @@ class OpenClawShell extends LitElement {
442455 this . stopRouteSubscription = undefined ;
443456 this . stopOverlaySubscription ?.( ) ;
444457 this . stopOverlaySubscription = undefined ;
458+ this . stopRuntimeConfigSubscription ?.( ) ;
459+ this . stopRuntimeConfigSubscription = undefined ;
445460 this . stopThemeSubscription ?.( ) ;
446461 this . stopThemeSubscription = undefined ;
447462 this . agentsListClient = null ;
@@ -565,6 +580,23 @@ class OpenClawShell extends LitElement {
565580 ) ;
566581 }
567582
583+ private ensureRuntimeConfig ( snapshot : {
584+ client : GatewayBrowserClient | null ;
585+ connected : boolean ;
586+ } ) {
587+ // The sidebar hides config-gated routes (Workboard), so the snapshot must
588+ // load eagerly instead of waiting for a page that happens to fetch it.
589+ if ( snapshot . connected && snapshot . client ) {
590+ void this . context ?. runtimeConfig . ensureLoaded ( ) ;
591+ }
592+ }
593+
594+ private enabledRouteIds ( ) : readonly RouteId [ ] {
595+ return isWorkboardEnabledInConfigSnapshot ( this . context ?. runtimeConfig . state . configSnapshot )
596+ ? APP_ROUTE_IDS
597+ : ROUTE_IDS_WITHOUT_WORKBOARD ;
598+ }
599+
568600 private ensureAgentsList ( snapshot : { client : GatewayBrowserClient | null ; connected : boolean } ) {
569601 if ( ! snapshot . connected || ! snapshot . client ) {
570602 this . agentsListClient = null ;
@@ -692,7 +724,7 @@ class OpenClawShell extends LitElement {
692724 .basePath =${ context . basePath }
693725 .activeRouteId =${ activeRoute }
694726 .activePluginTabId=${ activePluginTabId }
695- .enabledRouteIds=${ APP_ROUTE_IDS }
727+ .enabledRouteIds=${ this . enabledRouteIds ( ) }
696728 .sessionKey=${ this . activeSessionKey }
697729 .collapsed=${ navCollapsed }
698730 .connected=${ this . gatewayConnected }
0 commit comments