fix(ui): snap player view directly from Studio Reference mockup

Wholesale replacement of the player view markup + a verbatim mockup
CSS block scoped to .now-playing. Previous approach kept restyling
legacy classes which left a layered, inconsistent result. This is a
clean snap: same DOM structure as the mockup, same CSS rules, mapped
onto existing JS-touched IDs.

Markup
- One <section class="now-playing"> with vinyl-stage on left and
  track-masthead on right
- Vinyl spins via data-playstate and contains album art as the
  circular center label (existing #album-art img preserved)
- SVG tonearm pivots in/out by data-playstate
- Track masthead: .kicker (copper italic mono), large italic-serif
  .track-title, italic .track-byline, mono .track-album
- Meta grid: 2 cells (State / Source) with mono labels + italic
  serif values
- 30 .spectrum bars between metadata and transport
- .transport: progress-row (timecode + .progress-track + timecode)
  and .controls (3 .btn-trans buttons + .vu-cluster)
- Volume slider, mute button, visualizer toggle moved to a
  .visually-hidden block — they remain functional for JS / a11y
  but no longer compete for visual real estate. Volume control
  happens via the always-visible mini player.

VU cluster (mockup-faithful)
- 140x60 VU meter with conic-gradient grid background, copper
  needle, "VU" label
- Stacked readout: "OUT <strong>SYS</strong>" / "VOL <strong>72%</strong>"
- Click anywhere on cluster toggles mute (calls toggleMute())
- When muted: needle turns rust, readout strong turns rust,
  OUT label switches to MUTE

JS hooks
- updatePlaybackState already sets :root[data-playstate] (drives
  spin + tonearm)
- Volume tick now updates #vu-vol and #vu-out
- updateMuteIcon updates #vu-out + .vu-cluster.muted class

Scoping
- All new CSS is .now-playing-prefixed so other tabs and dialogs
  are untouched
- Legacy .progress-bar:hover scaleY and ::after scale(0) are
  defeated with !important inside .now-playing
This commit is contained in:
2026-04-25 01:43:11 +03:00
parent d9d4672ca3
commit 77b39e5684
3 changed files with 646 additions and 70 deletions
+39 -50
View File
@@ -157,10 +157,10 @@
</div>
<div class="player-container" data-tab-content="player" role="tabpanel" id="panel-player">
<div class="player-layout now-playing">
<section class="now-playing player-layout">
<!-- Vinyl stage with album art as label -->
<div class="album-art-container vinyl-stage">
<!-- Vinyl stage with album art as label, plus tonearm -->
<div class="vinyl-stage album-art-container">
<div class="vinyl">
<div class="vinyl-label">
<img id="album-art-glow" class="album-art-glow" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300'%3E%3Crect fill='%23282828' width='300' height='300'/%3E%3C/svg%3E" alt="" aria-hidden="true">
@@ -188,21 +188,19 @@
</div>
<!-- Track masthead -->
<div class="player-details track-masthead">
<div class="track-masthead player-details">
<div class="kicker"><span data-i18n="player.kicker">Now Playing</span></div>
<div class="track-info">
<div id="track-title" data-i18n="player.no_media">No media playing</div>
<div id="artist"></div>
<div id="album"></div>
</div>
<h1 class="track-title" id="track-title" data-i18n="player.no_media">No media playing</h1>
<div class="track-byline" id="artist"></div>
<div class="track-album" id="album"></div>
<!-- Editorial metadata grid (State + Source; timecodes are on the timeline) -->
<!-- 2-cell metadata grid -->
<div class="meta-grid meta-grid-2">
<div class="meta-cell">
<div class="meta-label" data-i18n="meta.state">State</div>
<div class="meta-value">
<div class="label" data-i18n="meta.state">State</div>
<div class="value">
<svg class="state-icon" id="state-icon" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/>
</svg>
@@ -210,8 +208,8 @@
</div>
</div>
<div class="meta-cell">
<div class="meta-label" data-i18n="meta.source">Source</div>
<div class="meta-value source-value">
<div class="label" data-i18n="meta.source">Source</div>
<div class="value source-value">
<span class="source-icon" id="sourceIcon"></span>
<span id="source" data-i18n="player.unknown_source">Unknown</span>
</div>
@@ -228,64 +226,55 @@
<span></span><span></span><span></span><span></span><span></span>
</div>
<!-- Transport: progress + controls + VU cluster -->
<!-- Transport -->
<div class="transport">
<div class="progress-container progress-row">
<div class="progress-row">
<span class="timecode elapsed" id="current-time">0:00</span>
<div class="progress-bar progress-track" id="progress-bar" data-duration="0" role="slider" aria-label="Playback position" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0">
<div class="progress-track progress-bar" id="progress-bar" data-duration="0" role="slider" aria-label="Playback position" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0">
<div class="progress-fill" id="progress-fill"></div>
</div>
<span class="timecode" id="total-time">0:00</span>
</div>
<div class="controls">
<button onclick="previousTrack()" data-i18n-title="player.previous" title="Previous" id="btn-previous">
<svg viewBox="0 0 24 24">
<path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/>
</svg>
<button class="btn-trans" onclick="previousTrack()" data-i18n-title="player.previous" title="Previous" id="btn-previous">
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
</button>
<button class="primary" onclick="togglePlayPause()" data-i18n-title="player.play" title="Play/Pause" id="btn-play-pause">
<svg viewBox="0 0 24 24" id="play-pause-icon">
<path d="M8 5v14l11-7z"/>
</svg>
<button class="btn-trans primary" onclick="togglePlayPause()" data-i18n-title="player.play" title="Play/Pause" id="btn-play-pause">
<svg viewBox="0 0 24 24" id="play-pause-icon"><path d="M8 5v14l11-7z"/></svg>
</button>
<button onclick="nextTrack()" data-i18n-title="player.next" title="Next" id="btn-next">
<svg viewBox="0 0 24 24">
<path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/>
</svg>
<button class="btn-trans" onclick="nextTrack()" data-i18n-title="player.next" title="Next" id="btn-next">
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
</button>
<!-- VU cluster: needle visual + slider + readout -->
<div class="vu-cluster">
<div class="vu-cluster" onclick="toggleMute()" title="Click to mute / use mini player to adjust volume" role="button" tabindex="0">
<div class="vu-meter" aria-hidden="true">
<div class="vu-needle" id="vuNeedle"></div>
</div>
<div class="volume-container">
<button class="mute-btn" onclick="toggleMute()" data-i18n-title="player.mute" title="Mute" id="btn-mute">
<svg viewBox="0 0 24 24" id="mute-icon">
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>
</svg>
</button>
<input type="range" id="volume-slider" min="0" max="100" value="50" aria-label="Volume">
<div class="volume-display" id="volume-display">50%</div>
<div class="vu-readout">
<span>OUT <strong id="vu-out">SYS</strong></span>
<span>VOL <strong id="vu-vol">50%</strong></span>
</div>
</div>
</div>
</div>
<!-- Audio visualizer toggle (button shown by JS only when supported) -->
<div class="source-info">
<span class="source-label">
<span class="vinyl-mode-label" data-i18n="player.modes">Modes</span>
</span>
<div class="player-toggles">
<button class="vinyl-toggle-btn" onclick="toggleVisualizer()" id="visualizerToggle" data-i18n-title="player.visualizer" title="Audio visualizer" style="display:none">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M3 18h2v-8H3v8zm4 0h2V6H7v12zm4 0h2V2h-2v16zm4 0h2v-6h-2v6zm4 0h2V9h-2v9z"/></svg>
</button>
</div>
<!-- Hidden but functional: slider + mute + visualizer toggle.
Adjustment happens via the always-visible mini player. -->
<div class="visually-hidden">
<button class="mute-btn" onclick="toggleMute()" data-i18n-title="player.mute" title="Mute" id="btn-mute">
<svg viewBox="0 0 24 24" id="mute-icon">
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>
</svg>
</button>
<input type="range" id="volume-slider" min="0" max="100" value="50" aria-label="Volume">
<div id="volume-display">50%</div>
<button onclick="toggleVisualizer()" id="visualizerToggle" data-i18n-title="player.visualizer" title="Audio visualizer" style="display:none">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M3 18h2v-8H3v8zm4 0h2V6H7v12zm4 0h2V2h-2v16zm4 0h2v-6h-2v6zm4 0h2V9h-2v9z"/></svg>
</button>
</div>
</div>
</div>
</section>
</div>
<!-- Media Browser Section -->