diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index 7c2979d..23c5871 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -1060,7 +1060,19 @@ function vvPollMonitor(live) { // single-host install "This host" and "Mesh" are two names for the same view, and the // Media Stack tab hides its equivalent for the same reason. const _strmScopeEl = document.getElementById('vv-streams-scope'); - if (_strmScopeEl) _strmScopeEl.style.display = Object.keys(ptRemote).length ? 'flex' : 'none'; + const _strmHasPeer = Object.keys(ptRemote).length > 0; + if (_strmScopeEl) _strmScopeEl.style.display = _strmHasPeer ? 'flex' : 'none'; + + // A remembered "mesh" outlives the partner that justified it — an offboard, or a browser + // carrying the preference to a single-host install. The control is hidden in that state, + // so there would be no way back: the card would sit in a mesh view of a mesh that is not + // there, with nothing on screen to say why. The scope follows the control. + if (!_strmHasPeer && vvStreamScope === 'mesh') { + vvStreamScope = 'local'; + try { localStorage.setItem(VV_STRM_SCOPE_KEY, 'local'); } catch (_) {} + vvStreamScopeSync(); + vvPollStreams(); + } const ptStatus = pt.enabled ? `enabled · sync every ${pt.sync_min}min` : `disabled`; @@ -2554,7 +2566,26 @@ function vvRenderStreams() { // Local by default, and it stays the cheap path — one call per media server on this box. Mesh adds // a bounded SSH hop per partner, paid only while the operator is looking at it. +// Remembered per browser, because which scope you want is a property of the screen in front of +// you: the wall dashboard wants the whole mesh, a laptop opened to check one box does not. A +// first visit — and anything unrecognised in storage — is local. +// +// Validated against the two real scopes rather than trusted. A stale or hand-edited key would +// otherwise put the card in a scope with no button to leave it by. +const VV_STRM_SCOPE_KEY = 'vv-streams-scope'; let vvStreamScope = 'local'; +try { + const _s = localStorage.getItem(VV_STRM_SCOPE_KEY); + if (_s === 'local' || _s === 'mesh') vvStreamScope = _s; +} catch (_) {} +// The markup ships with "This host" lit, so this only ever has to move the highlight. +if (vvStreamScope !== 'local') vvStreamScopeSync(); + +function vvStreamScopeSync() { + document.querySelectorAll('#vv-streams [data-strmscope]').forEach(b => + b.classList.toggle('on', b.dataset.strmscope === vvStreamScope)); +} + let vvStreamNodes = []; function vvPollStreams() { @@ -2605,8 +2636,8 @@ document.getElementById('vv-streams')?.addEventListener('click', ev => { if (next === vvStreamScope) return; vvStreamScope = next; - document.querySelectorAll('#vv-streams [data-strmscope]').forEach(b => - b.classList.toggle('on', b.dataset.strmscope === next)); + try { localStorage.setItem(VV_STRM_SCOPE_KEY, next); } catch (_) {} + vvStreamScopeSync(); // Switching is a request to see the other view now, not at the next tick. const el = document.getElementById('vv-streams-body');