Commit Graph

5723 Commits

Author SHA1 Message Date
Claude
3f39819fbe
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
2026-03-22 12:03:29 +00:00
ibs0d
a92a95dea4
Merge pull request #112 from ibs0d/claude/camera-group-keyboard-shortcuts-YaGM0
feat: persist fitToScreen state per camera group
2026-03-22 22:34:27 +11:00
Claude
608e545bb1
feat: persist fitToScreen state per camera group
Replace shared 'liveFitToScreen' key with '${cameraGroup}-fitToScreen'
so each camera group remembers its own fit-to-screen preference.

https://claude.ai/code/session_018vH9fGSi5McLLa47GEiZJC
2026-03-22 11:30:24 +00:00
ibs0d
928ad18264
Merge pull request #111 from ibs0d/claude/camera-group-keyboard-shortcuts-YaGM0
fix: guard against null key in camera group keyboard shortcut handler
2026-03-22 22:07:35 +11:00
Claude
91a6a650f3
fix: guard against null key in camera group keyboard shortcut handler
https://claude.ai/code/session_018vH9fGSi5McLLa47GEiZJC
2026-03-22 11:01:57 +00:00
ibs0d
aa762a5ff7
Merge pull request #110 from ibs0d/claude/camera-group-keyboard-shortcuts-YaGM0
feat: add keyboard shortcuts 1-9, 0 to switch camera groups
2026-03-22 21:41:46 +11:00
Claude
3043aa4d5b
feat: add keyboard shortcuts 1-9, 0 to switch camera groups
Keys 1-9 switch to camera groups 1-9 (sorted by order), key 0 switches
to the 10th group. Shortcuts are ignored when viewing a single camera
(selectedCameraName is set) and do not interfere with text input fields
(handled by useKeyboardListener's built-in focus detection).

https://claude.ai/code/session_018vH9fGSi5McLLa47GEiZJC
2026-03-22 10:37:54 +00:00
ibs0d
f48ae049f1
Merge pull request #109 from ibs0d/claude/enable-fit-to-screen-SkNlL
Enable Fit to Screen by default in live view
2026-03-22 19:59:19 +11:00
Claude
ec4d8b705a
Enable Fit to Screen by default in live view 2026-03-22 08:57:55 +00:00
ibs0d
a279d0753a
Merge pull request #108 from ibs0d/claude/persist-camera-order-BkmUM
Fix fitCameraOrder reset on every re-render due to unstable cameras ref
2026-03-22 18:37:30 +11:00
Claude
a95e5321d0
Fix fitCameraOrder reset on every re-render due to unstable cameras ref
Replace cameras array dependency in the reset useEffect with a stable
cameraKey string (sorted camera names joined by comma). Since cameras
is recreated on every parent render, the previous effect fired on each
re-render, immediately clearing the persisted order. The key changes
only when the actual set of cameras changes.

https://claude.ai/code/session_018sRNpvMwxLgt5Un8PCjfRU
2026-03-22 07:36:16 +00:00
ibs0d
00ca5bafc0
Merge pull request #107 from ibs0d/claude/persist-camera-order-BkmUM
Persist camera order in Fit to Screen mode across reloads
2026-03-22 18:16:50 +11:00
Claude
53caf3312b
Persist camera order in Fit to Screen mode across reloads
Replace in-memory fitLayoutOverride (useState) with fitCameraOrder
(useUserPersistence<string[]>) keyed as `${cameraGroup}-fit-camera-order`.
The fit layout now restores the saved camera order on load and recomputes
positions from current fitGridParams, so the order survives page reloads
and window resizes without storing screen-size-dependent coordinates.

https://claude.ai/code/session_018sRNpvMwxLgt5Un8PCjfRU
2026-03-22 07:12:18 +00:00
ibs0d
4f7d3f41b5
Merge pull request #106 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag reset: use primitive deps in fitLayoutOverride…
2026-03-22 17:52:42 +11:00
Claude
8216394c59
Fix fit-to-screen drag reset: use primitive deps in fitLayoutOverride effect
fitGridParams is recreated on every useMemo run, causing the useEffect to
fire and clear fitLayoutOverride after each swap. Switch deps to
fitGridParams?.gridUnitsPerCam and fitGridParams?.colsPerRow (primitives)
so the reset only triggers when the grid geometry actually changes.

Also remove all debug console.log added during investigation.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 06:49:16 +00:00
ibs0d
e9f7198e5f
Merge pull request #105 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Debug: expand handleFitDragStop logging + add activeGridLayout log
2026-03-22 17:34:50 +11:00
Claude
43d196c57c
Debug: expand handleFitDragStop logging + add activeGridLayout log
https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 06:32:55 +00:00
ibs0d
be97838a76
Merge pull request #104 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Debug: add console.log to handleFitDragStop for drag coordinate inspe…
2026-03-22 17:17:39 +11:00
Claude
afd5c30942
Debug: add console.log to handleFitDragStop for drag coordinate inspection
https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 06:13:30 +00:00
ibs0d
53343ac878
Merge pull request #103 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag: use newItem.x/y grid coords instead of mouse …
2026-03-22 16:57:18 +11:00
Claude
95b9b0b7b9
Fix fit-to-screen drag: use newItem.x/y grid coords instead of mouse pixels
Replace getBoundingClientRect+clientX/Y with newItem.x/y from react-grid-layout.
With noCompactor, newItem reports the free grid position where the element was
dropped — reliable across all rows without pixel math or scroll issues.

Also remove handleFitDrag/fitDragRef (no longer needed) and generate a full
snapBack layout so displaced items snap back correctly on no-op drops.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 05:37:14 +00:00
ibs0d
6e0b30ec9c
Merge pull request #102 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag: use noCompactor for free horizontal movement
2026-03-22 12:06:55 +11:00
Claude
8fc7f6bdb7
Fix fit-to-screen drag: use noCompactor for free horizontal movement
In fit-to-screen mode, pass noCompactor to <Responsive> so elements
follow the mouse freely without vertical compaction pushing them down.
The swap-on-drop logic (mouse coordinates in onDragStop) stays unchanged.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 00:31:56 +00:00
ibs0d
f74910a20d
Merge pull request #101 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag types to match EventCallback signature
2026-03-22 11:05:11 +11:00
Claude
29e2003a84
Fix fit-to-screen drag types to match EventCallback signature
All LayoutItem args are nullable (LayoutItem | null) and event is Event
not MouseEvent — matching the actual react-grid-layout EventCallback type.
fitDragRef simplified to string | null. Cast event to MouseEvent inside
the handler to access clientX/Y for pixel-accurate slot detection.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-22 00:04:21 +00:00
ibs0d
b393f20857
Merge pull request #99 from ibs0d/revert-98-claude/add-fit-to-screen-mode-XvHoV
Revert "Fix fit-to-screen horizontal swap using real mouse coordinates"
2026-03-22 10:48:50 +11:00
ibs0d
b277b0bd98
Revert "Fix fit-to-screen horizontal swap using real mouse coordinates" 2026-03-22 10:48:32 +11:00
ibs0d
83dbc9fc30
Merge pull request #98 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen horizontal swap using real mouse coordinates
2026-03-22 00:14:15 +11:00
Claude
051f680b04
Fix fit-to-screen horizontal swap using real mouse coordinates
rgl with compactType=vertical doesn't move items horizontally, so
layoutItem.x in onDragStop stays near its origin, making horizontal
slot detection wrong.

Switch to tracking event.clientX/Y from onDrag (fitDragRef), then in
onDragStop translate the final mouse position against the .grid-layout
element's bounding rect to get pixel-accurate targetCol/targetRow.
This makes horizontal, vertical, and long-distance swaps all reliable.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-21 13:12:41 +00:00
ibs0d
cd03cc0ccd
Merge pull request #97 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag: support non-adjacent and horizontal swaps
2026-03-21 23:51:47 +11:00
Claude
3d1de5bf69
Fix fit-to-screen drag: support non-adjacent and horizontal swaps
Previous approach sorted react-grid-layout's post-drag positions to infer
order, which broke for non-adjacent and horizontal moves because rgl pushes
items down instead of swapping them.

New approach:
- onDrag records which item is being dragged (draggedItemRef)
- onDragStop uses the dragged item's final x/y to compute the target slot
  in our own grid, then performs a clean swap in the ordered name array
- Layout is always fully regenerated from our order array, ignoring rgl's
  position arithmetic entirely

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-21 12:49:02 +00:00
ibs0d
3a550ebc2f
Merge pull request #96 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Fix fit-to-screen drag card disappearing and swap icon
2026-03-21 23:31:52 +11:00
Claude
46334ec8be
Fix fit-to-screen drag card disappearing and swap icon
handleFitDragStop now sorts dragged items by position to determine new
order, then recalculates all x/y coords into a strict dense grid instead
of spreading react-grid-layout's arbitrary y values — prevents cards from
being pushed off-screen after a drag.

Also replaces LuMaximize with LuScanBarcode for the fit-to-screen button.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-21 12:26:03 +00:00
ibs0d
f2b6f08124
Merge pull request #95 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Allow drag reordering in Fit to Screen mode
2026-03-21 22:59:03 +11:00
Claude
853978ed4d
Allow drag reordering in Fit to Screen mode
In fitToScreen mode, drag is now enabled so users can reorder cameras
while in edit mode. A fitLayoutOverride state captures the new order
after each drag, normalizing w/h back to gridUnitsPerCam to prevent
size changes. The override resets automatically when the camera list or
grid parameters change.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-21 11:53:49 +00:00
ibs0d
fc5e59ec54
Merge pull request #94 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Add ui.rotate support to PreviewPlayer (scrubbing preview in recordings)
2026-03-21 21:50:12 +11:00
Claude
e03f626aeb
Add ui.rotate support to PreviewPlayer (scrubbing preview in recordings)
- PreviewPlayer: add rotate prop, pass through to PreviewVideoPlayer and
  PreviewFramesPlayer
- PreviewVideoPlayer: add rotate prop + ResizeObserver; wrap <img> and
  <video> in width/height-swap container with rotate(90deg) transform;
  add h-full when rotate to fix height chain
- PreviewFramesPlayer: same pattern for <img> frame previews
- DynamicVideoPlayer: pass rotate={rotate} to PreviewPlayer

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 10:31:22 +00:00
ibs0d
8a83bb2e04
Merge pull request #93 from ibs0d/claude/add-fit-to-screen-mode-XvHoV
Add Fit to Screen mode to Live view grid
2026-03-21 21:22:52 +11:00
Claude
be1a19bccd
Add Fit to Screen mode to Live view grid
Adds a toggle button to the Live view toolbar that automatically arranges
all cameras to fit within the viewport without scrolling. Uses a brute-force
algorithm to find the optimal number of columns that maximizes camera size
while keeping all cameras visible. State persists via IndexedDB.

https://claude.ai/code/session_01Cu7YDRKZrYX3sBs6g9w2dy
2026-03-21 10:17:13 +00:00
ibs0d
81f3619542
Merge pull request #92 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Fix ui.rotate in HlsVideoPlayer: broken CSS height chain on desktop
2026-03-21 20:03:40 +11:00
Claude
6f70c1511d
Fix ui.rotate in HlsVideoPlayer: broken CSS height chain on desktop
TransformComponent's contentStyle had height: undefined on desktop,
so the rotate container's ResizeObserver measured height=0, causing
the inner div to get width=0 and the video to be invisible.

Adding || rotate to the height condition ensures the height chain is
intact when rotation is active, matching the isMobile path that already
set height: "100%".

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 09:02:48 +00:00
ibs0d
e0ee08ac15
Merge pull request #91 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Add ui.rotate support to RecordingView / HlsVideoPlayer
2026-03-21 19:45:49 +11:00
Claude
4a35ce1f70
Add ui.rotate support to RecordingView / HlsVideoPlayer
- HlsVideoPlayer: add rotate prop; when true, wraps <video> in a
  ResizeObserver-tracked container that swaps width/height and applies
  rotate(90deg) transform, mirroring the MsePlayer grid-rotation logic
- DynamicVideoPlayer: thread rotate prop through to HlsVideoPlayer
- RecordingView: invert getCameraAspect ratio (1/ratio) for cameras
  with ui.rotate so the outer container gets portrait proportions;
  pass rotate={camera.ui?.rotate} to DynamicVideoPlayer

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 08:41:56 +00:00
ibs0d
cdb58a43a5
Merge pull request #90 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Fix ui.rotate: broken CSS height chain causing blank video
2026-03-21 19:23:11 +11:00
Claude
25a869eb43
Fix ui.rotate: broken CSS height chain causing blank video
Root cause: LivePlayer's outer div has no explicit height (only w-full),
so when MsePlayer reads containerSize.height via ResizeObserver it gets 0.
With isRotatedGrid=true, MsePlayer sets the inner div width:
containerSize.height → width: 0 → video invisible.

Fix:
- Add size-full to LivePlayer className when camera.ui?.rotate, ensuring
  height: 100% propagates through the chain so MsePlayer gets real dims
- Re-add cameraAspectRatio inversion (1/ratio) for portrait container
  layout; now that the height chain is intact this works correctly:
  portrait container → LivePlayer size-full → MsePlayer real dims → swap+rotate

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 08:17:40 +00:00
ibs0d
3c111b2f3b
Merge pull request #89 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Fix: revert cameraAspectRatio inversion for ui.rotate cameras
2026-03-21 18:58:51 +11:00
Claude
54b3717a60
Fix: revert cameraAspectRatio inversion for ui.rotate cameras
The previous commit caused a double dimension swap for rotated cameras:
- LiveCameraView was inverting the aspect ratio (1/ratio) → portrait container
- MsePlayer was then swapping width/height again internally when
  isRotatedGrid=true → video got zero/invalid dimensions, nothing visible

The MsePlayer already handles the full rotation internally via CSS variables
(transform + width/height swap). The container in LiveCameraView should
keep the original (landscape) aspect ratio, matching the grid cell behavior
in DraggableGridLayout where this works correctly.

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 07:55:26 +00:00
ibs0d
a8ee19397c
Merge pull request #88 from ibs0d/claude/add-camera-rotation-support-Lg2l9
Add ui.rotate support to LiveCameraView single-camera view
2026-03-21 18:34:35 +11:00
Claude
ea9ca2f8d1
Add ui.rotate support to LiveCameraView single-camera view
- Invert cameraAspectRatio when camera.ui?.rotate is true so the
  container dimensions match the rotated video (width↔height swap)
- Pass CSS variables --frigate-mse-grid-rotated and
  --frigate-mse-grid-rotation to LivePlayer, enabling the existing
  MsePlayer rotation/swap logic for single-camera view
- Fullscreen orientation lock works automatically: an inverted ratio
  < 1 causes portrait lock for a normally-landscape camera

https://claude.ai/code/session_01CDLHQPGpf8w44jpsG8g8nM
2026-03-21 07:32:14 +00:00
ibs0d
28b5b17b58
Merge pull request #87 from ibs0d/claude/fix-camera-filter-history-q8W9C
fix: pass camera name to Review page when navigating from Live Camera…
2026-03-21 17:45:52 +11:00