From 9bbdef0949f5f62abd90450adbbbf01dc6fe6296 Mon Sep 17 00:00:00 2001 From: Kevin Rajan <7121943+kvnloo@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:23:51 -0600 Subject: [PATCH] fix/Improving UX for Display/Scaling Changes (#332) * fix:scaling - in the settings pane, when changing the scale size by dragging the icon across the bar, the view reloads as you are doing it so it makes it difficult to change the scale properly. also the - and + buttons don't increase or decrease the scale by 5%. these have now been fixed. * added NaN guard * added type=button --------- Co-authored-by: Andy <119136210+AndyMik90@users.noreply.github.com> --- .../components/settings/DisplaySettings.tsx | 125 +++++++++++++++--- .../src/shared/i18n/locales/en/settings.json | 3 +- .../src/shared/i18n/locales/fr/settings.json | 3 +- implementation_plan.json | 26 ++++ 4 files changed, 131 insertions(+), 26 deletions(-) create mode 100644 implementation_plan.json diff --git a/apps/frontend/src/renderer/components/settings/DisplaySettings.tsx b/apps/frontend/src/renderer/components/settings/DisplaySettings.tsx index 5eb05573..1b2f1393 100644 --- a/apps/frontend/src/renderer/components/settings/DisplaySettings.tsx +++ b/apps/frontend/src/renderer/components/settings/DisplaySettings.tsx @@ -1,4 +1,5 @@ -import { Monitor, ZoomIn, ZoomOut, RotateCcw } from 'lucide-react'; +import { useState } from 'react'; +import { Monitor, ZoomIn, ZoomOut, RotateCcw, Check } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { cn } from '../../lib/utils'; import { Label } from '../ui/label'; @@ -30,19 +31,63 @@ export function DisplaySettings({ settings, onSettingsChange }: DisplaySettingsP const currentScale = settings.uiScale ?? UI_SCALE_DEFAULT; - const handleScaleChange = (newScale: number) => { - // Clamp to valid range + // Local state for pending scale changes - prevents view reload until user applies + const [pendingScale, setPendingScale] = useState(null); + + // Track the last scale that was committed to the store (triggers view reload) + // This is different from currentScale which updates on every onSettingsChange call + const [committedScale, setCommittedScale] = useState(currentScale); + + // Display value: use pending scale if set, otherwise use current applied scale + const displayScale = pendingScale ?? currentScale; + + // Check if there are pending changes to apply + // Compare against committedScale (store-applied value), not currentScale (display value) + const hasPendingChanges = pendingScale !== null && pendingScale !== committedScale; + + // Update pending scale (for slider and +/- buttons) - doesn't trigger view reload + const updatePendingScale = (newScale: number) => { const clampedScale = Math.max(UI_SCALE_MIN, Math.min(UI_SCALE_MAX, newScale)); - - // Update local draft state + setPendingScale(clampedScale); + // Update settings for display but don't trigger store update (no view reload) onSettingsChange({ ...settings, uiScale: clampedScale }); + }; - // Apply immediately to store for live preview (triggers App.tsx useEffect) + // Apply pending changes to store (triggers view reload) + const handleApplyChanges = () => { + if (pendingScale !== null) { + updateStoreSettings({ uiScale: pendingScale }); + setCommittedScale(pendingScale); + setPendingScale(null); + } + }; + + // Handle preset button clicks - apply immediately (presets are intentional selections) + const handlePresetChange = (newScale: number) => { + const clampedScale = Math.max(UI_SCALE_MIN, Math.min(UI_SCALE_MAX, newScale)); + onSettingsChange({ ...settings, uiScale: clampedScale }); updateStoreSettings({ uiScale: clampedScale }); + setCommittedScale(clampedScale); + setPendingScale(null); + }; + + // Handle slider drag - only update pending state + const handleSliderChange = (newScale: number) => { + if (Number.isNaN(newScale)) return; + updatePendingScale(newScale); + }; + + // Handle zoom button clicks - increment/decrement by step (updates pending state) + const handleZoomOut = () => { + updatePendingScale(displayScale - UI_SCALE_STEP); + }; + + const handleZoomIn = () => { + updatePendingScale(displayScale + UI_SCALE_STEP); }; const handleReset = () => { - handleScaleChange(UI_SCALE_DEFAULT); + handlePresetChange(UI_SCALE_DEFAULT); }; return ( @@ -62,8 +107,9 @@ export function DisplaySettings({ settings, onSettingsChange }: DisplaySettingsP const isSelected = currentScale === preset.value; return ( handleScaleChange(parseInt(e.target.value, 10))} + value={displayScale} + onChange={(e) => handleSliderChange(parseInt(e.target.value, 10))} className={cn( 'flex-1 h-2 bg-muted rounded-lg appearance-none cursor-pointer', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', @@ -143,7 +203,35 @@ export function DisplaySettings({ settings, onSettingsChange }: DisplaySettingsP '[&::-moz-range-thumb]:hover:scale-110' )} /> - + + {/* Scale markers */} @@ -152,13 +240,6 @@ export function DisplaySettings({ settings, onSettingsChange }: DisplaySettingsP {UI_SCALE_MAX}% - - {/* Preview hint */} -
-

- {t('scale.preview')} -

-
); diff --git a/apps/frontend/src/shared/i18n/locales/en/settings.json b/apps/frontend/src/shared/i18n/locales/en/settings.json index da7a34fa..0ad3a67d 100644 --- a/apps/frontend/src/shared/i18n/locales/en/settings.json +++ b/apps/frontend/src/shared/i18n/locales/en/settings.json @@ -49,8 +49,7 @@ "fineTuneDescription": "Adjust from 75% to 200% in 5% increments", "default": "Default", "comfortable": "Comfortable", - "large": "Large", - "preview": "Changes preview immediately. Click Save Settings to persist." + "large": "Large" }, "general": { "otherAgentSettings": "Other Agent Settings", diff --git a/apps/frontend/src/shared/i18n/locales/fr/settings.json b/apps/frontend/src/shared/i18n/locales/fr/settings.json index 76486585..04477a42 100644 --- a/apps/frontend/src/shared/i18n/locales/fr/settings.json +++ b/apps/frontend/src/shared/i18n/locales/fr/settings.json @@ -49,8 +49,7 @@ "fineTuneDescription": "Ajustez de 75% à 200% par incréments de 5%", "default": "Par défaut", "comfortable": "Confortable", - "large": "Grand", - "preview": "Les changements sont prévisualisés immédiatement. Cliquez sur Enregistrer pour les conserver." + "large": "Grand" }, "general": { "otherAgentSettings": "Autres paramètres de l'agent", diff --git a/implementation_plan.json b/implementation_plan.json new file mode 100644 index 00000000..d44f4f68 --- /dev/null +++ b/implementation_plan.json @@ -0,0 +1,26 @@ +{ + "spec_id": "011-fix-scale-adjustment-and-view-reload-issues", + "subtasks": [ + { + "id": "1", + "title": "Fix slider to defer view reload until drag ends and add zoom button functionality", + "status": "completed" + } + ], + "qa_signoff": { + "status": "fixes_applied", + "timestamp": "2025-12-27T02:20:00Z", + "fix_session": 0, + "issues_fixed": [ + { + "title": "Remove preview hint textbox", + "fix_commit": "2653019" + }, + { + "title": "Remove unused preview translation keys", + "fix_commit": "e17536c" + } + ], + "ready_for_qa_revalidation": true + } +}