diff --git a/docs/features/ui.md b/docs/features/ui.md
index 3b29249..a9fc991 100644
--- a/docs/features/ui.md
+++ b/docs/features/ui.md
@@ -176,6 +176,18 @@ The overflow popover itself is capped at `max-h-[calc(100dvh-7rem)]` with `overf
The opt-in audio-quality footer ([`AudioQualityFooter`](../../src/components/player/AudioQualityFooter.tsx), pinned via Settings → Appearance → Player bar layout) is a thin strip below the player bar that surfaces the source file specs in compact form (`48 kHz · 256 kb/s · 6 Mo` on the left, `AAC · 24bit · 48kHz` on the right; bitrates ≥ 1000 kbps render as `Mb/s`). When the engine is resampling — source rate ≠ output device rate — the left chunk renders an arrow instead: `48 kHz → 44.1 kHz · …`, so the user can spot the conversion at a glance without opening the popover. The arrow is gated on the device rate being known (the engine reports `0` before the first stream opens); otherwise we fall back to the source rate alone rather than printing a misleading `48 kHz → null`. The Hi-Res pill surfaces when [`isHiRes`](../../src/lib/hiRes.ts) accepts the source bit depth / sample rate combination.
+### Hi-Res / DSD badge
+
+[`HiResBadge`](../../src/components/common/HiResBadge.tsx) is the green pill that decorates track rows, album grid tiles, and the player-bar metadata when the source qualifies as Hi-Res (`isHiRes` — ≥ 24-bit, ≥ 44.1 kHz) or as DSD (`dsdLabel` returns `DSD64` / `DSD128` / …). Three variants:
+
+| Variant | Used in | Style |
+| --------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| `overlay` | Album / artist grid covers (default). | Absolute-positioned pill in the cover's top-left corner with a drop shadow. |
+| `inline` | TrackTable rows, sidebar lists. | Inline rounded pill next to the title. |
+| `text` | Player bar — under the artist name. | Spotify-style minimal green uppercase text, no pill background, blends into the metadata stack. |
+
+All variants are gated by [`useHiResBadgeVisibility`](../../src/hooks/useHiResBadgeVisibility.ts), which reads `profile_setting['ui.show_hi_res_badge']` (default `true`) and re-reads on the `waveflow:hi-res-badge-visibility` window event. Settings → Appearance ships [`HiResBadgeCard`](../../src/components/views/settings/HiResBadgeCard.tsx) to flip the flag — when off, every mounted `HiResBadge` returns `null` in one render, including the player-bar text label. Per-profile so a kid's profile can hide the audiophile chrome while the audiophile profile keeps it.
+
Hovering (or keyboard-focusing) the footer opens [`AudioPipelinePopover`](../../src/components/player/AudioPipelinePopover.tsx) — an audiophile-grade breakdown of what the engine is actually doing.
#### Sections displayed
diff --git a/src/components/common/HiResBadge.tsx b/src/components/common/HiResBadge.tsx
index 49dc0d0..f8a085b 100644
--- a/src/components/common/HiResBadge.tsx
+++ b/src/components/common/HiResBadge.tsx
@@ -1,4 +1,5 @@
import { dsdLabel, isHiRes } from "../../lib/hiRes";
+import { useHiResBadgeVisibility } from "../../hooks/useHiResBadgeVisibility";
/**
* Hi-Res Audio badge — shown when the source file is delivered at a
@@ -6,18 +7,25 @@ import { dsdLabel, isHiRes } from "../../lib/hiRes";
* DSD (in which case the badge says "DSD64", "DSD128", etc. instead
* of "Hi-Res 24-bit").
*
- * Two visual variants:
- * - `overlay` is intended to sit on top of an album cover (top-left
+ * Three visual variants:
+ * - `overlay` (default) sits on top of an album cover (top-left
* corner, drop shadow);
* - `inline` is for sidebar / row contexts where the badge sits next
- * to text.
+ * to text;
+ * - `text` is the minimal Spotify-style green text label used under
+ * the artist name in the player bar — no background pill so it
+ * nests cleanly inside dense metadata.
+ *
+ * Globally hidden by the per-profile
+ * `profile_setting['ui.show_hi_res_badge']` toggle — flipping that off
+ * returns `null` everywhere this component is mounted.
*/
interface HiResBadgeProps {
bitDepth: number | null;
sampleRate: number | null;
/** Codec label from the scanner (e.g. "FLAC", "DSD128"). */
codec?: string | null;
- variant?: "overlay" | "inline";
+ variant?: "overlay" | "inline" | "text";
/** Override the visible text. Default is "Hi-Res {bitDepth}-bit". */
label?: string;
}
@@ -29,13 +37,21 @@ export function HiResBadge({
variant = "overlay",
label,
}: HiResBadgeProps) {
+ const { visible } = useHiResBadgeVisibility();
// DSD wins over the generic Hi-Res check — a DSF/DFF file reports
// bit_depth=1 but is anything but lossy, and the user expects the
// rate label (DSD64/128/...) rather than "Hi-Res 1-bit".
const dsd = dsdLabel(codec);
const isVisible = dsd !== null || isHiRes(bitDepth, sampleRate);
- if (!isVisible) return null;
+ if (!isVisible || !visible) return null;
const text = label ?? dsd ?? `Hi-Res ${bitDepth}-bit`;
+ if (variant === "text") {
+ return (
+
+ {text}
+
+ );
+ }
if (variant === "inline") {
return (
diff --git a/src/components/player/PlayerBar.tsx b/src/components/player/PlayerBar.tsx
index 7f62338..3c721f4 100644
--- a/src/components/player/PlayerBar.tsx
+++ b/src/components/player/PlayerBar.tsx
@@ -13,6 +13,7 @@ import { useSleepTimer } from "../../hooks/useSleepTimer";
import { usePlayerBarLayout } from "../../hooks/usePlayerBarLayout";
import { Artwork } from "../common/Artwork";
import { ArtistLink } from "../common/ArtistLink";
+import { HiResBadge } from "../common/HiResBadge";
import { PlaybackControls } from "./PlaybackControls";
import { ProgressBar } from "./ProgressBar";
import { SleepTimerMenu } from "./SleepTimerMenu";
@@ -166,6 +167,19 @@ export function PlayerBar({ onNavigateToArtist }: PlayerBarProps) {
(currentTrack?.album_title ?? t("player.inactive"))
)}
+ {/* Spotify-style minimal quality label under the artist.
+ HiResBadge renders null when the track isn't Hi-Res /
+ DSD OR when the user has hidden the badge from
+ Settings → Appearance, so the row collapses naturally
+ for lossy / lossless-16-bit content. */}
+ {currentTrack && (
+
+ )}
{currentTrack && !isSpotify && (