fix build warnings from tailwind, fonts, and fast refresh (#24359)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s

fix duplicated styles, move fonts to src/assets/fonts for vite to bundle (nginx already rewrites correctly), and fix fast refresh for PreviewController, auth context/provider, and statusbar context
This commit is contained in:
Josh Hawkins
2026-09-15 15:33:09 -06:00
committed by GitHub
parent cabdffea20
commit 79ca18d439
40 changed files with 112 additions and 115 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -2,7 +2,7 @@ import { useEmbeddingsReindexProgress } from "@/api/ws";
import {
StatusBarMessagesContext,
StatusMessage,
} from "@/context/statusbar-provider";
} from "@/context/statusbar-context";
import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
import StatusBarNotices from "@/components/health/StatusBarNotices";
import { cn } from "@/lib/utils";
@@ -10,7 +10,7 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import { FrigateConfig } from "@/types/frigateConfig";
import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
@@ -61,7 +61,7 @@ import {
JsonValue,
} from "@/types/configForm";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import {
cameraUpdateTopicMap,
globalCameraDefaultSections,
+1 -1
View File
@@ -19,7 +19,7 @@ import useNavigation from "@/hooks/use-navigation";
import {
StatusBarMessagesContext,
StatusMessage,
} from "@/context/statusbar-provider";
} from "@/context/statusbar-context";
import { Link } from "react-router-dom";
import { cn } from "@/lib/utils";
import { isMobile } from "react-device-detect";
@@ -0,0 +1,13 @@
export abstract class PreviewController {
public camera = "";
constructor(camera: string) {
this.camera = camera;
}
abstract scrubToTimestamp(time: number): boolean;
abstract finishedSeeking(): void;
abstract setNewPreviewStartTime(time: number): void;
}
+1 -14
View File
@@ -22,6 +22,7 @@ import {
} from "@/hooks/use-camera-previews";
import { useTranslation } from "react-i18next";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { PreviewController } from "./PreviewController";
type PreviewPlayerProps = {
previewRef?: (ref: HTMLDivElement | null) => void;
@@ -102,20 +103,6 @@ export default function PreviewPlayer({
);
}
export abstract class PreviewController {
public camera = "";
constructor(camera: string) {
this.camera = camera;
}
abstract scrubToTimestamp(time: number): boolean;
abstract finishedSeeking(): void;
abstract setNewPreviewStartTime(time: number): void;
}
type PreviewVideoPlayerProps = {
visibilityRef?: (ref: HTMLDivElement | null) => void;
className?: string;
@@ -1,6 +1,6 @@
import { Recording } from "@/types/record";
import { DynamicPlayback } from "@/types/playback";
import { PreviewController } from "../PreviewPlayer";
import { PreviewController } from "../PreviewController";
import { TimeRange, TrackingDetailsSequence } from "@/types/timeline";
import {
calculateInpointOffset,
@@ -16,7 +16,8 @@ import {
RecordingCoverage,
} from "@/types/record";
import { Preview } from "@/types/preview";
import PreviewPlayer, { PreviewController } from "../PreviewPlayer";
import PreviewPlayer from "../PreviewPlayer";
import { PreviewController } from "../PreviewController";
import { DynamicVideoController } from "./DynamicVideoController";
import HlsVideoPlayer, { HlsSource } from "../HlsVideoPlayer";
import { useDetailStream } from "@/context/detail-stream-context";
@@ -65,7 +65,7 @@ import {
import { buildConfigDataForPath } from "@/utils/configUtil";
import { useConfigSchema } from "@/hooks/use-config-schema";
import { useRestart } from "@/api/ws";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import RestartDialog from "@/components/overlay/dialog/RestartDialog";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import SaveAllPreviewPopover from "@/components/overlay/detail/SaveAllPreviewPopover";
@@ -99,7 +99,11 @@ export default function AnimatedCircularProgressBar({
</svg>
<span
data-current-value={currentPercent}
className="duration-[var(--transition-length)] delay-[var(--delay)] absolute inset-0 m-auto size-fit ease-linear animate-in fade-in"
className="absolute inset-0 m-auto size-fit ease-linear animate-in fade-in"
style={{
animationDuration: "var(--transition-length)",
animationDelay: "var(--delay)",
}}
>
{currentPercent}%
</span>
+25
View File
@@ -0,0 +1,25 @@
import { createContext } from "react";
export interface AuthState {
user: { username: string; role: string | null } | null;
allowedCameras: string[];
isLoading: boolean;
isAuthenticated: boolean; // true if auth is required
}
interface AuthContextType {
auth: AuthState;
login: (user: AuthState["user"]) => void;
logout: () => void;
}
export const AuthContext = createContext<AuthContextType>({
auth: {
user: null,
allowedCameras: [],
isLoading: true,
isAuthenticated: false,
},
login: () => {},
logout: () => {},
});
@@ -1,30 +1,7 @@
import axios from "axios";
import { createContext, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import useSWR from "swr";
interface AuthState {
user: { username: string; role: string | null } | null;
allowedCameras: string[];
isLoading: boolean;
isAuthenticated: boolean; // true if auth is required
}
interface AuthContextType {
auth: AuthState;
login: (user: AuthState["user"]) => void;
logout: () => void;
}
export const AuthContext = createContext<AuthContextType>({
auth: {
user: null,
allowedCameras: [],
isLoading: true,
isAuthenticated: false,
},
login: () => {},
logout: () => {},
});
import { AuthContext, AuthState } from "./auth-context";
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [auth, setAuth] = useState<AuthState>({
+1 -1
View File
@@ -6,7 +6,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import { StatusBarMessagesProvider } from "@/context/statusbar-provider";
import { LanguageProvider } from "./language-provider";
import { StreamingSettingsProvider } from "./streaming-settings-provider";
import { AuthProvider } from "./auth-context";
import { AuthProvider } from "./auth-provider";
type TProvidersProps = {
children: ReactNode;
+28
View File
@@ -0,0 +1,28 @@
import { createContext } from "react";
export type StatusMessage = {
id: string;
text: string;
color?: string;
link?: string;
};
export type StatusMessagesState = {
[key: string]: StatusMessage[];
};
type StatusBarMessagesContextValue = {
messages: StatusMessagesState;
addMessage: (
key: string,
message: string,
color?: string,
messageId?: string,
link?: string,
) => string | undefined;
removeMessage: (key: string, messageId: string) => void;
clearMessages: (key: string) => void;
};
export const StatusBarMessagesContext =
createContext<StatusBarMessagesContextValue | null>(null);
+4 -33
View File
@@ -1,42 +1,13 @@
import { useState, ReactNode, useCallback, useMemo } from "react";
import {
createContext,
useState,
ReactNode,
useCallback,
useMemo,
} from "react";
export type StatusMessage = {
id: string;
text: string;
color?: string;
link?: string;
};
export type StatusMessagesState = {
[key: string]: StatusMessage[];
};
StatusBarMessagesContext,
StatusMessagesState,
} from "@/context/statusbar-context";
type StatusBarMessagesProviderProps = {
children: ReactNode;
};
type StatusBarMessagesContextValue = {
messages: StatusMessagesState;
addMessage: (
key: string,
message: string,
color?: string,
messageId?: string,
link?: string,
) => string | undefined;
removeMessage: (key: string, messageId: string) => void;
clearMessages: (key: string) => void;
};
export const StatusBarMessagesContext =
createContext<StatusBarMessagesContextValue | null>(null);
export function StatusBarMessagesProvider({
children,
}: StatusBarMessagesProviderProps) {
+18 -18
View File
@@ -48,126 +48,126 @@ html {
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("../fonts/Inter-Thin.woff2") format("woff2");
src: url("./assets/fonts/Inter-Thin.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("../fonts/Inter-ThinItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-ThinItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("../fonts/Inter-ExtraLight.woff2") format("woff2");
src: url("./assets/fonts/Inter-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("../fonts/Inter-ExtraLightItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-ExtraLightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("../fonts/Inter-Light.woff2") format("woff2");
src: url("./assets/fonts/Inter-Light.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("../fonts/Inter-LightItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-LightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/Inter-Regular.woff2") format("woff2");
src: url("./assets/fonts/Inter-Regular.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("../fonts/Inter-Italic.woff2") format("woff2");
src: url("./assets/fonts/Inter-Italic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("../fonts/Inter-Medium.woff2") format("woff2");
src: url("./assets/fonts/Inter-Medium.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("../fonts/Inter-MediumItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-MediumItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/Inter-SemiBold.woff2") format("woff2");
src: url("./assets/fonts/Inter-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("../fonts/Inter-SemiBoldItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/Inter-Bold.woff2") format("woff2");
src: url("./assets/fonts/Inter-Bold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("../fonts/Inter-BoldItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-BoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("../fonts/Inter-ExtraBold.woff2") format("woff2");
src: url("./assets/fonts/Inter-ExtraBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("../fonts/Inter-ExtraBoldItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-ExtraBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("../fonts/Inter-Black.woff2") format("woff2");
src: url("./assets/fonts/Inter-Black.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("../fonts/Inter-BlackItalic.woff2") format("woff2");
src: url("./assets/fonts/Inter-BlackItalic.woff2") format("woff2");
}
.react-resizable-handle {
+2 -3
View File
@@ -61,9 +61,8 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import PreviewPlayer, {
PreviewController,
} from "@/components/player/PreviewPlayer";
import PreviewPlayer from "@/components/player/PreviewPlayer";
import { PreviewController } from "@/components/player/PreviewController";
import SummaryTimeline from "@/components/timeline/SummaryTimeline";
import { RecordingStartingPoint } from "@/types/record";
import VideoControls from "@/components/player/VideoControls";
+2 -3
View File
@@ -3,9 +3,8 @@ import ReviewFilterGroup from "@/components/filter/ReviewFilterGroup";
import DebugReplayDialog from "@/components/overlay/DebugReplayDialog";
import ExportDialog from "@/components/overlay/ExportDialog";
import ActionsDropdown from "@/components/overlay/ActionsDropdown";
import PreviewPlayer, {
PreviewController,
} from "@/components/player/PreviewPlayer";
import PreviewPlayer from "@/components/player/PreviewPlayer";
import { PreviewController } from "@/components/player/PreviewController";
import { DynamicVideoController } from "@/components/player/dynamic/DynamicVideoController";
import DynamicVideoPlayer from "@/components/player/dynamic/DynamicVideoPlayer";
import QualitySelector from "@/components/player/QualitySelector";
@@ -58,7 +58,7 @@ import {
import type { ProfileState } from "@/types/profile";
import { getProfileColor } from "@/utils/profileColors";
import { isReplayCamera } from "@/utils/cameraUtil";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import { cn } from "@/lib/utils";
import {
Select,
@@ -12,7 +12,7 @@ import { toast } from "sonner";
import { Separator } from "@/components/ui/separator";
import { Link } from "react-router-dom";
import { LuExternalLink } from "react-icons/lu";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import {
Select,
SelectContent,
@@ -14,7 +14,7 @@ import { useCallback, useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import axios from "axios";
import { toast } from "sonner";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { StatusBarMessagesContext } from "@/context/statusbar-context";
import { cn } from "@/lib/utils";
type RegionGridSettingsViewProps = {