refactor(settings): interface scale as a segmented picker (#1197)

Replace the Interface Scale zoom slider and its tick row with the shared
SettingsSegmented control — one button per preset (80–150%), matching the
Lyrics, Queue and Track-transitions sections. Drops the slider/tick
alignment issue entirely. Legacy off-preset values snap to the nearest
preset so one button is always active. Removes the now-dead slider CSS.
This commit is contained in:
Psychotoxical
2026-06-27 02:11:09 +02:00
committed by GitHub
parent d5ad275851
commit d70060923b
2 changed files with 19 additions and 69 deletions
+19 -41
View File
@@ -14,7 +14,8 @@ import { IS_LINUX, IS_WINDOWS } from '../../utils/platform';
import SettingsSubSection from '../SettingsSubSection';
import { SettingsGroup } from './SettingsGroup';
import { SettingsToggle } from './SettingsToggle';
import { SettingsSubCard, SettingsField, SettingsRow, SettingsValue } from './SettingsSubCard';
import { SettingsSubCard, SettingsField, SettingsValue } from './SettingsSubCard';
import { SettingsSegmented, type SegmentedOption } from './SettingsSegmented';
import { SeekbarPreview } from '../WaveformSeekPreview';
import WindowButtonPreview from '../WindowButtonPreview';
@@ -182,47 +183,24 @@ export function AppearanceTab() {
{(() => {
const presets = [80, 90, 100, 110, 125, 150];
const currentPct = Math.round(fontStore.uiScale * 100);
let idx = presets.indexOf(currentPct);
if (idx < 0) {
// Snap legacy off-preset values to the closest preset.
idx = presets.reduce((best, p, i) =>
Math.abs(p - currentPct) < Math.abs(presets[best] - currentPct) ? i : best, 0);
}
// Snap a legacy off-preset value to the closest preset so one
// button is always marked active.
const activePct = presets.includes(currentPct)
? currentPct
: presets.reduce(
(best, p) => (Math.abs(p - currentPct) < Math.abs(best - currentPct) ? p : best),
presets[0],
);
const options: SegmentedOption<string>[] = presets.map(p => ({
id: String(p),
label: `${p}%`,
}));
return (
<>
<SettingsRow>
<input
type="range"
min={0}
max={presets.length - 1}
step={1}
value={idx}
onChange={e => fontStore.setUiScale(presets[parseInt(e.target.value, 10)] / 100)}
className="ui-scale-slider"
/>
<SettingsValue>{currentPct}%</SettingsValue>
</SettingsRow>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
{presets.map(p => {
const active = currentPct === p;
return (
<button
key={p}
className="btn btn-ghost"
style={{
fontSize: 11,
padding: '2px 6px',
opacity: active ? 1 : 0.5,
color: active ? 'var(--accent)' : undefined,
}}
onClick={() => fontStore.setUiScale(p / 100)}
>
{p}%
</button>
);
})}
</div>
</>
<SettingsSegmented
options={options}
value={String(activePct)}
onChange={id => fontStore.setUiScale(parseInt(id, 10) / 100)}
/>
);
})()}
</SettingsField>
-28
View File
@@ -1,31 +1,3 @@
/* ─── UI Scale Slider ─── */
.ui-scale-slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
border-radius: 2px;
background: var(--border-subtle);
outline: none;
cursor: pointer;
}
.ui-scale-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--accent);
cursor: pointer;
transition: transform 0.1s ease, box-shadow 0.1s ease;
}
.ui-scale-slider::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
}
/* ── Folder Browser (Miller Columns) ──────────────────────────────────────── */
.folder-browser {