From 57413a07a17aabb2dcb1dceea58db4a913ec5c56 Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Sun, 16 Aug 2026 12:58:05 +0000 Subject: [PATCH] Hide the list pane on the home view until the first search --- frontend/app.js | 29 ++++++++++++++++++++++++++--- frontend/index.html | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index 9d96a90..b9fd720 100755 --- a/frontend/app.js +++ b/frontend/app.js @@ -251,6 +251,9 @@ const DEFAULT_SETTINGS = { let data = { playlists: [], history: [], queue: [], customVideos: [], settings: { ...DEFAULT_SETTINGS }, resumePositions: {}, rememberPos: {}, playCount: {}, abMarkers: {}, lastAutoBackup: 0, profile: null }; let view = { type: 'search' }; // 'search'|'history'|'playlist'|'settings'|'queue'|'saved'|'downloads'|'channel' let searchResults = []; +// Distinguishes "never searched" (hide the list pane, show the landing) from +// "searched and got nothing back" (keep the pane so the user sees why). +let searchAttempted = false; let channelData = { name: '', url: '', key: '', results: [], loading: false }; let queue = []; // list of video objects for autoplay let queueIndex = -1; @@ -2056,6 +2059,9 @@ function currentList() { } function showSearchSkeletons() { + // A search is in flight — the pane is about to have results, so reveal it + // now rather than popping in only once they land. + setListPaneHidden(false); els.status.classList.add('hidden'); els.cards.innerHTML = ''; for (let i = 0; i < 8; i++) { @@ -2072,7 +2078,17 @@ function showSearchSkeletons() { } } +// Before the first search the list pane has nothing to say but "Search for +// something to begin" — hide it outright so the playlist board (or the hero) +// owns the full width on a fresh launch. Any search or view change brings it +// back. Must run before the early returns below, which skip the rest. +function setListPaneHidden(hidden) { + const pane = document.getElementById('listPane'); + if (pane) pane.classList.toggle('hidden', hidden); +} + function renderList() { + setListPaneHidden(view.type === 'search' && !searchResults.length && !searchAttempted); if (view.type === 'settings') { renderSettings(); return; } if (view.type === 'saved') { renderSaved(); return; } if (view.type === 'downloads') { renderDownloads(); return; } @@ -2190,9 +2206,13 @@ function renderList() { empty.className = 'empty-state'; if (view.type === 'search') { - // Hero landing is already in the player pane — list pane stays bare. + // Landing (hero or playlist board) lives in the player pane. Before the + // first search this pane is hidden outright; after one that came back + // empty it stays visible so the result is legible. els.status.classList.remove('hidden'); - els.status.textContent = 'Search for something to begin.'; + els.status.textContent = searchAttempted + ? 'No results for that search.' + : 'Search for something to begin.'; return; } @@ -3264,7 +3284,9 @@ function scrollListIntoViewPortrait() { if (!isPortraitPWA()) return; const body = document.querySelector('.body'); const listPane = document.querySelector('.list-pane'); - if (!body || !listPane) return; + // offsetParent is null while the pane is hidden (pre-first-search) — its + // rect would be all zeros and scroll the body to a bogus offset. + if (!body || !listPane || !listPane.offsetParent) return; const top = body.scrollTop + listPane.getBoundingClientRect().top - body.getBoundingClientRect().top; body.scrollTo({ top, behavior: 'smooth' }); } @@ -3386,6 +3408,7 @@ function wireUI() { const res = await API.search(q); if (!res || !res.ok) throw new Error(res?.error || 'Search failed'); searchResults = res.results || []; + searchAttempted = true; renderList(); } catch (err) { els.cards.innerHTML = ''; diff --git a/frontend/index.html b/frontend/index.html index 9a59c21..f7921e0 100755 --- a/frontend/index.html +++ b/frontend/index.html @@ -225,7 +225,7 @@ -
+

Search