diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx
index dc6e0018..3e36eafb 100644
--- a/desktop/src/__tests__/generalSettings.test.tsx
+++ b/desktop/src/__tests__/generalSettings.test.tsx
@@ -268,7 +268,7 @@ describe('Settings > General tab', () => {
expect((uiZoomHeading.compareDocumentPosition(webFetchHeading) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true)
})
- it('previews UI zoom while dragging and applies it only when the user releases the slider', async () => {
+ it('applies UI zoom while dragging so the slider and app zoom stay synchronized', async () => {
render()
fireEvent.click(screen.getByText('General'))
@@ -287,7 +287,9 @@ describe('Settings > General tab', () => {
})
expect(screen.getAllByText('125%')).toHaveLength(2)
- expect(useSettingsStore.getState().setUiZoom).not.toHaveBeenCalledWith(1.25)
+ expect(useSettingsStore.getState().setUiZoom).toHaveBeenCalledWith(1.25)
+ expect(useSettingsStore.getState().uiZoom).toBe(1.25)
+ expect(slider).toHaveValue('1.25')
expect(slider).toHaveClass('settings-zoom-range')
expect(slider.closest('.settings-zoom-control')).toHaveClass('is-dragging')
expect(slider.closest('.settings-zoom-control')).toHaveStyle({ '--settings-zoom-range-progress': '50%' })
@@ -297,6 +299,7 @@ describe('Settings > General tab', () => {
})
expect(useSettingsStore.getState().setUiZoom).toHaveBeenCalledWith(1.25)
+ expect(slider.closest('.settings-zoom-control')).not.toHaveClass('is-dragging')
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: 'Reset UI zoom to 100%' }))
@@ -305,6 +308,22 @@ describe('Settings > General tab', () => {
expect(useSettingsStore.getState().setUiZoom).toHaveBeenLastCalledWith(1)
})
+ it('updates the UI zoom slider when shortcut zoom changes the shared setting while Settings is open', async () => {
+ render()
+
+ fireEvent.click(screen.getByText('General'))
+
+ const slider = screen.getByLabelText('UI Zoom')
+
+ await act(async () => {
+ useSettingsStore.setState({ uiZoom: 1.1 })
+ })
+
+ expect(slider).toHaveValue('1.1')
+ expect(screen.getAllByText('110%')).toHaveLength(2)
+ expect(slider.closest('.settings-zoom-control')).toHaveStyle({ '--settings-zoom-range-progress': '40%' })
+ })
+
it('opens the Token usage tab from Settings navigation above Diagnostics', () => {
render()
diff --git a/desktop/src/pages/Settings.tsx b/desktop/src/pages/Settings.tsx
index fc34c1ae..01d07690 100644
--- a/desktop/src/pages/Settings.tsx
+++ b/desktop/src/pages/Settings.tsx
@@ -1396,7 +1396,7 @@ function GeneralSettings() {
const [isUiZoomDragging, setIsUiZoomDragging] = useState(false)
const webSearchDirty = JSON.stringify(webSearchDraft) !== JSON.stringify(webSearch)
const uiZoomPercent = Math.round(uiZoomDraft * 100)
- const uiZoomRangeProgress = `${((uiZoomDraft - UI_ZOOM_MIN) / (UI_ZOOM_MAX - UI_ZOOM_MIN)) * 100}%`
+ const uiZoomRangeProgress = `${Math.round(((uiZoomDraft - UI_ZOOM_MIN) / (UI_ZOOM_MAX - UI_ZOOM_MIN)) * 1000) / 10}%`
useEffect(() => {
setWebSearchDraft(webSearch)
@@ -1523,9 +1523,11 @@ function GeneralSettings() {
}
}
- const commitUiZoom = (value: number) => {
+ const commitUiZoom = (value: number, options: { keepDragging?: boolean } = {}) => {
const nextZoom = Number.isFinite(value) ? value : UI_ZOOM_DEFAULT
- setIsUiZoomDragging(false)
+ if (!options.keepDragging) {
+ setIsUiZoomDragging(false)
+ }
setUiZoomDraft(nextZoom)
setUiZoom(nextZoom)
}
@@ -1599,21 +1601,17 @@ function GeneralSettings() {
max={UI_ZOOM_MAX}
step={UI_ZOOM_STEP}
value={uiZoomDraft}
- onPointerDown={(e) => {
+ onPointerDown={() => {
setIsUiZoomDragging(true)
- e.currentTarget.setPointerCapture?.(e.pointerId)
- }}
- onPointerUp={(e) => commitUiZoom(e.currentTarget.valueAsNumber)}
- onPointerCancel={(e) => commitUiZoom(e.currentTarget.valueAsNumber)}
- onChange={(e) => setUiZoomDraft(e.currentTarget.valueAsNumber)}
- onKeyUp={(e) => {
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'].includes(e.key)) {
- commitUiZoom(e.currentTarget.valueAsNumber)
- }
}}
+ onPointerUp={() => setIsUiZoomDragging(false)}
+ onPointerCancel={() => setIsUiZoomDragging(false)}
+ onChange={(e) => commitUiZoom(e.currentTarget.valueAsNumber, { keepDragging: isUiZoomDragging })}
onBlur={(e) => {
if (uiZoomDraft !== uiZoom) {
commitUiZoom(e.currentTarget.valueAsNumber)
+ } else {
+ setIsUiZoomDragging(false)
}
}}
className="settings-zoom-range w-full"
diff --git a/desktop/src/theme/globals.css b/desktop/src/theme/globals.css
index 5e394bbe..88e7a40d 100644
--- a/desktop/src/theme/globals.css
+++ b/desktop/src/theme/globals.css
@@ -199,6 +199,7 @@
cursor: pointer;
background: transparent;
accent-color: var(--color-brand);
+ touch-action: none;
}
.settings-zoom-range:focus {