@@ -481,10 +481,16 @@ const buildDockerRuntimeInfoRenderEntry = (name, entry = {}, previousEntry = nul
481481 const previousInfo = previous ?. info && typeof previous . info === 'object' ? previous . info : { } ;
482482 const previousState = previousInfo . State && typeof previousInfo . State === 'object' ? previousInfo . State : { } ;
483483 const previousConfig = previousInfo . Config && typeof previousInfo . Config === 'object' ? previousInfo . Config : { } ;
484+ const sourceInfo = source ?. info && typeof source . info === 'object' ? source . info : { } ;
485+ const sourceState = sourceInfo . State && typeof sourceInfo . State === 'object' ? sourceInfo . State : { } ;
484486 const stateKind = String ( source . state || '' ) . trim ( ) . toLowerCase ( ) ;
485487 const running = source . running === true || stateKind === 'running' ;
486488 const paused = source . paused === true || stateKind === 'paused' ;
487489 const manager = String ( source . manager || previousState . manager || '' ) . trim ( ) ;
490+ const labelWebUi = String ( labels [ 'net.unraid.docker.webui' ] || '' ) . trim ( ) ;
491+ const labelTsWebUi = String ( labels [ 'net.unraid.docker.tailscale.webui' ] || '' ) . trim ( ) ;
492+ const resolvedWebUi = resolvePreferredWebuiValue ( sourceState . WebUi , source . WebUi , source . webui , previousState . WebUi , labelWebUi ) ;
493+ const resolvedTsWebUi = resolvePreferredWebuiValue ( sourceState . TSWebUi , source . TSWebUi , previousState . TSWebUi , labelTsWebUi ) ;
488494 const nextEntry = previous ? { ...previous } : { } ;
489495 nextEntry . shortId = String ( source . id || previous ?. shortId || '' ) . trim ( ) ;
490496 nextEntry . shortImageId = String ( source . shortImageId || previous ?. shortImageId || '' ) . trim ( ) ;
@@ -507,8 +513,8 @@ const buildDockerRuntimeInfoRenderEntry = (name, entry = {}, previousEntry = nul
507513 Autostart : source . autostart === true ,
508514 Updated : ( typeof previousState . Updated === 'boolean' ) ? previousState . Updated : null ,
509515 manager,
510- WebUi : String ( labels [ 'net.unraid.docker.webui' ] || previousState . WebUi || '' ) . trim ( ) ,
511- TSWebUi : String ( labels [ 'net.unraid.docker.tailscale.webui' ] || previousState . TSWebUi || '' ) . trim ( )
516+ WebUi : resolvedWebUi ,
517+ TSWebUi : resolvedTsWebUi
512518 } ,
513519 Ports : Array . isArray ( previousInfo . Ports ) ? previousInfo . Ports : [ ] ,
514520 template : previousInfo . template || null
@@ -547,9 +553,21 @@ const sanitizeImageSrc = typeof utils.sanitizeImageSrc === 'function'
547553 } ) ;
548554const WEBUI_LINK_REL = 'noopener noreferrer' ;
549555const WEBUI_OPEN_REL = 'noopener' ;
556+ const WEBUI_TEMPLATE_TOKEN_REGEX = / \[ (?: I P | P O R T : [ ^ \] ] + | H O S T N A M E | M A G I C D N S | N O S E R V E ) \] / i;
557+ const hasUnresolvedWebuiTemplateTokens = ( value ) => WEBUI_TEMPLATE_TOKEN_REGEX . test ( String ( value || '' ) . trim ( ) ) ;
558+ const resolvePreferredWebuiValue = ( ...candidates ) => {
559+ for ( const candidate of candidates ) {
560+ const raw = String ( candidate || '' ) . trim ( ) ;
561+ if ( ! raw || / ^ j a v a s c r i p t : / i. test ( raw ) || hasUnresolvedWebuiTemplateTokens ( raw ) ) {
562+ continue ;
563+ }
564+ return raw ;
565+ }
566+ return '' ;
567+ } ;
550568const getSafeWebuiUrl = ( value ) => {
551569 const raw = String ( value || '' ) . trim ( ) ;
552- return raw && ! / ^ j a v a s c r i p t : / i. test ( raw ) ? raw : '' ;
570+ return raw && ! / ^ j a v a s c r i p t : / i. test ( raw ) && ! hasUnresolvedWebuiTemplateTokens ( raw ) ? raw : '' ;
553571} ;
554572const openWebuiInNewTab = ( url ) => {
555573 const safeUrl = getSafeWebuiUrl ( url ) ;
@@ -2083,7 +2101,7 @@ const buildRuntimeContainerEntry = (name, sourceMeta = null) => {
20832101 id : source . id || String ( runtime ?. shortId || '' ) . trim ( ) ,
20842102 name : String ( runtime ?. info ?. Name || source . name || key ) . trim ( ) || key ,
20852103 icon : source . icon || runtime ?. Labels ?. [ 'net.unraid.docker.icon' ] || '/plugins/dynamix.docker.manager/images/question.png' ,
2086- webui : String ( source . webui || runtimeState . WebUi || runtimeState . TSWebUi || '' ) . trim ( ) ,
2104+ webui : resolvePreferredWebuiValue ( runtimeState . WebUi , runtimeState . TSWebUi , source . webui ) ,
20872105 shell : source . shell || runtime ?. info ?. Shell || '/bin/sh' ,
20882106 pause : hasRuntimePause ? ( runtimeState . Paused === true ) : ( source . pause === true ) ,
20892107 state : hasRuntimeState ? ( runtimeState . Running === true ) : ( source . state === true ) ,
@@ -2101,13 +2119,10 @@ const getFolderRuntimeContainers = (folder) => {
21012119 return { } ;
21022120 }
21032121 const runtime = folder . runtimeContainers ;
2104- if ( runtime && typeof runtime === 'object' && ! Array . isArray ( runtime ) && Object . keys ( runtime ) . length > 0 ) {
2105- return runtime ;
2106- }
21072122 const containers = folder . containers ;
21082123 const names = readFolderContainerNames ( containers ) ;
21092124 if ( ! names . length ) {
2110- return { } ;
2125+ return ( runtime && typeof runtime === 'object' && ! Array . isArray ( runtime ) ) ? runtime : { } ;
21112126 }
21122127 const sourceMap = ( containers && typeof containers === 'object' && ! Array . isArray ( containers ) ) ? containers : { } ;
21132128 const collected = { } ;
@@ -2117,6 +2132,7 @@ const getFolderRuntimeContainers = (folder) => {
21172132 }
21182133 collected [ name ] = buildRuntimeContainerEntry ( name , sourceMap [ name ] ) ;
21192134 }
2135+ folder . runtimeContainers = collected ;
21202136 return collected ;
21212137} ;
21222138
@@ -2607,8 +2623,8 @@ const syncDockerVisibleFoldersFromRuntimeCache = () => {
26072623 const runtimeContainers = folderHasChildren ( id )
26082624 ? buildRuntimeContainerMapForFolder ( id , true )
26092625 : getFolderRuntimeContainers ( folder ) ;
2626+ folder . runtimeContainers = runtimeContainers ;
26102627 if ( folderHasChildren ( id ) ) {
2611- folder . runtimeContainers = runtimeContainers ;
26122628 syncParentFolderVisualState ( id , folder ?. status ?. expanded === true ) ;
26132629 }
26142630 updateFolderRowStatusFromContainers ( id , folder , runtimeContainers ) ;
@@ -2618,6 +2634,18 @@ const syncDockerVisibleFoldersFromRuntimeCache = () => {
26182634 applyDockerFocusedFolderState ( ) ;
26192635} ;
26202636
2637+ const buildDockerWebuiSignature = ( source ) => {
2638+ const map = source && typeof source === 'object' ? source : { } ;
2639+ const names = Object . keys ( map ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
2640+ if ( ! names . length ) {
2641+ return '' ;
2642+ }
2643+ return names . map ( ( name ) => {
2644+ const state = map [ name ] ?. info ?. State && typeof map [ name ] . info . State === 'object' ? map [ name ] . info . State : { } ;
2645+ return `${ name } :${ getSafeWebuiUrl ( state . WebUi ) } |${ getSafeWebuiUrl ( state . TSWebUi ) } ` ;
2646+ } ) . join ( '|' ) ;
2647+ } ;
2648+
26212649const queueDockerDeferredRuntimeInfoHydration = ( generation , stateSignature , fullInfoPromise = null ) => {
26222650 const requestPromise = fullInfoPromise && typeof fullInfoPromise . then === 'function'
26232651 ? fullInfoPromise
@@ -2640,12 +2668,18 @@ const queueDockerDeferredRuntimeInfoHydration = (generation, stateSignature, ful
26402668 if ( ! parsed || Object . keys ( parsed ) . length <= 0 ) {
26412669 return ;
26422670 }
2671+ const previousWebuiSignature = buildDockerWebuiSignature ( dockerRuntimeInfoByName ) ;
26432672 dockerRuntimeInfoByName = normalizeDockerRuntimeInfoMap ( parsed , dockerRuntimeInfoByName ) ;
2673+ const nextWebuiSignature = buildDockerWebuiSignature ( dockerRuntimeInfoByName ) ;
26442674 if ( stateSignature ) {
26452675 lastLiveRefreshStateSignature = stateSignature ;
26462676 }
26472677 markDockerFatalBannerStep ( 'Docker runtime details hydrated' ) ;
26482678 recordDockerFatalBannerAction ( 'Docker runtime details hydrated' ) ;
2679+ if ( previousWebuiSignature !== nextWebuiSignature ) {
2680+ queueLoadlistRefresh ( ) ;
2681+ return ;
2682+ }
26492683 syncDockerVisibleFoldersFromRuntimeCache ( ) ;
26502684 } )
26512685 . catch ( ( ) => { } ) ;
0 commit comments