Files
Psychotoxical-psysonic/src/components/settings/PlayerBarLayoutCustomizer.tsx
T
cucadmuh e8e41752a7 feat(playback): global speed with three strategies (#852)
* feat(playback): global speed with three strategies

Add Settings → Audio and player-bar controls for global playback speed
(speed with auto pitch correction as default, varispeed, manual pitch shift).
Time-stretch runs on a background worker; Orbit sessions force 1.0× passthrough.

* fix(playback): align seekbar, seek, and progress on content timeline

Unify UI timebase across varispeed and preserve strategies: full-track
duration, speed-scaled progress for DSP paths, and content-timeline seeks
without varispeed scaling. Reset the sample counter after seek so clicks
land correctly; restart playback on strategy/enable changes instead of
fragile hot-switching.

* fix(ui): anchor playback speed popover like volume controls

Replace the centered EQ-style modal with a player-bar popover (outside
click, Escape, reposition on scroll). Show compact controls in the bar
and overflow menu; keep strategy hints and labels in Settings only.

* docs(release): CHANGELOG and credits for playback speed (PR #852)

* docs(changelog): add playback speed entry for PR #852

* fix(clippy): simplify raw_counter_samples branch for CI

Collapse duplicate if branches flagged by clippy::if-same-then-else.

* fix(ui): wheel on pitch slider adjusts pitch in speed popover

In compact player-bar controls, scroll over the pitch row changes pitch;
elsewhere in the panel changes speed. Stop propagation so overflow menu
wheel does not tweak volume.

* fix(playback): address PR #852 review and drop ineffective dynamic imports

Translate playback-rate strings for de/fr/es/zh/nb/nl/ro; restamp sample
counter on live preserve-path speed changes; use neutral rate atomics for
radio progress; static-import playerStore in playListenSession (move preview
volume sync to previewPlayerVolumeSync side-effect module).

* fix(i18n): translate playback-rate strategy labels in all locales

Replace leftover English Varispeed/Pitch strings in ru and other non-en
settings blocks so popover strategy buttons and hints read natively.

* fix(i18n): refine German varispeed label to "Tonhöhe folgt dem Tempo"

---------

Co-authored-by: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com>
2026-05-22 18:59:28 +02:00

55 lines
2.2 KiB
TypeScript

import React from 'react';
import { useTranslation } from 'react-i18next';
import { Gauge, Heart, PictureInPicture2, SlidersVertical, Star } from 'lucide-react';
import LastfmIcon from '../LastfmIcon';
import {
usePlayerBarLayoutStore,
type PlayerBarLayoutItemId,
} from '../../store/playerBarLayoutStore';
const PLAYER_BAR_LAYOUT_LABEL_KEYS: Record<PlayerBarLayoutItemId, string> = {
starRating: 'settings.playerBarStarRating',
favorite: 'settings.playerBarFavorite',
lastfmLove: 'settings.playerBarLastfmLove',
playbackRate: 'settings.playerBarPlaybackRate',
equalizer: 'settings.playerBarEqualizer',
miniPlayer: 'settings.playerBarMiniPlayer',
};
const PLAYER_BAR_LAYOUT_ICONS: Record<PlayerBarLayoutItemId, React.ReactNode> = {
starRating: <Star size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />,
favorite: <Heart size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />,
lastfmLove: (
<span style={{ color: 'var(--text-muted)', display: 'inline-flex', flexShrink: 0 }} aria-hidden>
<LastfmIcon size={16} />
</span>
),
playbackRate: <Gauge size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />,
equalizer: <SlidersVertical size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />,
miniPlayer: <PictureInPicture2 size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />,
};
export function PlayerBarLayoutCustomizer() {
const { t } = useTranslation();
const items = usePlayerBarLayoutStore(s => s.items);
const toggleItem = usePlayerBarLayoutStore(s => s.toggleItem);
return (
<div className="settings-card" style={{ padding: '4px 0' }}>
{items.map((it) => {
const label = t(PLAYER_BAR_LAYOUT_LABEL_KEYS[it.id]);
return (
<div key={it.id} className="sidebar-customizer-row">
{PLAYER_BAR_LAYOUT_ICONS[it.id]}
<span style={{ flex: 1, fontSize: 14, opacity: it.visible ? 1 : 0.45 }}>{label}</span>
<label className="toggle-switch" aria-label={label}>
<input type="checkbox" checked={it.visible} onChange={() => toggleItem(it.id)} />
<span className="toggle-track" />
</label>
</div>
);
})}
</div>
);
}