video side: Settings shows the real music settings page (identically)

Video Settings was a 'coming soon' placeholder. Now it reuses the actual
#settings-page, shown identically for now (no hiding of music-only bits yet) —
the foundation for adding the Movies/TV library mapping next.
- video-side.js: SHARED_PAGES maps video-settings -> the music 'settings' page;
  showPage sets body[data-video-page] and triggers the shared loadPageData
  loader (same init music navigation uses) instead of a video subpage.
- CSS reveals #settings-page (and hides the video host) when
  data-side=video + data-video-page=video-settings; id selector outranks the
  blanket music-page hide. 81 tests green.
This commit is contained in:
BoulderBadgeDad 2026-06-14 00:17:00 -07:00
parent 061079f0f6
commit 0d86d84307
3 changed files with 38 additions and 0 deletions

View file

@ -184,6 +184,16 @@ def test_scan_module_referenced_and_isolated():
assert "/api/video/scan/request" in _SCAN_JS
def test_video_settings_reuses_real_music_settings_page():
# The video Settings nav shows the actual #settings-page (identically, for
# now) via CSS + the shared loadPageData loader — not a video subpage.
assert "'video-settings': 'settings'" in _JS or '"video-settings": "settings"' in _JS
assert "loadPageData" in _JS
css = _CSS_PATH.read_text(encoding="utf-8")
assert 'data-video-page="video-settings"] #settings-page' in css
assert 'data-video-page="video-settings"] #video-page-host' in css
def test_controller_is_isolated_iife_with_no_globals():
stripped = _JS.strip()
# Wrapped in an IIFE → declares no module-level globals that could collide

View file

@ -108,6 +108,16 @@ body[data-side="video"] .status-section {
display: none;
}
/* ── Shared pages: show the REAL music page on the video side ───────────── */
/* For now Settings is shown identically (no hiding of music-only bits yet).
The id selector + extra attribute outrank the blanket music-page hide rule. */
body[data-side="video"][data-video-page="video-settings"] #settings-page {
display: block !important;
}
body[data-side="video"][data-video-page="video-settings"] #video-page-host {
display: none !important;
}
/* ── Dashboard header: match music, minus the sweep animation ──────────── */
/* The header reuses music's .dashboard-header markup/CSS for an identical look;
we only mute the ambient sweep on the video side (equiv may return later,

View file

@ -40,6 +40,11 @@
{ id: 'video-help', label: 'Help & Docs', shared: true },
];
// "Shared" video pages reuse the REAL music page (shown identically on the
// video side for now) instead of a video subpage: video page id -> music
// page id. CSS reveals the music page; we trigger its loader once shown.
var SHARED_PAGES = { 'video-settings': 'settings' };
function readSide() {
try {
return localStorage.getItem(SIDE_KEY) === 'video' ? 'video' : 'music';
@ -88,6 +93,19 @@
// for this event instead of being called directly, keeping each isolated.
function showPage(pageId) {
var meta = pageMeta(pageId);
// Drives the CSS that reveals shared music pages (e.g. Settings) and
// hides the video host for them.
document.body.setAttribute('data-video-page', meta.id);
var sharedMusicId = SHARED_PAGES[meta.id];
if (sharedMusicId) {
// The real music page is shown by CSS; load its data the same way a
// music-side navigation would. (loadPageData is a shared global.)
if (typeof loadPageData === 'function') loadPageData(sharedMusicId);
document.dispatchEvent(new CustomEvent('soulsync:video-page-shown', { detail: meta.id }));
return;
}
var host = document.getElementById('video-page-host');
if (!host) return;
var matched = null;