Two small, unrelated requests handled together. The search bar (tiles.ts's shared .searchBar, used by views/library/ artist/album) scrolled away with the page - asked to stay reachable while scrolling a long grid. position: sticky; top: 0 on the input itself; base.css's own generic input rule already gives it an opaque background, which is what keeps tiles scrolling underneath from showing through once it's stuck - nothing else needed changing. Seeking within the current music track was explicit, deliberate scope this session's own "participative playlist" work had cut - the progress bar was marked read-only specifically because MusicQueueService had no seek concept, just play/pause/advance. The permission model was already decided by that class's own documented reasoning for why *anyone* can pause/resume (reversible, doesn't change what's playing) - a seek is exactly that same shape, continuous instead of binary, so it gets the same treatment, not voteSkip's majority gate. MusicQueueService.seek mirrors setPaused's own elapsed-time math (clamped into [0, durationSeconds]), with one real difference: setPaused's resume branch gets away with calling scheduleAdvance directly only because pausing already cleared the timer first - scheduleAdvance itself never clears a prior one. Seeking while still playing has no such guarantee, so skipping that clear first would leave the OLD timer (counting down against the old position) running alongside the new one, eventually firing playNext at the wrong moment. New musicSeek socket event, no host check (same as musicPlaystate). Client-side, MusicBar's progress track gained the same pointerdown/move/up drag-to-seek handlers the video player's seek bar already has - no host branch to port this time, since a listen party has none - plus a local, optimistic activeDriver.currentTime write before broadcasting, for the same instant-feedback reason the video player's own seekFromPointerEvent does that. Verified: full typecheck/test/build on both projects (128/128 server tests - new coverage for seek's clamping, its paused-vs-playing branches, and a real-timer test proving the old advance timer is cleared rather than left running alongside a re-armed one - the exact bug the naive "just call scheduleAdvance again" version would have shipped). No server needed for the sticky search bar - checked by reading .mainContent's own layout directly (no nested scroll container between it and the document, confirming position: sticky resolves against the page scroll as intended). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
39 lines
2.1 KiB
HTML
39 lines
2.1 KiB
HTML
<div id="musicBarQueue" class="musicBarQueue hide">
|
|
<div class="pageHeader">
|
|
<h2>File d'attente</h2>
|
|
</div>
|
|
<div id="queueList" class="trackList"><p class="hint">Rien en attente.</p></div>
|
|
</div>
|
|
|
|
<!-- Click/drag to seek - see MusicBar.ts's pointerdown/move/up handlers.
|
|
No host check needed to enable this, unlike the video player's own
|
|
seek bar: a listen party has no host at all, and a seek here gets
|
|
the exact same "anyone can do it" treatment play/pause already has
|
|
(see MusicQueueService.seek's own comment for why that's the right
|
|
model for this, not voteSkip's majority gate). -->
|
|
<div id="trackProgress" class="musicBarProgressTrack"><div id="trackProgressFill" class="musicBarProgressFill"></div></div>
|
|
|
|
<div class="musicBarRow">
|
|
<div id="presenceOverlay" class="presenceOverlay musicBarPresence"></div>
|
|
|
|
<img id="trackImage" class="musicBarImage" alt="" onerror="this.classList.add('noImage')" />
|
|
<div class="musicBarInfo">
|
|
<strong id="trackName">-</strong>
|
|
<span class="hint" id="trackArtist"></span>
|
|
</div>
|
|
|
|
<span id="trackPosition" class="hint musicBarPosition">0:00 / 0:00</span>
|
|
<button class="iconBtn" id="playPauseBtn" title="Lecture / pause" aria-label="Lecture / pause">▶</button>
|
|
<button class="iconBtn hide" id="castBtn" title="Caster sur un Chromecast" aria-label="Caster sur un Chromecast" aria-pressed="false">
|
|
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
|
<rect x="1" y="4" width="22" height="16" rx="2" fill="none" stroke="currentColor" stroke-width="2"></rect>
|
|
<path d="M4 15a5 5 0 0 1 5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
|
|
<path d="M4 11a9 9 0 0 1 9 9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
|
|
<circle cx="4" cy="20" r="1.5" fill="currentColor"></circle>
|
|
</svg>
|
|
</button>
|
|
<button class="linklike" id="voteSkipBtn">Passer (vote)</button>
|
|
<button class="linklike" id="toggleQueueBtn">File d'attente</button>
|
|
<button class="linklike" id="leaveBtn">Quitter</button>
|
|
</div>
|
|
<audio id="barAudio"></audio>
|