From 3f39819fbec1fe48fae621214b95f4e91b458436 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Mar 2026 12:03:29 +0000 Subject: [PATCH] feat: persist camera zoom per camera group Each camera now stores zoom state under a group-scoped key (live:grid-card:zoom::), so different groups can have independent zoom levels for the same camera. https://claude.ai/code/session_01WidMYGkyBCFf4L9PnFEiZ5 --- web/src/utils/cameraZoom.ts | 16 +++++++++++++--- web/src/views/live/DraggableGridLayout.tsx | 8 ++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/web/src/utils/cameraZoom.ts b/web/src/utils/cameraZoom.ts index c89cd20d9..09b5cea12 100644 --- a/web/src/utils/cameraZoom.ts +++ b/web/src/utils/cameraZoom.ts @@ -201,14 +201,23 @@ export function getNextScaleFromWheelDelta( return clampScale(currentScale + direction * step, minScale, maxScale); } -export function getCameraZoomStorageKey(cameraName: string): string { +export function getCameraZoomStorageKey( + cameraName: string, + cameraGroup?: string, +): string { + if (cameraGroup) { + return `live:grid-card:zoom:${cameraGroup}:${cameraName}`; + } return `live:grid-card:zoom:${cameraName}`; } export function loadPersistedCameraZoomState( cameraName: string, + cameraGroup?: string, ): CameraZoomPersistedState | undefined { - const serialized = localStorage.getItem(getCameraZoomStorageKey(cameraName)); + const serialized = localStorage.getItem( + getCameraZoomStorageKey(cameraName, cameraGroup), + ); if (!serialized) { return undefined; @@ -225,9 +234,10 @@ export function loadPersistedCameraZoomState( export function savePersistedCameraZoomState( cameraName: string, state: CameraZoomPersistedState, + cameraGroup?: string, ): void { localStorage.setItem( - getCameraZoomStorageKey(cameraName), + getCameraZoomStorageKey(cameraName, cameraGroup), JSON.stringify(state), ); } diff --git a/web/src/views/live/DraggableGridLayout.tsx b/web/src/views/live/DraggableGridLayout.tsx index 119c06d43..8a7c6b8f9 100644 --- a/web/src/views/live/DraggableGridLayout.tsx +++ b/web/src/views/live/DraggableGridLayout.tsx @@ -661,7 +661,7 @@ export default function DraggableGridLayout({ return prev; } - const persisted = loadPersistedCameraZoomState(cameraName); + const persisted = loadPersistedCameraZoomState(cameraName, cameraGroup); if (!persisted) { return prev; } @@ -677,7 +677,7 @@ export default function DraggableGridLayout({ }; }); }, - [getCardZoomDimensions], + [cameraGroup, getCardZoomDimensions], ); const getDefaultZoomTransform = useCallback( @@ -724,7 +724,7 @@ export default function DraggableGridLayout({ contentHeight: content?.clientHeight ?? bounds.height, }); - savePersistedCameraZoomState(cameraName, persisted); + savePersistedCameraZoomState(cameraName, persisted, cameraGroup); return { ...prev, @@ -732,7 +732,7 @@ export default function DraggableGridLayout({ }; }); }, - [getDefaultZoomTransform], + [cameraGroup, getDefaultZoomTransform], ); const detachCardZoomWheelListener = useCallback((cameraName: string) => {