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
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
- 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
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
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
- 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
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
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
- 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
When clicking the History button on a specific camera's Live view,
append `?cameras=<camera_name>` to the review URL so the camera
filter is pre-set to that camera instead of showing "All Cameras".
The Events (Review) page already supports reading the `cameras` URL
parameter via useSearchEffect - no changes needed there.
Fixes: #12776, #16987https://claude.ai/code/session_01PnMA1HcuKsEXcvVLaXRgF1
When scrubbing in RecordingView, tall cameras passed size-full (width:100% + height:100%)
to PreviewPlayer, causing the browser to ignore aspect-ratio. Replacing size-full with
w-full lets height be computed from width + aspect-ratio, preserving correct proportions.
https://claude.ai/code/session_019sUH2h6HoVswdtD7EbhAJa
Replace size-full (100%×100%) on img/video elements with max-h-full max-w-full
so portrait (9:16) and 4:3 cameras maintain their natural proportions during
scrubbing in RecordingView. Add items-center to flex containers so content
stays vertically centered within the available space.
https://claude.ai/code/session_01H1uowWMpsNm1U8HdcSP8AA
* fix genai settings ui
- add roles widget to select roles for genai providers
- add dropdown in semantic search to allow selection of embeddings genai provider
* tweak grouping to prioritize fieldOrder before groups
previously, groups were always rendered first. now fieldOrder is respected, and any fields in a group will cause the group and all the fields in that group to be rendered in order. this allows moving the enabled switches to the top of the section
* mobile tweaks
stack buttons, add more space on profiles pane, and move the overridden badge beneath the description
* language consistency
* prevent camera config sections from being regenerated for profiles
* conditionally import axengine module
to match other detectors
* i18n
* update vscode launch.json for new integrated browser
* formatting
- MsePlayer: change default object-fit fallback from fill to contain
(grid layout keeps fill via --frigate-mse-object-fit:fill CSS variable)
- PreviewPlayer: add object-contain class to video element
- HlsVideoPlayer: add object-contain class to video element
Recordings view and timeline preview now preserve aspect ratio,
while live grid continues to stretch corridor cameras as before.
Add optional `roles` field to camera groups config to control which user
roles can see each group. Groups without roles are visible only to admins.
Admin users always see all groups. Backend filters groups in GET /config
based on remote-role header. Frontend adds roles multiselect in group
editor (admin only).
https://claude.ai/code/session_011sp9kHQfM39JvVxKHFh1Xq