diff --git a/frontend/admin.html b/frontend/admin.html index 0f1c511..a8cabc4 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -412,17 +412,44 @@ .tabs .tab { flex: none; } } .nav { display: none; } + .song-info { min-width: 0; flex: 1; } + .song-actions { display: flex; flex-wrap: wrap; gap: 8px; } + .song-edit { display: none; } + .m-only { display: none; } @media (max-width: 760px) { .tabs, .top .desk-only { display: none; } .top .brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; } .top .back-app, .top #logoutBtn { padding-inline: 10px; flex: none; } - .top { padding: 10px 14px; } + .top { padding: calc(10px + env(safe-area-inset-top, 0px)) calc(14px + env(safe-area-inset-right, 0px)) 10px calc(14px + env(safe-area-inset-left, 0px)); } + .deck { top: calc(54px + env(safe-area-inset-top, 0px)); } + + /* Song cards: what it is on top, every action full width beneath it. */ + #songList { display: grid; gap: 12px; } + .song-row { display: grid; gap: 14px; padding: 16px; border-radius: 16px; border: 0; background: var(--bg-2); } + .song-row:hover { border-color: transparent; } + .song-open { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; min-height: 0; flex-basis: auto; } + .song-row b { font-size: 17px; line-height: 1.3; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } + .song-row small { display: block; margin-top: 2px; font-size: 14px; color: var(--text-2); } + .song-row .st { justify-self: start; font-size: 12px; padding: 4px 10px; } + .song-actions { display: grid; grid-template-columns: 1fr; gap: 8px; } + .song-actions .btn { width: 100%; min-height: 46px; justify-content: center; font-size: 15px; margin: 0; } + .song-edit { display: inline-flex; } + .m-only { display: inline; } .d-only { display: none; } + .nav { position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; display: flex; gap: 4px; height: calc(var(--nav-h) + var(--safe)); padding: 6px 6px var(--safe); overflow-x: auto; overflow-y: hidden; - overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; scrollbar-width: thin; + overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; scrollbar-width: none; + scroll-snap-type: x proximity; scroll-padding-inline: 6px; + padding-left: calc(6px + env(safe-area-inset-left, 0px)); padding-right: calc(6px + env(safe-area-inset-right, 0px)); background: var(--bg-1); border-top: 1px solid var(--line-2); } + .nav::-webkit-scrollbar { display: none; } + /* A flex row drops its end padding when it scrolls; this spacer keeps the + last tab clear of the right edge. */ + .nav::after { content: ""; flex: 0 0 6px; } + .nav .tab { scroll-snap-align: start; } + .nav .tab:last-of-type { scroll-snap-align: end; } .nav .tab { flex: 0 0 68px; min-width: 68px; min-height: 44px; flex-direction: column; justify-content: center; gap: 3px; height: auto; padding: 4px 3px; font-size: 10px; line-height: 1.05; text-align: center; white-space: normal; border-radius: 12px; } .nav .tab .badge { position: absolute; top: 2px; left: 55%; } .steps { grid-template-columns: 1fr; } @@ -1628,10 +1655,13 @@ const q = $('songFilter').value.trim().toLowerCase(); const rows = SONGS.filter((r) => !q || (r.title + ' ' + (r.sub || '')).toLowerCase().includes(q)); $('songList').innerHTML = rows.length ? rows.map((r) => `
- ${r.sourceUrl ? `Source` : ''} -
`).join('') +
+ + + ${r.sourceUrl ? `SourceOpen source page` : ''} +
`).join('') : '
Nothing matches — paste a link above to open any YouTube song.
'; paintWhisper(); } @@ -1790,7 +1820,7 @@ document.querySelectorAll('[data-whisper]').forEach(btn => { const job = whisperJobs.get(btn.dataset.whisper); btn.disabled = whisperStarting.has(btn.dataset.whisper) || activeWhisper(job); - btn.textContent = job?.status === 'running' ? 'Transcribing…' : job?.status === 'queued' ? 'Queued' : 'Whisper'; + btn.textContent = job?.status === 'running' ? 'Transcribing…' : job?.status === 'queued' ? 'Queued' : (matchMedia('(max-width: 760px)').matches ? 'Transcribe with Whisper' : 'Whisper'); }); const job = whisperJobs.get(LE.id); $('whisperBtn').disabled = whisperStarting.has(LE.id) || activeWhisper(job);