From ca035ebfc4ba700ace2a3006a36154a433e6ea2f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 18 Jul 2026 00:36:30 -0400 Subject: [PATCH 1/6] refac --- .../lib/components/Settings/Appearance.svelte | 102 ++++++++++++++++-- cptr/frontend/src/lib/i18n/locales/de.json | 3 + cptr/frontend/src/lib/i18n/locales/en.json | 3 + cptr/frontend/src/lib/i18n/locales/es.json | 3 + cptr/frontend/src/lib/i18n/locales/fr.json | 3 + cptr/frontend/src/lib/i18n/locales/ja.json | 3 + cptr/frontend/src/lib/i18n/locales/ko.json | 3 + cptr/frontend/src/lib/i18n/locales/pt-BR.json | 3 + cptr/frontend/src/lib/i18n/locales/ru.json | 3 + cptr/frontend/src/lib/i18n/locales/zh-CN.json | 3 + cptr/frontend/src/lib/i18n/locales/zh-TW.json | 3 + cptr/frontend/src/lib/stores.ts | 54 +++++++++- cptr/frontend/src/lib/utils/appearance.ts | 25 ++++- pyproject.toml | 2 +- uv.lock | 2 +- 15 files changed, 198 insertions(+), 17 deletions(-) diff --git a/cptr/frontend/src/lib/components/Settings/Appearance.svelte b/cptr/frontend/src/lib/components/Settings/Appearance.svelte index 34af60cb..52dca493 100644 --- a/cptr/frontend/src/lib/components/Settings/Appearance.svelte +++ b/cptr/frontend/src/lib/components/Settings/Appearance.svelte @@ -2,10 +2,20 @@ import { toast } from 'svelte-sonner'; import Icon from '../Icon.svelte'; import { t } from '$lib/i18n'; - import { expandToolDetails, textScale, theme, themeConfig, widescreenMode } from '$lib/stores'; + import { + borderContrast, + expandToolDetails, + textScale, + theme, + themeConfig, + widescreenMode + } from '$lib/stores'; import ToggleSwitch from '$lib/components/common/ToggleSwitch.svelte'; import type { Theme, ThemeConfig } from '$lib/stores'; import { + DEFAULT_BORDER_CONTRAST, + MAX_BORDER_CONTRAST, + normalizeBorderContrast, normalizeHexColor, resolveThemeMode, resolveThemeConfig, @@ -14,16 +24,18 @@ const minTextScale = 1; const maxTextScale = 1.5; + const borderContrastStep = 0.5; let fileInput: HTMLInputElement; let scaleEnabled = $state(false); let scaleDraft = $state(1); + let borderContrastDraft = $state(DEFAULT_BORDER_CONTRAST); let colorDrafts = $state({ background: '', foreground: '' }); const resolvedTheme = $derived(resolveThemeMode($theme)); const resolvedConfig = $derived(resolveThemeConfig($theme, $themeConfig)); const hasCustomAppearance = $derived( - Boolean($themeConfig || $textScale !== null || $widescreenMode) + Boolean($themeConfig || $textScale !== null || $borderContrast !== null || $widescreenMode) ); $effect(() => { @@ -37,6 +49,7 @@ } else if (!scaleEnabled) { scaleDraft = 1; } + borderContrastDraft = $borderContrast ?? DEFAULT_BORDER_CONTRAST; }); function setTheme(v: Theme) { @@ -88,6 +101,11 @@ return `${scale.toFixed(scale % 1 === 0 ? 0 : 2)}x`; } + function borderContrastLabel(contrast: number | null) { + if (contrast === null) return $t('general.default'); + return `${contrast.toFixed(contrast % 1 === 0 ? 0 : 1)}%`; + } + function setTextScalePreference(scale: number | string) { const next = normalizeTextScale(scale); scaleDraft = next; @@ -100,11 +118,18 @@ } } + function setBorderContrastPreference(contrast: number | string) { + const next = normalizeBorderContrast(contrast) ?? DEFAULT_BORDER_CONTRAST; + borderContrastDraft = next; + borderContrast.set(next === DEFAULT_BORDER_CONTRAST ? null : next); + } + function resetAppearance() { themeConfig.set(null); scaleEnabled = false; scaleDraft = 1; textScale.set(null); + borderContrast.set(null); widescreenMode.set(false); } @@ -113,6 +138,7 @@ theme: $theme, themeConfig: sanitizeThemeConfig($themeConfig), textScale: $textScale, + borderContrast: $borderContrast, widescreenMode: $widescreenMode }; @@ -162,11 +188,15 @@ : undefined; const importedWidescreenMode = typeof parsed?.widescreenMode === 'boolean' ? parsed.widescreenMode : undefined; + const importedBorderContrast = + normalizeBorderContrast(parsed?.borderContrast) ?? + (parsed?.highContrastBorders === true ? 12 : undefined); if ( !importedConfig && !importedTheme && importedScale === undefined && + importedBorderContrast === undefined && importedWidescreenMode === undefined ) { throw new Error('empty theme'); @@ -174,6 +204,10 @@ if (importedTheme) theme.set(importedTheme); if (importedConfig) themeConfig.set(importedConfig); if (importedScale !== undefined) textScale.set(importedScale === 1 ? null : importedScale); + if (importedBorderContrast !== undefined) + borderContrast.set( + importedBorderContrast === DEFAULT_BORDER_CONTRAST ? null : importedBorderContrast + ); if (importedWidescreenMode !== undefined) widescreenMode.set(importedWidescreenMode); toast.success($t('appearance.imported')); } catch { @@ -275,6 +309,57 @@ /> +
+
+ + {$t('appearance.borderContrast')} + + +
+
+ + setBorderContrastPreference(borderContrastDraft)} + /> + +
+
+