fix(settings): align AutoDJ and Hi-Res sub-options with the Normalization design (#1175)

* fix(settings): box the AutoDJ and Hi-Res sub-options like Normalization

The AutoDJ overlap-cap and the Hi-Res blend-rate options sat bare in their
sections instead of in the bordered sub-card the Normalization block uses for
its per-mode controls. Wrap both (and the crossfade-seconds slider, for
consistency within Track transitions) in the shared settings-norm-block card,
and drop the redundant SettingsGroup wrapper from the Hi-Res block — the Audio
tab already wraps that section in a group, so the block added a second border.

* docs(changelog): audio sub-section design fix; move recent entries to the end of Fixed

The #1172 and #1174 entries were prepended to the Fixed section instead of
appended; move them to the end where new entries belong, and add the #1175
audio settings entry.
This commit is contained in:
Psychotoxical
2026-06-24 21:53:04 +02:00
committed by GitHub
parent 9bbe69e7e7
commit 1031590742
3 changed files with 74 additions and 75 deletions
@@ -4,7 +4,6 @@ import {
sanitizeHiResCrossfadeResampleHz,
} from '../../../utils/audio/hiResCrossfadeResample';
import type { TFunction } from 'i18next';
import { SettingsGroup } from '../SettingsGroup';
interface Props {
enabled: boolean;
@@ -29,28 +28,28 @@ export function HiResCrossfadeResampleBlock({
if (!enabled) return null;
return (
<SettingsGroup>
<p className="settings-row-label" style={{ marginBottom: '0.5rem' }}>
{t('settings.hiResCrossfadeResampleTitle')}
</p>
<p className="settings-row-desc" style={{ marginBottom: '0.75rem' }}>
{t('settings.hiResCrossfadeResampleDesc')}
</p>
<div className="settings-segmented" style={{ marginBottom: '0.75rem' }}>
{HI_RES_CROSSFADE_RESAMPLE_OPTIONS.map((hz) => (
<button
key={hz}
type="button"
className={`btn ${resampleHz === hz ? 'btn-primary' : 'btn-ghost'}`}
onClick={() => onResampleHzChange(sanitizeHiResCrossfadeResampleHz(hz))}
>
{labelForHz(t, hz)}
</button>
))}
<div className="settings-norm-block" style={{ marginTop: '0.85rem' }}>
<div className="settings-norm-field">
<span className="settings-norm-label" style={{ minWidth: 0 }}>
{t('settings.hiResCrossfadeResampleTitle')}
</span>
<div className="settings-norm-help">{t('settings.hiResCrossfadeResampleDesc')}</div>
<div className="settings-segmented">
{HI_RES_CROSSFADE_RESAMPLE_OPTIONS.map((hz) => (
<button
key={hz}
type="button"
className={`btn ${resampleHz === hz ? 'btn-primary' : 'btn-ghost'}`}
onClick={() => onResampleHzChange(sanitizeHiResCrossfadeResampleHz(hz))}
>
{labelForHz(t, hz)}
</button>
))}
</div>
<div className="settings-norm-help" role="note">
{t('settings.hiResCrossfadeResampleWarning')}
</div>
</div>
<p className="settings-row-desc" role="note" style={{ marginBottom: 0, opacity: 0.85 }}>
{t('settings.hiResCrossfadeResampleWarning')}
</p>
</SettingsGroup>
</div>
);
}
@@ -70,36 +70,33 @@ export function TrackTransitionsBlock({ t }: Props) {
</div>
{mode === 'crossfade' && (
<div style={{ paddingLeft: '1rem', marginTop: '0.7rem', display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap' }}>
<input
type="range"
min={0.1}
max={10}
step={0.1}
value={auth.crossfadeSecs}
disabled={hostControlled}
onChange={e => auth.setCrossfadeSecs(parseFloat(e.target.value))}
style={{ flex: 1, minWidth: 80, maxWidth: 200 }}
id="crossfade-secs-slider"
/>
<span style={{ fontSize: 13, color: 'var(--text-secondary)', minWidth: 36 }}>
{t('settings.crossfadeSecs', { n: auth.crossfadeSecs.toFixed(1) })}
</span>
<div className="settings-norm-block" style={{ marginTop: '0.85rem' }}>
<div className="settings-norm-row">
<input
type="range"
min={0.1}
max={10}
step={0.1}
value={auth.crossfadeSecs}
disabled={hostControlled}
onChange={e => auth.setCrossfadeSecs(parseFloat(e.target.value))}
id="crossfade-secs-slider"
/>
<span className="settings-norm-value">
{t('settings.crossfadeSecs', { n: auth.crossfadeSecs.toFixed(1) })}
</span>
</div>
</div>
)}
{mode === 'autodj' && (
<>
<div style={{ paddingLeft: '1rem', fontSize: 12, color: 'var(--text-muted)', marginTop: '0.7rem' }}>
{t('settings.autoDjDesc')}
</div>
<div style={{ paddingLeft: '1rem', marginTop: '0.9rem' }}>
<p className="settings-row-label" style={{ marginBottom: '0.45rem' }}>
<div className="settings-norm-block" style={{ marginTop: '0.85rem' }}>
<div className="settings-norm-help">{t('settings.autoDjDesc')}</div>
<div className="settings-norm-field">
<span className="settings-norm-label" style={{ minWidth: 0 }}>
{t('settings.autodjOverlapCapTitle')}
</p>
<p className="settings-row-desc" style={{ marginBottom: '0.6rem' }}>
{t('settings.autodjOverlapCapDesc')}
</p>
<div className="settings-segmented" style={{ marginBottom: auth.autodjOverlapCapMode === 'limit' ? '0.65rem' : 0 }}>
</span>
<div className="settings-norm-help">{t('settings.autodjOverlapCapDesc')}</div>
<div className="settings-segmented">
<button
type="button"
className={`btn ${auth.autodjOverlapCapMode === 'auto' ? 'btn-primary' : 'btn-ghost'}`}
@@ -118,7 +115,7 @@ export function TrackTransitionsBlock({ t }: Props) {
</button>
</div>
{auth.autodjOverlapCapMode === 'limit' && (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap' }}>
<div className="settings-norm-row">
<input
type="range"
min={AUTODJ_OVERLAP_CAP_MIN_SEC}
@@ -127,25 +124,22 @@ export function TrackTransitionsBlock({ t }: Props) {
value={auth.autodjOverlapCapSec}
disabled={hostControlled}
onChange={e => auth.setAutodjOverlapCapSec(parseInt(e.target.value, 10))}
style={{ flex: 1, minWidth: 80, maxWidth: 200 }}
id="autodj-overlap-cap-slider"
/>
<span style={{ fontSize: 13, color: 'var(--text-secondary)', minWidth: 48 }}>
<span className="settings-norm-value">
{t('settings.autodjOverlapCapSecs', { n: auth.autodjOverlapCapSec })}
</span>
</div>
)}
</div>
<div style={{ paddingLeft: '1rem', marginTop: '0.7rem' }}>
<SettingsToggle
label={t('settings.autodjSmoothSkip')}
desc={t('settings.autodjSmoothSkipDesc')}
checked={auth.autodjSmoothSkip}
disabled={hostControlled}
onChange={auth.setAutodjSmoothSkip}
/>
</div>
</>
<SettingsToggle
label={t('settings.autodjSmoothSkip')}
desc={t('settings.autodjSmoothSkipDesc')}
checked={auth.autodjSmoothSkip}
disabled={hostControlled}
onChange={auth.setAutodjSmoothSkip}
/>
</div>
)}
</SettingsGroup>
);