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');