From 21899380b14ad294d5886882ceadf33a42c2d972 Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Sat, 24 Jan 2026 14:29:50 -0500 Subject: [PATCH 1/3] add auto-save annotations feature with configurable toggle Adds a new setting to automatically save annotation changes after a 2-second debounce delay. The feature batches rapid edits to reduce server requests and respects readonly mode. Disabled by default for backward compatibility. Co-Authored-By: Claude Opus 4.5 --- .../components/TrackSettingsPanel.vue | 40 +++++++++++++++++++ client/dive-common/components/Viewer.vue | 27 +++++++++++++ client/dive-common/store/settings.ts | 6 +++ 3 files changed, 73 insertions(+) diff --git a/client/dive-common/components/TrackSettingsPanel.vue b/client/dive-common/components/TrackSettingsPanel.vue index 71b1ec1b2..40a1291c7 100644 --- a/client/dive-common/components/TrackSettingsPanel.vue +++ b/client/dive-common/components/TrackSettingsPanel.vue @@ -33,6 +33,7 @@ export default defineComponent({ filterTracksByFrame: 'Filter the track list by those with detections in the current frame', autoZoom: 'Automatically zoom to the track when selected', showMultiCamToolbar: 'Show multi-camera tools in the top toolbar when a track is selected', + autoSave: 'Automatically save annotation changes after a short delay. Changes are batched to reduce server requests.', }); const modes = ref(['Track', 'Detection']); // Add unknown as the default type to the typeList @@ -362,6 +363,45 @@ export default defineComponent({ + +
+ Auto-Save Settings +
+ + + + + + + + {{ help.autoSave }} + + + diff --git a/client/dive-common/components/Viewer.vue b/client/dive-common/components/Viewer.vue index b3da641be..d3e863d0f 100644 --- a/client/dive-common/components/Viewer.vue +++ b/client/dive-common/components/Viewer.vue @@ -467,6 +467,32 @@ export default defineComponent({ watch(imageEnhancements, debouncedSaveImageEnhancements, { deep: true }); + // Auto-save annotations when enabled + const debouncedAutoSave = debounce( + async () => { + if (readonlyState.value) return; + if (pendingSaveCount.value === 0) return; + if (saveInProgress.value) return; + await save(props.currentSet); + }, + 2000, + { trailing: true, leading: false }, + ); + + watch( + pendingSaveCount, + (newCount, oldCount) => { + if ( + clientSettings.autoSaveSettings.enabled + && newCount > oldCount + && newCount > 0 + && !readonlyState.value + ) { + debouncedAutoSave(); + } + }, + ); + // Navigation Guards used by parent component async function warnBrowserExit(event: BeforeUnloadEvent) { if (pendingSaveCount.value === 0) return; @@ -766,6 +792,7 @@ export default defineComponent({ handleResize(); }); onBeforeUnmount(() => { + debouncedAutoSave.cancel(); if (controlsRef.value) observer.unobserve(controlsRef.value.$el); }); diff --git a/client/dive-common/store/settings.ts b/client/dive-common/store/settings.ts index adab2e24f..9a80ed72c 100644 --- a/client/dive-common/store/settings.ts +++ b/client/dive-common/store/settings.ts @@ -48,6 +48,9 @@ interface AnnotationSettings { multiCamSettings: { showToolbar: boolean; }; + autoSaveSettings: { + enabled: boolean; + }; } const defaultSettings: AnnotationSettings = { @@ -105,6 +108,9 @@ const defaultSettings: AnnotationSettings = { multiCamSettings: { showToolbar: true, }, + autoSaveSettings: { + enabled: false, // Disabled by default for backward compatibility + }, }; // Utility to safely load from localStorage From 23b9c6343e62ef5df156d62cddca2940c871f4e0 Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Sat, 24 Jan 2026 14:34:39 -0500 Subject: [PATCH 2/3] add auto-save settings to Settings views and update save icon - Add auto-save toggle to desktop Settings view under Annotation Settings - Add Annotation Settings section to web-girder Settings view with multi-camera toolbar and auto-save toggles - Change save icon to show gear (mdi-content-save-cog) when auto-save is enabled to visually indicate automated saving is active Co-Authored-By: Claude Opus 4.5 --- client/dive-common/components/Viewer.vue | 2 +- .../desktop/frontend/components/Settings.vue | 12 ++++++ client/platform/web-girder/views/Settings.vue | 38 +++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/client/dive-common/components/Viewer.vue b/client/dive-common/components/Viewer.vue index d3e863d0f..6570423b3 100644 --- a/client/dive-common/components/Viewer.vue +++ b/client/dive-common/components/Viewer.vue @@ -1108,7 +1108,7 @@ export default defineComponent({ @click="save(currentSet)" > - mdi-content-save + {{ clientSettings.autoSaveSettings.enabled ? 'mdi-content-save-cog' : 'mdi-content-save' }} diff --git a/client/platform/desktop/frontend/components/Settings.vue b/client/platform/desktop/frontend/components/Settings.vue index 93ce64c5e..b3b40736c 100644 --- a/client/platform/desktop/frontend/components/Settings.vue +++ b/client/platform/desktop/frontend/components/Settings.vue @@ -196,6 +196,18 @@ export default defineComponent({ /> + + + + + Platform support diff --git a/client/platform/web-girder/views/Settings.vue b/client/platform/web-girder/views/Settings.vue index 957fd7337..9a594f4f0 100644 --- a/client/platform/web-girder/views/Settings.vue +++ b/client/platform/web-girder/views/Settings.vue @@ -1,8 +1,14 @@ @@ -10,8 +16,34 @@ export default Vue.extend({ - + Annotation Settings + + + + + + + + + + + + From 04627ef2f9259a81ee0e362ac0dbd84c223ce8dc Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Mon, 26 Jan 2026 14:02:54 -0500 Subject: [PATCH 3/3] Fix auto-save exiting editing mode after 2 seconds The save() function was unconditionally exiting editing mode before saving, which caused issues when auto-save was enabled: users would be kicked out of polygon/segmentation editing mode after 2 seconds when auto-save triggered. Added an optional exitEditingMode parameter (defaults to false) so auto-save no longer disrupts the user's editing session. Co-Authored-By: Claude Opus 4.5 --- client/dive-common/components/Viewer.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/dive-common/components/Viewer.vue b/client/dive-common/components/Viewer.vue index 6570423b3..676e61105 100644 --- a/client/dive-common/components/Viewer.vue +++ b/client/dive-common/components/Viewer.vue @@ -412,10 +412,11 @@ export default defineComponent({ handler.stopLinking(); } }); - async function save(setVal?: string) { - // If editing the track, disable editing mode before save + async function save(setVal?: string, exitEditingMode = false) { + // Only exit editing mode if explicitly requested (e.g., manual save) + // Auto-save should NOT disrupt the user's editing session saveInProgress.value = true; - if (editingTrack.value) { + if (exitEditingMode && editingTrack.value) { handler.trackSelect(selectedTrackId.value, false); } const saveSet = setVal === 'default' ? undefined : setVal;