mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Stop zoom slider flicker during drag
Dragging the Settings zoom slider was applying app-wide zoom on every range change. That resized the Settings panel and the range control under the pointer, creating visible flicker and a poor drag feel. The slider now keeps drag movement local to the Settings preview and commits the shared app zoom only when the drag is released or when keyboard/non-pointer changes occur. The control step is also reduced to one percent so adjustments feel less coarse. Constraint: App-wide zoom changes resize the control currently being dragged Rejected: Throttle live app zoom during drag | still resizes the target under the pointer and can flicker on slower WebViews Confidence: high Scope-risk: narrow Directive: Do not apply app-wide zoom continuously during pointer drag; keep live resizing to keyboard shortcuts and final slider commit Tested: bun run check:desktop Tested: agent-browser Settings smoke confirms drag preview keeps cssZoom at 1 until release, then commits 1.23
This commit is contained in:
parent
c279643cce
commit
5965c4a3bd
@ -268,7 +268,7 @@ describe('Settings > General tab', () => {
|
||||
expect((uiZoomHeading.compareDocumentPosition(webFetchHeading) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true)
|
||||
})
|
||||
|
||||
it('applies UI zoom while dragging so the slider and app zoom stay synchronized', async () => {
|
||||
it('previews UI zoom while dragging and applies it once on release', async () => {
|
||||
render(<Settings />)
|
||||
|
||||
fireEvent.click(screen.getByText('General'))
|
||||
@ -278,6 +278,7 @@ describe('Settings > General tab', () => {
|
||||
expect(screen.getByText('0 resets zoom to 100%.')).toBeInTheDocument()
|
||||
|
||||
const slider = screen.getByLabelText('UI Zoom')
|
||||
expect(slider).toHaveAttribute('step', '0.01')
|
||||
|
||||
fireEvent.pointerDown(slider, { pointerId: 1 })
|
||||
await act(async () => {
|
||||
@ -287,8 +288,8 @@ describe('Settings > General tab', () => {
|
||||
})
|
||||
|
||||
expect(screen.getAllByText('125%')).toHaveLength(2)
|
||||
expect(useSettingsStore.getState().setUiZoom).toHaveBeenCalledWith(1.25)
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1.25)
|
||||
expect(useSettingsStore.getState().setUiZoom).not.toHaveBeenCalledWith(1.25)
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1)
|
||||
expect(slider).toHaveValue('1.25')
|
||||
expect(slider).toHaveClass('settings-zoom-range')
|
||||
expect(slider.closest('.settings-zoom-control')).toHaveClass('is-dragging')
|
||||
|
||||
@ -4,7 +4,7 @@ export const DEFAULT_APP_ZOOM = 1
|
||||
export const MIN_APP_ZOOM = 0.5
|
||||
export const MAX_APP_ZOOM = 2
|
||||
export const APP_ZOOM_STEP = 0.1
|
||||
export const APP_ZOOM_CONTROL_STEP = 0.05
|
||||
export const APP_ZOOM_CONTROL_STEP = 0.01
|
||||
|
||||
export type AppZoomAction = 'in' | 'out' | 'reset'
|
||||
|
||||
|
||||
@ -1394,6 +1394,7 @@ function GeneralSettings() {
|
||||
const [notificationActionRunning, setNotificationActionRunning] = useState(false)
|
||||
const [uiZoomDraft, setUiZoomDraft] = useState(uiZoom)
|
||||
const [isUiZoomDragging, setIsUiZoomDragging] = useState(false)
|
||||
const isUiZoomDraggingRef = useRef(false)
|
||||
const webSearchDirty = JSON.stringify(webSearchDraft) !== JSON.stringify(webSearch)
|
||||
const uiZoomPercent = Math.round(uiZoomDraft * 100)
|
||||
const uiZoomRangeProgress = `${Math.round(((uiZoomDraft - UI_ZOOM_MIN) / (UI_ZOOM_MAX - UI_ZOOM_MIN)) * 1000) / 10}%`
|
||||
@ -1523,11 +1524,14 @@ function GeneralSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
const commitUiZoom = (value: number, options: { keepDragging?: boolean } = {}) => {
|
||||
const setUiZoomDraggingState = (dragging: boolean) => {
|
||||
isUiZoomDraggingRef.current = dragging
|
||||
setIsUiZoomDragging(dragging)
|
||||
}
|
||||
|
||||
const commitUiZoom = (value: number) => {
|
||||
const nextZoom = Number.isFinite(value) ? value : UI_ZOOM_DEFAULT
|
||||
if (!options.keepDragging) {
|
||||
setIsUiZoomDragging(false)
|
||||
}
|
||||
setUiZoomDraggingState(false)
|
||||
setUiZoomDraft(nextZoom)
|
||||
setUiZoom(nextZoom)
|
||||
}
|
||||
@ -1602,16 +1606,27 @@ function GeneralSettings() {
|
||||
step={UI_ZOOM_STEP}
|
||||
value={uiZoomDraft}
|
||||
onPointerDown={() => {
|
||||
setIsUiZoomDragging(true)
|
||||
setUiZoomDraggingState(true)
|
||||
}}
|
||||
onPointerUp={(e) => commitUiZoom(e.currentTarget.valueAsNumber)}
|
||||
onPointerCancel={() => {
|
||||
setUiZoomDraggingState(false)
|
||||
setUiZoomDraft(uiZoom)
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const nextZoom = Number.isFinite(e.currentTarget.valueAsNumber)
|
||||
? e.currentTarget.valueAsNumber
|
||||
: UI_ZOOM_DEFAULT
|
||||
setUiZoomDraft(nextZoom)
|
||||
if (!isUiZoomDraggingRef.current) {
|
||||
setUiZoom(nextZoom)
|
||||
}
|
||||
}}
|
||||
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)
|
||||
setUiZoomDraggingState(false)
|
||||
}
|
||||
}}
|
||||
className="settings-zoom-range w-full"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user