From 4a0056aec9128e75ad5321031ae82f3f007bb37d Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Mon, 10 Aug 2026 11:23:14 -0400 Subject: [PATCH 1/3] Make right-click recenter opt-in Right-click recentering the annotator has been on unconditionally since #1747, which fights right-click's primary role as the edit action. Gate it on a User Settings toggle, defaulting off. --- .../components/UserSettingsDialog.vue | 8 ++++ client/dive-common/store/settings.ts | 9 ++++ .../useLayerManagerAlignedView.spec.ts | 41 ++++++++++++++++++- .../useLayerManagerAlignedView.ts | 8 ++-- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/client/dive-common/components/UserSettingsDialog.vue b/client/dive-common/components/UserSettingsDialog.vue index d5c69d7b1..1a7039b91 100644 --- a/client/dive-common/components/UserSettingsDialog.vue +++ b/client/dive-common/components/UserSettingsDialog.vue @@ -38,6 +38,14 @@ export default defineComponent({ hint="Show multi-camera tools in the top toolbar when a track is selected." persistent-hint /> + warpLayer), geoOn: vi.fn(), + center: vi.fn(), layers: () => [nativeLayer, warpLayer], }; @@ -92,7 +94,7 @@ function makeHarness() { }); const wrapper = mount(Host); return { - wrapper, annotator, nativeFeature, warpFeature, imgA, + wrapper, annotator, nativeFeature, warpFeature, imgA, viewer, }; } @@ -225,3 +227,40 @@ describe('useLayerManagerAlignedView large-image native hide', () => { expect(nextOsm.visible).toHaveBeenCalledWith(false); }); }); + +describe('right-click recenter', () => { + /** The mouseclick handler AlignedImageLayer registered on the viewer. */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function clickHandler(viewer: any) { + const call = viewer.geoOn.mock.calls.find((c: unknown[]) => c[0] === 'geo_mouseclick'); + return call[1] as (e: unknown) => void; + } + const rightClick = { geo: { x: 12, y: 34 }, buttonsDown: { right: true } }; + + afterEach(() => { + clientSettings.navigationSettings.rightClickRecenter = false; + }); + + it('does not recenter by default', async () => { + const { viewer } = makeHarness(); + await nextTick(); + clickHandler(viewer)(rightClick); + expect(viewer.center).not.toHaveBeenCalled(); + }); + + it('recenters on right-click once the setting is enabled', async () => { + clientSettings.navigationSettings.rightClickRecenter = true; + const { viewer } = makeHarness(); + await nextTick(); + clickHandler(viewer)(rightClick); + expect(viewer.center).toHaveBeenCalledWith({ x: 12, y: 34 }); + }); + + it('ignores a left-click even when enabled', async () => { + clientSettings.navigationSettings.rightClickRecenter = true; + const { viewer } = makeHarness(); + await nextTick(); + clickHandler(viewer)({ geo: { x: 1, y: 2 }, buttonsDown: { left: true } }); + expect(viewer.center).not.toHaveBeenCalled(); + }); +}); diff --git a/client/src/components/layerManager/useLayerManagerAlignedView.ts b/client/src/components/layerManager/useLayerManagerAlignedView.ts index 0ae34aa1c..d09ceffcd 100644 --- a/client/src/components/layerManager/useLayerManagerAlignedView.ts +++ b/client/src/components/layerManager/useLayerManagerAlignedView.ts @@ -1,6 +1,7 @@ import { computed, watch, ComputedRef, Ref, } from 'vue'; +import { clientSettings } from 'dive-common/store/settings'; import type { AggregateMediaController, MediaController } from '../annotators/mediaControllerType'; import type AlignedViewStore from '../../alignedView/AlignedViewStore'; import AlignedImageLayer from '../../layers/AlignedImageLayer'; @@ -123,9 +124,10 @@ export default function useLayerManagerAlignedView(options: { } }, getTransform: () => alignedDisplayTransform.value, - // Right-click means "remove last point" while creating/editing - // geometry; recenter everywhere else. - getRecenterEnabled: () => !editingModeRef.value, + // Opt-in, and never while creating/editing geometry, where right-click + // already means "remove last point". + getRecenterEnabled: () => clientSettings.navigationSettings.rightClickRecenter + && !editingModeRef.value, }); /** From 14cb2b8231a49e9d530622d47acc4d6c220ce203 Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Mon, 10 Aug 2026 12:40:10 -0400 Subject: [PATCH 2/3] Gate right-click recenter on the camera-lock toggle Reuse the existing "center camera on selected track" control instead of a second setting for the same idea. Still off by default. --- client/dive-common/components/UserSettingsDialog.vue | 8 -------- client/dive-common/store/settings.ts | 9 --------- .../layerManager/useLayerManagerAlignedView.spec.ts | 12 ++++++------ .../layerManager/useLayerManagerAlignedView.ts | 7 ++++--- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/client/dive-common/components/UserSettingsDialog.vue b/client/dive-common/components/UserSettingsDialog.vue index 1a7039b91..d5c69d7b1 100644 --- a/client/dive-common/components/UserSettingsDialog.vue +++ b/client/dive-common/components/UserSettingsDialog.vue @@ -38,14 +38,6 @@ export default defineComponent({ hint="Show multi-camera tools in the top toolbar when a track is selected." persistent-hint /> - { const rightClick = { geo: { x: 12, y: 34 }, buttonsDown: { right: true } }; afterEach(() => { - clientSettings.navigationSettings.rightClickRecenter = false; + clientSettings.annotatorPreferences.lockedCamera.enabled = false; }); - it('does not recenter by default', async () => { + it('does not recenter while the camera lock is off', async () => { const { viewer } = makeHarness(); await nextTick(); clickHandler(viewer)(rightClick); expect(viewer.center).not.toHaveBeenCalled(); }); - it('recenters on right-click once the setting is enabled', async () => { - clientSettings.navigationSettings.rightClickRecenter = true; + it('recenters on right-click once the camera lock is on', async () => { + clientSettings.annotatorPreferences.lockedCamera.enabled = true; const { viewer } = makeHarness(); await nextTick(); clickHandler(viewer)(rightClick); expect(viewer.center).toHaveBeenCalledWith({ x: 12, y: 34 }); }); - it('ignores a left-click even when enabled', async () => { - clientSettings.navigationSettings.rightClickRecenter = true; + it('ignores a left-click even when the lock is on', async () => { + clientSettings.annotatorPreferences.lockedCamera.enabled = true; const { viewer } = makeHarness(); await nextTick(); clickHandler(viewer)({ geo: { x: 1, y: 2 }, buttonsDown: { left: true } }); diff --git a/client/src/components/layerManager/useLayerManagerAlignedView.ts b/client/src/components/layerManager/useLayerManagerAlignedView.ts index d09ceffcd..237dff206 100644 --- a/client/src/components/layerManager/useLayerManagerAlignedView.ts +++ b/client/src/components/layerManager/useLayerManagerAlignedView.ts @@ -124,9 +124,10 @@ export default function useLayerManagerAlignedView(options: { } }, getTransform: () => alignedDisplayTransform.value, - // Opt-in, and never while creating/editing geometry, where right-click - // already means "remove last point". - getRecenterEnabled: () => clientSettings.navigationSettings.rightClickRecenter + // Follows the camera-lock toggle, the existing control for "the camera may + // recenter itself". Never while creating/editing geometry, where + // right-click already means "remove last point". + getRecenterEnabled: () => !!clientSettings.annotatorPreferences.lockedCamera.enabled && !editingModeRef.value, }); From 84e54728ca243d2b39da485ecf6fd867f4859820 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Mon, 10 Aug 2026 14:28:59 -0400 Subject: [PATCH 3/3] suppress warnings in test files --- .../components/layerManager/useLayerManagerAlignedView.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/layerManager/useLayerManagerAlignedView.spec.ts b/client/src/components/layerManager/useLayerManagerAlignedView.spec.ts index 9b9eace1c..b3129596d 100644 --- a/client/src/components/layerManager/useLayerManagerAlignedView.spec.ts +++ b/client/src/components/layerManager/useLayerManagerAlignedView.spec.ts @@ -1,4 +1,5 @@ // @vitest-environment jsdom +/* eslint-disable vue/one-component-per-file -- harness Host components for mount() */ import { defineComponent, ref, nextTick } from 'vue'; // eslint-disable-next-line import/no-extraneous-dependencies -- @vue/test-utils is only used in tests import { mount } from '@vue/test-utils';