From b451d0a4f10db1f13540a6fca94bb2fa98595cf4 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 13 May 2024 09:00:34 -0600 Subject: [PATCH 1/3] Improve mask error (#11361) * Don't apply masks / zones that are outside of the detect resolution * formatting --- frigate/util/config.py | 45 ++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/frigate/util/config.py b/frigate/util/config.py index e882f0bfd..f395ee125 100644 --- a/frigate/util/config.py +++ b/frigate/util/config.py @@ -157,26 +157,45 @@ def get_relative_coordinates( points = m.split(",") if any(x > "1.0" for x in points): - relative_masks.append( - ",".join( - [ - f"{round(int(points[i]) / frame_shape[1], 3)},{round(int(points[i + 1]) / frame_shape[0], 3)}" - for i in range(0, len(points), 2) - ] + rel_points = [] + for i in range(0, len(points), 2): + x = int(points[i]) + y = int(points[i + 1]) + + if x > frame_shape[1] or y > frame_shape[0]: + logger.error( + f"Not applying mask due to invalid coordinates. {x},{y} is outside of the detection resolution {frame_shape[1]}x{frame_shape[0]}. Use the editor in the UI to correct the mask." + ) + continue + + rel_points.append( + f"{round(x / frame_shape[1], 3)},{round(y / frame_shape[0], 3)}" ) - ) + + relative_masks.append(",".join(rel_points)) else: relative_masks.append(m) mask = relative_masks elif isinstance(mask, str) and any(x > "1.0" for x in mask.split(",")): points = mask.split(",") - mask = ",".join( - [ - f"{round(int(points[i]) / frame_shape[1], 3)},{round(int(points[i + 1]) / frame_shape[0], 3)}" - for i in range(0, len(points), 2) - ] - ) + rel_points = [] + + for i in range(0, len(points), 2): + x = int(points[i]) + y = int(points[i + 1]) + + if x > frame_shape[1] or y > frame_shape[0]: + logger.error( + f"Not applying mask due to invalid coordinates. {x},{y} is outside of the detection resolution {frame_shape[1]}x{frame_shape[0]}. Use the editor in the UI to correct the mask." + ) + return [] + + rel_points.append( + f"{round(x / frame_shape[1], 3)},{round(y / frame_shape[0], 3)}" + ) + + mask = ",".join(rel_points) return mask From b10ae68c1fda11e4ceb5a2da283b11a24b37f706 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 14 May 2024 07:38:03 -0600 Subject: [PATCH 2/3] Tweaks and fixes (#11372) * Ensure camera activity is up to date * Persist playback rate between cameras * Add setting for default playback rate * Fix audio events saving image * Formatting * Use select component --- frigate/review/maintainer.py | 56 +++++++++----------- web/src/api/ws.tsx | 17 +++++- web/src/components/player/HlsVideoPlayer.tsx | 28 +++++++--- web/src/components/settings/General.tsx | 43 +++++++++++++++ web/src/hooks/use-camera-activity.ts | 6 ++- 5 files changed, 111 insertions(+), 39 deletions(-) diff --git a/frigate/review/maintainer.py b/frigate/review/maintainer.py index 77d3d2a6b..4f419b36b 100644 --- a/frigate/review/maintainer.py +++ b/frigate/review/maintainer.py @@ -68,7 +68,8 @@ class PendingReviewSegment: self.last_update = frame_time # thumbnail - self.frame = np.zeros((THUMB_HEIGHT * 3 // 2, THUMB_WIDTH), np.uint8) + self._frame = np.zeros((THUMB_HEIGHT * 3 // 2, THUMB_WIDTH), np.uint8) + self.has_frame = False self.frame_active_count = 0 self.frame_path = os.path.join( CLIPS_DIR, f"review/thumb-{self.camera}-{self.id}.webp" @@ -101,25 +102,27 @@ class PendingReviewSegment: color_frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420) color_frame = color_frame[region[1] : region[3], region[0] : region[2]] width = int(THUMB_HEIGHT * color_frame.shape[1] / color_frame.shape[0]) - self.frame = cv2.resize( + self._frame = cv2.resize( color_frame, dsize=(width, THUMB_HEIGHT), interpolation=cv2.INTER_AREA ) - if self.frame is not None: + if self._frame is not None: + self.has_frame = True cv2.imwrite( - self.frame_path, self.frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60] + self.frame_path, self._frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60] ) def save_full_frame(self, camera_config: CameraConfig, frame): color_frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420) width = int(THUMB_HEIGHT * color_frame.shape[1] / color_frame.shape[0]) - self.frame = cv2.resize( + self._frame = cv2.resize( color_frame, dsize=(width, THUMB_HEIGHT), interpolation=cv2.INTER_AREA ) - if self.frame is not None: + if self._frame is not None: + self.has_frame = True cv2.imwrite( - self.frame_path, self.frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60] + self.frame_path, self._frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60] ) def get_data(self, ended: bool) -> dict: @@ -194,7 +197,10 @@ class ReviewSegmentMaintainer(threading.Thread): ) -> None: """Update segment.""" prev_data = segment.get_data(ended=False) - segment.update_frame(camera_config, frame, objects) + + if frame is not None: + segment.update_frame(camera_config, frame, objects) + new_data = segment.get_data(ended=False) self.requestor.send_data(UPSERT_REVIEW_SEGMENT, new_data) self.requestor.send_data( @@ -282,33 +288,23 @@ class ReviewSegmentMaintainer(threading.Thread): except FileNotFoundError: return else: + if not segment.has_frame: + try: + frame_id = f"{camera_config.name}{frame_time}" + yuv_frame = self.frame_manager.get( + frame_id, camera_config.frame_shape_yuv + ) + segment.save_full_frame(camera_config, yuv_frame) + self.frame_manager.close(frame_id) + self.update_segment(segment, camera_config, None, []) + except FileNotFoundError: + return + if segment.severity == SeverityEnum.alert and frame_time > ( segment.last_update + THRESHOLD_ALERT_ACTIVITY ): - if segment.frame is None: - try: - frame_id = f"{camera_config.name}{frame_time}" - yuv_frame = self.frame_manager.get( - frame_id, camera_config.frame_shape_yuv - ) - segment.save_full_frame(camera_config, yuv_frame) - self.frame_manager.close(frame_id) - except FileNotFoundError: - return - self.end_segment(segment) elif frame_time > (segment.last_update + THRESHOLD_DETECTION_ACTIVITY): - if segment.frame is None: - try: - frame_id = f"{camera_config.name}{frame_time}" - yuv_frame = self.frame_manager.get( - frame_id, camera_config.frame_shape_yuv - ) - segment.save_full_frame(camera_config, yuv_frame) - self.frame_manager.close(frame_id) - except FileNotFoundError: - return - self.end_segment(segment) def check_if_new_segment( diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx index 34fe4b2f0..5e347e1d4 100644 --- a/web/src/api/ws.tsx +++ b/web/src/api/ws.tsx @@ -204,13 +204,26 @@ export function useFrigateStats(): { payload: FrigateStats } { return { payload: JSON.parse(payload as string) }; } -export function useInitialCameraState(camera: string): { +export function useInitialCameraState( + camera: string, + refreshOnStart: boolean, +): { payload: FrigateCameraState; } { const { value: { payload }, - } = useWs("camera_activity", ""); + send: sendCommand, + } = useWs("camera_activity", "onConnect"); const data = JSON.parse(payload as string); + + useEffect(() => { + if (refreshOnStart) { + sendCommand("onConnect"); + } + // only refresh when onRefresh value changes + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [refreshOnStart]); + return { payload: data ? data[camera] : undefined }; } diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index c52bddb59..023e94a6c 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -15,6 +15,7 @@ import { FrigateConfig } from "@/types/frigateConfig"; import { AxiosResponse } from "axios"; import { toast } from "sonner"; import { useOverlayState } from "@/hooks/use-overlay-state"; +import { usePersistence } from "@/hooks/use-persistence"; // Android native hls does not seek correctly const USE_NATIVE_HLS = !isAndroid; @@ -111,6 +112,11 @@ export default function HlsVideoPlayer({ const [isPlaying, setIsPlaying] = useState(true); const [muted, setMuted] = useOverlayState("playerMuted", true); const [volume, setVolume] = useOverlayState("playerVolume", 1.0); + const [defaultPlaybackRate] = usePersistence("playbackRate", 1); + const [playbackRate, setPlaybackRate] = useOverlayState( + "playbackRate", + defaultPlaybackRate ?? 1, + ); const [mobileCtrlTimeout, setMobileCtrlTimeout] = useState(); const [controls, setControls] = useState(isMobile); const [controlsOpen, setControlsOpen] = useState(false); @@ -162,7 +168,7 @@ export default function HlsVideoPlayer({ }} setControlsOpen={setControlsOpen} setMuted={(muted) => setMuted(muted, true)} - playbackRate={videoRef.current?.playbackRate ?? 1} + playbackRate={playbackRate ?? 1} hotKeys={hotKeys} onPlayPause={(play) => { if (!videoRef.current) { @@ -184,9 +190,13 @@ export default function HlsVideoPlayer({ videoRef.current.currentTime = Math.max(0, currentTime + diff); }} - onSetPlaybackRate={(rate) => - videoRef.current ? (videoRef.current.playbackRate = rate) : null - } + onSetPlaybackRate={(rate) => { + setPlaybackRate(rate); + + if (videoRef.current) { + videoRef.current.playbackRate = rate; + } + }} onUploadFrame={async () => { if (videoRef.current && onUploadFrame) { const resp = await onUploadFrame(videoRef.current.currentTime); @@ -255,8 +265,14 @@ export default function HlsVideoPlayer({ onLoadedMetadata={() => { handleLoadedMetadata(); - if (videoRef.current && volume) { - videoRef.current.volume = volume; + if (videoRef.current) { + if (playbackRate) { + videoRef.current.playbackRate = playbackRate; + } + + if (volume) { + videoRef.current.volume = volume; + } } }} onEnded={onClipEnded} diff --git a/web/src/components/settings/General.tsx b/web/src/components/settings/General.tsx index bdd30fdb7..9af7c17df 100644 --- a/web/src/components/settings/General.tsx +++ b/web/src/components/settings/General.tsx @@ -9,6 +9,17 @@ import { Button } from "../ui/button"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { del as delData } from "idb-keyval"; +import { usePersistence } from "@/hooks/use-persistence"; +import { isSafari } from "react-device-detect"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, +} from "../ui/select"; + +const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16]; export default function General() { const { data: config } = useSWR("config"); @@ -38,6 +49,8 @@ export default function General() { document.title = "General Settings - Frigate"; }, []); + const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1); + return ( <>
@@ -64,6 +77,36 @@ export default function General() {
+
+
+
Default Playback Rate
+
+

Default playback rate for recordings playback.

+
+
+
+ +
Low Data Mode
diff --git a/web/src/hooks/use-camera-activity.ts b/web/src/hooks/use-camera-activity.ts index 074c2be52..7019dafda 100644 --- a/web/src/hooks/use-camera-activity.ts +++ b/web/src/hooks/use-camera-activity.ts @@ -19,12 +19,16 @@ type useCameraActivityReturn = { export function useCameraActivity( camera: CameraConfig, + refreshOnStart: boolean = true, ): useCameraActivityReturn { const [objects, setObjects] = useState([]); // init camera activity - const { payload: initialCameraState } = useInitialCameraState(camera.name); + const { payload: initialCameraState } = useInitialCameraState( + camera.name, + refreshOnStart, + ); const updatedCameraState = useDeepMemo(initialCameraState); From 1757f4cb04c401a5efcfc7fc69a684b7da7298af Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 14 May 2024 10:06:44 -0500 Subject: [PATCH 3/3] Use prettier-plugin-tailwindcss (#11373) * use prettier-plugin-tailwindcss to keep class names organized * use prettierrc file to ensure formatting on save works with vscode * classname reorder with prettier-plugin-tailwindcss --- web/.eslintrc.cjs | 6 ++ web/.prettierrc | 3 + web/package-lock.json | 91 +++++++++++++++++-- web/package.json | 1 + web/src/App.tsx | 2 +- web/src/components/Statusbar.tsx | 16 ++-- web/src/components/Wrapper.tsx | 2 +- web/src/components/bar/TimelineBar.tsx | 52 +++++------ web/src/components/camera/CameraImage.tsx | 6 +- .../components/camera/DebugCameraImage.tsx | 4 +- .../components/camera/ResizingCameraImage.tsx | 4 +- web/src/components/card/AnimatedEventCard.tsx | 8 +- web/src/components/card/ExportCard.tsx | 24 ++--- web/src/components/card/ReviewCard.tsx | 6 +- .../dynamic/CameraFeatureToggle.tsx | 2 +- web/src/components/dynamic/NewReviewData.tsx | 8 +- .../components/filter/CameraGroupSelector.tsx | 40 ++++---- web/src/components/filter/FilterSwitch.tsx | 4 +- web/src/components/filter/LogLevelFilter.tsx | 12 +-- .../components/filter/ReviewActionGroup.tsx | 12 +-- .../components/filter/ReviewFilterGroup.tsx | 22 ++--- web/src/components/filter/ZoneMaskFilter.tsx | 10 +- web/src/components/graph/SystemGraph.tsx | 10 +- web/src/components/icons/IconPicker.tsx | 14 +-- web/src/components/icons/LiveIcons.tsx | 8 +- .../indicators/CameraActivityIndicator.tsx | 12 +-- web/src/components/indicators/Chip.tsx | 4 +- .../indicators/ImageLoadingIndicator.tsx | 2 +- web/src/components/menu/AccountSettings.tsx | 4 +- web/src/components/menu/GeneralSettings.tsx | 38 ++++---- web/src/components/navigation/Bottombar.tsx | 10 +- web/src/components/navigation/NavItem.tsx | 2 +- web/src/components/navigation/Sidebar.tsx | 8 +- web/src/components/overlay/ExportDialog.tsx | 18 ++-- web/src/components/overlay/LogInfoDialog.tsx | 6 +- .../components/overlay/MobileCameraDrawer.tsx | 6 +- .../overlay/MobileReviewSettingsDrawer.tsx | 24 ++--- .../overlay/MobileTimelineDrawer.tsx | 6 +- .../overlay/ReviewActivityCalendar.tsx | 2 +- .../components/overlay/SaveExportOverlay.tsx | 6 +- .../overlay/TimelineDataOverlay.tsx | 4 +- web/src/components/overlay/VainfoDialog.tsx | 2 +- .../components/player/BirdseyeLivePlayer.tsx | 12 +-- web/src/components/player/HlsVideoPlayer.tsx | 4 +- web/src/components/player/LivePlayer.tsx | 20 ++-- web/src/components/player/PreviewPlayer.tsx | 14 +-- .../player/PreviewThumbnailPlayer.tsx | 20 ++-- web/src/components/player/VideoControls.tsx | 10 +- web/src/components/settings/General.tsx | 16 ++-- web/src/components/settings/MasksAndZones.tsx | 40 ++++---- .../settings/MotionMaskEditPane.tsx | 8 +- web/src/components/settings/MotionTuner.tsx | 26 +++--- .../settings/ObjectMaskEditPane.tsx | 8 +- .../components/settings/ObjectSettings.tsx | 38 ++++---- .../settings/PolygonEditControls.tsx | 4 +- web/src/components/settings/PolygonItem.tsx | 8 +- web/src/components/settings/ZoneEditPane.tsx | 26 +++--- web/src/components/timeline/EventSegment.tsx | 10 +- web/src/components/timeline/MotionSegment.tsx | 8 +- .../components/timeline/ReviewTimeline.tsx | 40 ++++---- .../components/timeline/SummarySegment.tsx | 2 +- .../components/timeline/SummaryTimeline.tsx | 6 +- .../components/timeline/segment-metadata.tsx | 10 +- web/src/pages/ConfigEditor.tsx | 8 +- web/src/pages/Exports.tsx | 8 +- web/src/pages/Logs.tsx | 40 ++++---- web/src/pages/Settings.tsx | 18 ++-- web/src/pages/SubmitPlus.tsx | 46 +++++----- web/src/pages/System.tsx | 16 ++-- web/src/pages/UIPlayground.tsx | 14 +-- web/src/utils/timelineUtil.tsx | 36 ++++---- web/src/views/events/EventView.tsx | 48 +++++----- web/src/views/events/RecordingView.tsx | 36 ++++---- web/src/views/live/DraggableGridLayout.tsx | 24 ++--- web/src/views/live/LiveBirdseyeView.tsx | 12 +-- web/src/views/live/LiveCameraView.tsx | 14 +-- web/src/views/live/LiveDashboardView.tsx | 20 ++-- web/src/views/system/CameraMetrics.tsx | 24 ++--- web/src/views/system/GeneralMetrics.tsx | 46 +++++----- web/src/views/system/StorageMetrics.tsx | 18 ++-- 80 files changed, 682 insertions(+), 597 deletions(-) create mode 100644 web/.prettierrc diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs index deba5f544..883537d0f 100644 --- a/web/.eslintrc.cjs +++ b/web/.eslintrc.cjs @@ -44,6 +44,12 @@ module.exports = { { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, ], "no-console": "error", + "prettier/prettier": [ + "warn", + { + plugins: ["prettier-plugin-tailwindcss"], + }, + ], }, overrides: [ { diff --git a/web/.prettierrc b/web/.prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/web/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/web/package-lock.json b/web/package-lock.json index 1a680777a..77db8051e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -96,9 +96,10 @@ "fake-indexeddb": "^5.0.2", "jest-websocket-mock": "^2.5.0", "jsdom": "^24.0.0", - "msw": "^2.2.14", + "msw": "^2.3.0", "postcss": "^8.4.38", "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.14", "tailwindcss": "^3.4.3", "typescript": "^5.4.5", "vite": "^5.2.11", @@ -848,9 +849,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.26.15", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.26.15.tgz", - "integrity": "sha512-HM47Lu1YFmnYHKMBynFfjCp0U/yRskHj/8QEJW0CBEPOlw8Gkmjfll+S9b8M7V5CNDw2/ciRxjjnWeaCiblSIQ==", + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", "dev": true, "dependencies": { "@open-draft/deferred-promise": "^2.2.0", @@ -5595,9 +5596,9 @@ "dev": true }, "node_modules/msw": { - "version": "2.2.14", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.2.14.tgz", - "integrity": "sha512-64i8rNCa1xzDK8ZYsTrVMli05D687jty8+Th+PU5VTbJ2/4P7fkQFVyDQ6ZFT5FrNR8z2BHhbY47fKNvfHrumA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.0.tgz", + "integrity": "sha512-cDr1q/QTMzaWhY8n9lpGhceY209k29UZtdTgJ3P8Bzne3TSMchX2EM/ldvn4ATLOktpCefCU2gcEgzHc31GTPw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -5605,7 +5606,7 @@ "@bundled-es-modules/statuses": "^1.0.1", "@inquirer/confirm": "^3.0.0", "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.26.14", + "@mswjs/interceptors": "^0.29.0", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", "@types/statuses": "^2.0.4", @@ -6181,6 +6182,80 @@ "node": ">=6.0.0" } }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz", + "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig-melody": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig-melody": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", diff --git a/web/package.json b/web/package.json index 4638572a6..d41c9ea75 100644 --- a/web/package.json +++ b/web/package.json @@ -104,6 +104,7 @@ "msw": "^2.3.0", "postcss": "^8.4.38", "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.14", "tailwindcss": "^3.4.3", "typescript": "^5.4.5", "vite": "^5.2.11", diff --git a/web/src/App.tsx b/web/src/App.tsx index a86e79491..eb81bc812 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -31,7 +31,7 @@ function App() { {isMobile && }
diff --git a/web/src/components/Statusbar.tsx b/web/src/components/Statusbar.tsx index c2e81cf90..9fb45ba42 100644 --- a/web/src/components/Statusbar.tsx +++ b/web/src/components/Statusbar.tsx @@ -55,11 +55,11 @@ export default function Statusbar() { }, [potentialProblems, addMessage, clearMessages]); return ( -
-
+
+
{cpuPercent && ( -
+
-
+
{Object.entries(messages).length === 0 ? ( -
+
System is healthy
) : ( Object.entries(messages).map(([key, messageArray]) => ( -
+
{messageArray.map(({ id, text, color, link }: StatusMessage) => { const message = (
{ - return
{children}
; + return
{children}
; }; export default Wrapper; diff --git a/web/src/components/bar/TimelineBar.tsx b/web/src/components/bar/TimelineBar.tsx index cd168f3cd..fe05b876f 100644 --- a/web/src/components/bar/TimelineBar.tsx +++ b/web/src/components/bar/TimelineBar.tsx @@ -27,11 +27,11 @@ export default function TimelineBar({ return (
{graphData != undefined && ( -
+
{getHourBlocks().map((idx) => { return (
); })} -
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:00" : "%I:00%P", @@ -56,8 +56,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:05" : "%I:05%P", @@ -66,8 +66,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:10" : "%I:10%P", @@ -76,8 +76,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:15" : "%I:15%P", @@ -86,8 +86,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:20" : "%I:20%P", @@ -96,8 +96,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:25" : "%I:25%P", @@ -106,8 +106,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:30" : "%I:30%P", @@ -116,8 +116,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:35" : "%I:35%P", @@ -126,8 +126,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:40" : "%I:40%P", @@ -136,8 +136,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:45" : "%I:45%P", @@ -146,8 +146,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:50" : "%I:50%P", @@ -156,8 +156,8 @@ export default function TimelineBar({ })}
-
-
+
+
{formatUnixTimestampToDateTime(startTime, { strftime_fmt: config?.ui.time_format == "24hour" ? "%H:55" : "%I:55%P", diff --git a/web/src/components/camera/CameraImage.tsx b/web/src/components/camera/CameraImage.tsx index b4fb707bc..b2bf33dfd 100644 --- a/web/src/components/camera/CameraImage.tsx +++ b/web/src/components/camera/CameraImage.tsx @@ -45,7 +45,7 @@ export default function CameraImage({ {enabled ? ( { setHasLoaded(true); @@ -62,12 +62,12 @@ export default function CameraImage({ }} /> ) : ( -
+
Camera is disabled in config, no stream or snapshot available!
)} {!hasLoaded && enabled ? ( -
+
) : null} diff --git a/web/src/components/camera/DebugCameraImage.tsx b/web/src/components/camera/DebugCameraImage.tsx index af8f3bf72..62c7c13cc 100644 --- a/web/src/components/camera/DebugCameraImage.tsx +++ b/web/src/components/camera/DebugCameraImage.tsx @@ -56,7 +56,7 @@ export default function DebugCameraImage({ cameraClasses="relative w-full h-full flex justify-center" />
diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index b80a4f939..098d2f419 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -125,8 +125,8 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { ); const content = ( @@ -33,7 +33,7 @@ export function LogLevelFilterButton({ return ( {trigger} - + {content} @@ -59,9 +59,9 @@ export function GeneralFilterContent({ return ( <>
-
+