@@ -2956,6 +2956,213 @@ const syncDockerVisibleFoldersFromRuntimeCache = () => {
29562956} ;
29572957
29582958const readDockerListViewMode = ( ) => ( $ . cookie ( 'docker_listview_mode' ) == 'advanced' ? 'advanced' : 'basic' ) ;
2959+ const DOCKER_RUNTIME_PRIVACY_TOGGLE_ID = 'fvplus-docker-runtime-privacy-toggle' ;
2960+ const DOCKER_RUNTIME_PRIVACY_TOGGLE_SHELL_ID = 'fvplus-docker-runtime-privacy-shell' ;
2961+ const DOCKER_RUNTIME_PRIVACY_TOGGLE_FALLBACK_HOST_ID = 'fvplus-docker-runtime-toolbar-controls' ;
2962+ let dockerRuntimePrivacyToggleMountQueued = false ;
2963+
2964+ const readDockerRuntimePrivacyMode = ( ) => utils . normalizePrefs ( folderTypePrefs || { } ) . dashboard ?. privacyMode === true ;
2965+
2966+ const buildDockerRuntimePrivacyPrefsPayload = ( enabled ) => {
2967+ const current = utils . normalizePrefs ( folderTypePrefs || { } ) ;
2968+ return utils . normalizePrefs ( {
2969+ ...current ,
2970+ dashboard : {
2971+ ...( current . dashboard || { } ) ,
2972+ privacyMode : enabled === true
2973+ }
2974+ } ) ;
2975+ } ;
2976+
2977+ const persistDockerRuntimePrivacyMode = async ( enabled ) => {
2978+ const nextPrefs = buildDockerRuntimePrivacyPrefsPayload ( enabled ) ;
2979+ const payload = {
2980+ type : 'docker' ,
2981+ prefs : JSON . stringify ( {
2982+ dashboard : nextPrefs . dashboard
2983+ } )
2984+ } ;
2985+ const request = window . FolderViewPlusRequest ;
2986+ if ( request && typeof request . postJson === 'function' ) {
2987+ try {
2988+ return await request . postJson ( '/plugins/folderview.plus/server/prefs.php' , payload , {
2989+ retries : 1 ,
2990+ retryDelayMs : 260
2991+ } ) ;
2992+ } catch ( _error ) {
2993+ // Fall through to the legacy POST path so the runtime toggle still works
2994+ // when the request wrapper is late or degraded.
2995+ }
2996+ }
2997+ const response = await $ . post ( '/plugins/folderview.plus/server/prefs.php' , payload ) . promise ( ) ;
2998+ return parseJsonPayloadSafe ( response ) ;
2999+ } ;
3000+
3001+ const findDockerRuntimeListViewToggleAnchor = ( ) => {
3002+ const table = document . querySelector ( 'table#docker_containers' ) ;
3003+ if ( ! table ) {
3004+ return null ;
3005+ }
3006+ const scopes = [
3007+ table . parentElement ,
3008+ table . parentElement ?. parentElement ,
3009+ document . body
3010+ ] . filter ( Boolean ) ;
3011+ const switchSelector = 'input[type="checkbox"], .switch-button, .switch-button-background' ;
3012+ for ( const scope of scopes ) {
3013+ const candidates = Array . from ( scope . querySelectorAll ( 'label, div, span, td, th' ) ) ;
3014+ for ( const candidate of candidates ) {
3015+ const text = String ( candidate . textContent || '' ) . replace ( / \s + / g, ' ' ) . trim ( ) . toLowerCase ( ) ;
3016+ if ( ! text . includes ( 'basic view' ) ) {
3017+ continue ;
3018+ }
3019+ if ( candidate . querySelector ( switchSelector ) ) {
3020+ return candidate ;
3021+ }
3022+ if ( candidate . parentElement && candidate . parentElement . querySelector ( switchSelector ) ) {
3023+ return candidate . parentElement ;
3024+ }
3025+ }
3026+ }
3027+ return null ;
3028+ } ;
3029+
3030+ const ensureDockerRuntimePrivacyFallbackHost = ( ) => {
3031+ const table = document . querySelector ( 'table#docker_containers' ) ;
3032+ const mountRoot = table ?. parentElement ;
3033+ if ( ! mountRoot ) {
3034+ return null ;
3035+ }
3036+ let host = document . getElementById ( DOCKER_RUNTIME_PRIVACY_TOGGLE_FALLBACK_HOST_ID ) ;
3037+ if ( ! host ) {
3038+ host = document . createElement ( 'div' ) ;
3039+ host . id = DOCKER_RUNTIME_PRIVACY_TOGGLE_FALLBACK_HOST_ID ;
3040+ host . className = 'fvplus-docker-runtime-toolbar-controls' ;
3041+ }
3042+ if ( host . parentElement !== mountRoot ) {
3043+ mountRoot . insertBefore ( host , table ) ;
3044+ } else if ( host . nextElementSibling !== table ) {
3045+ mountRoot . insertBefore ( host , table ) ;
3046+ }
3047+ return host ;
3048+ } ;
3049+
3050+ const resolveDockerRuntimePrivacyToggleMount = ( ) => {
3051+ const anchor = findDockerRuntimeListViewToggleAnchor ( ) ;
3052+ if ( anchor && anchor . parentElement ) {
3053+ return {
3054+ anchor,
3055+ host : anchor . parentElement ,
3056+ fallback : false
3057+ } ;
3058+ }
3059+ const fallbackHost = ensureDockerRuntimePrivacyFallbackHost ( ) ;
3060+ if ( ! fallbackHost ) {
3061+ return null ;
3062+ }
3063+ return {
3064+ anchor : null ,
3065+ host : fallbackHost ,
3066+ fallback : true
3067+ } ;
3068+ } ;
3069+
3070+ const renderDockerRuntimePrivacyToggle = ( ) => {
3071+ const mount = resolveDockerRuntimePrivacyToggleMount ( ) ;
3072+ if ( ! mount ?. host ) {
3073+ return ;
3074+ }
3075+ let shell = document . getElementById ( DOCKER_RUNTIME_PRIVACY_TOGGLE_SHELL_ID ) ;
3076+ if ( shell && shell . parentElement !== mount . host ) {
3077+ shell . remove ( ) ;
3078+ shell = null ;
3079+ }
3080+ if ( ! shell ) {
3081+ shell = document . createElement ( 'div' ) ;
3082+ shell . id = DOCKER_RUNTIME_PRIVACY_TOGGLE_SHELL_ID ;
3083+ }
3084+ shell . className = `fvplus-docker-runtime-toggle-shell${ mount . fallback ? ' is-fallback' : '' } ` ;
3085+ if ( mount . anchor ) {
3086+ if ( mount . anchor . nextElementSibling !== shell ) {
3087+ mount . anchor . insertAdjacentElement ( 'afterend' , shell ) ;
3088+ }
3089+ } else if ( mount . host . firstElementChild !== shell ) {
3090+ mount . host . insertBefore ( shell , mount . host . firstChild ) ;
3091+ }
3092+ const enabled = readDockerRuntimePrivacyMode ( ) ;
3093+ shell . innerHTML = `
3094+ <span class="fvplus-docker-runtime-toggle-label">Privacy</span>
3095+ <input id="${ DOCKER_RUNTIME_PRIVACY_TOGGLE_ID } " class="basic-switch fvplus-docker-runtime-privacy-switch" type="checkbox" ${ enabled ? 'checked' : '' } >
3096+ ` ;
3097+ const $input = $ ( `#${ DOCKER_RUNTIME_PRIVACY_TOGGLE_ID } ` ) ;
3098+ if ( ! $input . length ) {
3099+ return ;
3100+ }
3101+ if ( typeof $input . switchButton === 'function' ) {
3102+ $input . switchButton ( {
3103+ labels_placement : 'right' ,
3104+ off_label : $ . i18n ( 'off' ) ,
3105+ on_label : $ . i18n ( 'on' ) ,
3106+ checked : enabled
3107+ } ) ;
3108+ }
3109+ $input . off ( 'change.fvDockerRuntimePrivacy' ) . on ( 'change.fvDockerRuntimePrivacy' , function onDockerRuntimePrivacyChange ( ) {
3110+ void setDockerRuntimePrivacyMode ( $ ( this ) . is ( ':checked' ) ) ;
3111+ } ) ;
3112+ } ;
3113+
3114+ const queueDockerRuntimePrivacyToggleMount = ( ) => {
3115+ if ( dockerRuntimePrivacyToggleMountQueued ) {
3116+ return ;
3117+ }
3118+ dockerRuntimePrivacyToggleMountQueued = true ;
3119+ const flush = ( ) => {
3120+ dockerRuntimePrivacyToggleMountQueued = false ;
3121+ renderDockerRuntimePrivacyToggle ( ) ;
3122+ } ;
3123+ if ( typeof window ?. requestAnimationFrame === 'function' ) {
3124+ window . requestAnimationFrame ( flush ) ;
3125+ return ;
3126+ }
3127+ setTimeout ( flush , 0 ) ;
3128+ } ;
3129+
3130+ const setDockerRuntimePrivacyMode = async ( enabled , options = { } ) => {
3131+ const nextEnabled = enabled === true ;
3132+ const previousPrefs = utils . normalizePrefs ( folderTypePrefs || { } ) ;
3133+ const previousEnabled = previousPrefs . dashboard ?. privacyMode === true ;
3134+ if ( previousEnabled === nextEnabled ) {
3135+ queueDockerRuntimePrivacyToggleMount ( ) ;
3136+ return ;
3137+ }
3138+ folderTypePrefs = buildDockerRuntimePrivacyPrefsPayload ( nextEnabled ) ;
3139+ applyRuntimePrefs ( folderTypePrefs ) ;
3140+ queueDockerRuntimePrivacyToggleMount ( ) ;
3141+ if ( options . persist === false ) {
3142+ return ;
3143+ }
3144+ const result = await dockerSafeUiActionRunner . run ( 'docker-runtime-privacy-toggle' , async ( ) => {
3145+ const response = await persistDockerRuntimePrivacyMode ( nextEnabled ) ;
3146+ folderTypePrefs = utils . normalizePrefs ( response ?. prefs || folderTypePrefs ) ;
3147+ applyRuntimePrefs ( folderTypePrefs ) ;
3148+ queueDockerRuntimePrivacyToggleMount ( ) ;
3149+ return response ;
3150+ } , {
3151+ userVisible : false
3152+ } ) ;
3153+ if ( ! result . ok ) {
3154+ folderTypePrefs = previousPrefs ;
3155+ applyRuntimePrefs ( folderTypePrefs ) ;
3156+ queueDockerRuntimePrivacyToggleMount ( ) ;
3157+ swal ( {
3158+ title : 'Privacy toggle save failed' ,
3159+ text : `${ escapeHtml ( String ( result . error ?. message || 'FolderView Plus could not save the Docker privacy toggle.' ) ) } <br>Reverted to the previous state.` ,
3160+ type : 'error' ,
3161+ html : true ,
3162+ confirmButtonText : 'OK'
3163+ } ) ;
3164+ }
3165+ } ;
29593166
29603167const emitDockerListViewModeChange = ( mode , source = 'cookie-write' ) => {
29613168 if ( typeof window ?. dispatchEvent !== 'function' || typeof window ?. CustomEvent !== 'function' ) {
@@ -3011,10 +3218,12 @@ const syncDockerListViewModeFromCookie = (source = 'passive') => {
30113218 source : String ( source || 'passive' ) . trim ( ) || 'passive'
30123219 } ) ;
30133220 if ( ! loadedFolder || ! globalFolders || Object . keys ( globalFolders ) . length <= 0 ) {
3221+ queueDockerRuntimePrivacyToggleMount ( ) ;
30143222 return ;
30153223 }
30163224 syncDockerVisibleFoldersFromRuntimeCache ( ) ;
30173225 scheduleDockerRuntimeWidthReflow ( 'listview-mode-change' , 12 ) ;
3226+ queueDockerRuntimePrivacyToggleMount ( ) ;
30183227} ;
30193228
30203229const startDockerListViewModeObserver = ( ) => {
@@ -5304,6 +5513,7 @@ window.listview = () => {
53045513 } else {
53055514 if ( FOLDER_VIEW_DEBUG_MODE ) console . log ( '[FV3_DEBUG] Patched listview: loadedFolder is true. Skipped createFolders.' ) ;
53065515 }
5516+ queueDockerRuntimePrivacyToggleMount ( ) ;
53075517 queueDockerRuntimeResizerBind ( ) ;
53085518 if ( FOLDER_VIEW_DEBUG_MODE ) console . log ( '[FV3_DEBUG] Patched listview: Exit.' ) ;
53095519} ;
@@ -5805,6 +6015,7 @@ const applyRuntimePrefs = (prefs) => {
58056015 $ ( 'body' ) . toggleClass ( 'fvplus-performance-mode' , normalized . performanceMode === true ) ;
58066016 $ ( 'body' ) . toggleClass ( 'fvplus-performance-mode-strict' , dockerRuntimePerformanceProfile ?. strict === true ) ;
58076017 $ ( 'body' ) . toggleClass ( 'fvplus-privacy-docker-runtime' , normalized ?. dashboard ?. privacyMode === true ) ;
6018+ queueDockerRuntimePrivacyToggleMount ( ) ;
58086019 scheduleLiveRefresh ( normalized ) ;
58096020} ;
58106021
@@ -5880,6 +6091,7 @@ bindDockerHostOpenDockerPatch();
58806091bindDockerUpdateActionClickCapture ( ) ;
58816092bindDockerPostUpdateRenderReconcile ( ) ;
58826093startDockerListViewModeObserver ( ) ;
6094+ queueDockerRuntimePrivacyToggleMount ( ) ;
58836095
58846096if ( FOLDER_VIEW_DEBUG_MODE ) {
58856097 console . log ( '[FV3_DEBUG] Global variables initialized:' , {
0 commit comments