From d70060923bd3f16b23af0f5e93cb6458ce30162d Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Sat, 27 Jun 2026 02:11:09 +0200 Subject: [PATCH] refactor(settings): interface scale as a segmented picker (#1197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/components/settings/AppearanceTab.tsx | 60 +++++++---------------- src/styles/components/ui-scale-slider.css | 28 ----------- 2 files changed, 19 insertions(+), 69 deletions(-) diff --git a/src/components/settings/AppearanceTab.tsx b/src/components/settings/AppearanceTab.tsx index e3983ad9..37b8bfea 100644 --- a/src/components/settings/AppearanceTab.tsx +++ b/src/components/settings/AppearanceTab.tsx @@ -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[] = presets.map(p => ({ + id: String(p), + label: `${p}%`, + })); return ( - <> - - fontStore.setUiScale(presets[parseInt(e.target.value, 10)] / 100)} - className="ui-scale-slider" - /> - {currentPct}% - -
- {presets.map(p => { - const active = currentPct === p; - return ( - - ); - })} -
- + fontStore.setUiScale(parseInt(id, 10) / 100)} + /> ); })()} diff --git a/src/styles/components/ui-scale-slider.css b/src/styles/components/ui-scale-slider.css index 92c3065f..129c4405 100644 --- a/src/styles/components/ui-scale-slider.css +++ b/src/styles/components/ui-scale-slider.css @@ -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 {