# UI / Visual-Polish Updates

Backlog for the visual-polish items requested after the 2.0.2 pass. Marked `[✔️]`
when landed. All extend the existing Lightify visual language (dark ground, teal
accent, frameless window) — no imported design systems.

## Landed

- [✔️] **② Dynamic album-art accent.** _(2026-07-09)_ `applyDynamicAccent` samples
  the cover's saturation-weighted dominant color (CORS image → 16×16 canvas, dark
  lift) and sets `--accent-dynamic` / `--accent-dynamic-soft`; progress fill, volume
  thumb, EQ bars, and an art glow tint to it. Falls back to teal on no-art or a
  tainted canvas.

- [✔️] **③ Recent-list refinement.** _(2026-07-09)_ Relative timestamps ("2m ago")
  on Recent rows, animated equalizer bars on the actively-playing row (replacing the
  static glyph), and dimmed sidebar glyphs. `formatRelativeTime` + `renderSidebarTracks`
  + CSS.

- [✔️] **④ Motion on state changes.** _(2026-07-09)_ Cover crossfade on track change
  (`art-swap` animation, fired only when the src changes), eased progress fill, and
  play-button press feedback. Respects `prefers-reduced-motion`. (True play↔pause icon
  morph deferred — needs the stacked-icon refactor.)

## Open

- [✔️] **⑤ Direct-manipulation queue.** _(2026-07-09)_ Per-row **drag grip**
  (native DnD, isolated from the selection gesture via `pointerdown` stopPropagation)
  reorders, and a hover **× button** removes — only on a local mirror. Backed by pure
  `moveQueueItem` + `reorderLightifyQueue`/`removeLightifyQueueItem` with **3 harness
  tests**. Swipe-to-remove deferred (needs pointer tuning + visual testing); the ×
  button covers removal.

- [✔️] **⑥ Skeletons + friendly empty states.** _(2026-07-09)_ `renderListSkeleton`
  shimmer rows replace "Loading…" text in the library/paged/search-drill lists;
  empty/error messages get a centered `.list-empty` treatment. Respects
  `prefers-reduced-motion`.

- [✔️] **§A Mini-player overhaul.** _(2026-07-10, shipped 2.0.8)_ The single mini
  mode became three layouts driven by `state.miniMode` (`off|square|bar|nano`):
  `setMiniPlayer`→`setMiniMode(mode)` with body classes `mini-square` (360×420),
  `mini-bar` (460×112, grid reflow: cover ▸ text/progress/transport), `mini-nano`
  (200×200 cover-only, transport pill on hover, click cover → expand to square).
  Added **idle-fade** chrome (pointer-idle timer → `body.mini-idle`, reduced-motion
  gated), an **edge progress** line (`#mini-edge`, driven from `renderPlaybackProgress`),
  an **album-tinted frame** (`--accent-dynamic-soft` inset border), and a **snap
  control** (`#mini-modes`) to switch layouts; the last layout persists via
  `lightify.miniMode.v1`. Rounded corners deferred (needs `transparent:true`).
  Frontend/CSS only — 29 tests stay green.
  **Refined (2.0.9)** after user review: fixed the window not shrinking
  (`unmaximize()` + dynamic `setMinSize` before `setSize`, since a maximized window
  ignores resize and the targets sit below the 320×260 min-size); statusbar hidden in
  mini; square now has a **single** progress indicator (removed the inline bar, kept the
  edge line), **enlarged** art (`min(70vw,240px)`) and transport **pinned to the bottom**;
  bar rebuilt as a true media strip (cover ▸ title/progress ▸ transport, vertically
  centered) with the edge line dropped to avoid a double; nano cover fills the content
  area (respects the titlebar instead of `100vh`); snap control moved into the titlebar.
  Layouts verified quantitatively in a headless render harness.

- [ ] **⑦ Frameless-window chrome pass (titlebar).** Remaining half of ⑦: refine the
  custom titlebar (hover states on min/max/close, clear drag region, consistent
  transport iconography). Mini-player half done above (§A).
