feat: persist camera zoom per camera group

Each camera now stores zoom state under a group-scoped key
(live:grid-card:zoom:<group>:<camera>), so different groups
can have independent zoom levels for the same camera.

https://claude.ai/code/session_01WidMYGkyBCFf4L9PnFEiZ5
This commit is contained in:
Claude 2026-03-22 12:03:29 +00:00
parent a92a95dea4
commit 3f39819fbe
No known key found for this signature in database
2 changed files with 17 additions and 7 deletions

View File

@ -201,14 +201,23 @@ export function getNextScaleFromWheelDelta(
return clampScale(currentScale + direction * step, minScale, maxScale); 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}`; return `live:grid-card:zoom:${cameraName}`;
} }
export function loadPersistedCameraZoomState( export function loadPersistedCameraZoomState(
cameraName: string, cameraName: string,
cameraGroup?: string,
): CameraZoomPersistedState | undefined { ): CameraZoomPersistedState | undefined {
const serialized = localStorage.getItem(getCameraZoomStorageKey(cameraName)); const serialized = localStorage.getItem(
getCameraZoomStorageKey(cameraName, cameraGroup),
);
if (!serialized) { if (!serialized) {
return undefined; return undefined;
@ -225,9 +234,10 @@ export function loadPersistedCameraZoomState(
export function savePersistedCameraZoomState( export function savePersistedCameraZoomState(
cameraName: string, cameraName: string,
state: CameraZoomPersistedState, state: CameraZoomPersistedState,
cameraGroup?: string,
): void { ): void {
localStorage.setItem( localStorage.setItem(
getCameraZoomStorageKey(cameraName), getCameraZoomStorageKey(cameraName, cameraGroup),
JSON.stringify(state), JSON.stringify(state),
); );
} }

View File

@ -661,7 +661,7 @@ export default function DraggableGridLayout({
return prev; return prev;
} }
const persisted = loadPersistedCameraZoomState(cameraName); const persisted = loadPersistedCameraZoomState(cameraName, cameraGroup);
if (!persisted) { if (!persisted) {
return prev; return prev;
} }
@ -677,7 +677,7 @@ export default function DraggableGridLayout({
}; };
}); });
}, },
[getCardZoomDimensions], [cameraGroup, getCardZoomDimensions],
); );
const getDefaultZoomTransform = useCallback( const getDefaultZoomTransform = useCallback(
@ -724,7 +724,7 @@ export default function DraggableGridLayout({
contentHeight: content?.clientHeight ?? bounds.height, contentHeight: content?.clientHeight ?? bounds.height,
}); });
savePersistedCameraZoomState(cameraName, persisted); savePersistedCameraZoomState(cameraName, persisted, cameraGroup);
return { return {
...prev, ...prev,
@ -732,7 +732,7 @@ export default function DraggableGridLayout({
}; };
}); });
}, },
[getDefaultZoomTransform], [cameraGroup, getDefaultZoomTransform],
); );
const detachCardZoomWheelListener = useCallback((cameraName: string) => { const detachCardZoomWheelListener = useCallback((cameraName: string) => {